资讯动态

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

发布时间:2026/9/4 22:20:19 来源:尧图企业网站定制
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 头部的fdforward 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 块a和bvoid *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链表中已经形成了自引用环。随后攻击者按如下步骤申请并覆写第一次调用malloc(0x20)返回指针a。此时链表头变为bb-fd仍指向a。写入数据到a覆盖其fd字段为一个伪造的目标地址例如栈上伪造的 chunk 或__malloc_hook前方满足 size 检查的位置fake_chunk。第二次调用malloc(0x20)返回指针b。此时链表头重新回到a但a-fd已经被攻击者修改为fake_chunk。第三次调用malloc(0x20)再次返回指针a。此时分配器将a-fd即fake_chunk设置为新的链表头。第四次调用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_addrglibc 缓解机制的技术演进随着 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.32Safe-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 随机化掩码写入非法构造的指针会在解引用时触发页错误异常崩溃。现代防御与编码修复规范在实际工业代码与红蓝对抗场景中针对此类堆安全隐患需要从多层进行彻底阻断指针释放后立即置空Dangling Pointer Elimination在所有free(ptr)操作后强制设置ptr NULL从根源上杜绝 UAFUse-After-Free与 Double Free。#define SAFE_FREE(p) do { \ if ((p) ! NULL) { \ free(p); \ (p) NULL; \ } \ } while(0)启用现代内存分配器替代方案在对安全性要求极高的场景中使用Hardened Malloc如 Android 默认的 Scudo 分配器或 GrapheneOS hardened_malloc引入基于 chunk 元数据的隔离、校验和随机化防护使传统的堆结构篡改手法彻底失效。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价