Tcache Stashing Unlink Attack
字数 1702 2025-08-22 12:22:24
Tcache Stashing Unlink Attack 技术分析
1. 攻击概述
Tcache Stashing Unlink Attack 是一种针对 glibc 堆管理器的攻击技术,即使在较新版本的 glibc(如 2.36)中仍然有效。该攻击利用了 small bin 向 tcache 转移 chunk 时的逻辑缺陷,可以实现任意地址写入 libc 地址(main_arena)和伪造 chunk 分配。
2. 核心原理
2.1 漏洞代码分析
漏洞存在于 malloc.c 中处理 small bin 分配的逻辑:
if (in_smallbin_range(nb)) {
idx = smallbin_index(nb);
bin = bin_at(av, idx);
if ((victim = last(bin)) != bin) {
bck = victim->bk;
if (__glibc_unlikely(bck->fd != victim))
malloc_printerr("malloc(): smallbin double linked list corrupted");
set_inuse_bit_at_offset(victim, nb);
bin->bk = bck;
bck->fd = bin;
// 将剩余 small bin chunks 放入 tcache
size_t tc_idx = csize2tidx(nb);
if (tcache && tc_idx < mp_.tcache_bins) {
mchunkptr tc_victim;
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = last(bin)) != bin) {
if (tc_victim != 0) {
bck = tc_victim->bk; // 关键点:没有检查 bck->fd == tc_victim
set_inuse_bit_at_offset(tc_victim, nb);
bin->bk = bck;
bck->fd = bin; // 任意地址写入 bin 地址
tcache_put(tc_victim, tc_idx);
}
}
}
void *p = chunk2mem(victim);
alloc_perturb(p, bytes);
return p;
}
}
2.2 关键漏洞点
- 缺乏完整性检查:当将 small bin 中的 chunk 转移到 tcache 时,没有检查
tc_victim->bk->fd == tc_victim,而 small bin 分配时有此检查 - 任意地址写入:通过控制
tc_victim->bk,可以在bck->fd处写入 bin 的地址(main_arena+96) - calloc 绕过 tcache:calloc 会直接使用 small bin 而不会先检查 tcache,这是攻击能够触发的关键
3. 攻击条件
- 能够覆盖 victim chunk 的 bk 指针
- 至少能够使用一次 calloc 分配内存
- 需要一个可写地址来绕过 glibc 的检查(fake_chunk->bk 必须指向可写地址)
4. 攻击步骤详解
4.1 堆布局准备
- 分配 9 个 0x90 大小的 chunk
- 释放其中 7 个到 tcache bin:
- 不能连续释放相邻 chunk,防止 unsorted bin 合并
- 先释放 chunk3-chunk8
- 然后释放 chunk1(填满 tcache)
- 释放 chunk0 和 chunk2 到 unsorted bin
- 分配一个大于 0x90 的 chunk,将 chunk0 和 chunk2 放入 small bin
- 分配两个 0x90 chunk,从 tcache 取出,为 small bin 转移到 tcache 腾出空间
4.2 伪造 chunk
- 在目标地址(如栈上)构造 fake chunk
- 设置 fake_chunk->bk 为可写地址(如 fake_chunk+0x10)以绕过检查
- 修改 small bin 中最后一个 chunk 的 bk 指针指向 fake_chunk-0x10
4.3 触发攻击
- 使用 calloc 分配 0x90 chunk:
- 从 small bin 取出一个 chunk
- 将剩余 small bin chunk(被我们控制 bk 的)和 fake chunk 放入 tcache
- 在 fake_chunk->bk->fd(即 fake_chunk+0x10+0x10)处写入 bin 地址
- 后续 malloc(0x90) 将返回 fake_chunk+0x10
5. 保护机制绕过
关键检查点:
if (__glibc_unlikely(bck->fd != victim)) {
malloc_printerr("malloc(): smallbin double linked list corrupted");
}
绕过方法:
- 设置 fake_chunk->bk 指向一个可写地址(如 fake_chunk+0x10)
- 确保该地址+0x10(即 bck->fd)处可写,这样检查时不会崩溃
6. 攻击效果
- 任意地址写入:在 fake_chunk->bk->fd 处写入 main_arena 地址
- 任意地址分配:后续分配可以从伪造的 chunk 处返回内存
7. 攻击模板
# 假设 target 是我们要写入的地址
# 绕过保护:target+0x8 处必须是一个可读写地址
# 1. 堆布局
for i in range(9):
add(0x90) # 0,1,2,3...
for i in range(3, 9):
free(i)
free(1)
free(0)
free(2)
add(0xa0) # 将 unsorted bin 转为 small bin
add(0x90) # 腾出 tcache 空间
add(0x90)
# 2. 修改 bk 指针
edit(2, bk=target-0x10) # 修改 small bin 最后一个 chunk 的 bk
# 3. 触发攻击
calloc(1, 0x90) # 触发 small bin 向 tcache 转移
# 4. 获取伪造 chunk
malloc(0x90) # 将返回伪造的 chunk
8. 实际应用场景
- 劫持程序控制流:通过写入 libc 地址或分配特定位置 chunk 实现
- 绕过 ASLR:泄漏 libc 地址
- 实现任意地址读写:通过伪造 chunk 实现内存读写
9. 防御措施
- 更新 glibc:虽然该攻击在较新版本仍有效,但应使用最新版本
- 加强堆指针检查:确保所有 chunk 指针都经过严格验证
- 限制 calloc 使用:避免在关键代码路径使用 calloc
该攻击技术展示了 glibc 堆管理器中安全检查的不一致性,强调了即使现代防护机制下,细微的逻辑缺陷仍可能导致严重的安全问题。