刚学了unlink,总结下利用方式和思路
0x0 unlink漏洞分析
概览
free函数在释放堆块时,会判断相邻前,后堆块是否为空闲堆块,是就会进行合并,然后利用unlink机制将该空闲堆块从unsorted bin中取下.
源码细节
触发条件
1 | if (!prev_inuse(p)) { |
可以让一个构造出来的fake chunk
被unlink
导致一次固定地址写.
1 |
|
这里需要绕过检测__builtin_expect (FD->bk != P || BK->fd != P, 0),使P->fd->bk == P && P->bk->fd == P
为真.
所以fake chunk
可以如下构造.
1 | fake_pre_size | fake_size | &target - 0x18 | &target - 0x10 |
0x1分析程序
通过反汇编可知,程序是一个笔记本程序,可以对笔记本进行增加,删除,修改操作。我首先考虑栈溢出的情况,但是没有找到可以溢出的位置。然后又寻找uaf和double free漏洞是否存在,发现所有位置在free后都置为了0,也没有这个漏洞。最后发现它在修改操作上出现了一些问题。
它进行的修改操作:
- 输入要修改note的索引
- 检测该索引是否有note存在,若无则退出程序
- 输入要修改后内容的最大字节数
- 输入内容
可以想到如果我们输入的修改后的最大字节数比一开始malloc的空间大的话,我们就可以在输入时覆盖掉当前这个note后面的chunk的内容。我们就可以用unlink的方式实现任意地址写
0x2利用方式
写好增加,删除,修改的函数,避免写一些重复。
1 | def add_note(size, stri): |
申请三个note,使其足够大,避免free后被加入到fastbin中。因为fastbin中的空闲块不会合并,自然也就无法利用unlink了
1 | payload = p64(0) + p64(0x90) + p64(buf-0x18) + p64(buf-0x10) + 'a'*0x70 + p64(0x90) + p64(0xa0) |
这时构造payload,unlink操作后buf = &buf - 0x18
0x3 exp
1 | from pwn import * |
0x4 总结堆溢出&& unlink
如果程序存在堆溢出漏洞,可以在当前chunk
伪造一个小0x10的fake chunk
,同时将下一个chunk
头部的prve size
修改,然后free
下一chunk
触发unlink
。
1 | pre_size1 | size1 | fake_pre_size = pre_size1 ? pre_size1 + 0x10 : 0 | fake_size = size1 - 0x10 | &target - 0x18 | &target - 0x10 | padding | fake_size align | size2 & ~1 |
要求target = P
.结果target = &target - 0x18
.