如果给得起,就在 top chunk 的 head 处,以用户申请大小所匹配的 chunk 大小为偏移量,将 top chunk 的位置推到新的位置,而原来的 top chunk head 处就作为新的堆块被分配给用户了;
如果我们能控制 top chunk 在这个过程中偏移到任意位置,也就是说,如果我们能控制用户申请的大小为任意值,我们就能将 top chunk 劫持到任意内存地址,然后就可以控制目标内存。
简言之
溢出已经分配的 chunk,覆盖到 top chunk 的 size 位;
算出 top chunk 与目标地址的距离,将 top chunk 位置推到目标地址
溢出 top chunk
当我们 malloc 一个堆块,此堆块的下一个就是 top chunk 时,当我们输入的数据大小能够超过申请的大小,就能堆溢出到 top chunk,当我们将 top chunk 的 size 字段改得非常大时就可以通过检查了,一般我们会传入 -1 ,因为 ptmalloc 的源码中对于 size 使用 unsigned long 进行强转抓换,负数用补码表示,将 -1 当成无符号数为 0xffffffffffffffff ,已经非常大了,用于绕过 if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) 验证。
例题
buu 上的 bcloud_bctf_2016
程序开的保护如下
本题最大的漏洞就在这两个函数中
func1
s 处输入 0x40 个字符能够覆盖到指针 v2 ,之后的堆地址又赋给了 v2 ,意味着 v2 处 s 的 0截断字符 被覆盖成了堆指针,从而在 strcpy 处可以泄露处可以泄露出堆地址
func2
也是同理,并且因为 v3 和 s 只相差 4 个字符的缘故,可以修改到 top chunk 的 size 位
由于程序没有开 pie ,因此地址之间的偏移可以直接算出来,达到了 house of force 的条件,计算出 top chunk 和 chunk_addr (heap array) 的地址,就可以将 top chunk 指针 指向 heap array 从而控制整个堆指针数组,实现任意地址的读写。