profile image

L o a d i n g . . .

Use After Free

줄여서 UAF 라고 부른다.

UAF 는 메모리 영역을 free 한 후에 재사용하게 될 경우를 말한다.

 

#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{
    int* heap1;
    int* heap2;
    int* heap3;
 
    heap1 = (int*)malloc(256);
    heap2 = (int*)malloc(256);
 
    printf("heap1 : %p\n",heap1);
    printf("heap2 : %p\n",heap2);
 
    *heap2 = 1234;
    printf("heap2 number : %d\n",*heap2);
 
    free(heap2);
    printf("free heap2\n");
 
    heap3 = (int*)malloc(256);
    printf("new heap : %p\n",heap3);
    printf("new heap number: %d\n",*heap3);
 
    return 0;
}

위 코드는 256 바이트 만큼의 heap 영역을 두 번 할당하고, 두번째 할당했던 메모리 영역을 free 한 뒤, 256 바이트 만큼의 heap 영역을 다시 할당한 모습이다.

 

실행하면 다음과 같은 결과가 나온다.

heap1 : 0xa77010
heap2 : 0xa77120
heap2 number : 1234
free heap2
new heap : 0xa77120
new heap number: 1234

여기서 알 수 있는 사실은 다음과 같다.

 

먼저 heap2가 free 된 후 똑같은 크기인 heap3를 할당해보니 같은 주소에 할당이 되었는데, 힙은 할당을 좀 더 효율적으로 하기 위해 매번 할당하는 것이 아닌, 반환된 heap 영역의 크기를 기억해놨다가 같은 크기의 할당 요청이 들어오면 이전 영역을 재사용한다.

 

그리고, free 를 한다고 해서 해당 영역이 초기화되는 것이 아니다. heap2 에서 할당한 영역에 “1234” 라는 숫자를 써준 뒤 free 하고, 그 영역을 heap3가 재사용하게 됐을 때, “1234”라는 값이 그대로 들어있다.

 

이렇게 해제된 공간을 재사용할 경우, 원하지 않는 값을 참조할 수 있게 된다.

 


다음의 예제를 통해 UAF 를 이해해보자!

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
typedef struct {
    char name[10];
    void (*print)(void*);
} test;
 
typedef struct {
    char name[128];
} string;
 
void printName(test* t)
{
    printf("%s\n",t->name);
}
 
void shell(void)
{
    printf("This is Shell\n");
}
 
int main(void)
{
    test* t1;
    string* s1;
 
    /* malloc and free */
    t1 = malloc(256);
 
    strcpy(t1->name, "DOG");
    t1->print = (void*)printName;
 
    t1->print(t1);
 
    free(t1);
 
    /* malloc */
    s1 = malloc(256);
 
    scanf("%128s",s1->name);
 
    /* use */
    t1->print(t1);
 
    return 0;
}

test 구조체를 동적 할당하여 사용한 뒤 해제하고,
string 구조체를 동적 할당하여 사용하고,
test 구조체를 재사용한 코드이다.

 

256 바이트의 test를 해제한 후, 같은 크기의 string을 할당하면 해제되었던 test의 위치에 메모리가 할당된다.
이때 test를 해제했다는 사실을 깜빡하고 test의 값을 호출하면, 어떤 값으로 바뀌었을지 모르는 포인터를 참조하게 된다.

 

test 구조체가 해제된 후, 같은 위치에 string 구조체가 할당되는데,
이때 test 구조체의 멤버 변수 중 print 라는 함수 포인터의 값을 덮을 수 있다.
원래는 printName 함수의 주소가 들어있지만, 이 값을 shell 함수의 주소로 덮은 뒤 마지막의 test->print(t1) 가 실행되면 원래의 printName 함수 대신 shell 함수가 실행된다.

 

정리 : test 구조체 포인터 변수 t1은 여전히 해제된 메모리를 가리키는 주소를 가지고 있으며, 해당 영역의 값은 초기화되지 않았기 때문에 구조체 멤버에 접근해서 실행시킬 수 있는 것이다.

 

 

 

참고
https://bpsecblog.wordpress.com/2016/10/06/heap_vuln/
https://www.lazenca.net/pages/viewpage.action?pageId=1148139

 

'Hacking > Pwnable' 카테고리의 다른 글

[Heap exploitation] Fastbin dup  (0) 2021.05.11
[Heap exploitation] Heap Overflow  (0) 2021.05.08
[Pwnable] Heap3  (0) 2021.05.06
[Pwnable] Heap2  (0) 2021.05.05
[Pwnable] Heap1  (0) 2021.05.05
복사했습니다!