starCTF 2021 babyheap
1 分析
题目包含一个可执行程序和一个libc。
程序提供了六个功能,在delete中free后未将指针置NULL,导致UAF。
tcache
libc版本是2.27,free后的chunk会被扔到tcache中。由于本题所用的libc做了改动,无法通过double free形成一个环状以达到任意地址写。
add一次,delete两次后,堆空间状态如下。可以看到tcachebins中只有一个free chunk,并且其bk的位置被写入了0x0000555555757010。就是这个标志导致无法free两次成一个环状,可通过edit将该处写成别的值,绕过检查。但是由于edit无法写到fd的位置,所以还是无法简单地用tcache double free的方法利用。
1 | gef➤ heap bins |
fastbin
tcache中无法利用,那么把tcache的一条链填满,让free chunk进入fastbin中。
题目源码里leaveyouname()函数中会申请一个0x400的超大chunk,申请超大chunk时会触发fastbin中的free chunk合并——跟top chunk挨着的会合并到top chunk中然后分配出去,不跟top chunk挨着的会合并起来放到unsortedbin或者smallbin中。
进入unsortedbin或者smallbin中的堆块会有main_arena的地址信息,可以通过泄露这个地址进而泄露libc。
add 16个不同的index(从0到15),然后全部delete掉,得到如下bin状态。06在tcache中,715在fastbin中。此时申请大堆块,会将715全部合并到top chunk,然后分配给新的申请。这样将不会有chunk进入unsortedbin或者smallbin。所以需要在715中保持一个chunk不释放,将原本空间连续的chunk分成两部分。
比如,将index为10的chunk不delete。这样在申请大堆块时,79会合并到smallbin中,1115会合并到top chunk中然后分配给新的申请。
1 | gef➤ heap bins |
利用上述思路,泄露main_arena地址进而获得libc基址的代码如下:
1 | from pwn import * |
UAF
上述11~15 fastbin chunk跟top chunk合并后分配给了leaveyouname中的name=malloc(0x400)。
此时name指向1115,add函数中的pools[11]pools[15]也指向该处(delete函数中free pools[]后未将其置NULL)。
所以可以借助name的输入布置chunk,再通过pools[]数组操作。
1 | for i in range(16): |
执行完leaveyouname后的堆空间:
1 | gef➤ heap chunks |
EXP
1 | from pwn import * |