一道tache的题目,开始学tcache的时候没有调通,今天正好室友也在调这个题目,就又参考CTF-WIKI上的exp调了一下。
题目描述 & 题目漏洞
题目有malloc、free和show三个功能,题目开始calloc了一段内存来存储后续分配的堆块指针和size,题目最多申请10个chunk,idx0~idx9,每次的大小都是固定的0x100,程序接受用户输入的my_read函数有一个off by null的漏洞,但是题目会过滤掉’\x00’的字符,因为题目每次申请的chunk都是固定的0x100,所以在进行常规的chunk overlapping时,不能伪造诸如0x200、0x100的prev_size,利用过程比较巧妙地是利用unsortedbin每次合并或切割chunk时写入prev_size,从而构造unlink的条件。
1 | unsigned __int64 __fastcall my_read(_BYTE *buf, int size) |
另外libc版本是2.27,有tcache,在利用过程中要注意tcache的填充。
利用过程
利用思路:\
首先申请9个chunk并全部释放,idx6~idx8进入到unsortedbin中,prev_size分别为0x100、0x200、0x300,这里的prev_size说的不太准确,应该是freed chunk与下一个chunk复用的数据域填充的是这个chunk的size。
1 | for i in range(7): |
1 | gdb-peda$ x /8gx 0x555555757900+0x100 #idx6 A |
再申请7个chunk,清空tcache,然后申请3个chunk,即idx7~idx9,分配idx7时A的prev_size直接分配到idx7中,不会变化,C的orev_size变为0x200,分配idx8时,B的prev_size分配到idx8中,0x200不会改变,C的prev_size变为0x100,最后分配idx9,C的prev_size最终变为0x100。
1 | for i in range(7): |
1 | gdb-peda$ x /8gx 0x555555757900+0x100 #idx7 A |
要构造idx8的chunk overlap,需要idx7是freed的状态,利用off by null修改idx9的size由0x101变为0x100,如果释放idx9,触发unlink,通过0x200的prev_size找到idx7,因为idx7是freed状态,可以进行合并,因此0x200+0x100=0x300进入到unsortedbin中,造成了B块的重叠。要注意tcache的填充。
1 | for i in range(6): |
这时包含idx0的chunk进入到unsortedbin中,再申请一个0x100的chunk,show(0)就可以泄露libc:
1 | ##leak libc |
此时申请idx9,它的地址与idx0是相同的,然后分别释放构造double free,exp这里先释放了idx1和idx2是因为题目限制chunk的数目为10,此时只能申请一个idx9了,double free需要再申请3个chunk,因此先释放两个,最后修改free_hook为one_gadget,然后释放一个chunk触发get shell。
1 | add(0x10,'aaaa\n') #9 == 0 |
完整exp如下:
1 | from pwn import * |
参考
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/tcache_attack-zh/