← 목록으로

힙 익스플로잇 기초: Use-After-Free와 Double Free

[Pwn] Pwnable · 작성: 2026-07-19 19:31:03 · 조회 4

힙(heap)은 malloc/free로 프로그래머가 직접 관리하는 동적 메모리 영역이다. 스택과 달리 언제 할당되고 해제되는지가 코드 흐름에 따라 달라지기 때문에, "메모리 관리 실수"를 이용하는 취약점 클래스가 따로 존재한다. 힙 익스플로잇은 스택 계열보다 대체로 난이도가 높다.

Use-After-Free (UAF)

free()로 해제한 메모리를 가리키는 포인터(dangling pointer)를 그대로 계속 쓸 때 발생한다.

char *p = malloc(64);
free(p);
strcpy(p, "still writable");   // 해제된 메모리에 여전히 쓸 수 있다

free()는 "이 메모리를 다시 써도 된다"고 할당자에게 알릴 뿐, 메모리 자체를 즉시 지우거나 접근 불가능하게 만들지 않는다. 그래서 해제된 직후에도 그 포인터로 읽고 쓰는 게 물리적으로는 가능하다. 문제는 그 사이에 다른 malloc 호출이 같은 메모리 영역을 재사용할 수 있다는 것 — 해제된 객체 A의 메모리를 공격자가 통제 가능한 객체 B가 덮어쓰게 만들면, 이후 코드가 "여전히 A인 줄 알고" 그 메모리를 쓸 때 실제로는 B의 데이터(공격자가 심어둔 값)로 동작하게 된다. 예를 들어 A가 함수 포인터를 가진 객체였다면, B로 그 함수 포인터 자리를 원하는 주소로 덮어써서 임의 코드 실행까지 이어질 수 있다.

Double Free

같은 포인터를 두 번 free()할 때 발생한다.

char *p = malloc(64);
free(p);
free(p);   // 이미 해제된 걸 또 해제

대부분의 할당자(glibc의 ptmalloc 등)는 해제된 청크(chunk)를 "free list"라는 연결 리스트로 관리하는데, 같은 청크를 두 번 넣으면 이 리스트 구조가 꼬인다. 이후 malloc을 몇 번 더 호출하면 같은 메모리 영역이 두 개의 서로 다른 포인터에 동시에 할당되는 상황을 만들 수 있고, 그 결과 한쪽 포인터로 쓴 값이 다른 쪽 포인터로도 보이게 된다 — UAF와 비슷한 결과로 이어진다.

힙 익스플로잇이 어려운 이유

학습 순서 제안

  1. malloc/free가 내부적으로 청크를 어떻게 관리하는지(청크 헤더 구조, 크기별 bin) 먼저 공부한다
  2. how2heap(shellphish 저장소)의 예제로 tcache poisoning, fastbin dup 같은 기본 기법을 하나씩 따라 해본다
  3. pwnable.kr, pwnable.tw의 힙 카테고리 문제로 실전 감각을 쌓는다

스택 오버플로우보다 훨씬 배경 지식이 많이 필요한 주제라, 앞선 글들(메모리 구조, 스택 오버플로우, ROP)을 먼저 소화한 뒤 넘어오는 걸 권한다.

Pwnable 카테고리의 글 (8/14)

  1. 메모리 구조 기초: 스택, 힙, 레지스터부터 이해하기
  2. 스택 버퍼 오버플로우: 가장 기본적인 메모리 손상 공격
  3. Format String 취약점: printf가 위험해지는 순간
  4. Stack Canary: 원리와 우회 방법
  5. NX/DEP 우회와 Return-to-libc
  6. ASLR과 정보 유출(Leak)
  7. ROP (Return-Oriented Programming) 완전 정복
  8. 힙 익스플로잇 기초: Use-After-Free와 Double Free
  9. pwntools 실전 활용법
  10. UDF(User-Defined Function) 기반 DB 권한 상승: 원리부터 실전까지
  11. Pass-the-Hash / Pass-the-Ticket: 원리와 실전
  12. eBPF 기반 지속성(Persistence) 기법
  13. Full RELRO 환경에서의 GOT Overwrite와 우회
  14. ret2dlresolve: 심볼 정보 없이 임의 함수 호출하기
← ROP (Return-Oriented Programming) 완전 정복 pwntools 실전 활용법 →