버퍼 오버플로우는 주로 C/C++ 언어에서의 경계 검사 부재를 이용해,
스택 프레임을 덮어쓰고 RET 주소를 조작하여 공격자가 원하는 코드를 실행하는 공격입니다.
이를 막기 위해 운영체제, 컴파일러, 하드웨어 등 다양한 계층에서 다음과 같은 보호기법이 사용됩니다.
✅ 주요 오버플로우 방어 기법 8가지 (정리표 먼저 보기)
기법명 | 보호 대상 | 주체 | 설명 |
---|
Stack Canary | 스택 | 런타임 | RET 주소 앞에 감시값 삽입 |
StackGuard | 스택 | 컴파일러 | Stack Canary를 구현한 기법 |
StackShield | 스택 | 컴파일러 | RET 주소를 별도 저장해 보호 |
ASLR | 메모리 전체 | 운영체제 | 주소 무작위화 |
DEP/NX bit | 메모리 영역 | 하드웨어+OS | 실행 불가 영역 지정 |
PIE | 실행파일 | 컴파일러+OS | 코드 영역 자체도 랜덤화 |
Fortify Source | 함수 호출 | 컴파일러 | 위험 함수 감지 및 보호 |
CFI | 흐름 제어 | 컴파일러+OS | 비정상 함수 호출 차단 |
1. Stack Canary
- 위치: 스택 내부
- 도입 계층: 런타임
- 목적: 스택 프레임 보호
✅ 동작 원리
- 함수 호출 시, 지역 변수와 RET 주소 사이에 Canary 값 삽입
- 함수 종료 시 Canary 값이 바뀌었는지 확인
- 값이 바뀌었으면 스택 오버플로우 공격으로 간주 후 종료
✅ 유형 (GCC 기준)
-fstack-protector
: 기본 보호 (대상 함수 제한)
-fstack-protector-strong
: 더 많은 함수 보호
-fstack-protector-all
: 모든 함수 보호
2. StackGuard
- Stack Canary 기법의 구현체 중 하나
- GCC에 통합된 형태로 구현되었고,
-fstack-protector
옵션이 사실상 StackGuard입니다.
✅ 핵심 특징
- 스택 프레임마다 랜덤 Canary 삽입
- Canary가 손상되면
__stack_chk_fail()
호출로 비정상 종료
3. StackShield
- RET 주소를 별도의 안전한 공간에 백업해 두는 방식
✅ 동작 방식
- 함수 진입 시 RET 주소를 StackShield Table에 저장
- 함수 리턴 시, 원래 스택에 있는 RET 주소와 비교
- 다르면 프로그램 종료
✅ 장점
- Canary처럼 변조 감지보다 RET 보호를 직접적으로 수행
- Canary 우회 공격에 강함
❌ 단점
- 구현 복잡도 ↑
- GCC 최신 버전에서는 기본 미지원
4. ASLR (Address Space Layout Randomization)
- 실행 중인 프로그램의 메모리 구조를 매번 다르게 배치하는 OS 보호 기법
✅ 보호 대상
- 스택, 힙, 라이브러리, 코드 등 모든 주소 공간
✅ 효과
- 공격자가 정확한 주소를 알 수 없어, RET 덮기 실패 확률 ↑
- 쉘코드 주소 예측 불가능
❌ 우회 가능성
- 정보 유출(vulnerability leak)이 발생하면 ASLR 무력화 가능
- 일부 환경(Wine, 일부 Windows 환경)에서는 제한적으로 동작
5. DEP / NX-bit (Data Execution Prevention)
- 데이터 영역을 실행하지 못하게 막는 하드웨어+OS 보안 기술
✅ 주요 원리
- 스택/힙/데이터 영역에 NX(Non-Executable) 속성 부여
- 쉘코드를 올려도 실행 불가
✅ 시스템 적용 예
- Windows:
bcdedit /set nx AlwaysOn
- Linux:
exec-shield
, PaX
, grsecurity
❌ 우회 가능성
- ROP (Return-Oriented Programming) 등의 기법으로 우회 가능
6. PIE (Position Independent Executable)
실행 파일 자체를 ASLR처럼 주소 무작위화 가능하게 컴파일하는 방식
✅ PIE vs Non-PIE
항목 | PIE | Non-PIE |
---|
주소 | 매 실행 시 바뀜 | 고정 주소 |
보안 | ASLR 완전 적용 | ASLR 제한 적용 |
✅ 사용법 (GCC)
gcc -fPIE -pie hello.c -o hello
→ 실행할 때마다 .text
영역 주소도 바뀜
7. Fortify Source
위험 함수(strcpy
, gets
등) 사용 시 자동으로 보안 기능 추가
✅ 특징
- 컴파일러 레벨 보호
- 컴파일 시 버퍼 크기를 비교해서 경고 또는 종료 처리
✅ 사용법
gcc -D_FORTIFY_SOURCE=2 -O2 example.c -o example
최적화(-O2 이상) 옵션과 함께 사용해야 활성화됨
8. CFI (Control Flow Integrity)
프로그램 흐름이 예상된 제어 흐름을 벗어나지 않도록 감시하는 기술
✅ 원리
- 함수 포인터, RET 주소, vtable 등의 호출 흐름을 화이트리스트 기반으로 검증
- 비정상 호출 시 차단
✅ 적용 예
- LLVM의 CFI, MS의 Control Flow Guard(CFG), Google의 IFCC
✅ 시각 요약: 스택 보호 중심 구조도
[ 지역 변수 ] ← 공격자 입력 가능
[ Stack Canary ] ← 변조 감시
[ RET 주소 ] ← 공격자가 노리는 대상
[ StackShield Table ] ← 백업된 RET
- Canary: 감시
- Shield: 백업
- DEP: 실행 차단
- ASLR: 주소 예측 차단
🔚 마무리 요약표
기법 | 범주 | 주체 | 요약 |
---|
Stack Canary / StackGuard | 스택 보호 | 컴파일러/런타임 | 감시값을 통해 RET 덮기 탐지 |
StackShield | 스택 보호 | 컴파일러 | RET 주소 자체를 별도 저장 |
ASLR | 메모리 난수화 | 운영체제 | 주소 무작위화 |
DEP (NX-bit) | 메모리 실행 차단 | 하드웨어/OS | 스택/힙에서 코드 실행 차단 |
PIE | 실행파일 난수화 | 컴파일러 | 코드 영역 자체의 위치 무작위화 |
Fortify Source | 함수 호출 보호 | 컴파일러 | 위험 함수 감지 및 대체 |
CFI | 흐름 보호 | 컴파일러/OS | 비정상 함수 호출 차단 |
9. Stack Canary / StackGuard
🧨 우회기법
방법 | 설명 |
---|
Canary 유출 | 포인터 누출/format string 취약점 등을 이용해 Canary 값을 유출하고 동일하게 덮어씀 |
Partial Overwrite | RET 주소만 정확히 덮고, Canary는 건드리지 않도록 정밀하게 오버플로우 |
Non-RET 공격 | Canary는 RET만 보호하므로, 함수 포인터, GOT, vtable 등 다른 제어 지점 공격 |
🔍 탐지기법
방법 | 설명 |
---|
로그 분석 | __stack_chk_fail() 호출 여부 (SIGABRT 발생) 로그 추적 |
ASAN/UBSAN 사용 | Address Sanitizer 등으로 Canary 손상 여부 추적 가능 |
스택 프레임 분석 | GDB로 함수 리턴 전/후 Canary 값 비교 |
10. StackShield
🧨 우회기법
방법 | 설명 |
---|
간접 호출 우회 | StackShield는 직접적인 RET 덮기만 보호하므로, |
함수 포인터, GOT를 노리는 공격은 우회 가능 | |
컴파일러 우회 | StackShield가 적용되지 않은 바이너리나 외부 라이브러리 호출로 우회 |
🔍 탐지기법
방법 | 설명 |
---|
RET 주소 위조 감지 | StackShield Table과 스택의 RET 불일치 확인 |
공격 시 로그/코어덤프 분석 | RET Jump 타겟 주소가 비정상적이면 의심 |
정적 분석 도구 사용 | Ghidra, IDA로 RET 흐름 확인 |
11. ASLR
🧨 우회기법
방법 | 설명 |
---|
Information Leak | 포인터 누수, format string, /proc/self/maps 등으로 주소 유출 후 우회 |
Brute Force | 재시도 가능한 환경에서 반복 실행 (ex: CGI 서비스, 다중 요청 등) |
Relative Addressing | PIE 미적용 바이너리에서 .text 주소는 고정 → 오프셋 계산 가능 |
🔍 탐지기법
방법 | 설명 |
---|
메모리 덤프 분석 | maps 파일이나 core dump에서 base 주소가 변하는지 확인 |
시스템 호출 감시 | mmap , execve 호출 시 주소 랜덤 여부 검사 |
로그 내 실패 반복 확인 | 반복적인 segmentation fault → brute force 추정 가능 |
12. DEP / NX-bit
🧨 우회기법
방법 | 설명 |
---|
ROP (Return-Oriented Programming) | 실행 가능한 기존 코드 조각(gadget)을 연결해 쉘코드 없이 명령 수행 |
JOP (Jump-Oriented Programming) | RET 대신 JMP를 이용한 우회 공격 |
RWX 메모리 확보 | mmap, VirtualAlloc 등을 통해 실행 가능한 메모리 확보 |
🔍 탐지기법
방법 | 설명 |
---|
ROP Chain 감지 | 연속된 RET 패턴, 스택의 gadget 주소 확인 |
메모리 실행 속성 모니터링 | 페이지 속성 변화 감시 (Windows에서는 EMET 등 활용) |
동적 분석 도구 사용 | Cuckoo Sandbox, Valgrind 등으로 메모리 접근 추적 |
13. PIE
🧨 우회기법
방법 | 설명 |
---|
정보 유출 | GOT, 함수 포인터 등으로 base address 추출 |
동일 바이너리 재사용 | PIE 없이 빌드된 구버전 또는 외부 공유 라이브러리 악용 |
Relocation Leak | ELF의 PLT/GOT를 통해 offset 계산 |
🔍 탐지기법
방법 | 설명 |
---|
ELF 헤더 확인 | readelf -h , checksec 명령어로 PIE 적용 여부 확인 |
실행 시 base address 추적 | 여러 번 실행하여 .text 주소 변화 여부 확인 |
정적 분석 도구 | Ghidra에서 PIE 적용된 코드의 주소가 고정되지 않음 확인 가능 |
14. Fortify Source
🧨 우회기법
방법 | 설명 |
---|
보호 대상 함수 외 사용 | 보호되지 않은 memcpy , 직접 포인터 조작으로 우회 |
정적 링크 우회 | 보호되지 않은 라이브러리 직접 링크 |
컴파일 시 우회 | -D_FORTIFY_SOURCE=0 로 비활성화된 바이너리 사용 |
🔍 탐지기법
방법 | 설명 |
---|
소스 코드 분석 | 취약한 함수 사용 여부 탐지 (strcpy 등) |
컴파일 로그 확인 | 보호된 함수는 내부적으로 __chk 함수로 변환됨 |
GDB 디버깅 중 SIGABRT 감지 | fortify 충돌 시 종료됨 |
15. CFI (Control Flow Integrity)
🧨 우회기법
방법 | 설명 |
---|
타입 일치 악용 | 함수 포인터 타입만 맞으면 호출 가능 → 제한적 우회 |
vtable 변조 | C++ 가상 함수 테이블 위조 → 정적 검증 우회 |
해당 영역 제외 빌드 | CFI 적용되지 않은 바이너리 조작 |
🔍 탐지기법
방법 | 설명 |
---|
정적 분석 도구 | Clang CFI, LLVM Pass, BinDiff 등으로 흐름 무결성 분석 |
런타임 모니터링 | CFG 미스매치 로그 추적 (Windows에서는 CFG 로그 발생) |
Fuzzing | 함수 호출 흐름을 무작위로 유도해 미스매치 유도 |
🔚 종합 정리표
기법 | 우회기법 | 탐지기법 |
---|
Stack Canary | Canary 유출, Partial Overwrite | __stack_chk_fail , Canary 값 비교 |
StackShield | RET 이외 제어 공격 | RET 백업/실제 비교 |
ASLR | 주소 유출, Brute Force | Base 주소 변화, maps 확인 |
DEP | ROP, JOP, mmap 사용 | Gadget 감지, 메모리 실행 속성 추적 |
PIE | GOT, PLT leak | checksec , .text 주소 확인 |
Fortify Source | 비보호 함수, 빌드 옵션 우회 | __chk 함수 확인, 소스 분석 |
CFI | 타입 우회, vtable 변조 | 런타임 미스매치, CFG 로그 분석 |