House of emma
House of emma
SpaceDraG0nHouse of emma
在 GLibc2.34
版本中,我们以往堆利用最常用的两个 Hook
:malloc_hook
、free_hook
直接被取消,导致以往的大部分堆利用手法直接失效,这时候我们急需发现一个类似于 __free_hook
这样的函数指针调用,从而来削弱 getshell
的限制条件。
House of emma
因此诞生
利用范围:
2.23
—— 至今
利用条件:
- 需要两次任意地址写已知地址
- 需要泄露libc地址以及堆块地址
- 能够触发IO流(House of kiwi 、FSOP)
由于在 glibc-2.24
的时候添加了对 vtable
的合法性检查,所以我们不能像以往一样直接对 vtable
进行一个劫持,但是这个检查对具体位置相对宽松,我们还是可以在一定范围之内对 vtable
起始位置进行一个偏移 ,我们就可以通过偏移来调用在 vtable
表中的任意函数,因此我们可以考虑将其指定为以下几个函数。
1 | static ssize_t |
这几个函数存在任意函数指针的调用,且函数指针来源于 _IO_cookie_file
结构体,这个结构体是 _IO_FILE_plus
的扩展,如果我们可以控制 IO
的内容,大概率这部分数据也是可控的 ,并且其第一个参数也是来源于这个结构,所以我们可以把它当作类似于 __free_hook
的 Hook
来利用 。
绕过 PTR_DEMANGLE
由于_IO_cookie_write
等函数的调用涉及到指针加密 :ROR 移位 0x11 后再与指针进行异或
1 | extern uintptr_t __pointer_chk_guard attribute_relro; |
其指针加密的 key
值存在于 TLS + 0x30
处 ,要想调用目标函数,我们就必须要对放置的目标函数进行一个解加密,但是由于 key
值是完全随机的,所以常规来讲我们是不可能直接破解这个加密方式的,但是由于 key
值处可写 ,我们可以利用其他手法,如 largebin attack
、tcache stash unlinking
等 ,向 TLS + 0x30
的位置写入一个已知地址 ,把原本随机的 key
值覆盖成我们已知的地址,这样一来,我们就可以对这个安全保护进行一个绕过了 (实际攻击的时候,远程的 TLS
位置可能会有一些偏移,需要爆破)
1 | # 加密函数 循环左移 |
这里我贴一个解密脚本,是在覆盖 key
为已知地址的情况下实现的 。
整体的利用思路分为三大步
- 劫持
stderr
到堆块上,同时伪造好IO
结构 ,改写stderr
的vtable
为_IO_cookie_jumps + 0x40
,使其调用_IO_cookie_wirte
。 - 覆盖
TLS + 0x30
处的key
值为已知地址 。 - 布置好
IO
结构后 ,触发__malloc_assert
(参考House of kiwi
)。
攻击过程中可能会遇到的问题:
- 可能会出现
stderr
指针在bss
段,而不是libc
的情况,这种情况就不能使用上面的常规打法 ,如果我们无法直接纂改,就只能考虑打FSOP
的方式 ,但是exit
调用也涉及到指针保护,此时的key
值已经被我们修改 ,使其无法执行正确的函数地址 。
以上情况的解决方法是,构造两个 IO_FILE
结构 ,后者位于前者的 _chain
处,这里我们可以考虑 House of apple1
+ House of emma
的打法 ,使用 House of apple1
来修改 TLS + 0x30
的 key
值为已知地址,然后使用 House of emma
执行调用函数 。
当然如果有这种条件那我们应该可以直接打 House of apple2
了 。
glibc-2.36
之后__malloc_assert
被修改成:
1 | _Noreturn static void |
移除了 fxprint
函数
调用过程及可控寄存器:
1 | __malloc_assert => __fxprintf => __vfxprintf => locked_vfxprintf => __vfprintf_internal => _IO_cookie_write |
rax
是我们可以直接控制的,最后 _IO_cookie_write
也是 call rax
, 实现任意函数调用 ,其中 rdi
是我们可以控制的 ,rbp
指向的是我们伪造的 IO_FILE
结构体 ,如果程序没有开启沙箱限制,我们就可以直接执行 system("/bin/sh");
来拿到 shell
,但是如果程序开启了沙箱限制,这里就需要考虑两种情况,一种是 glibc-2.29
之前 ,setcontext + xx
是通过 rdi
来控制寄存器的,我们就可以直接 call setcontext + xx
然后布局好 rop
,打 orw
即可 ,但是如果是 glibc-2.29
之后, 那么我们就只能通过一些 magic_gadget
来实现一些寄存器的转化,最好找一个能够通过 rdi
来给rdx
赋值的寄存器,因为 rdi
是可控的 ,然后还需要一个 call
的调用,使得攻击得以持续 。
这里恰好有这么一条寄存器可以做到上述攻击方式 。
例题演示:[湖湘杯 2021]house_of_emma
常规检查:
64位 保护全开
禁用了 execve
调用 。
IDA:
main():
看到 vm
调用就可以猜出来大概是一个 vmpwn
题
程序的逻辑不难理解,还非常好处理,这里我就不一一展示每一个函数的内容了,我只大致说一下:
calloc_add
函数可以利用calloc
创造0x40F < size < 0x500
大小的堆块 。delete
函数free
后没有清空堆块指针,存在明显的uaf
漏洞 。show
和edit
查看与修改堆块内容,没有什么问题 。
这里直接延用我上面讲过的打法就行,劫持 stderr
到堆块上,布置好 IO
结构,使其调用 _IO_cookie_write
,call rax
处我们不能直接调用 system('/bin/sh')
因为程序禁用了 execve
,这个时候我们考虑打 orw
,由于libc版本在 glibc-2.35
, 所以我们需要使用以下 gadget
,来控制 rdx
寄存器,同时记得绕过指针保护 :
1 | 0x00000000001675b0: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20]; |
最后 call setcontext + 61
布局寄存器,执行 rop
即可获取到 flag
。
EXP:
1 | from pwn import* |
参考链接:
[原创]【伽玛】第七届“湖湘杯” House _OF _Emma | 设计思路与解析-Pwn-看雪-安全社区|安全招聘|kanxue.com