hitcon2014_stkof(unlink)
很经典的unlink入门题。
还是菜单题,没什么好说的。
4个函数,一个申请,一个编辑,一个free,一个意义不明(?)。
简单的来说,在sub_4009E8()里存在堆溢出漏洞,没有检查长度。然后在sub_400B07()里free掉chunk之后是会把bss段上那个数组相应的位置清0的。
还有一点就是,这题没有使用setvbuf()来关闭缓冲,所以在第一次输入和输出的时候都会malloc 0x411的chunk作为缓冲区(但是gdb调试的时候发现,输入的缓冲区会开到0x1011)
没有开FULL RELRO,所以got表还是可以改的。
由于没有打印堆块的功能,所以这题需要用到unlink来改写某个函数的got表指向puts@plt(没错,我又要改free的got表)
还是分为两步来做:
- leak libc:
准备工作:申请一个chunk,把输入输出缓冲区的malloc给弄出来,保证后面的chunk都是连续的。
然后申请一个用来uaf的chunk和一个被free后能放进unsortedbin的chunk。再申请一个chunk保证要free进unsortedbin的chunk不与topchunk物理相邻(其实没必要,反正unlink的时候能和前面的fakechunk合并就行,但是这个chunk后面可以用来存”/bin/sh”)。
unlink的作用简单来说,就是可以把一个指向能造成uaf的chunk的指针改写成其位置-0x18(x86下是-0xc)
假设能造成uaf的chunk的指针为ptr,那么unlink成功过后就能让*ptr = ptr-0x18。这样一来就可以去改一些东西了。(fastbin和smallbin是没有unlink机制的)
unlink最麻烦的地方在于如下检查机制:
1 | // 由于 P 已经在双向链表中,所以有两个地方记录其大小,所以检查一下其大小是否一致(size检查) |
检查下一个chunk的prev_size和当前chunk的size是否相同,这个很好解决,但是为了不让我们利用unlink随意伪造fd和bk,检查了FD->bk和BK->fd是否都指向当前free掉的chunk,这就有点令人头疼了。
不过,利用malloc申请到chunk的时候,返回给数组的那个位置(我们假设他为ptr),以ptr-0x18为chunkheader的一个fakechunk的bk位置刚好就会是ptr,以ptr-0x10为chunkheader的fakechunk的fd位置也刚好是ptr。这样一来,只要把被free掉的chunk(也就是header是*ptr的那个chunk)的fd和bk分别设置为ptr-0x18和ptr-0x10就能顺利unlink,把 *ptr的值改为ptr-0x18,以此来达到相关目的了。
这道题由于free掉之后会把数组那一位指针清0,所以需要我们自己构造一个被free掉的fakechunk。
因为这个fakechunk的位置需要在数组能找到,而数组刚好存的都是userdata的位置,也就是每个chunk位置后面0x10的位置,而这0x10就是fakechunk的header。(让这个fakechunk和那个会被放进unsortedbin的chunk物理相邻,虽然没法直接让那个chunk触发unlink,但是可以让他和这个fakechunk合并的时候触发这个chunk的unlink)
一定要注意,得把下一个chunk的size从0x91改成0x90,不然prev_inuse位被标记的时候是不会前向合并的。
这样一来就成功把*arr[2]改成arr[2]-0x18了,这个时候利用他把free的got表写到这个数组上来,然后改成puts@plt去打印出能够leak libc的一个地址。
这里我把free的got表和puts的got表分别写到了arr[0]和arr[1]的位置,然后把free的got表改成puts的plt表,再执行free(1)就能leak libc了。
- get shell:
因为got表是可以改的,那getshell就很简单了,我是把free的got表又改成了system@plt,然后去free之前那个填好了”/bin/sh”的chunk就完成了。
exp:
1 | from pwn import * |