news 2026/9/4 22:20:06

Fastbin Dup 利用原理与双重释放(Double Free)缓解机制演进

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Fastbin Dup 利用原理与双重释放(Double Free)缓解机制演进

Fastbin Dup 利用原理与双重释放(Double Free)缓解机制演进

在 Linux glibc 堆内存管理机制中,Fastbin 是为了加速小尺寸内存分配而设立的单向无头链表结构。早期二进制利用中,Fastbin Dup 作为最基础且威力巨大的堆利用手法之一,核心在于通过非连续的双重释放(Double Free)破坏堆管理器的内部链表结构,使得两个指向同一块物理内存的指针同时处于可用状态,进而篡改链表指针实现任意地址写。

Fastbin 机制与 Double Free 形成机理

在 glibc 堆分配器中,Fastbin 处理通常小于 80 字节(x86-64 下默认最大支持 0x80 字节的 chunk 大小,视全局变量global_max_fast而定)的内存块。Fastbin 采用单向链表结构管理空闲 chunk,遵循后进先出(LIFO)原则。分配器使用 chunk 头部的fd(forward pointer)指针指向链表中前一个被释放的 chunk,并且在释放时不进行相邻 chunk 的合并(不清除 inuse 标志位),以此保证极致的分配与回收性能。

在 glibc 2.26 之前,glibc 对 fastbin chunk 的free操作仅进行了一处极为简陋的安全检查:校验当前被释放的 chunk 地址是否与对应 fastbin 链表头部的 chunk 地址相同。

/* glibc 早期 _int_free 中对 fastbin 的安全检查 */ if (__builtin_expect (old == p, 0)) { errstr = "double free or corruption (fasttop)"; goto errout; }

这段检查只比对了old(当前 fastbin 头节点)与p(待释放 chunk),这意味着如果我们在两次释放同一个 chunk 之间插入对另一个 chunk 的释放操作,就可以轻松绕过此检查。

经典 Fastbin Dup 攻击路径推演

假设程序中按顺序申请了两个大小相等的 fastbin 块ab

void *a = malloc(0x20); // 实际 chunk 尺寸 0x30 void *b = malloc(0x20); // 实际 chunk 尺寸 0x30 void *c = malloc(0x20); // 执行交替释放 free(a); // fastbin[0x30]: a -> NULL free(b); // fastbin[0x30]: b -> a -> NULL free(a); // 绕过 old == p 检查,fastbin[0x30]: a -> b -> a -> ... (形成环)

此时fastbin链表中已经形成了自引用环。随后攻击者按如下步骤申请并覆写:

  1. 第一次调用malloc(0x20)返回指针a。此时链表头变为bb->fd仍指向a
  2. 写入数据到a,覆盖其fd字段为一个伪造的目标地址(例如栈上伪造的 chunk 或__malloc_hook前方满足 size 检查的位置fake_chunk)。
  3. 第二次调用malloc(0x20)返回指针b。此时链表头重新回到a,但a->fd已经被攻击者修改为fake_chunk
  4. 第三次调用malloc(0x20)再次返回指针a。此时分配器将a->fd(即fake_chunk)设置为新的链表头。
  5. 第四次调用malloc(0x20)将直接返回fake_chunk的用户数据区,攻击者成功在受控目标内存地址获取可写句柄。
// 伪造利用代码段 void *p1 = malloc(0x20); void *p2 = malloc(0x20); void *p3 = malloc(0x20); free(p1); free(p2); free(p1); char *r1 = (char *)malloc(0x20); // 篡改 r1 (即 p1) 的 fd 指针,指向目标 fake chunk *(unsigned long long *)r1 = target_fake_chunk_addr; char *r2 = (char *)malloc(0x20); // 获得 p2 char *r3 = (char *)malloc(0x20); // 再次获得 p1 char *r4 = (char *)malloc(0x20); // 成功拿到 target_fake_chunk_addr

glibc 缓解机制的技术演进

随着 glibc 版本的不断更新,针对 fastbin 双重释放和堆指针篡改的防护措施逐步收紧。

1. glibc 2.26:引入 tcache 对 fastbin 的截流

在 glibc 2.26 中引入了线程本地缓存(Thread Local Cache, tcache)。默认情况下,小于等于 0x408 字节的 chunk 会优先进入对应大小的 tcache bin(容量上限为 7 个 chunk)。

此时若要利用 fastbin dup,必须先耗尽(fill up)对应 size 的 tcache 链表。例如连续申请 7 个同尺寸 chunk 并释放填满 tcache,后续的 chunk 释放才会真正进入 fastbin,从而激活 fastbin dup 的利用链路。

2. glibc 2.29:遍历 fastbin 链表杜绝重入

从 glibc 2.29 开始,分配器在释放 fastbin chunk 时不再仅仅检查头节点,而是遍历该 fastbin 桶中的全部节点:

/* glibc 2.29+ _int_free 中的 fastbin 链表遍历检测 */ if (__builtin_expect (old == p, 0)) malloc_printerr ("double free or corruption (fasttop)"); { mchunkptr tmp = old; while (tmp) { if (__builtin_expect (tmp == p, 0)) malloc_printerr ("double free or corruption (fasttop)"); tmp = tmp->fd; } }

这一检查直接终结了free(a) -> free(b) -> free(a)这种经典环状构造手法。一旦a已经存在于当前 fastbin 链表中的任何位置,再次释放a都会触发 abort 并打印错误信息。

3. glibc 2.32:Safe-Linking 指针混淆

glibc 2.32 引入了Safe-Linking(安全指针链接)机制,对 fastbin 和 tcache 的fd单链表指针进行位运算加密。

其核心混淆公式为:

#define PROTECT_PTR(pos, ptr) \ ((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr))) #define REVEAL_PTR(ptr) \ PROTECT_PTR (&ptr, ptr)

在 safe-linking 下,写入fd的指针必须是(当前 chunk 存放 fd 的地址 >> 12) ^ 真实目标地址。若攻击者无法泄露堆基址(Heap Base Address),就无法算出有效的 ASLR 随机化掩码,写入非法构造的指针会在解引用时触发页错误异常崩溃。

现代防御与编码修复规范

在实际工业代码与红蓝对抗场景中,针对此类堆安全隐患需要从多层进行彻底阻断:

  1. 指针释放后立即置空(Dangling Pointer Elimination)
    在所有free(ptr)操作后强制设置ptr = NULL,从根源上杜绝 UAF(Use-After-Free)与 Double Free。
#define SAFE_FREE(p) do { \ if ((p) != NULL) { \ free(p); \ (p) = NULL; \ } \ } while(0)
  1. 启用现代内存分配器替代方案
    在对安全性要求极高的场景中,使用Hardened Malloc(如 Android 默认的 Scudo 分配器或 GrapheneOS hardened_malloc),引入基于 chunk 元数据的隔离、校验和随机化防护,使传统的堆结构篡改手法彻底失效。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 22:19:50

React 并发特性 useTransition 的内部状态跃迁

React 并发特性 useTransition 的内部状态跃迁在单线程的 JavaScript 运行时里,UI 渲染与用户交互始终在争夺主线程的控制权。在传统的 React 16/17 同步递归调度下,一旦组件树庞大或计算逻辑繁重,一次 setState 触发的 Diff 过程就如同一辆刹…

作者头像 李华
网站建设 2026/9/4 22:17:34

PHP+Swoole构建高并发大屏幕互动系统:架构设计与实战指南

简介:这是一套开箱即用的2024年现场大屏幕互动系统PHP源码,专为年会、发布会、展会等线下活动策划者及中小型技术团队设计,解决实时互动弱、微信上墙卡顿、多端兼容差等落地难题。压缩包共427.64MB,含完整PHP项目文件、后台管理模…

作者头像 李华
网站建设 2026/9/4 22:16:25

Windows注册表深度解析:从dll错误到LocalGPO修复指南

经常能在技术社区里看到这样几类 Windows 问题:系统开机后突然提示某个 dll 文件找不到,安装好的 msi 程序双击毫无反应,性能监视器报错说“无法读取 usbperf\performance 注册表项下的 first counter 值”,还有不少网友反馈处理组…

作者头像 李华
网站建设 2026/9/4 22:13:39

从零搭建车牌识别系统:YOLOv5与CRNN的实战应用与优化

简介:本资源是一个面向人工智能课程设计与毕业设计实践的深度学习车牌识别系统完整实现,聚焦智能交通、停车场管理及治安监控等实际场景,适合具备Python与PyTorch基础的本科生、研究生开展项目开发与边缘部署学习。压缩包共104个文件&#xf…

作者头像 李华
网站建设 2026/9/4 22:11:28

基于STM32F103C8T6的桌面激光雕刻机:从电路设计到运动控制全解析

简介:本资源是一套面向嵌入式初学者与课程设计者的简易激光雕刻机完整开发方案,基于主流ARM Cortex-M3内核的STM32F103C8T6单片机实现运动控制与激光调制,适用于电子设计实训、毕业设计及创客实践等场景。压缩包共319个文件,涵盖A…

作者头像 李华
网站建设 2026/9/4 22:11:13

技术博文创作指南:如何为AI写作助手提供有效主题?

很抱歉,这个标题看起来并不是一个技术开发主题,而更像是一节舞蹈或健身课程的名称(例如某个舞室或教练的排课信息),缺少 CSDN 技术博文所需的编程语言、框架、项目背景或可复现的实操内容。 如果直接硬写成一篇“技术…

作者头像 李华