house of water : 一种无泄露情况下的堆利用手法
| 0 | 前言
一种可以在没有显式打印函数存在情况下,将libc
指针写入tcachebins
的手法
虽然条件苛刻但是思路十分巧妙
| 1 | 利用条件
- 我们可以对堆块进行
use-after-free
操作 - 能够
malloc/free
一个较大堆块(0x9000
)
无需溢出与泄露内存即可完成攻击
我们在攻击的过程中需要几枚悬空指针,如果在真实ctf
环境中要先堆好风水,预留好指针的位置
| 2 | 攻击演示
0x00设置伪造chunksize
当我们申请了0x3d0
与0x3e0
两种堆块并释放后,在tcachebins_struct
中会出现一个0x10001
1 | add(0,0x3d8) |
如下
这个可以作为我们伪造的size
,让ptmalloc
误认为这里存在一个大小为0x10000
,已被申请的堆块
假如这个大堆块伪造成功,这个堆块的内容便与tcachebins_struct
中的指针数组重叠(0x20,0x30的指针)
如将libc
写入,我们就可以从tcachebins
中申请libc
地址
0x01填充big_chunk
1 | for i in range(7): |
七个堆块不仅要用来填充0x10000
的伪造chunk
,还需要堵住tcachebins
中0x90
的位置,让其他释放的0x90
堆块不进入tcachebins
剩下的三个0x88
的堆块在后续中的作用关键,它们需要进入unsort_bins
中,所以使用0x20
的堆块将它们隔开
最后使用0xf000
不满0x10000
的堆块,并在最后申请的小堆块中伪造pre_size
与size
此时big_chunk
的前(pre_inuse:0x10001
)后(presize:0x10000
)都设置好了
0x02填充tcachebins
1 | for i in range(7): |
前面说的,将tcachebins
中的0x90
部分填满7
0x03伪造big_chunk的fd,bk
我将chunk[9,11,13]
分别命名为usort_start
,usort_middle
与usort_end
1 | usort_start=0x000055555555be60 |
如果我们想将libc
指针写入bigchunk
,我们就要利用unsortedbins
中堆块被申请后脱钩
时不会清空fd
,bk
指针的操作
而我们不能直接free
掉big_chunk
(会产生double free or corruption (!prev)
)
我们就想能不能通过末尾覆盖fd/bk
,将一个合法的unsortedbins
修改为big_chunk
此时我们通过以上free
操作,将usort_start-0x8
与usort_end-0x8
写入了teachebins
的0x20/0x30
指针处,也就是 big_chunk 的fd/bk
处
图中白框处同时是big_chunk
的fd/bk
0x04伪造unsortedbins的fd/bk
1 | free(13) |
现在我们只需要再将usort_start
与usort_end
放入unsortedbins
并修改着两个堆块的fd/bk
指针为heap_base+0x80
就可以手动形成一个unsortbins
链
在未修改前,unsortedbins
的结构
1 | unsortedbin |
其中0x55555555be50
为usort_start
,0x55555555bfb0
为usort_end
通过uaf
部分写usort_start.fd
与usort_end.bk
,会将unsortedbins
的结构修改为(只差了三个字节,所以还得爆破4bits
)
1 | unsortedbin |
可以看到0x55555555b080(big_chunk)
已经成功取代0x55555555bf00(usort_middle)
0x05将libc
印在teachebins
如果此时我们如果申请一个大于0x90
的堆块,ptmalloc
就会从big_chunk
中取出堆块,并在脱钩时将libc
写入申请的chunk
的fd/bk
位
1 | add(20,0x388) |
成功在未泄露的情况下申请到libc
指针
在glibc
源码中
✅ malloc 为了防止“被重用的 freed chunk”再次参与链表,防御 unsorted bin attack,这里会手动清除 fd/bk。
glibc 的开发者用一种”自指向 main_arena 的方式”来标识:这个 chunk 已经被取出,不应该再进 bin。
| 3 | 总POC
1 |
|
1 | from pwn import * |