Skip to main content

0x07 소프트웨어 보안 4 (Temporal Memory Safety & PLT/GOT)

1. 힙 기반 공격의 등장

1.1 공격 방향의 전환

스택 공격의 어려움 증가:
• DEP (Data Execution Prevention)
• Stack Canary
• ASLR (Address Space Layout Randomization)

공격자의 대응:
스택 → 힙 기반 공격으로 전환

1.2 힙 공격의 주요 대상

웹 브라우저 및 컴포넌트

힙 사용량이 많은 프로그램들:
• 웹 브라우저 (Chrome, Firefox, Edge)
• 동적 메모리 할당 (malloc()/new) 집약적 사용

취약한 서드파티 컴포넌트:
• Adobe Flash (대부분 사라짐)
• Java 플러그인 (인기 감소)
• ActiveX (여전히 위험)

1.3 스택 vs 힙 비교

메모리 특성 비교

구분스택 (Stack)힙 (Heap)
위치고정 메모리 위치런타임 동적 할당
주소 지정스택포인터 + 상대오프셋포인터를 통한 접근
레이아웃컴파일러가 미리 결정프로그래머가 런타임 제어
용도지역변수, 반환주소, 함수 인자객체, 큰 버퍼
특성자동 관리수동 관리 (C/C++)
공유스레드별 독립스레드 간 공유

메모리 레이아웃

메모리 배치 (32비트 기준):
0x00000000
├─ program code (코드 영역)
├─ data (전역 변수)
├─ bss (초기화되지 않은 전역 변수)
├─ heap ↓ (아래쪽으로 성장)

│ ... 빈 공간 ...

└─ stack ↑ (위쪽으로 성장)
0xFFFFFFFF

예시 코드:
int bss_var; // bss 영역
int data_var = VALUE; // data 영역
int foo() {
int stack_var; // stack 영역
void* ptr = malloc(128); // heap 영역
}

2. 힙 오버플로우 공격

2.1 힙 오버플로우 기본 개념

힙 오버플로우:
• 스택 버퍼 오버플로우와 유사한 원리
• 힙에 할당된 버퍼의 경계를 넘어서는 쓰기

스택과의 차이점:
• 힙 카나리는 존재하지 않음
• 스택 카나리처럼 모든 힙 쓰기를 검사하는 것은 성능상 불가능
• 반환 주소가 힙에 없음

2.2 공격 대상 찾기

핵심 질문: "무엇을 덮어쓸 것인가?"
답변: "상황에 따라 다름... 정말로 많이 다름"

공격 대상:
• 제어 흐름 데이터 (Control-flow data)
• 상황별/프로그램별 중요 데이터
• 거대한 공격 표면 (Huge attack surface)

2.3 C++ 가상 함수 테이블 (Vtable) 공격

가상 함수의 구현 원리

C++ 가상 함수:
• 런타임 다형성(polymorphism) 지원
• 객체의 실제 타입에 따라 올바른 함수 호출

구현 방법:
• 각 객체는 가상 함수 테이블(Vtable) 포인터 보유
• Vtable은 해당 클래스의 가상 함수 주소들을 저장

Vtable 공격 구조

정상적인 C++ 객체:
┌─────────────────┐
│ Vtable Pointer │ → Vtable → func1(), func2(), func3()
├─────────────────┤
│ Member Data │
└─────────────────┘

공격 후:
┌─────────────────┐
│ Fake Vtable Ptr │ → 공격자 제어 → 악성 함수
├─────────────────┤
│ Corrupted Data │
└─────────────────┘

2.4 콜백 함수 공격

// 콜백 함수 구조체 예시
struct vuln_struct {
char data1[128];
...
void (*onSomeEvent)(int event); // 콜백 함수 포인터
...
}

공격 과정:
1. 버퍼 오버플로우로 콜백 함수 포인터 덮어씀
2. 해당 이벤트 트리거
3. 공격자가 제어하는 함수 실행

2.5 제어 흐름 데이터 덮어쓰기

실제 공격 시나리오:
struct BrowserStruct {
...
void (*eventCallBack)(void); // 이벤트 핸들러
...
}

공격 전:
┌─────────────────┐
│ Buffer │
├─────────────────┤
│ BrowserStruct │
│ eventCallBack = │ → handleEvent()
│ &handleEvent() │
└─────────────────┘

공격 후:
┌─────────────────┐
│ AAAAAAAAAAAAA │ ← 오버플로우 데이터
├─────────────────┤
│ BrowserStruct │
│ eventCallBack = │ → 0x6161616161 (공격자 제어)
│ 0x6161616161 │
└─────────────────┘

2.6 비제어흐름 데이터 공격

데이터 전용 공격 (Data-only Attack):
struct BrowserStruct {
...
String WebPluginAddr; // 플러그인 주소
...
}

공격 시나리오:
원래 값: "http://trusted-website.edu/plugin/"
공격 후: "http://malicious-attacker.com/malware.exe"

결과: 신뢰할 수 없는 플러그인 로드 → 시스템 감염

💡 추가 정보: 데이터 전용 공격은 제어 흐름을 직접 바꾸지 않고도 프로그램의 논리를 조작하여 공격 목적을 달성하는 기법입니다.

3. Use-After-Free (UAF) 공격

3.1 UAF 기본 개념

Use-After-Free (UAF):
• 댕글링 포인터(Dangling Pointer) 악용
• 해제된 메모리를 여전히 참조하는 포인터 사용
• 시간적 메모리 안전성(Temporal Memory Safety) 위반

UAF 퀴즈

// C 버전
Object *obj = malloc(sizeof(Object));
printf("%p", obj); // "0xdeadbeef" 출력
free(obj);
printf("%p", obj); // obj의 값은? → 여전히 "0xdeadbeef"

// C++ 버전
Object *obj = new Object;
cout << obj; // "0xdeadbeef" 출력
delete obj;
cout << obj; // obj의 값은? → 여전히 "0xdeadbeef"

핵심: free()delete 후에도 포인터 변수의 값은 변하지 않음!

3.2 UAF 공격 메커니즘

1단계: 객체 할당

코드: malloc(obj);
힙: [Object] ← obj 포인터가 가리킴
스택: [Object* obj]

2단계: 객체 해제

코드: free(obj);
힙: [?????] ← 해제된 메모리
스택: [Object* obj] ← 댕글링 포인터 (여전히 같은 주소)

3단계: 다른 객체 할당

코드: malloc(otherObj);
힙: [OtherObject] ← 같은 위치에 다른 객체 할당될 수 있음
스택: [Object* obj] ← 여전히 원래 주소 가리킴

4단계: 해제된 객체 사용

코드: obj->func();  // call obj+0x10
결과: obj가 가리키는 위치 (obj+0x10)의 데이터를 함수 주소로 해석
실제: OtherObject의 데이터가 함수 주소로 오해석됨

3.3 UAF의 복잡성

멀티스레드 환경에서의 UAF

문제의 복잡성:
• 힙 객체는 여러 함수와 스레드 간 공유
• 동시성(Concurrency)과 잠금(Locking) 문제
• 프로그래밍 수업에서 배운 멀티스레딩의 어려움

Thread 1 Thread 2 Thread 3 ...
[obj ptr] [obj ptr] [obj ptr]
↓ ↓ ↓
공유 힙 메모리 [Object/OtherObject]

UAF 버그의 특성

발견의 어려움:
• 타이밍에 의존적인 버그
• 다양한 실행 경로에서 발생
• 디버깅 시에는 재현되지 않을 수 있음

공격자의 활용:
• 의도적으로 적절한 타이밍에 힙 메모리 할당
• 댕글링 포인터에 "가짜 객체" 제공
• 이를 통해 다양한 공격 수행

3.4 UAF에서 ROP로의 전환

가짜 객체 생성

공격자의 힙 조작:
코드: obj->func(); // call obj+0x10

힙 상태:
┌─────────────────┐
│ 공격자 제어 데이터│
├─────────────────┤
│ 0xdeadbeefaaaaa │ ← obj+0x10 위치
│ aaaaaaaaaaaaaaaa│
│ aaaaaaaaaaaaaaaa│
└─────────────────┘

결과: obj+0x10 위치의 값을 함수 주소로 호출

스택 피봇 가젯 활용

유용한 스택 피봇 가젯들:
• add esp, offset; ret - 스택 포인터 조정
• mov esp, register; ret - 레지스터 값을 스택 포인터로
• sub esp, offset; ret - 스택 포인터 감소
• leave; ret - 함수 에필로그
• call register - 레지스터 값으로 호출
• xchg register, esp; ret - 레지스터와 스택 포인터 교환

UAF에서 ROP 체인으로

1단계: 가짜 객체에 스택 피봇 가젯 주소 배치
힙:
┌─────────────────┐
│ &(Stack Pivot) │ ← obj+0x10 위치
├─────────────────┤
│ aaaaaaaaaaaaaaa │
│ aaaaaaaaaaaaaaa │
└─────────────────┘

2단계: 스택 피봇 실행 후 ROP 체인 실행
스택 (ROP 체인으로 피봇됨):
┌─────────────────┐
│ &[pop eax; ret] │
├─────────────────┤
│ 0x12345678 │
├─────────────────┤
│ &[pop ecx; ret] │
├─────────────────┤
│ 0xcafebabe │
├─────────────────┤
│ &[mov [eax],ecx]│
└─────────────────┘

3.5 브라우저 UAF 공격

JavaScript의 역할:
• 브라우저 함수들을 유연하게 트리거
• 힙에 특정 타입의 객체들을 할당
• 타이밍을 정교하게 제어

실제 공격 시나리오:
• Drive-by-Download 공격에 대량 활용
• 공격자가 웹서버 해킹
• 웹페이지에 JavaScript 익스플로잇 삽입
• 방문자에게 악성코드 유포 (비트코인 마이너, 랜섬웨어)

3.6 기타 힙 공격 기법

힙 공격의 진화

방어 기법에 대한 대응:
• DEP, ASLR, 힙 메타데이터 보호에 맞서 진화

주요 기법들:
• Heap Spray (힙 스프레이): 200MB의 셸코드+NOP 슬라이드를 힙에 뿌리기
• Heap Feng Shui (힙 풍수): 익스플로잇을 위한 힙 레이아웃 조작 기술
• Pointer Spraying: 포인터 값들을 대량으로 힙에 배치

현대 브라우저 방어

Chrome 등 현대 브라우저의 방어:
• 샌드박싱 (Sandboxing)
• 다양한 방어 메커니즘

공격자의 대응:
• 샌드박스 탈출 기법
• 시스템 콜을 통한 직접적인 커널 취약점 공격

💡 추가 정보: 현대 브라우저 보안은 다층 방어 구조로 되어 있으며, 공격자들은 여러 취약점을 연계한 체인 공격을 통해 방어를 우회하려고 시도합니다.

4. PLT/GOT 공격

4.1 공유 라이브러리 개념

공유 라이브러리란?

공유 라이브러리 (Shared Libraries):
• 정적 라이브러리와 대비되는 동적 라이브러리
• Linux: .so 파일 확장자
• Windows: .dll 파일 확장자

장점:
• 물리적으로 하나의 복사본만 메모리에 로드
• 여러 프로세스의 가상 주소 공간에 매핑
• 메모리 효율성 증대

공유 라이브러리 로딩

로딩 과정:
1. 프로그램 실행 파일에는 공유 라이브러리 내용 미포함
2. 프로세스 초기화 시 메모리 공간에 로드
3. 런타임에 동적으로 링킹

메모리 매핑:
Process A Process B
┌─────────────┐ ┌─────────────┐
│ .text │ │ .text │
├─────────────┤ ├─────────────┤
│ libc │ ←───────→ │ libc │ (동일 물리 메모리)
│ (shared) │ │ (shared) │
└─────────────┘ └─────────────┘

4.2 동적 링킹

동적 링킹의 필요성

문제: 컴파일 시점에 공유 라이브러리 심볼 주소 미확정
예시: printf() 함수의 주소를 런타임까지 알 수 없음
이유:
• 공유 라이브러리가 런타임에 로드됨
• ASLR로 인해 매번 다른 주소에 로드됨

동적 링킹 특성

동적 링킹의 원리:
• 외부 참조의 로드와 링킹을 런타임까지 연기
• OS 로더는 메인 프로그램만 로드
• 지연 바인딩 (Lazy Binding): 참조될 때만 동적 라이브러리 로드

해결책: 간접 참조 (Indirection)
• 컴파일 시 알 수 없는 것 → 함수 포인터 + call rax 방식
• ELF는 런타임 간접 참조를 위한 특별한 구조 사용

4.3 PLT와 GOT 구조

PLT와 GOT 정의

PLT (Procedure Linkage Table):
• 공유 라이브러리 함수 호출을 위한 스텁 코드
• 초기에는 .got.plt 섹션을 참조하여 심볼 확인
• 해결되지 않았으면 동적 링커 호출

GOT (Global Offset Table):
• 외부 심볼을 위한 오프셋 테이블
• .got.plt: PLT 엔트리를 위한 GOT 섹션
• 동적 링커가 첫 번째 함수 호출 시 채움

동적 링킹 실행 과정

1단계: 첫 번째 printf() 호출

0x40137f <+239>: call 0x401040 <printf@plt>

printf@plt:
0x401040 <+0>: jmp QWORD PTR [rip+0x2fda] # 0x404020 <printf@got.plt>
0x401046 <+6>: push 0x1 # 함수 인덱스
0x40104b <+11>: jmp 0x401020 # 동적 링커로

2단계: GOT 엔트리 확인

gef➤ x/g 0x404020
0x404020 <printf@got.plt>: 0x0000000000401046 # 아직 해결 안됨

printf@plt의 두 번째 명령어 주소가 저장되어 있음
→ 동적 링커 호출로 이어짐

3단계: 동적 링커 실행

0x401020: push QWORD PTR [rip+0x2fe2]  # 0x404008 (링크맵)
0x401026: jmp QWORD PTR [rip+0x2fe4] # 0x404010 (링커 함수)

gef➤ x/g 0x404010
0x404010: 0x00007ffff7fd8d30 # _dl_runtime_resolve_xsavec

4단계: 심볼 해결 후 재호출

동적 링커가 printf 심볼 해결 후:

gef➤ x/g 0x404020
0x404020 <printf@got.plt>: 0x00007ffff7dcd6f0 # __printf 실제 주소

이후 printf@plt 호출 시:
jmp QWORD PTR [rip+0x2fda] # 직접 __printf로 점프

4.4 PLT/GOT 공격 기법

공격 벡터들

1. 메모리 노출 공격

GOT 엔트리 유출을 통한 ASLR 우회:
• PLT/GOT는 메인 실행 파일 이미지에 위치
• PIE 미적용 시 알려진 고정 주소
• 채워진 GOT 엔트리 유출 → libc 함수 주소 획득
• libc 기본 주소 계산 → ASLR 완전 우회

2. GOT 덮어쓰기 공격

함수 호출 리다이렉션:
원래: printf() 호출 → GOT[printf] → libc printf()
공격: printf() 호출 → GOT[printf] → maliciousFunc()

공격 과정:
1. 임의 쓰기 취약점으로 GOT 엔트리 덮어씀
2. 해당 함수 호출 시 공격자 함수 실행
3. 시스템 장악

3. ROP 가젯으로 활용

return-to-GOT 기법:
• GOT 엔트리들을 ROP 가젯으로 활용
• 예: strcpy GOT 엔트리를 ROP 체인에서 호출
• 유용한 libc 함수들의 직접 호출 가능

PLT/GOT 공격의 특징

장점 (공격자 관점):
• PIE 미적용 시 주소 예측 가능
• 다양한 공격 벡터 제공
• ASLR 우회의 강력한 수단

방어의 어려움:
• 동적 링킹의 본질적 특성
• 성능을 위한 지연 바인딩
• 런타임 쓰기 가능성 필요

💡 추가 정보: PLT/GOT 공격은 ELF 바이너리의 동적 링킹 메커니즘을 악용한 공격으로, RELRO(RELocation Read-Only) 보호 기법으로 어느 정도 방어할 수 있지만 완전하지는 않습니다.

예상 시험문제

1. 힙과 스택의 보안 특성 비교

문제: 힙 기반 공격이 스택 기반 공격보다 어려운 이유와 쉬운 이유를 각각 설명하고, 힙 공격에서 주요 타겟이 되는 데이터 유형 3가지를 예시와 함께 서술하시오.

모범답안:

힙 공격이 어려운 이유:

  1. 힙 카나리 없음: 스택과 달리 모든 힙 쓰기를 검사하는 것은 성능상 불가능
  2. 반환 주소 없음: 스택처럼 명확한 제어 흐름 타겟이 힙에 직접 존재하지 않음
  3. 예측 어려운 레이아웃: 동적 할당으로 인해 메모리 레이아웃이 실행시마다 다름

힙 공격이 쉬운 이유:

  1. 거대한 공격 표면: 다양한 데이터 구조와 객체들이 공격 대상
  2. 스레드 간 공유: 여러 스레드에서 접근하는 복잡성으로 버그 발생 확률 높음
  3. 프로그래머 의존적: 수동 메모리 관리로 인한 버그 가능성 증가

주요 공격 타겟:

  1. C++ 가상 함수 테이블 (Vtable):
class Shape {
public:
virtual void draw() = 0; // 가상 함수
};

// 공격: Vtable 포인터를 덮어써서 draw() 호출 시 악성 함수 실행
  1. 콜백 함수 포인터:
struct EventHandler {
char buffer[128];
void (*onClick)(int x, int y); // 콜백 함수
};

// 공격: onClick 포인터를 덮어써서 이벤트 발생 시 공격자 코드 실행
  1. 프로그램 논리 데이터:
struct UserSession {
char username[64];
bool isAdmin; // 권한 플래그
char* configFile; // 설정 파일 경로
};

// 공격: isAdmin을 true로, configFile을 악성 파일 경로로 변경

2. Use-After-Free 공격의 메커니즘

문제: Use-After-Free(UAF) 취약점이 발생하는 원리를 단계별로 설명하고, 멀티스레드 환경에서 UAF가 더 위험한 이유를 서술하시오.

모범답안:

UAF 발생 원리:

1단계: 객체 할당

Object *obj = malloc(sizeof(Object));
// obj = 0x12345678 (예시 주소)

2단계: 객체 해제

free(obj);
// 메모리는 해제되었지만 obj 변수는 여전히 0x12345678을 가리킴
// 이때 obj는 댕글링 포인터(Dangling Pointer) 상태

3단계: 메모리 재사용

OtherObject *other = malloc(sizeof(OtherObject));
// 운영체제가 같은 주소 0x12345678에 다른 객체 할당 가능

4단계: 해제된 객체 접근

obj->someFunction();  // obj+0x10 위치의 데이터를 함수 주소로 해석
// 실제로는 OtherObject의 데이터가 함수 주소로 오해석됨

멀티스레드 환경에서의 위험성:

  1. 타이밍 경쟁 조건:
Thread 1: obj를 해제 (free)
Thread 2: 같은 obj 포인터로 함수 호출 시도
Thread 3: 새로운 객체를 같은 위치에 할당

위험: Thread 2가 Thread 3의 객체를 잘못된 타입으로 접근
  1. 공유 메모리 복잡성:

    • 힙 객체는 여러 스레드에서 공유
    • 한 스레드에서 해제한 객체를 다른 스레드에서 접근
    • 동기화 메커니즘의 복잡성으로 버그 발견 어려움
  2. 디버깅의 어려움:

    • 타이밍에 의존적인 버그로 재현이 어려움
    • 개발 환경과 운영 환경에서 다른 동작
    • 공격자는 의도적으로 타이밍을 조작하여 악용

3. PLT와 GOT의 동작 원리

문제: ELF 바이너리에서 PLT(Procedure Linkage Table)와 GOT(Global Offset Table)가 동적 링킹을 지원하는 메커니즘을 설명하고, 이를 악용한 공격 방법 2가지를 서술하시오.

모범답안:

PLT/GOT 동작 메커니즘:

구조와 역할:

PLT (Procedure Linkage Table):
• 공유 라이브러리 함수 호출을 위한 스텁 코드
• 각 외부 함수마다 PLT 엔트리 존재
• GOT를 참조하여 실제 함수 주소 확인

GOT (Global Offset Table):
• 외부 심볼의 실제 주소를 저장하는 테이블
• 동적 링커가 런타임에 실제 주소로 채움
• .got.plt 섹션이 PLT 전용 GOT

동적 링킹 과정:

첫 번째 호출 시:

main():
call printf@plt

printf@plt:
jmp [printf@got.plt] # GOT 엔트리 참조
push function_index # 아직 해결 안된 경우
jmp dynamic_linker # 동적 링커 호출

동적 링커:
1. 심볼 "printf" 검색
2. libc에서 실제 주소 찾기
3. GOT 엔트리에 실제 주소 저장
4. printf 함수로 점프

두 번째 호출부터:

printf@plt:
jmp [printf@got.plt] # 이미 해결된 주소로 직접 점프

PLT/GOT 공격 방법:

1. GOT 덮어쓰기 공격:

공격 과정:
1. 임의 쓰기 취약점으로 GOT 엔트리 수정
printf@got.plt: 0x7ffff7dcd6f0 → 0x41414141 (공격자 주소)

2. printf() 호출 시:
call printf@plt → jmp [printf@got.plt] → 공격자 함수 실행

응용:
• system() GOT 엔트리를 조작하여 셸 실행
• 중요 함수를 NOP으로 변경하여 보안 검사 우회

2. 메모리 노출을 통한 ASLR 우회:

공격 과정:
1. 메모리 읽기 취약점으로 GOT 엔트리 유출
leak = read(printf@got.plt) // 예: 0x7ffff7dcd6f0

2. libc 기본 주소 계산
libc_base = leak - printf_offset // printf의 libc 내 오프셋

3. 다른 함수 주소 계산
system_addr = libc_base + system_offset

4. ROP 체인이나 다른 공격에서 계산된 주소 사용

방어 기법:

  • RELRO (RELocation Read-Only): GOT를 읽기 전용으로 설정
  • PIE: PLT/GOT 주소도 무작위화
  • Control Flow Integrity: 간접 호출 검증

4. 힙 Feng Shui 기법

문제: "Heap Feng Shui" 기법의 개념을 설명하고, 공격자가 힙 레이아웃을 어떻게 조작하여 익스플로잇에 유리한 상황을 만드는지 구체적인 예시와 함께 서술하시오.

모범답안:

Heap Feng Shui 개념:

정의: 공격자가 의도적으로 힙 메모리 할당/해제 패턴을 조작하여 
익스플로잇에 유리한 힙 레이아웃을 만드는 기법

목적:
• 취약한 객체와 공격 대상 객체를 인접하게 배치
• 오버플로우 시 정확한 타겟을 덮어쓸 수 있도록 조작
• 예측 가능한 메모리 레이아웃 구성

기본 원리:

힙 할당자의 특성 활용:
1. 같은 크기의 객체들은 연속적으로 할당되는 경향
2. 해제된 메모리는 재사용됨
3. 할당 순서가 메모리 레이아웃에 영향

공격자의 제어:
• JavaScript, ActionScript 등을 통한 정교한 메모리 할당
• 타이밍 제어로 원하는 레이아웃 달성

구체적인 공격 시나리오:

1단계: 레이아웃 준비

// 브라우저 환경에서 JavaScript로 힙 조작
var preparation_arrays = [];
for (var i = 0; i < 100; i++) {
preparation_arrays[i] = new Array(64); // 64개 요소 배열 생성
}

// 특정 크기의 객체들로 힙을 "정리"
for (var i = 0; i < 50; i++) {
preparation_arrays[i] = null; // 50개 배열 해제
}

2단계: 타겟 객체 배치

// 취약한 객체 생성
var vulnerable_object = create_vulnerable_object(); // 128바이트 크기

// 공격 대상 객체를 바로 다음에 배치
var target_object = create_target_object(); // 함수 포인터 포함

힙 레이아웃:
┌─────────────────┐
│ vulnerable_obj │ ← 버퍼 오버플로우 발생 지점
├─────────────────┤
│ target_object │ ← 함수 포인터 등 중요 데이터
│ func_ptr = xxx │
└─────────────────┘

3단계: 공격 실행

// 버퍼 오버플로우 트리거
overflow_vulnerable_object("A".repeat(128 + 8) + evil_function_address);

결과:
┌─────────────────┐
AAAAAAAAAAAAA │ ← 오버플로우된 데이터
├─────────────────┤
│ target_object │
│ func_ptr = evil │ ← 덮어써진 함수 포인터
└─────────────────┘

// 타겟 객체의 함수 호출 시 공격자 코드 실행
target_object.trigger_function_call();

고급 기법들:

홀 펀칭 (Hole Punching):

목적: 특정 위치에 정확한 크기의 "구멍" 생성

과정:
1. 큰 객체들로 힙 공간 채우기
2. 중간 객체들을 선택적으로 해제
3. 원하는 크기의 빈 공간 생성
4. 취약한 객체를 정확한 위치에 할당

타입 컨퓨전 유도:

목적: 같은 크기의 다른 타입 객체들을 혼동시키기

과정:
1. 같은 크기의 Object A와 Object B 준비
2. Object A 해제
3. 같은 위치에 Object B 할당
4. Object A 포인터로 Object B에 접근하여 타입 컨퓨전 발생

실제 브라우저 공격 예시:

Flash/ActionScript 환경:
• Vector 객체의 length 필드 조작
• ByteArray 객체의 연속 할당으로 예측 가능한 레이아웃
• 가비지 컬렉션 타이밍 조작으로 정교한 제어

JavaScript 환경:
• ArrayBuffer 객체 활용
• 문자열 객체의 연속 할당
• TypedArray를 통한 메모리 직접 접근

5. UAF에서 ROP로의 공격 전환

문제: Use-After-Free 취약점을 이용하여 ROP 공격으로 전환하는 과정을 단계별로 설명하고, 스택 피봇(Stack Pivot) 가젯의 역할과 종류를 서술하시오.

모범답안:

UAF에서 ROP로의 전환 과정:

1단계: UAF 조건 생성

// 객체 할당 및 해제
VulnObject *obj = malloc(sizeof(VulnObject));
// ... 객체 사용 ...
free(obj); // 객체 해제
// obj는 여전히 해제된 메모리를 가리키는 댕글링 포인터

2단계: 힙 메모리 재활용

// 공격자가 의도적으로 같은 크기의 데이터 할당
AttackerData *fake = malloc(sizeof(VulnObject));
// fake 객체가 obj가 가리키던 위치에 할당될 확률 높음

3단계: 가짜 객체 구성

공격자가 제어하는 힙 데이터:
┌─────────────────┐
│ &(pivot_gadget) │ ← obj->vtable이나 함수 포인터 위치
├─────────────────┤
│ controlled_data │
├─────────────────┤
│ controlled_data │
└─────────────────┘

목표: obj->func() 호출 시 pivot_gadget 실행

4단계: 스택 피봇 실행

// 예: mov esp, eax; ret 가젯 실행
// eax에는 공격자가 제어하는 ROP 체인 주소가 들어있음

실행 전: ESP = 0x7fff1234 (원래 스택)
실행 후: ESP = 0x12345678 (공격자 제어 메모리)

5단계: ROP 체인 실행

새로운 스택 (공격자 제어 영역):
┌─────────────────┐ ← 새로운 ESP
│ &(pop eax; ret) │
├─────────────────┤
│ 0x12345678 │ ← 첫 번째 가젯 인자
├─────────────────┤
│ &(pop ecx; ret) │
├─────────────────┤
│ 0xcafebabe │ ← 두 번째 가젯 인자
├─────────────────┤
│&(mov [eax],ecx) │
├─────────────────┤
│ ... │ ← 추가 ROP 가젯들
└─────────────────┘

스택 피봇 가젯의 역할:

역할: 스택 포인터(ESP/RSP)를 공격자가 제어하는 메모리 영역으로 이동

필요성:
• UAF는 힙에서 발생하지만 ROP는 스택 기반 공격
• 원래 스택은 공격자가 제어할 수 없음
• 스택을 공격자 제어 영역으로 "피봇"하여 ROP 실행

스택 피봇 가젯 종류:

1. 직접 이동 가젯들:

mov esp, eax; ret        # eax 값을 스택 포인터로
mov esp, ecx; ret # ecx 값을 스택 포인터로
mov rsp, rdi; ret # rdi 값을 스택 포인터로 (64비트)

2. 연산 기반 가젯들:

add esp, 0x100; ret      # 스택 포인터를 특정 오프셋만큼 이동
sub esp, 0x200; ret # 스택 포인터를 반대 방향으로 이동

3. 교환 가젯들:

xchg esp, eax; ret       # 스택 포인터와 레지스터 값 교환
xchg rsp, rdx; ret # 64비트 버전

4. 함수 에필로그 가젯들:

leave; ret               # mov esp, ebp; pop ebp; ret
# 함수 종료 시퀀스 활용

5. 푸시/팝 조합 가젯들:

push eax; pop esp; ret   # eax 값을 스택에 푸시 후 팝하여 esp로
push ecx; pop esp; ret # 다양한 레지스터 조합 가능

6. 메모리 간접 참조 가젯들:

mov esp, [eax]; ret      # eax가 가리키는 메모리 값을 esp로
mov rsp, [rdi+8]; ret # 구조체 멤버 접근 패턴

실제 공격 시나리오:

// 1. UAF 객체의 가상 함수 호출
obj->virtual_func(); // 실제로는 mov esp, eax; ret 실행

// 2. eax는 공격자가 힙에 준비한 ROP 체인 주소
// 3. 스택이 ROP 체인으로 피봇됨
// 4. ROP 체인이 순차적으로 실행되어 임의 코드 실행 달성

방어 기법:

Control Flow Integrity (CFI):
• 간접 호출의 대상 주소 검증
• 스택 피봇 같은 비정상적인 제어 흐름 탐지

Intel CET (Control-flow Enforcement Technology):
• 하드웨어 기반 제어 흐름 무결성
• Shadow Stack으로 반환 주소 보호

6. 현대 브라우저 보안과 샌드박스 우회

문제: 현대 웹 브라우저(Chrome 등)의 다층 보안 구조를 설명하고, 공격자들이 이러한 보안을 우회하는 방법들을 서술하시오.

모범답안:

현대 브라우저의 다층 보안 구조:

1. 프로세스 격리 (Process Isolation):

멀티 프로세스 아키텍처:
┌─────────────────┐
│ Browser Core │ ← 브라우저 메인 프로세스
├─────────────────┤
│ Renderer #1 │ ← 탭별 렌더링 프로세스
├─────────────────┤
│ Renderer #2 │ ← 각 탭은 독립된 프로세스
├─────────────────┤
│ Plugin Process │ ← 플러그인 전용 프로세스
├─────────────────┤
│ GPU Process │ ← GPU 가속 전용
└─────────────────┘

장점:
• 한 탭의 크래시가 전체 브라우저에 영향 없음
• 프로세스 간 메모리 보호
• 각 프로세스의 권한 최소화

2. 샌드박스 (Sandboxing):

샌드박스 레벨:
Untrusted Process (렌더러) ← 최소 권한
↓ IPC (Inter-Process Communication)
Broker Process (브라우저) ← 높은 권한

제한 사항:
• 파일 시스템 접근 금지
• 네트워크 접근 제한
• 시스템 API 호출 금지
• 다른 프로세스 접근 금지

3. 사이트 격리 (Site Isolation):

도메인별 프로세스 분리:
• 각 웹사이트는 별도 프로세스에서 실행
• Cross-Origin 데이터 접근 방지
• Spectre/Meltdown 류 사이드 채널 공격 방어

4. Content Security Policy (CSP):

// HTTP 헤더로 설정
Content-Security-Policy: script-src 'self'; object-src 'none';

효과:
• 인라인 스크립트 실행 방지
• 외부 도메인 리소스 로드 제한
eval() 같은 동적 코드 실행 금지

샌드박스 우회 방법들:

1. 브라우저 버그 체인 공격:

다단계 공격 (Exploit Chain):
1단계: 렌더러 프로세스 장악
• UAF, 힙 오버플로우 등으로 렌더러 내에서 코드 실행

2단계: 샌드박스 탈출
• IPC 버그 악용하여 브로커 프로세스로 권한 상승
• 또는 커널 익스플로잇으로 직접 시스템 권한 획득

3단계: 시스템 장악
• 파일 시스템 접근, 네트워크 통신, 악성코드 설치

2. IPC (Inter-Process Communication) 공격:

공격 대상: 프로세스 간 통신 메커니즘
• Message passing 버그
• Shared memory 취약점
• Named pipe 공격

예시:
Renderer Process Broker Process
│ │
│ ──── 악조된 IPC 메시지 ────→ │ ← 버퍼 오버플로우
│ │ 또는 타입 컨퓨전
│ ←─── 권한 상승 달성 ────── │

3. 커널 익스플로잇:

시스템 콜 공격:
• 렌더러 프로세스에서 직접 커널 취약점 공격
• 샌드박스를 완전히 우회하여 시스템 권한 획득

대표적인 커널 공격:
• Use-After-Free in kernel objects
• Race condition in device drivers
• Buffer overflow in system call handlers

4. 하드웨어 취약점 활용:

사이드 채널 공격:
• Spectre/Meltdown: CPU 추측 실행 버그
• Rowhammer: DRAM 물리적 특성 악용
• Cache timing attacks: 캐시 동작 패턴 분석

목표:
• 프로세스 경계 우회
• 메모리 보호 무력화
• 크로스 도메인 데이터 유출

실제 공격 사례 분석:

Project Zero의 브라우저 익스플로잇 체인:

1. JavaScript 엔진 JIT 버그
• Type confusion in V8 TurboFan
• Array bounds check elimination 우회

2. 렌더러 프로세스 장악
• Arbitrary read/write primitive 획득
• ROP/JOP 체인으로 코드 실행

3. 샌드박스 탈출
• Chrome IPC validation 버그
• 또는 Windows kernel 0-day 활용

4. 시스템 권한 획득
• 악성코드 설치 및 지속성 확보

방어 기법의 진화:

1. 더 강화된 샌드박스:

• Stronger sandbox policies
• Hardware-assisted isolation (Intel MPX, ARM Pointer Auth)
• Hypervisor-based protection

2. 컴파일 타임 방어:

• Control Flow Integrity (CFI)
• Stack clash protection
• Fortified source compilation

3. 런타임 방어:

• Intel CET (Control-flow Enforcement Technology)
• ARM Pointer Authentication
• Intel Memory Protection Extensions (MPX)

결론: 현대 브라우저 보안은 다층 방어 구조로 상당히 견고하지만, 공격자들은 여러 취약점을 연계한 정교한 체인 공격으로 이를 우회하고 있습니다. 보안은 지속적인 군비 경쟁의 양상을 보이며, 하드웨어 차원의 보안 기능과 메모리 안전 언어로의 전환이 근본적인 해결책으로 주목받고 있습니다.

핵심 요약

  • 힙 기반 공격은 스택 방어 기법 강화로 인한 공격자들의 새로운 전략으로, 웹 브라우저 등 힙 사용량이 많은 프로그램을 주요 표적
  • 힙 오버플로우는 스택과 달리 반환 주소가 없어 C++ Vtable, 콜백 함수, 프로그램 논리 데이터 등 다양한 제어/비제어 데이터를 공격 대상으로 함
  • **Use-After-Free(UAF)**는 해제된 메모리의 댕글링 포인터를 악용하는 시간적 메모리 안전성 위반으로, 멀티스레드 환경에서 특히 위험
  • 스택 피봇 가젯을 통해 UAF에서 ROP 공격으로 전환하여 임의 코드 실행 달성 가능
  • Heap Feng Shui는 공격자가 의도적으로 힙 레이아웃을 조작하여 익스플로잇에 유리한 메모리 배치를 만드는 고급 기법
  • PLT/GOT 공격은 동적 링킹 메커니즘을 악용하여 GOT 덮어쓰기메모리 노출을 통한 ASLR 우회 등을 수행
  • 현대 브라우저는 프로세스 격리, 샌드박스, 사이트 격리 등 다층 보안을 적용하지만, 공격자들은 체인 익스플로잇커널 공격으로 우회 시도
  • 메모리 안전성 문제는 여전히 현대 소프트웨어 보안의 핵심 과제로, 하드웨어 기반 방어 기법과 안전한 언어로의 전환이 근본적 해결책

💡 중요: 힙 기반 공격은 스택 공격보다 복잡하고 다양한 형태를 가지며, 특히 현대 웹 환경에서는 JavaScript와 결합하여 정교한 공격이 가능합니다. 따라서 메모리 안전성을 근본적으로 해결하는 접근이 필요하며, 다층 방어 전략과 함께 새로운 하드웨어 보안 기능의 활용이 중요합니다.