[Pwn] Pwnable · 작성: 2026-07-19 19:31:03 · 조회 4
힙(heap)은 malloc/free로 프로그래머가 직접 관리하는 동적 메모리 영역이다. 스택과 달리 언제 할당되고 해제되는지가 코드 흐름에 따라 달라지기 때문에, "메모리 관리 실수"를 이용하는 취약점 클래스가 따로 존재한다. 힙 익스플로잇은 스택 계열보다 대체로 난이도가 높다.
free()로 해제한 메모리를 가리키는 포인터(dangling pointer)를 그대로 계속 쓸 때 발생한다.
char *p = malloc(64);
free(p);
strcpy(p, "still writable"); // 해제된 메모리에 여전히 쓸 수 있다
free()는 "이 메모리를 다시 써도 된다"고 할당자에게 알릴 뿐, 메모리 자체를 즉시 지우거나 접근 불가능하게 만들지 않는다. 그래서 해제된 직후에도 그 포인터로 읽고 쓰는 게 물리적으로는 가능하다. 문제는 그 사이에 다른 malloc 호출이 같은 메모리 영역을 재사용할 수 있다는 것 — 해제된 객체 A의 메모리를 공격자가 통제 가능한 객체 B가 덮어쓰게 만들면, 이후 코드가 "여전히 A인 줄 알고" 그 메모리를 쓸 때 실제로는 B의 데이터(공격자가 심어둔 값)로 동작하게 된다. 예를 들어 A가 함수 포인터를 가진 객체였다면, B로 그 함수 포인터 자리를 원하는 주소로 덮어써서 임의 코드 실행까지 이어질 수 있다.
같은 포인터를 두 번 free()할 때 발생한다.
char *p = malloc(64);
free(p);
free(p); // 이미 해제된 걸 또 해제
대부분의 할당자(glibc의 ptmalloc 등)는 해제된 청크(chunk)를 "free list"라는 연결 리스트로 관리하는데, 같은 청크를 두 번 넣으면 이 리스트 구조가 꼬인다. 이후 malloc을 몇 번 더 호출하면 같은 메모리 영역이 두 개의 서로 다른 포인터에 동시에 할당되는 상황을 만들 수 있고, 그 결과 한쪽 포인터로 쓴 값이 다른 쪽 포인터로도 보이게 된다 — UAF와 비슷한 결과로 이어진다.
malloc/free가 내부적으로 청크를 어떻게 관리하는지(청크 헤더 구조, 크기별 bin) 먼저 공부한다스택 오버플로우보다 훨씬 배경 지식이 많이 필요한 주제라, 앞선 글들(메모리 구조, 스택 오버플로우, ROP)을 먼저 소화한 뒤 넘어오는 걸 권한다.