[TOC]
前置知识
House of Spirit 是 the Malloc Maleficarum
中的一种技术。
该技术的核心在于在目标位置处伪造 fastbin chunk,并将其释放,从而达到分配指定地址的 chunk 的目的。
要想构造 fastbin fake chunk,并且将其释放时,可以将其放入到对应的 fastbin 链表中,需要绕过一些必要的检测,即
- fake chunk 的 ISMMAP 位不能为 1,因为 free 时,如果是 mmap 的 chunk,会单独处理。
- fake chunk 地址需要对齐, MALLOC_ALIGN_MASK
- fake chunk 的 size 大小需要满足对应的 fastbin 的需求,同时也得对齐。
- fake chunk 的 next chunk 的大小不能小于
2 * SIZE_SZ
,同时也不能大于av->system_mem
。 - fake chunk 对应的 fastbin 链表头部不能是该 fake chunk,即不能构成 double free 的情况。
至于为什么要绕过这些检测,可以参考 free 部分的源码。
ZJCTF 2019]EasyHeap
==uaf + 堆溢出 + house of spirit + double free==
学习与收获
- 如果想覆盖任意地址的数据,那么可以在任意地址或其前面构造一个
fake_chunk
,然后使fastbins
中的fd
指针指向这个fake_chunk
的首地址(可以通过堆溢出),我们便能通过从fastbins
获取chunk
的方式来改写任意地址里的数据。 fastbins
中是以单链表形式管理fastbin_chunk
的,每个fastbin_chunk
的fd
指针指向链表中的下一个fastbin_chunk
,最后一个fastbin_chunk
的fd
的值是0
。- 程序中通常会有一张表来管理申请的
chunk
,本题中,heaparray
数组中的一个单元指向一个chunk
。
思路
整体思路:
程序中有_sytsem_函数,_RELRO_保护是_Partial RELRO_,所以我们可以劫持_free_的_got_表为_system@plt_,再_free_一个_chunk_即可得到_shell_。
程序中通常会有一张表来管理申请的_chunk_,本题中,_heaparray_数组便存放着我们申请的每一个堆块的内容地址。我先随便申请了两个_chunk_,0x6020e0
是_heaparray_的首地址,如图:

构造fake_chunk加入fastbins
我们需要在heaparray的地址前面找到一个合适的地址,来错位偏移构造一个size为0x7f
的fake_chunk,如图,以0x6020ad
为fake_chunk的首地址。

然后将fake_chunk添加到fastbins中,也就是让fastbin_chunk0的fd指针指向0x6020ad
这个地址。通过chunk1溢出,然后覆盖fastbins_chunk0的fd指针为0x6020ad
即可。

add(0x60,"a"*8) |
写free_got到heaparray[0]
把free_got
写到heaparray[0]
,这样后面修改chunk0的值为system_plt,其实就是修改free@got表的值为sysytm_plt,那么怎么写呢?
add(0x60,"eeee")#2 |
先再创建两个chunk,第一个就是释放后的fastbins_chunk0,即fastbin中地址为0x1d000e0
的chunk。然后我其实在_heap_中只看到3
个chunk,一直不知道chunk3去哪儿了。(其实chunk3就是前面构造的fake_chunk)

后来想到前面fastbin中的fastbins_chunk0的fd指针指向的是0x6020ad
,所以就看了这个地址,里面果然是chunk3的数据,也就是说chunk3就是前面构造的fake_chunk。如图可以看到写入的0x20*"f"
:

而0x6020ad
后面正好是数组heaprray的地址0x6020e0
,也就是说我们可以通过堆溢出填充chunk3到heaprray[0],并修改heaprray[0]的值。(这也是为什么前面要在heaparray的地址前面找合适的地址来构造fake_chunk了)
写system_plt到free_got
现在,heaparray[0]
的值是free_got
,我们直接修改chunk0的内容为system_plt
。然后再delete(1)
call free的时候,调用的其实是system函数。
payload = p64(elf.plt["system"])#修改free@got表为system的地址,后面再call free的时候,调用的其实是system函数 |
exp
from tools import * |
总结
如果想覆盖任意地址的数据,那么可以在任意地址或其前面构造一个fake_chunk
,然后使fastbins
中的fd
指针指向这个fake_chunk
的首地址(可以通过堆溢出),我们便能通过从fastbins
获取chunk
的方式来改写任意地址里的数据。