UAF(User After Free)는 Heap 메모리 취약점을 이용하는 공격이다.
1. Heap
Heap은 동적 메모리 할당을 위해 존재하는 공간이다.
UAF가 발생하는 이유는 Heap의 메모리 효율 관리목적에서부터 시작한다.
Heap 메모리 동적 할당을위해 Operation System은 할당을 진행하기 위한 Chunk를 size별로 관리한다. 예를들어 4byte , 16byte, 256byte와 같이 2의 제곱수에 의한 사이즈별로 메모리 공간을 관리한다. Linux Kernel Buddy 시스템을 이해하고 있다면 위와같이 이해가 빠를 수 있다.
결론적으로는 빠른 할당을 위해 size별로 free memory를 list만들어서 관리하는 것으로 이해하면 된다.
그리고 Free -> realloc의 속도를 빠르게 하기위해 free가 진행된다고 바로 해당 데이터를 제거하지않는 성질이 있다.
보통의 프로그램의 경우 malloc과 free가 반복되며 짧은시간에 다시 재할당된 메모리의 경우 기존과 같은동작을 하기 때문에 이와 같이 효율적으로 heap 공간의 해제와 할당 함수가 구현되어있다.
위 내용을 정리하면 아래와 같은 문제점이 발생하게 된다.
먼저 Object A가 malloc을 통해 Heap 메모리 공간을 할당받고 사용한다.
그리고 free() 를 통해서 Object A가 메모리공간을 해제한다.
해제 직후 Object B에서 Object A와 같은 size공간을 할당하면 위와 같이 기존 A의 Heap영역을 그대로 사용하게 된다.
즉 이경우 Object B입장에서는 새로 생성한 obejct임에도 불구하고 이미 chunk에 data들이 write되어있어문제가 발생할 수 있다.
2. UAF
User After Free는 위 Heap의 특징을 이용한 취약점이다.
할당 후 Free를 진행했음에도 기존에 object를 관리하기 위해 Heap영역을 가리키던 pointer자체는 null을 가리키는 것이아닌 제거되지 않은 메모리 공간을 가리키게된다.
이러한 포인터를 dangling pointer라고 하며 공격자는 dangling pointer를 활용하여 프로그래머가 의도하지 않은 공격을 진행할 수 있다.
#include <stdio.h> #include <stdlib.h> void print_test(void) { printf("Test UaF vuln()\n"); } typedef struct { void (*func)(void*); char name[10]; }UAF_TEST; int main (void) { UAF_TEST* M1; UAF_TEST* M2; M1 = (UAF_TEST*)malloc(256); strcpy(M1->name,"TEST_UAF"); M1->func = (void*)print_test; printf("Address : %02x\n", M1); printf("Data : %s\n",M1->name); printf("function : %02x\n",&M1->func); free(M1); M2 = (UAF_TEST*)malloc(256); printf("Address : %02x\n", M2); printf("function : %02x\n",&M2->func); M2->func; return 0; } |
위 코드는 User After Free를 발생시키는 기본적인 코드이다. 위 Heap 예제와 마찬가지로 Object가 malloc으로 할당을 진행하고 data, pointer값을 write진행한다. 이후 free -> Object2가 바로 할당을 진행한다.
Address : 8fc11a0 Data : TEST_UAF function : 8fc11a0 Address : 8fc11a0 function : 8fc11a0 |
위 프로그램을 실행하면 위 결과 값을 얻을 수 있다.
만약 scnaf와 같은 입력함수가 존재한다면 공격자는 dangling pointer를 활용해서 function이 가리키는 주소 값 0x8fc11a0에 공격자가 실행시키고자 하는 exploit code의 주소를 입력해서 공격할 수 있을 것이다.
추가적으로 gdb-peda를 통해 debug를 진행해보았는데
0x8fc11a0에서 print_test()주소가 입력된 것을 확인할 수 있었고 Data역시 0x8fc11a4에서 확인 가능했다.
free후 object2를 할당하여 메모리 공간을 확인하였을 때는 data값 일부와 print_test()를 가리키는 주소 값이 지워져 있었으나 공격자 입장에서 어느 주소 값이 func()가 가리키는 값인지 알 수 있기 때문에 해당 주소만 overwrite해주면 func()를 호출할 때 공격자가 의도한 프로그램이 실행하는 문제가 발생할 것이다.