0x07 소프트웨어 보안 3 (Advanced Software Security)
1. DEP 복습 및 한계
1.1 DEP(Data Execution Prevention) 개요
하드웨어 지원 (64비트 x86):
• NX (No-eXecute) 비트 도입
• 페이지 테이블 엔트리 플래그:
- P bit: 페이지 접근 가능 여부
- R/W bit: 페이지 수정 가능 여부
- XD bit: 페이지 코드 실행 가능 여부 (Execute Disable)
1.2 W⊕X 정책 적용
운영체제의 메모리 보호:
0x00000000
├─ .text : R-X (읽기, 실행 가능)
├─ .data : RW- (읽기, 쓰기 가능)
├─ .bss : RW- (읽기, 쓰기 가능)
├─ heap : RW- (읽기, 쓰기 가능) ← 실행 불가
└─ stack : RW- (읽기, 쓰기 가능) ← 실행 불가
0xFFFFFFFF
예외 사항:
• JIT (Just-In-Time Compilation): JavaScript 등
1.3 스택 기반 코드 주입 공격의 종료
DEP 적용 전: 스택에 셸코드 주입 → 실행 → 공격 성공
DEP 적용 후: 스택에 셸코드 주입 → 실행 시도 → Segmentation Fault
결과: 스택 기반 코드 주입 공격 완전 차단
2. 해커의 대응: 코드 재사용 공격
2.1 공격자의 새로운 전략
DEP 도입 (1997년) → 스택 실행 불가 → 코드 주입 불가능
↓
공격자의 통찰: "기존 코드를 재사용하자!"
↓
코드 재사용 공격 (Code Reuse Attack) 개발
💡 핵심 아이디어: 새로운 코드를 주입할 수 없다면, 이미 메모리에 존재하는 합법적인 코드를 악용하자.
3. ret2libc 공격
3.1 ret2libc 기본 개념
ret2libc (Return-to-libc):
• 최초의 코드 재사용 공격
• 목표: C 라이브러리 함수 악용
• 원리: 버퍼 오버플로우로 libc 함수 호출
주요 타겟 함수들
system("path"): 셸 명령 실행open("path", flags): 파일 열기read("fd", buf, sz): 파일 읽기
3.2 LIBC 라이브러리
LIBC (C 라이브러리):
• 대부분 프로그램이 공통으로 사용하는 표준 라이브러리
• printf, malloc, system 등 "내장" 함수들의 실제 구현체
• 거의 모든 프로그램에 링크되어 있음
공격에 유용한 함수들
- EXEC 계열:
execl,execlp,execve- 새 파일 실행 - system(): 주어진 셸 명령 실행
- mprotect(): 메모리 권한 변경
- mmap(): 메모리 매핑
3.3 ret2libc 공격 구조
기본 공격 스택 레이아웃
공격 페이로드:
┌─────────────────┐ ← 높은 주소
│ P │
├─────────────────┤
│ Q │
├─────────────────┤
│ "/bin/sh" │ ← 주입된 문자열
├─────────────────┤
│ Overwritten │ ← 덮어씀
├─────────────────┤
│ Addr of system()│ ← Return Address
├─────────────────┤
│ Dummy Ret Addr │ ← system() 반환 주소
├─────────────────┤
│ PTR to "/bin/sh"│ ← system() 인자
└─────────────────┘ ← ESP (낮은 주소)
3.4 x86 호출 규약 복습
함수 호출 과정
void foo() {
bar(a, b, c);
}
void bar() {
char buf[40];
return;
}
어셈블리 코드
foo():
push c ; 3번째 인자
push b ; 2번째 인자
push a ; 1번째 인자
call bar ; bar() 호출
bar():
push ebp ; 함수 프롤로그
mov ebp, esp
sub esp, 0x28
함수 진입 시 스택 구조
┌─────────────────┐
│ Return Address │ ← 함수 종료 후 돌아갈 주소
├─────────────────┤
│ A (1st arg) │ ← 첫 번째 인자
├─────────────────┤
│ B (2nd arg) │ ← 두 번째 인자
├─────────────────┤
│ C (3rd arg) │ ← 세 번째 인자
└─────────────────┘ ← ESP
3.5 ret2libc 실행 과정
1단계: 취약 함수에서 반환
victim_func():
...
ret ← EIP가 여기 위치
2단계: system() 함수 진입
스택 상태:
┌─────────────────┐
│ Addr of system()│ ← 덮어쓴 Return Address
├─────────────────┤
│ Dummy Ret Addr │ ← system() 종료 후 가는 곳
├─────────────────┤
│ PTR to "/bin/sh"│ ← system() 인자
└─────────────────┘ ← ESP
system() 관점에서:
• Return Address = Dummy Ret Addr
• 첫 번째 인자 = PTR to "/bin/sh"
3.6 다중 함수 호출
연속된 libc 함수 호출
스택 구조:
┌─────────────────┐
│ 1st LIBC Func │ ← 첫 번째 함수 주소
├─────────────────┤
│ 2nd LIBC Func │ ← 두 번째 함수 주소 (첫 번째 반환 주소)
├─────────────────┤
│ Arg to 1st │ ← 첫 번째 함수 인자
├─────────────────┤
│ Arg to 2nd │ ← 두 번째 함수 인자
└─────────────────┘
스택 포인터 조정
문제: 첫 번째 함수가 여러 인자를 가질 때 스택 정렬 문제
해결책: 스택 포인터 리프팅 (Stack Pointer Lifting)
; 프로그램 어딘가에 있는 가젯
pop
pop
ret
복잡한 다중 호출 예시
스택 레이아웃:
┌─────────────────┐
│ Addr of open() │ ← 첫 번째 함수
├─────────────────┤
│ Addr of POP/POP/│ ← 스택 정리 가젯
│ RET │
├─────────────────┤
│ 1st ARG for open│ ← open() 첫 번째 인자
├─────────────────┤
│ 2nd ARG for open│ ← open() 두 번째 인자
├─────────────────┤
│ Addr of read() │ ← 두 번째 함수
├─────────────────┤
│ Dummy Ret Addr │
├─────────────────┤
│ 1st ARG for read│ ← read() 첫 번째 인자
└─────────────────┘
실행 흐름:
open(path, flags)호출- POP/POP/RET 가젯 실행으로 스택 정리
read(fd, buf, count)호출
💡 추가 정보: 스택 포인터 리프팅은 ROP 공격의 기초가 되는 기법으로, 함수 호출 후 스택을 올바른 상태로 복원하는 역할을 합니다.
4. Return-Oriented Programming (ROP)
4.1 ROP의 진화
ret2libc의 한계:
• libc 함수들만 사용 가능
• 제한적인 기능
ROP의 등장:
• 임의의 코드 조각 재사용
• 튜링 완전한 프로그래밍 가능
4.2 가젯 (Gadget) 개념
가젯의 정의
가젯 (Gadget):
• ret 명령어로 끝나는 짧은 코드 조각
• 기존 프로그램의 코드를 재해석하여 생성
• 각 가젯은 작은 작업 수행 후 다음 가젯으로 제어 전달
가젯 예시 1: 함수 끝부분 활용
long ab_plus_c(long a, long b, long c) {
return a*b + c;
}
00000000004004d0 <ab_plus_c>:
4004d0: 48 0f af fe imul %rsi,%rdi
4004d4: 48 8d 04 17 lea (%rdi,%rdx,1),%rax ← 가젯 시작
4004d8: c3 retq ← 가젯 끝
가젯 주소: 0x4004d4
기능: rax ← rdi + rdx
가젯 예시 2: 바이트코드 재해석
void setval(unsigned *p) {
*p = 3347663060u; // 0xc78948d4
}
<setval>:
4004d9: c7 07 d4 48 89 c7 movl $0xc78948d4,(%rdi)
4004df: c3 retq
가젯 추출:
주소 0x4004dc부터:
48 89 c7 movq %rax, %rdi ← 가젯!
c3 retq
4.3 ROP 실행 메커니즘
ROP 체인 실행:
┌─────────────────┐
│ Gadget 1 │ ← ret 명령어로 여기로 점프
│ ... │
│ ret │ ← 다음 가젯 주소 pop
├─────────────────┤
│ Gadget 2 │ ← 두 번째 가젯 실행
│ ... │
│ ret │ ← 또 다음 가젯으로
├─────────────────┤
│ Gadget n │ ← 마지막 가젯
│ ... │
│ ret │
└─────────────────┘
↑
%rsp (스택 포인터)
4.4 ROP 체인 패턴화
패턴 1: 레지스터에 값 할당
unsigned int REG = VALUE;
ROP 체인:
┌─────────────────┐
│ &(pop reg; ret;)│ ← 가젯 주소
├─────────────────┤
│ VALUE │ ← 할당할 값
└─────────────────┘
패턴 2: 메모리 쓰기
unsigned int *PTR = ADDR;
*PTR = VALUE;
ROP 체인:
┌─────────────────┐
│ &(pop reg1; ret)│ ← 주소를 reg1에 로드
├─────────────────┤
│ ADDR │
├─────────────────┤
│ &(pop reg2; ret)│ ← 값을 reg2에 로드
├─────────────────┤
│ VALUE │
├─────────────────┤
│&(mov [reg1],reg2│ ← 메모리에 쓰기
│ ; ret) │
└─────────────────┘
패턴 3: 함수 호출 (x86-64)
func(arg1, arg2, ...);
x86-64 호출 규약:
- 첫 번째 인자:
%rdi - 두 번째 인자:
%rsi - 세 번째 인자:
%rdx - 네 번째 인자:
%rcx
ROP 체인:
┌─────────────────┐
│ &(pop rdi; ret) │ ← 첫 번째 인자 설정
├─────────────────┤
│ ARG1 │
├─────────────────┤
│ &(pop rsi; ret) │ ← 두 번째 인자 설정
├─────────────────┤
│ ARG2 │
├─────────────────┤
│ &func │ ← 함수 호출
└─────────────────┘
다중 함수 호출
func1(arg1, arg2);
func2(arg1);
ROP 체인:
┌─────────────────┐
│ &(pop rdi; ret) │ ← func1 첫 번째 인자
├─────────────────┤
│ ARG1 of func1 │
├─────────────────┤
│ &(pop rsi; ret) │ ← func1 두 번째 인자
├─────────────────┤
│ ARG2 of func1 │
├─────────────────┤
│ &func1 │ ← func1 호출
├─────────────────┤
│ &(pop rdi; ret) │ ← func2 첫 번째 인자
├─────────────────┤
│ ARG1 of func2 │
├─────────────────┤
│ &func2 │ ← func2 호출
└─────────────────┘
5. 주소 공간 레이아웃 무작위화 (ASLR)
5.1 ASLR 개념과 목적
ASLR (Address Space Layout Randomization):
• 코드 재사용 공격은 함수/가젯 주소를 알아야 함
• ASLR은 세그먼트의 기본 주소를 무작위화
• 공격자가 정확한 주소 예측을 어렵게 만듦
ASLR이 무작위화하는 영역
- 공유 라이브러리:
mylib.so등 - 스택 세그먼트: 지역 변수 영역
- 힙 세그먼트: 동적 할당 메모리
5.2 ASLR 메모리 레이아웃
ASLR 적용 전: ASLR 적용 후:
0x00000000 0x00000000
├─ .text (고정 주소) ├─ .text (고정 주소)
├─ .data (고정 주소) ├─ .data (고정 주소)
├─ libc (고정 주소) ├─ libc (무작위 주소) ← 변화
├─ heap (고정 주소) ├─ heap (무작위 주소) ← 변화
└─ stack (고정 주소) └─ stack (무작위 주소) ← 변화
0xFFFFFFFF 0xFFFFFFFF
5.3 ASLR 우회 기법
1. 무작위화되지 않은 부분 악용
- Ret2PLT: PLT(Procedure Linkage Table) 활용
- 실행 가능 코드 세그먼트의 가젯들:
.text영역 가젯 사용
2. 메모리 노출 버그와 연계
공격 단계:
1. 메모리 노출 버그로 주소 정보 유출
2. 유출된 주소로 ASLR 오프셋 계산
3. 계산된 주소로 ROP 체인 구성
4. 실제 공격 수행
3. 기타 상황별 우회 기법
- 프로그램별 특성 활용
- 부분 덮어쓰기 공격
- 브루트 포스 공격 (32비트 시스템)
6. PIE (Position Independent Executable)
6.1 PIE 개념
PIE (Position Independent Executable):
• 실행 파일 자체도 메모리 어디든 배치 가능하게 컴파일
• PC 상대 주소 지정 방식 사용
• 컴파일 옵션: gcc -pie -o prog prog.c
6.2 PIE의 구현
절대 주소 → 상대 주소 변환
// 현재 %RIP 값 얻기
0x565566db <+6>: call 0x56556784 <__x86.get_pc_thunk.di>
// %RIP를 %RDI에 저장
0x56556784 <__x86.get_pc_thunk.di+0>: mov edi, DWORD PTR [esp]
0x56556787 <__x86.get_pc_thunk.di+3>: ret
6.3 ASLR + PIE 조합
완전한 주소 무작위화:
• ASLR: 라이브러리, 스택, 힙 무작위화
• PIE: 실행 파일 코드 영역도 무작위화
• 결과: 모든 메모리 영역이 예측 불가능
6.4 ASLR + PIE 우회
남은 공격 벡터들
- 무작위화되지 않은 부분: 여전히 일부 고정 주소 존재
- 메모리 노출 버그: 더욱 중요해진 정보 유출 공격
- 상황별 우회 기법: 각 프로그램의 특성을 활용한 맞춤형 공격
💡 추가 정보: ASLR과 PIE는 공격을 막는 것이 아니라 어렵게 만드는 방어 기법입니다. 여전히 정보 유출 버그와 결합하면 우회가 가능하므로, 추가적인 방어 기법이 필요합니다.
예상 시험문제
1. ret2libc 공격 원리와 구현
문제: ret2libc 공격이 DEP를 우회하는 원리를 설명하고, system("/bin/sh")를 호출하는 공격 페이로드의 스택 구조를 그려서 설명하시오.
모범답안:
DEP 우회 원리:
- DEP는 데이터 영역(스택, 힙)에서 코드 실행을 금지
- ret2libc는 새로운 코드를 주입하지 않고 기존 라이브러리 함수 재사용
- 이미 실행 가능한 영역(.text)에 있는 libc 함수들을 악용
스택 구조:
공격 전 정상 스택: 공격 후 조작된 스택:
┌─────────────────┐ ┌─────────────────┐
│ Return Address │ │ &system() │ ← 덮어쓴 반환 주소
├─────────────────┤ ├─────────────────┤
│ Saved EBP │ │ Dummy Address │ ← system() 종료 후 주소
├─────────────────┤ ├─────────────────┤
│ buffer[128] │ │ &"/bin/sh" │ ← system() 인자
└─────────────────┘ └─────────────────┘
문자열 영역:
"/bin/sh\0" ← 버퍼 오버플로우로 주입된 문자열
실행 과정:
- 버퍼 오버플로우로 Return Address를
system()주소로 덮어씀 - 함수 종료 후
system()함수로 점프 system()은 스택에서 인자"/bin/sh"를 찾아 셸 실행- 공격자가 시스템 권한 획득
2. ROP 가젯의 개념과 활용
문제: ROP 공격에서 가젯(Gadget)의 개념을 설명하고, 다음 코드에서 사용 가능한 가젯을 찾아 그 기능을 설명하시오.
004004d0: 48 0f af fe imul %rsi,%rdi
004004d4: 48 8d 04 17 lea (%rdi,%rdx,1),%rax
004004d8: c3 retq
004004d9: c7 07 d4 48 89 c7 movl $0xc78948d4,(%rdi)
004004df: c3 retq
모범답안:
가젯의 개념:
- ROP에서 사용하는 짧은 코드 조각
- 반드시
ret명령어로 끝남 - 기존 프로그램의 코드를 재해석하여 생성
- 각 가젯은 작은 연산 수행 후 다음 가젯으로 제어 전달
발견된 가젯들:
가젯 1: 주소 0x4004d4
48 8d 04 17 lea (%rdi,%rdx,1),%rax
c3 retq
- 기능:
%rax ← %rdi + %rdx(두 레지스터 값 더하기) - 용도: 주소 계산, 산술 연산
가젯 2: 주소 0x4004dc (바이트코드 재해석)
48 89 c7 movq %rax, %rdi
c3 retq
- 기능:
%rdi ← %rax(레지스터 간 값 복사) - 용도: 함수 인자 준비 (x86-64에서 첫 번째 인자는 %rdi)
활용 예시:
ROP 체인으로 func(value) 호출:
┌─────────────────┐
│ &(pop rax; ret) │ ← value를 %rax에 로드
├─────────────────┤
│ value │
├─────────────────┤
│ 0x4004dc │ ← %rax → %rdi 복사 가젯
├─────────────────┤
│ &func │ ← 함수 호출
└─────────────────┘
3. ASLR의 동작 원리와 한계
문제: ASLR(Address Space Layout Randomization)이 코드 재사용 공격을 방어하는 원리를 설명하고, ASLR의 한계점과 우회 방법들을 서술하시오.
모범답안:
ASLR 방어 원리:
공격자의 필요 조건: 정확한 함수/가젯 주소 정보
ASLR의 대응: 메모리 세그먼트 기본 주소 무작위화
무작위화 대상:
• 공유 라이브러리 (libc.so): 매 실행시 다른 주소에 로드
• 스택 세그먼트: 지역 변수와 함수 호출 스택 위치 변경
• 힙 세그먼트: malloc() 등 동적 할당 메모리 위치 변경
결과: 공격자가 ret2libc, ROP 체인 구성 시 주소 예측 불가능
ASLR의 한계점:
-
부분적 무작위화:
- 실행 파일 자체(.text 영역)는 기본적으로 고정
- PLT(Procedure Linkage Table)는 여전히 예측 가능
-
엔트로피 부족:
- 32비트: 무작위화 범위가 작아 브루트포스 가능
- 주소 정렬 요구사항으로 실제 엔트로피 감소
-
정보 유출 취약점에 무력:
- 메모리 노출 버그 시 무작위화 무효화
- 포인터 하나만 유출되어도 전체 주소 계산 가능
주요 우회 방법들:
- 정보 유출 버그 활용:
공격 단계:
1. 메모리 읽기 버그로 libc 함수 주소 유출
2. 알려진 오프셋으로 다른 함수 주소 계산
3. 계산된 주소로 ROP 체인 구성
4. 실제 익스플로잇 수행
-
부분 덮어쓰기:
- 주소의 하위 바이트만 덮어써서 확률적 공격
- 여러 번 시도하여 성공 확률 높임
-
고정 영역 활용:
- PIE 미적용 시 .text 영역 가젯 사용
- PLT 테이블의 고정 주소 활용
4. PIE와 ASLR의 차이점
문제: PIE(Position Independent Executable)와 ASLR의 차이점을 설명하고, 두 기법을 모두 적용했을 때의 보안 효과를 분석하시오.
모범답안:
PIE와 ASLR의 차이점:
| 구분 | ASLR | PIE |
|---|---|---|
| 대상 | 라이브러리, 스택, 힙 | 실행 파일 자체 |
| 적용 시점 | 런타임 (OS 기능) | 컴파일 타임 (컴파일러 옵션) |
| 주소 지정 | 기존 절대 주소 유지 | PC 상대 주소로 변환 |
| 무작위화 영역 | 동적 세그먼트만 | .text, .data 등 모든 영역 |
개별 적용 시 한계:
ASLR만 적용:
보호됨: libc, 스택, 힙
취약점: .text 영역 (실행 파일 코드)
공격 가능: .text 영역의 가젯을 이용한 ROP
PIE만 적용:
보호됨: 실행 파일 코드 영역
취약점: 라이브러리, 스택, 힙
공격 가능: 고정 주소의 libc 함수 호출
ASLR + PIE 조합 효과:
완전한 주소 무작위화 달성:
┌─────────────────┐
│ .text (무작위) │ ← PIE로 보호
├─────────────────┤
│ .data (무작위) │ ← PIE로 보호
├─────────────────┤
│ libc (무작위) │ ← ASLR로 보호
├─────────────────┤
│ heap (무작위) │ ← ASLR로 보호
├─────────────────┤
│ stack (무작위) │ ← ASLR로 보호
└─────────────────┘
결과: 모든 메모리 영역이 예측 불가능
남은 공격 벡터:
- 정보 유출 공격: 여전히 가장 효과적인 우회 방법
- 부분 덮어쓰기: 확률적 공격 여전히 가능
- 사이드 채널 공격: 캐시, 타이밍 등을 통한 주소 추측
5. 코드 재사용 공격의 진화 과정
문제: 스택 기반 코드 주입 공격부터 ROP 공격까지의 진화 과정을 시간순으로 정리하고, 각 단계에서 도입된 방어 기법과 그에 대한 공격자의 대응을 설명하시오.
모범답안:
공격과 방어의 진화 과정:
1단계: 스택 기반 코드 주입 시대 (1980년대-1990년대)
공격 기법:
• 버퍼 오버플로우로 스택에 셸코드 주입
• Return Address를 셸코드 위치로 덮어씀
• NOP Sled로 정확한 주소 예측 문제 해결
방어 기법: 없음 (무방비 상태)
2단계: DEP 도입 (1997년-2000년대)
방어 기법: DEP (Data Execution Prevention)
• W⊕X 정책: 쓰기 가능한 메모리는 실행 불가
• 하드웨어 지원 (NX 비트)
• 스택, 힙에서 코드 실행 금지
효과: 스택 기반 코드 주입 공격 완전 차단
3단계: ret2libc 등장 (2000년대 초)
공격자 대응: ret2libc 공격
• 새 코드 주입 대신 기존 라이브러리 함수 재사용
• system(), execve() 등 유용한 함수 호출
• 스택 조작으로 함수 인자 전달
특징: DEP 완전 우회, 코드 실행 없이 시스템 장악
4단계: ASLR 도입 (2000년대 중반)
방어 기법: ASLR (Address Space Layout Randomization)
• 라이브러리, 스택, 힙 주소 무작위화
• 공격자의 주소 예측 능력 제한
• ret2libc 공격 어려움 증가
한계: 실행 파일 자체는 여전히 고정 주소
5단계: ROP 공격 완성 (2000년대 후반)
공격자 대응: Return-Oriented Programming
• 임의의 코드 조각(가젯) 재사용
• 튜링 완전한 프로그래밍 능력
• ASLR 우회를 위한 정보 유출 공격 결합
혁신점:
• 단순한 함수 호출을 넘어 복잡한 로직 구현 가능
• 가젯 체이닝으로 임의 연산 수행
6단계: PIE 추가 (2010년대)
방어 기법: PIE (Position Independent Executable)
• 실행 파일 코드 영역도 무작위화
• ASLR + PIE로 완전한 주소 무작위화
• 모든 메모리 영역 예측 불가능
현재 상황:
• 여전히 정보 유출 공격으로 우회 가능
• 더욱 정교한 공격 기법 필요
• 하드웨어 기반 새로운 방어 기법 연구 중
핵심 패턴:
방어 기법 도입 → 공격자 적응 → 새로운 공격 기법 → 강화된 방어 기법 → ...
특징:
• 각 방어 기법은 특정 공격만 차단
• 공격자는 항상 새로운 우회 방법 개발
• 완벽한 방어는 존재하지 않음
• 다층 방어(Defense in Depth) 전략 필요
6. ROP 체인 설계 문제
문제: 다음 조건을 만족하는 ROP 체인을 설계하시오.
mprotect(addr, len, PROT_READ|PROT_WRITE|PROT_EXEC)호출addr = 0x601000,len = 0x1000,prot = 7- 사용 가능한 가젯:
pop rdi; ret,pop rsi; ret,pop rdx; ret
모범답안:
x86-64 호출 규약:
함수 인자 전달 순서:
1st 인자: %rdi
2nd 인자: %rsi
3rd 인자: %rdx
mprotect() 함수 시그니처:
int mprotect(void *addr, size_t len, int prot);
// addr = 0x601000 (메모리 주소)
// len = 0x1000 (페이지 크기 4KB)
// prot = 7 (READ|WRITE|EXEC)
ROP 체인 설계:
스택 레이아웃:
┌─────────────────┐
│ &(pop rdi; ret) │ ← 첫 번째 인자 설정 가젯
├─────────────────┤
│ 0x601000 │ ← addr 값 (첫 번째 인자)
├─────────────────┤
│ &(pop rsi; ret) │ ← 두 번째 인자 설정 가젯
├─────────────────┤
│ 0x1000 │ ← len 값 (두 번째 인자)
├─────────────────┤
│ &(pop rdx; ret) │ ← 세 번째 인자 설정 가젯
├─────────────────┤
│ 7 │ ← prot 값 (세 번째 인자)
├─────────────────┤
│ &mprotect │ ← mprotect() 함수 호출
└─────────────────┘
실행 흐름:
pop rdi; ret실행:%rdi ← 0x601000pop rsi; ret실행:%rsi ← 0x1000pop rdx; ret실행:%rdx ← 7mprotect()호출:mprotect(0x601000, 0x1000, 7)
결과:
- 주소 0x601000부터 4KB 영역이 읽기/쓰기/실행 가능하게 변경
- 이후 해당 영역에 셸코드 주입하여 실행 가능
- DEP 우회 완료
핵심 요약
- DEP 도입으로 스택 기반 코드 주입 공격이 차단되었지만, 공격자들은 코드 재사용 공격으로 대응
- ret2libc는 최초의 코드 재사용 공격으로, 기존 라이브러리 함수를 악용하여 DEP 우회
- **ROP(Return-Oriented Programming)**는 임의의 코드 가젯을 체이닝하여 튜링 완전한 프로그래밍 실현
- 가젯은 ret로 끝나는 짧은 코드 조각으로, 기존 프로그램 코드를 재해석하여 생성
- ASLR은 메모리 주소 무작위화로 코드 재사용 공격을 어렵게 만들지만 완전히 막지는 못함
- PIE + ASLR 조합으로 모든 메모리 영역 무작위화 가능하지만 정보 유출 공격으로 여전히 우회 가능
- 공격과 방어의 진화는 지속적인 군비 경쟁 양상으로, 각 방어 기법마다 새로운 우회 기법 등장
- 다층 방어 전략이 필요하며, 단일 방어 기법으로는 완벽한 보안 불가능
💡 중요: 코드 재사용 공격은 현대 소프트웨어 보안의 핵심 위협으로, 메모리 안전성과 주소 무작위화만으로는 완전한 방어가 어렵습니다. Control Flow Integrity(CFI) 등 추가적인 방어 기법과 함께 메모리 안전 언어로의 전환이 근본적 해결책으로 주목받고 있습니다.