House Of Emma
在 libc-2.34 版本中,常用的 hook 都被 ban 掉了(malloc_hook,free_hook 等),我们要尝试通过控制 IO 来获取 shell
应用场景是:
- 可以任意写一个可控地址(LargeBin Attack,Tcache Stashing Unlink Attack)
- 可以触发 IO 流(FSOP,House Of Kiwi)
House Of Emma 原理
在 vtable 的合法范围内,存在一个 _IO_cookie_jumps(vtable 的一部分):
1 | static const struct _IO_jump_t _IO_cookie_jumps libio_vtable = { |
以下这几个函数内存在任意函数指针调用:
1 | static ssize_t |
1 | static ssize_t |
1 | static off64_t |
1 | static int |
上面这些函数的函数指针来源于_IO_cookie_file 结构体:
1 | struct _IO_cookie_file |
- 结构体
_IO_cookie_file
可以直接在 GDB 中打印 - 我们可以伪造整个
_IO_cookie_io_functions_t
结构体,把其当做一个类似于__free_hook
的 Hook 来利用
在此之前我们需要绕过 PTR_DEMANGLE:(指针加密)
- 指针加密是一种 glibc 安全功能,旨在增加攻击者在 glibc 结构中操纵指针(特别是函数指针)的难度(此功能也称为“指针重整”或“指针保护”)
- glibc 端口可以通过实现两个宏来支持指针加密:
1 | extern uintptr_t __pointer_chk_guard attribute_relro; |
__pointer_chk_guard
是在 TLS 中的一个值(在 GDB 中打印__pointer_chk_guard_local
就可以找到)- 将其 ROR 移位11位后(右移11位),再和原指针异或
1 | 0x00007ffff7e0d7b0 <+0>: endbr64 |
- [FS] 寄存器指向当前活动线程的 TLS 结构(线程结构),而
fs:0x30
处的值是普通“加密”算法的“密钥”
应对措施有:
- 可以将这个值泄露出来
- 也可以修改 TLS 中的这个值
这样一来就可以伪造 read_cb
函数指针的值(伪造整个 _IO_cookie_io_functions_t
结构体),从而实现任意代码执行的目的
知识补充:TLS 简析
TLS (Thread Local Storage) 是为了多线程考虑其线程本身需要维持一些状态而设置的一种机制,这个变量在它所在的线程内是全局可访问的,但是不能被其他线程访问到,这样就保持了数据的线程独立性
- TLS 全称为 Thread Local Storage,即线程本地存储:
- 在单线程模式下,所有整个程序生命周期的变量都是只有一份,那是因为只是一个执行单元
- 在多线程模式下,有些变量需要支持每个线程独享一份的功能,这种 每线程独享的变量 放到 每个线程专有的存储区域 ,所以称为线程本地存储(Thread Local Storage)或者线程私有数据(Thread Specific Data)
- 案例:Thread Local Storage(线程局部存储)TLS - 知乎
- [FS] 寄存器指向当前活动线程的 TLS 结构(线程结构),而
fs:0x30
处的值是普通“加密”算法的“密钥”
接下来我们就在 GDB 中看一看 TLS,打印 __pointer_chk_guard_local
1 | pwndbg> p __pointer_chk_guard_local |
搜索 __pointer_chk_guard_local
就可以进入 TLS:
1 | pwndbg> search -t qword 0xe30b334e3eb96731 /* 搜索__pointer_chk_guard_local */ |
- 像
canary
、tcache struct ptr
、key
这些值都保存在 TLS 中 - 在 TLS:[0x300] 的位置有 stack_addr,有时也可以进行利用
House Of Emma 利用姿势
先用两个 largebin attack 分别把 __pointer_chk_guard_local
和 stderr
控制为已知 heap 地址,然后利用 House Of Kiwi 中的方法,使 top chunk 不足以申请从而调用 sysmalloc
其中在 stderr
中的 fake_IO_FILE 如下:
1 | next_chain = 0 |
- 在 vtable 的检测中对具体位置的检测还是比较宽松的,这使得我们可以在一定的范围内对 vtable 表的起始位置进行偏移,使其我们可以通过偏移来调用在 vtable 表中的任意函数
- 这里对 vtable 进行偏移,使其可以调用
_IO_cookie_write
函数
1 | pwndbg> p (struct _IO_cookie_file)*(0x562b7f47b2a0) |
接下来我们来跟进一下 House Of Emma 的触发流程:
1 | ► 0x7ff1c80e5bb3 <_int_malloc+3539> call sysmalloc <sysmalloc> |
- 因为我们修改了 top chunk->P,所以触发
__malloc_assert
:
1 | ► 0x7ff1c80e4cb2 <sysmalloc+1714> call __malloc_assert <__malloc_assert> |
- 进入函数
__fxprintf
:
1 | ► 0x7ff1c80e295d <__malloc_assert+61> call __fxprintf <__fxprintf> |
- 进入函数
__vfxprintf
:
1 | ► 0x7ff1c80c3d68 <__fxprintf+136> call __vfxprintf <__vfxprintf> |
- 进入函数
locked_vfxprintf
:
1 | ► 0x7fc235f78c50 <__vfxprintf+80> call locked_vfxprintf <locked_vfxprintf> |
- 进入函数
__vfprintf_internal
:
1 | ► 0x7fc235f78b63 <locked_vfxprintf+291> call __vfprintf_internal <__vfprintf_internal> |
- 进入函数
_IO_cookie_write
:(前面伪造了 vtable,这里才可以调用_IO_cookie_write
)
1 | ► 0x7fc235f70015 <__vfprintf_internal+261> call qword ptr [rbx + 0x38] <_IO_cookie_write> |
- 在
_IO_cookie_write
中有一个call rax
(rax
需要经过 PTR_DEMANGLE 解密)
1 | 0x7fc235f79a16 <_IO_cookie_write+38> mov rbp, rdx |
- 由于整个
_IO_cookie_io_functions_t
已经被我们伪造(其中_IO_cookie_io_functions_t->write
被伪造为一个特殊的 gadget) - 利用这个 gadget 可以获取位于 [RDI] 中的
__cookie
(可控 heap 空间),并且调用位于__cookie+0x20
处的函数
1 | 0x7fc236047020 <getkeyserv_handle+528> mov rdx, qword ptr [rdi + 8] |
- 我们可以把
__cookie+0x20
处布置为setcontext+61
:(因为 [RDX] 是可控的)
1 | RAX 0x7fc236047020 (getkeyserv_handle+528) ◂— mov rdx, qword ptr [rdi + 8] |
- 最后利用 House Of Kiwi 中的方式 get shell