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







