个人理解
感觉House of Botcake就是double free + overlapping + _IO_FILE attack的结合使用,需要对堆结构有着较为详细的理解,也要有能管理堆顺序的能力,建议写题的时候还是标一标堆的index,这样在利用堆的时候会比较方便些
House of Botcake
想要利用这个攻击,我们得先了解_IO_FILE的结构
_IO_FILE
结构
1 | pwndbg> p stdout |
_flag详情
1 | #define _IO_MAGIC 0xFBAD0000 /* Magic number */ |
我们需要关注的只有三个,_IO_MAGIC 0xFBAD0000,_IO_IS_APPENDING 0x1000,_IO_CURRENTLY_PUTTING,通常情况下_flag为0xfbad2887,而我们攻击的时候常常需要把_flag修改为0xfbad1800,这正好对应这三个flag值
解释
为什么要这样设置呢?
首先,我们在这种攻击方式下,用的基本是puts函数,puts函数会经历以下函数调用
puts->_IO_file_xsputn->_IO_file_overflow->_IO_do_write
下面我来详细解释下这些flag值的作用
当_IO_CURRENTLY_PUTTING=0x800时通常表示当前文件流正在进行写入操作。
IO_IS_APPENDING=0x1000表示文件流是否以追加模式打开。
IO_MAGIC=0xFBAD0000表示**_IO_FILE结构是有效的**
在_IO_FILE有效的情况下,因为我们以追加模式进行写入操作,如果我们把_IO_write_base,_IO_write_ptr分别设置为想要泄露的地方,结束泄露的地址,我们就可以通过environ函数的存在泄露出栈地址,从而获得当前函数的返回地址,进行一系列操作
原理
在libc大于2.27版本之后,为了防止tcache攻击过于简单而引入了tcache_key,对应的位置是堆块的bk。当堆块被free进入tcache_bin后,如果遍历该大小的tcache_bin发现有相同地址的堆块就会触发double_free警告,从而被killed掉
而House of Botcake,充分利用了这一机制,如果将同一堆块一次free进unsorted_bin里(没有key),一次free进tcache_bin里,是不是就绕过检查了呢?这样就让double_free再度成为可能
大致思路
先申请十个堆块,然后一如既往填满tcache先,第十个堆块用来防止与top chunk合并

接着再free第九个堆块(index=8),使它进入unsorted_bin

此时完成了把目标堆块扔进unsorted_bin里,但我们还需要再free第八个堆块,此时会触发chunk合并


这种攻击方法常常需要修改fd指针来达成我们需要的操作,我们目标堆块尾地址为720,为了控制到该堆块,我们先申请一个多余堆块给目标堆块腾位置

接着我们再次free第九个堆块,实现double_free

此时我们完成了我们的目的,实现了double_free,接下来就是切割unsorted_bin的堆块了,这里我选择切割0x70的堆块

那么下次我们再申请出0x70的堆块时,data域就是710,对应720的prev和size,我们就可以轻而易举操控fd啦。后续还可接着free这两个堆块进行长久地fd指针修改,进行任意操作,整个流程差不多如此。
例题:[CISCN 2022 华东北]blue
代码审计
main
为了方便,我把去符号表的函数名都修改为了对应的功能函数名

add

delete

把指针置空了,没有UAF漏洞
show

只有一次show的机会
only_UAF

同样,也只有一次UAF的机会
思路
这个题比较特殊,只有一次UAF和show的机会,所以我们只能把UAF和show作用于泄露libc,这样就可以找到_IO_2_1_stdout_,利用House of Botcake将_IO_FILE的_IO_write_base和_IO_write_ptr修改为environ,environ+8,通过puts泄露add的栈地址,从而获得ret的地址。再次free目标堆块和辅助堆块,再次通过House of Botcake修改目标堆块的fd为add_ret地址,申请到栈上的同时往栈布置orw链即可
动调
许多步骤和之前是一样的,我就不重复演示了,直接从切割0x70chunk开始

可以看到,我们已经把目标chunk的fd修改成了stdout,我们再申请进去后修改它的结构为environ即可在puts泄露栈地址


在puts时就会泄露出environ了

再把目标堆块和辅助堆块free出来进行伪造chunk




到这就结束啦!
Payload
1 | from pwn import * |
总结
这个玩意感觉挺考验细心程度和对堆的理解,一个不留神就会弄错索引,伪造失败。堆好多house啊——什么时候才有我的house——