PWN-堆基础之Safe Unlink
Safe Unlink
简介
Safe Unlink增加了两个检测机制。
首先,检测大小是否一致,即检查next_chunk的prev_size和当前chunk的size域是否相等。
然后,检测双向链表完整性,即检查chunk.fd -> bk == chunk、chunk.bk -> fd == chunk。
条件
可以泄露保存chunk指针的数组地址。
存在堆溢出漏洞可以修改next_chunk的size域 或 存在UAF漏洞。
可以泄露libc地址。
利用
为方便描述,下文称chunk指针数组 = m_array。
- 在当前chunk数据段伪造一个fake_chunk,使得fake_chunk的fd指向&m_array - 0x18,bk指向
&m_array - 0x10。(绕过双向链表检测)
- 将next_chunk的prev_size修改为当前chunk大小,然后将size域中prev_inuse置0。(绕过大小
检查,prev_size用于程序定位到fake_chunk)
- free(next_chunk),程序会将next_chunk和fake_chunk合并:
程序会顺着fd指针,找到&m_array - 0x18,然后在程序认为的bk指针位置(即m_array位
置)填入&m_array - 0x10的地址。
然后会顺着bk指针,找到&m_array - 0x10,然后在程序认为的fd指针位置(即m_array位
置)填入&m_array - 0x18的地址。
- 此时,m_array数组的第一个指针指向&m_array - 0x18,我们填充 0x18垃圾数据 +
&__free_hook - 0x8,再次编辑即可修改freehook的内容。
即如图所示:
这时可以看到,我们伪造的被free的chunk_A中的fd->bk==0x603010
,但这个值并不是chunk本身,而是chunk中的user data起始位置
m_array
中的值是没办法直接改动的,因此我们在伪造chunk的时候干脆连chun A的起始位置一起伪造,伪造出一个从0x603010
开始的chunk,即在上面第二部构造next_chunk的prev_size修改为当前chunk - 0x10。
这样,我们就可以绕过safe unlink的缓解机制检查了。
之后,通过free(b)进行unlink,会将0x602060位置的内容写为0x602048,再使用edit()执行时就是往0x602048处写内容,也就是可以再次修改m_array的内容我们再修改一次m_array[0],将其修改为我们想要写入值的地址,比如修改为__free_hook的地址,再次malloc即可向 _free_hook处写内容,因此可以修改freehook为system了。
demo:
分析该程序主要函数,发现用选项3的时候最终实际上就是free(m_array[nb].user_data)
所以直接调用free(0)
就会调用system("/bin/sh")
因此,完整exp:
1 | chunk_A = malloc(0x88) |
tcache版本:
1 | heap_base = add(0,0xf8) - 0x260 |