知识点
- top_chunk的size大小一定是页对齐的,也就是0x?000,低三位要保持为0
- 保证篡改后不报错,则需要拿top_chunk的地址+size为页对齐即可
- 想要控制篡改后的size则可以通过先申请一个chunk来调整top_chunk的size后在改写
- 需要注意的是top_chunk的inuse位一定是1
- 当top_chunk的大小不够申请时(不能超过mmap阈值,否则会使用mmap来申请chunk),旧的top_chunk,先切分出两个0x10大小的chunk,剩下的空间如果不小于最小的chunk则使用int_free释放掉
程序分析
- heap题目功能有创建chunk以及输出chunk,没有释放
- add使用了gets函数,无限heap溢出
解题思路
- 通过unsorted-bin泄露出libc_base
- 通过tcache attack申请到malloc_hook的地址
- 改写为one_gadget
具体操作
-
没有free操作,我们可以通过改写top_chunk的size大小,申请0x20的chunk改写top_size,后申请一个比改写后大的chunk,得到一个在small-bin中的chunk,再次申请就会切分small-bin中的chunk,剩下的就会进入unsorted-bin
-
申请的chunk会从新的top_chunk中切分(高地址处),通过之前申请的0x18改写unsorted-chunk的size使其可以达到chunk2处,这样通过再次申请chunk,切分改写后的unsorted-chunk,使剩下的chunk刚好落在chunk2,输出就可以得到main-arena+offset,进而得到libc_base
- 这里需要注意的是,有以下安全检查,所以我们还需要在chunk2里面布置一下
- 第一个if:unsorted_chunk的大小不能小于或大于阈值
- 第二个if:下一个chunk(victim+size),不能小于大于阈值
- 第三个if:next_chunk的presize要和victim的size一致
- 第四个if:检查链是否被破坏,这里的话注意一下溢出的时候不要覆盖掉fd低位就行
- 第五个if:next_chunk的pre_inuse位需要为0
- 使用tcache_attack(类似fastbin attck)比fastbin-attck容易的多,因为tcache有记录chunk个数,所以我们需要让两个chunk进入相同的tcache链,操作如上,看exp
- tcache_get里面没有任何检查,所以我们直接写入malloc_hook的地址就可以直接申请出来
- 第一次的unsorted-bin至少需要保留0x20大小,这样我们申请出来,相比于tcache_chunk所在的位置是低地址,通过无限溢出,改写tcache的chunk为malloc_hook
- 写入one_gadget,获取shell
注意点
- 我们既然我们保留了unsorted-chunk一个chunk大小,我们就要注意后面申请的chunk不能低于或等于他,否则就会被直接申请走,后面我们就无法通过他来改写
- 注意伪造unsorted-chunk来绕过检查
exp
from pwn import *
context.update(os='linux',arch='amd64',log_level='debug')
#c=remote(b'node4.buuoj.cn',25937)
c=process(b'./heap2')
libc=