house of cat -2022强网杯pwn复现
前几天进行了 house of apple
的学习,而 house of appl2
和 house of cat
利用的大致思想是一样的(都是通过 wide_data->wide_vtable
中的函数指针进行的跳转),因此来复现一下去年强网杯的这道 house of cat
本题我感觉也比较有代表性,因为在 house of apple
的那篇文章中的例题最后触发攻击是在 exit
函数,但是如果题目中无法从 main
函数返回也没有 exit
函数,那就需要通过 malloc_assert
来触发最后的攻击,而本题就是通过这样的方式触发的攻击。
如何通过 malloc_assert
触发攻击
__malloc_assert
函数会在内存分配处理之前检查请求是否合法,如果检测到不合法的请求就会触发断言并终止程序,触发这个 __malloc_assert
函数有很多处,通常我们选择将 top chunk
的 size
改成非法(在 sysmalloc
函数中有针对这里的检查),这样再次申请堆块的时候就会触发 __malloc_assert
__malloc_assert
在 2.35
的 glibc
中源码如下
static void |
这里有这样一条执行链 __malloc_assert-> __fxprintf->__vfxprintf->locked_vfxprintf->__vfprintf_internal->_IO_file_xsputn
最后触发的 _IO_file_xsputn
是通过 vtable
中的函数指针来触发的,我们想要去劫持的话,首先将 _IO_2_1_stderr
结构体中的 vtable
改成 _IO_wfile_jumps+0x10
的地址(加 0x10
的原因是 _IO_file_xsputn
的地址在 _IO_file_jumps
中比 IO_file_seekoff
的地址低 0x10
个字节),这样原本跳转执行 _IO_file_xsputn
时,实际上执行的是 _IO_wfile_seekoff
如下图
_IO_wfile_seekoff
函数源码如下
_IO_wfile_seekoff (FILE *fp, off64_t offset, int dir, int mode) |
我们执行 _IO_wfile_seekoff
函数的目的就是为了触发 _IO_switch_to_wget_mode
函数
_IO_switch_to_wget_mode
函数源码如下
_IO_switch_to_wget_mode (FILE *fp) |
执行 _IO_switch_to_wget_mode
函数的目的就是为了触发 _IO_WOVERFLOW
,因为这个 _IO_WOVERFLOW
函数是通过 _wide_data->_wide_vtable
中所存放的函数指针进行跳转的, _wide_vtable
是我们可控的,从而在这里可以劫持程序的执行流。
想触发最后的 _IO_WOVERFLOW
,需要满足 fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
这个条件。
之所以先提上面的部分是因为本题接下来用的手法只有上面部分是 house of apple2
中没有提到的,其余部分都和 house of apple2
中的利用思路相似,就不再详细说明。
house of cat
附件:
链接: https://pan.baidu.com/s/1BSiI9TmmU7uqMr7Ou3bIxQ?pwd=ccp4 提取码: ccp4
保护策略
保护拉满,沙箱是白名单只能打 orw
,需要注意一下 read
的第一个参数只能设置为 0
,所以最后打 orw
之前需要先 close(0)
程序逻辑
程序是一个菜单的堆题,不过在使用程序的主要功能之前,需要输入一些数据来绕过这个检查,可能自己的逆向能力还得提高吧,反正这里的检查我是搞了好久,结论就是最开始输入 LOGIN | r00t QWB QWXFadmin
去进行登录,接下来每一次调用具体功能之前都要发送一句 CAT | r00t QWB QWXF$\xff
,接下来才能去执行正常的功能。
功能一共有四个 add
edit
show
delete
edit
函数只能使用两次,并且只能写入 0x30
字节的数据
delete
函数存在 UAF
漏洞
add
函数申请的堆块大小的范围是 0x418~0x46f
,申请完堆块后可以向里面写入 size
字节的数据
show
函数只能泄露 0x30
字节的数据
利用思路
- 泄露
libc
地址和堆地址 - 利用
edit
函数完成第一次large bin attack
向libc
中的全局变量stderr
写入一个堆地址,从而控制_IO_2_1_stderr
结构体的各个字段 - 第二次
large bin attack
去篡改top chunk
的size
将其改为非法(要往小了改,因为只有top chunk
无法满足要申请的size
时,才会触发sysmalloc
) 注意large bin attack
想将top chunk
的size
改小的话,需要地址错位 - 申请一个堆块,此时执行
__malloc_assert
触发攻击
思路不难,难点在于整体的一个堆风水和结构体布局需要慢慢调试,因为文章开头已经说明了如何通过 __malloc_assert
触发攻击,剩下的就是先劫持 _IO_2_1_stderr
结构体,将其的 vtable
字段改为 _IO_wfile_jumps+0x10
地址,然后 _wide_data->vtable
改为可控堆地址,使其执行 _IO_WOVERFLOW
的时候,可以进行劫持执行流(这里只说明了部分篡改的字段)
然后依然是 house of apple2 这篇文章的例题中提到的用 magic_gadget
进行一个栈迁移(如果需要看具体的细节请参考 house of apple
这篇文章),然后彻底控制程序的执行流,去打 rop
链,执行 close
open
read
write
函数
最后我出示一下伪造的两个结构体
EXP
from tools import * |