UAF (Use After Free)
System Hacking - UAF (Use After Free)
1. UAF란
프로그램이 해제된 (free) 메모리를 다시 사용하는 경우에 발생하는 취약점이다. 보통 free
함수를 통해 해제된 포인터를 다시 참조하거나 사용하는 코드가 존재할 때 발생하며, 이 메모리를 공격자가 악의적으로 제어 가능한 데이터로 덮어쓰면 프로그램 흐름이 조작되거나 정보를 유출할 수 있다.
발생 조건은 아래와 같다.
- 동적으로 할당된 메모리를
free
함수로 해제한 후, 해당 포인터를 다시 사용할 경우
2. 예시 코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *name;
} User;
int main() {
User *user = (User *)malloc(sizeof(User));
user->name = strdup("Alice");
free(user); // 메모리 해제
// [UAF 발생] 이미 해제된 user->name을 참조하거나 수정
printf("User name: %s\n", user->name);
return 0;
}
user
포인터에 할당한 후 해제하였지만, 이미 해제된 user->name
에 접근이 가능하여 정의되지 않은 동작을 일으킬 수 있다.
3. Exploit 예제
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *name;
void (*print_func)();
} User;
void print_name() {
printf("Hello from print_name!\n");
}
void hacked() {
printf("🔥 HACKED! Shellcode or arbitrary code execution!\n");
}
int main() {
User *u1 = malloc(sizeof(User));
u1->name = strdup("Alice");
u1->print_func = print_name;
free(u1); // 메모리 해제
// 🌟 UAF 발생
User *u2 = malloc(sizeof(User)); // 동일한 크기의 객체 재할당
// 공격자가 덮어쓰는 부분 (메모리 구조를 조작했다고 가정)
u2->name = strdup("Eve");
u2->print_func = hacked; // u1이 참조하던 함수 포인터가 덮어써짐
// 여전히 u1을 사용하려는 코드
u1->print_func(); // 👈 hacked() 함수가 실행됨 (exploit 성공)
return 0;
}
u1
구조체를 동적으로 할당하고, free
를 통해 해제하였다. 이후 같은 크기의 u2
를 다시 할당하면 동일한 힙 주소를 재사용하게되고, u2->print_func
값을 공격자가 원하는 함수로 설정하면 u1->print_func
를 호출하더라도 hacked
함수가 실행된다.
4. GLIBC의 힙 구조
glibc
의 malloc
함수는 free된 청크들을 사이즈별로 분류하여 재사용한다. 이 때 사용하는 구조는 아래와 같다.
구조 | 청크 크기 범위 | 특징 |
---|---|---|
fastbin | ≤ 0x80 (128 bytes) | 매우 빠르게 재사용됨. FIFO 아님 (LIFO). tcache보다 먼저 사용되지 않음 |
tcache | ≤ 0x410 (1040 bytes) | glibc 2.26+부터 도입된 스레드별 캐시. 매우 빠름. 최대 7개까지 유지 |
smallbin | 0x20 ~ 0x3f0 | FIFO 방식. 메인 아레나에 존재. double free 검사 등 안전성 높음 |
unsorted bin | 모든 크기 | free 직후 들어가는 일시적 공간. 이후 small/large bin으로 이동 |
4.1. tcache
tcache
는 Thread-Local Cache
로서, glibc 2.26부터 도입된 기능이다. 각 스레드에 대해 독립적인 캐시를 유지하여 할당/해제를 빠르게 할 수 있고, 크기별로 최대 7개까지 free된 청크를 저정한다. 또한 LIFO
방식을 사용하여 최근에 해제된 것부터 재사용한다.
매우 빠르게 동작하는 장점이 있지만, double free
, UAF
공격 등에 대해 검사가 느슨하여 안전성이 낮다. 이에 따라 마지막으로 해제되었던 chunk와 같은 크기의 chunk를 재할당하면 동일한 위치로 할당될 가능성이 높아서 UAF
공격이 수행될 수 있다.
4.2. smallbin
smallbin
은 특정 크기 (0x20 ~ 0x3f0)에 해당하는 작은 청크들을 위한 bin이다. free된 청크는 일단 unsorted bin
에 들어갔다가, 적절한 사이즈의 smallbin
으로 이동한다. FIFO
방식을 사용하며, fd
(forward), bk
(backward) 포인터로 연결된 이중 연결 리스트로 되어있다.
tcache
대비 double free
와 같은 안전 검사를 강화하여 안전하지만 느리다.