Pwnable/Techniques

Double Free Bug

Anatis 2021. 2. 23. 21:58

Double Free는 해제된 힙 청크를 다시 해제할 때 발생하는 버그이다. 힙을 해제하면 bin 이라는 연결 리스트에 추가되고, 힙을 재할당할 때 참조 된다.

 

해당 버그가 발생하면 연결 리스트에 중복된 힙 주로를 추가하기 때문에 두 개의 객체가 동일한 메모리를 사용할 수 있다.

ptmalloc2 할당자는 Double Free를 방지하기 위해 동일한 힙 청크를 연속으로 여러 번 해제할 수 없도록 하는 코드가 존재한다.

 

dfb1.c

// gcc -o dfb1 dfb1.c
#include <stdlib.h>

int main()
{
    char *ptr = malloc(32);
    char *ptr2 = malloc(32);

    free(ptr);
    free(ptr);

    return 0;
}

32 바이트의 크기로 할당된 청크를 두 번 연속 해제하는 예제이다. 예제를 실행하면 "double free or corruption (fasttop)"을 출력하고 비정상 종료 한다.

 

에러가 발생하는 원인 코드

if (__builtin_expect (old == p, 0)
{
       errstr = "double free or corruption (fasttop)";
      goto errout;
}

이전에 해제한 힙 청크의 포인터인 old와 현재 해제할 힙 청크의 포인터인 p가 같다면 에러 메세지를 출력하고 비정상 종료한다.

 

힙을 할당하고 해제하면 할당된 힙 청크 주소가 크기에 맞는 bin에 들어가고 같은 bin의 크기로 재할당 요청이 오면 순차적으로 할당이 된다.

 

만약 Double Free가 발생해 중복된 주소가 bin에 들어가 있으면, 같은 크기로 할당 요청이 여러 번 들어왓을 때 동일한 메모리에 두 개의 객체가 할당될 수 있다. 이때 입력이 가능하다면 free된 힙 청크의 메타데이터를 조작할 수 있다.

 

                                                                                                                                                                                      

dfb2.c

#include <stdlib.h>

int main()
{
    char *ptr = malloc(32);
    char *ptr2 = malloc(32);

    free(ptr);
    free(ptr2);
    free(ptr);

    return 0;
}

dfb2.c는 old와 p 포인터를 다르게 하여 검증을 우회하고 Double Free를 발생시키는 코드이다.

 

마지막 ptr이 해제될 때 old 포인터는 ptr2의 주소가 저장되고, p 포인터에는 ptr의 주소가 저장되면서 old와 p는 다른 주소를 가지기 때문에 검증을 우회하여 Double Free가 발생한다.

 

ptr2를 free를 한 직후의 fastbin freelist이다.

Double Free가 발생하기 전의 모습이다. FD 포인터는 해제된 영역을 순차적으로 가리키고 있다.

 

ptr을 다시 한번 해제한 이후의 fastbin freelist이다. 중복된 주소가 freelist에 들어가 있는 것을 확인할 수 있다.

 

fastbin에서 Double Free가 발생하고 같은 bin의 크기로 할당 요청이 들어온다면 0x602000, 0x602030, 0x602000 주소에 순차적으로 힙을 할당하여 두 개의 객체가 하나의 메모리를 사용할 수 있게 된다.