스택 가드
스택 가드(StackGuard)는 스택 버퍼 오버플로우 공격을 막기 위해 함수의 복귀 주소 주변에 카나리(canary) 값을 삽입하고 함수 종료 시 이를 검사하는 컴파일러 기반 방어 기법이다.
스택 가드는 1998년 Crispin Cowan 등이 USENIX Security Symposium에서 발표한 논문 〈StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks〉에서 제안된 기법이다. 이 논문은 스택 가드를 소스 코드 수정 없이 재컴파일만으로 적용할 수 있는 단순한 컴파일러 기법으로 설명하며, 원래 구현은 gcc에 대한 패치 형태였다.[1]
일반적인 스택 기반 버퍼 오버플로우 공격은 함수의 지역 버퍼를 초과해 데이터를 기록하면서 스택 프레임에 저장된 복귀 주소(return address)를 덮어쓰고, 함수 반환 시 공격자가 원하는 코드나 주소로 제어 흐름을 이동시키는 방식이다. 스택 가드는 복귀 주소와 지역 변수 사이에 검사값인 카나리를 배치하여, 복귀 주소가 변경되기 전에 카나리가 먼저 손상되도록 만든다.[2]
스택 가드가 적용된 함수는 대체로 다음과 같은 흐름으로 동작한다.
- 함수 진입 시 함수 프롤로그(prologue)에서 스택 프레임에 카나리 값을 저장한다.
- 지역 변수와 버퍼가 사용되는 동안 정상 실행을 계속한다.
- 함수 종료 직전 함수 에필로그(epilogue)에서 저장된 카나리 값과 기준 카나리 값을 비교한다.
- 값이 일치하면 정상적으로 복귀 주소를 사용해 호출자에게 반환한다.
- 값이 다르면 스택 손상으로 판단하고 프로그램을 중단하거나 오류 처리 루틴으로 이동한다.
스택 가드의 핵심 가정은 전형적인 스택 버퍼 오버플로우가 메모리를 순차적으로 덮어쓰기 때문에 복귀 주소를 바꾸려면 그 앞에 놓인 카나리 값도 함께 훼손된다는 것이다. 따라서 함수가 반환되기 전에 카나리 훼손 여부를 검사하면, 변조된 복귀 주소로 제어가 넘어가기 전에 공격을 차단할 수 있다.[3]
일반적인 스택 프레임 예시 [복귀 주소] [카나리 값] [지역 변수] [버퍼]
카나리(canary)는 스택 손상 여부를 탐지하기 위해 삽입되는 무결성 검사값이다. 명칭은 탄광에서 유독가스 감지를 위해 카나리아 새를 사용하던 관행에서 유래한 비유로, 위험이 치명적인 결과로 이어지기 전에 먼저 이상 징후를 드러내는 값을 뜻한다.
주요 방식은 다음과 같이 구분할 수 있다.
| 방식 | 설명 | 특징 |
|---|---|---|
| 널 카나리 | 문자열 처리 함수가 널 바이트에서 복사를 멈추는 성질을 이용해 널 문자를 포함한 값을 배치한다. | 단순하지만 공격자가 값을 예측하거나 우회할 가능성이 있다. |
| 터미네이터 카나리 | 널 문자, 줄바꿈, 캐리지 리턴, EOF 등 문자열 복사나 입력 처리에서 특별하게 취급되는 바이트를 포함한다. | 문자열 기반 오버플로우에 대한 방어 효과를 노린다. |
| 랜덤 카나리 | 프로그램 시작 시 또는 프로세스 실행 시 난수 기반 카나리를 생성해 사용한다. | 공격자가 카나리 값을 모르면 복귀 주소 변조와 동시에 올바른 카나리를 재현하기 어렵다. |
| 랜덤 XOR 카나리 | 난수 카나리에 복귀 주소나 프레임 정보 등을 XOR해 검사값을 구성한다. | 단순한 카나리 복사뿐 아니라 일부 제어 정보 변조까지 함께 탐지하려는 방식이다. |
초기 StackGuard 논문은 카나리 값이 정적이면 공격자가 값을 추측해 공격 문자열 안에 같은 값을 포함할 수 있으므로, 프로그램 시작 시 난수 카나리를 선택하는 방식을 제시했다.[4]
현대 컴파일러에서는 스택 가드 계열 기능이 보통 스택 보호기(stack protector) 옵션으로 제공된다. GCC는 -fstack-protector 옵션을 통해 취약한 객체가 있는 함수에 가드 변수를 추가하고, 함수 진입 시 초기화한 뒤 함수 종료 시 검사한다. 검사 실패 시 오류 메시지를 출력하고 프로그램을 종료한다.[5]
GCC의 대표적인 관련 옵션은 다음과 같다.
| 옵션 | 설명 |
|---|---|
-fstack-protector |
alloca를 호출하거나 일정 크기 이상의 버퍼를 가진 함수 등 취약할 가능성이 있는 함수에 보호 코드를 삽입한다.
|
-fstack-protector-all |
모든 함수에 스택 보호 코드를 삽입한다. |
-fstack-protector-strong |
일반 -fstack-protector보다 더 넓은 휴리스틱을 사용해 지역 배열이나 지역 프레임 주소 참조가 있는 함수까지 보호 대상으로 포함한다.
|
-fstack-protector-explicit |
명시적으로 stack_protect 속성이 지정된 함수만 보호한다.
|
Clang도 -fstack-protector, -fstack-protector-all, -fstack-protector-strong 옵션을 제공한다. Clang 문서는 스택 보호기가 취약한 함수의 스택 프레임에 가드 값을 추가하고 함수 종료 시 검사하며, 가드 값은 취약한 변수의 버퍼 오버플로우가 복귀 주소를 덮기 전에 먼저 덮어쓰도록 배치되어야 한다고 설명한다.[6]
스택 가드는 스택 기반 버퍼 오버플로우에 대한 대표적인 방어 기법이지만 모든 메모리 손상 공격을 막지는 못한다.
- 카나리 값이 정보 누출 취약점 등을 통해 노출되면 공격자가 올바른 카나리 값을 보존한 채 복귀 주소를 조작할 수 있다.
- 복귀 주소가 아닌 함수 포인터, 구조체 필드, 전역 데이터, 힙 메타데이터 등을 덮어쓰는 공격에는 직접적인 방어 효과가 제한적이다.
- 함수가 보호 대상으로 선택되지 않거나, 최적화와 ABI, 플랫폼 특성에 따라 보호 범위가 달라질 수 있다.
- 카나리는 공격을 사전에 제거하는 기법이 아니라 함수 반환 시점에 손상을 탐지하는 기법이므로, 이미 일부 프로그램 상태가 손상되었을 수 있다.
StackGuard 논문에서도 카나리 방식은 성능 면에서 유리하지만 카나리 값을 추측하거나 우회하려는 공격 가능성이 있으며, 복귀 주소가 아닌 다른 제어 데이터나 전역 데이터 영역을 덮어쓰는 공격에는 별도의 고려가 필요하다고 설명한다.[7]
스택 가드는 단독으로 사용되기보다 다른 메모리 보호 기법과 함께 적용된다.
| 기법 | 역할 |
|---|---|
| ASLR | 주소 공간 배치를 난수화하여 공격자가 코드, 라이브러리, 스택, 힙 주소를 예측하기 어렵게 한다. |
| NX / DEP | 스택이나 힙 같은 데이터 영역에서 코드 실행을 막는다. |
| FORTIFY_SOURCE | 일부 표준 라이브러리 호출에 대해 컴파일 시점 또는 실행 시점 경계 검사를 강화한다. |
| RELRO | ELF 바이너리의 일부 재배치 영역을 읽기 전용으로 만들어 GOT 덮어쓰기 공격 등을 어렵게 한다. |
| 스택 클래시 보호 | 큰 스택 할당이 운영체제의 스택 가드 페이지를 건너뛰는 공격을 완화한다. |
GCC의 -fhardened 옵션은 현재 GNU/Linux 대상에서 -fstack-protector-strong, -fstack-clash-protection, PIE, RELRO 등 여러 보안 강화 옵션을 함께 활성화하는 방식으로 제공된다.[8]
- ↑ 〈StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks〉, USENIX, https://www.usenix.org/conference/7th-usenix-security-symposium/stackguard-automatic-adaptive-detection-and-prevention, 확인일: 2026-06-08
- ↑ 〈StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks〉, USENIX PDF, https://www.usenix.org/legacy/publications/library/proceedings/sec98/full_papers/cowan/cowan.pdf, 확인일: 2026-06-08
- ↑ 〈StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks〉, USENIX PDF, https://www.usenix.org/legacy/publications/library/proceedings/sec98/full_papers/cowan/cowan.pdf, 확인일: 2026-06-08
- ↑ 〈StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks〉, USENIX PDF, https://www.usenix.org/legacy/publications/library/proceedings/sec98/full_papers/cowan/cowan.pdf, 확인일: 2026-06-08
- ↑ 〈Instrumentation Options〉, GCC Online Documentation, https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html, 확인일: 2026-06-08
- ↑ 〈Clang command line argument reference〉, Clang Documentation, https://clang.llvm.org/docs/ClangCommandLineReference.html, 확인일: 2026-06-08
- ↑ 〈StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks〉, USENIX PDF, https://www.usenix.org/legacy/publications/library/proceedings/sec98/full_papers/cowan/cowan.pdf, 확인일: 2026-06-08
- ↑ 〈Instrumentation Options〉, GCC Online Documentation, https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html, 확인일: 2026-06-08
