| name | binary-exploitation-methodology |
| description | 二进制漏洞利用基础方法论。涵盖漏洞识别(栈溢出/堆溢出/格式化字符串/UAF)、保护机制分析(ASLR/NX/Stack Canary/PIE/RELRO)、利用策略选择(ret2win/ret2shellcode/ROP/ret2libc)、调试与利用开发流程。当 Agent 需要分析二进制程序漏洞、选择利用策略、或理解保护机制绕过方法时触发。区别于 CTF pwn 技巧,侧重实战方法论。 |
| metadata | {"tags":"二进制利用,栈溢出,rop,保护机制,gdb,pwntools,实战","category":"exploit/binary"} |
二进制漏洞利用基础方法论
核心流程:分析目标二进制 → 识别保护机制 → 判断漏洞类型 → 选择利用策略 → 开发利用代码 → 测试验证
深入参考
Phase 1: 目标分析
目标二进制初步分析:
├─ 文件类型与架构
│ ├─ file <binary>
│ ├─ ELF 32/64-bit, LSB/MSB
│ └─ 架构: x86, x86_64, ARM, MIPS
├─ 链接方式
│ ├─ 动态链接 → 有 libc, GOT/PLT 可利用
│ └─ 静态链接 → 无外部库,gadget 丰富但无 libc 函数地址泄露
├─ 符号信息
│ ├─ stripped → 需要逆向分析函数
│ └─ not stripped → 函数名可见,分析更容易
└─ 基本字符串搜索
└─ strings <binary> | grep -i "flag\|shell\|bin/sh\|password"
初步分析命令
file ./target_binary
ldd ./target_binary
nm ./target_binary 2>/dev/null | head -30
strings ./target_binary | grep -i "flag\|shell\|/bin/sh\|system\|exec"
objdump -d -Mintel ./target_binary | less
Phase 2: 保护机制识别
保护机制检查决策树:
├─ checksec --file=<binary>
├─ CANARY(Stack Canary / 栈金丝雀)
│ ├─ 启用 → 需要泄露 canary 值才能覆盖返回地址
│ │ ├─ 格式化字符串泄露
│ │ ├─ 信息泄露漏洞
│ │ └─ brute force(仅 fork 模式有效)
│ └─ 未启用 → 可直接覆盖返回地址
├─ NX(No-Execute / 不可执行)
│ ├─ 启用 → 栈上 shellcode 不可执行,需用 ROP/ret2libc
│ └─ 未启用 → 可在栈上放置 shellcode 并跳转执行
├─ PIE(Position Independent Executable)
│ ├─ 启用 → 代码段地址随机化,需要泄露基地址
│ │ ├─ partial overwrite(只改低位字节)
│ │ └─ 信息泄露获取代码段地址
│ └─ 未启用 → 代码段地址固定,可直接使用 gadget 地址
├─ ASLR(地址空间布局随机化)
│ ├─ 启用 → 栈/堆/libc 地址随机
│ │ ├─ 信息泄露获取 libc 基地址
│ │ ├─ ret2plt 间接调用
│ │ ├─ ret2dlresolve 动态解析
│ │ └─ brute force(32-bit 熵较低)
│ └─ 未启用 → 地址可预测
├─ RELRO(Relocation Read-Only)
│ ├─ Full RELRO → GOT 只读,不可覆写 GOT 条目
│ ├─ Partial RELRO → GOT 可写,可覆写 GOT 实现任意调用
│ └─ No RELRO → .dynamic 也可写
└─ Fortify Source
└─ 编译时替换不安全函数为安全版本
检查命令
checksec --file=./target_binary
readelf -l ./target_binary | grep GNU_STACK
readelf -h ./target_binary | grep Type
cat /proc/sys/kernel/randomize_va_space
readelf -l ./target_binary | grep GNU_RELRO
readelf -d ./target_binary | grep BIND_NOW
Phase 3: 漏洞类型判断
漏洞类型识别决策树:
├─ 栈溢出(Stack Buffer Overflow)
│ ├─ 特征: gets/strcpy/sprintf/scanf 等不安全函数
│ ├─ 可覆盖: 返回地址、保存的寄存器、局部变量
│ └─ 子类型
│ ├─ 标准溢出 → 覆盖返回地址
│ ├─ Off-by-one → 只能覆盖 1 字节(通常是 EBP 低字节)
│ └─ 整数溢出导致的栈溢出
├─ 格式化字符串(Format String)
│ ├─ 特征: printf(user_input) 而非 printf("%s", user_input)
│ ├─ 能力: 任意读 + 任意写
│ └─ 利用: %p 泄露栈内容, %n 写入任意地址
├─ 堆溢出(Heap Overflow)
│ ├─ 特征: malloc 后写入超出分配大小
│ ├─ 利用: 覆盖堆元数据或相邻 chunk 的数据
│ └─ 进阶: fastbin attack, tcache poisoning, unsafe unlink
├─ Use-After-Free (UAF)
│ ├─ 特征: free 后指针未置空且继续使用
│ ├─ 利用: 重新分配同大小 chunk 控制已释放内存
│ └─ 通常需要理解对象布局
├─ 数组越界(Array Out-of-Bounds)
│ ├─ 特征: 可控的数组索引缺少边界检查
│ └─ 利用: 任意读/写
└─ 整数溢出
├─ 特征: 大小计算中的整数截断/回绕
└─ 通常作为触发其他漏洞的前置条件
漏洞定位方法
objdump -d ./target_binary | grep -E "gets|strcpy|sprintf|scanf|strcat"
python3 -c "print('A'*200)" | ./target_binary
python3 -c "print('A'*500)" | ./target_binary
Phase 4: 利用策略选择
利用策略总决策树:
目标: 调用已有函数(ret2win)
├─ 无 PIE + 无 Canary → 直接覆盖返回地址为 win 函数地址
├─ 有 PIE → 先泄露基地址或 partial overwrite
├─ 有 Canary → 先泄露 canary
├─ 需要设置参数
│ ├─ ROP gadgets 充足 → ROP chain 设置寄存器
│ ├─ SROP → sigreturn 控制所有寄存器
│ └─ ret2csu / ret2vdso → 控制多个寄存器
└─ WWW (Write-What-Where) → 通过任意写调用 win 函数
目标: 获取 Shell (RCE)
├─ NX 未启用
│ ├─ 无 ASLR → ret2shellcode(跳转到栈上 shellcode)
│ ├─ 有 ASLR → ret2esp / ret2reg(通过 gadget 跳转到 shellcode)
│ └─ 有 NX → ROP 调用 mprotect 使栈可执行,再跳转 shellcode
├─ 通过 syscall
│ ├─ ret2syscall → ROP chain 调用 execve("/bin/sh", 0, 0)
│ └─ SROP → sigreturn 设置寄存器后 syscall
├─ 通过 libc
│ ├─ 静态链接 + 无 PIE → system 和 "/bin/sh" 地址固定
│ ├─ 无 ASLR → libc 地址固定,直接 ret2libc
│ ├─ 有 ASLR + 无 PIE + binary 有 system → ret2plt(system)
│ ├─ 有 ASLR + 无 PIE + binary 无 system
│ │ ├─ ret2dlresolve → 动态解析 system 地址
│ │ └─ 泄露 libc 地址 → 计算 system 偏移
│ └─ 有 ASLR + 有 PIE + 未知 libc
│ ├─ 先绕过 PIE(泄露代码段地址)
│ ├─ 泄露 libc 函数地址 → 确定 libc 版本
│ └─ 计算 system 和 "/bin/sh" 地址
└─ Stack Pivoting
├─ Off-by-one → 控制 EBP → 控制 ESP → 控制 RET
└─ 构造 payload 到可控内存,通过 EBP 跳转
保持循环利用(Eternal Loop)
单次漏洞触发不够时,需要多次利用:
├─ ROP chain 中跳回 main 或漏洞函数地址
├─ 覆写 GOT 中 exit 条目 → 跳回漏洞点
└─ .fini_array 写入两个函数:
├─ 函数 1: 调用漏洞函数
└─ 函数 2: 调用 __libc_csu_fini(再次触发 .fini_array)
Phase 5: 利用开发
确定溢出偏移量
python3 -c "from pwn import *; print(cyclic(300))" | ./target_binary
msf-pattern_create -l 300
msf-pattern_offset -l 300 -q <value>
pwntools 利用模板
from pwn import *
context.binary = elf = ELF('./target_binary')
context.log_level = 'debug'
offset = 72
payload = b'A' * offset
payload += p64(elf.symbols['win_function'])
p = process('./target_binary')
p.sendline(payload)
p.interactive()
ROP Chain 构造
ROPgadget --binary ./target_binary | grep "pop rdi"
ROPgadget --binary ./target_binary | grep "pop rsi"
ropper --file ./target_binary --search "pop rdi"
one_gadget /lib/x86_64-linux-gnu/libc.so.6
Phase 6: 测试验证
验证流程:
├─ 本地测试
│ ├─ 关闭 ASLR 测试: echo 0 > /proc/sys/kernel/randomize_va_space
│ ├─ 确认 exploit 可靠触发
│ └─ 开启 ASLR 测试稳定性
├─ 调试失败排查
│ ├─ 栈对齐问题(x86_64 要求 16 字节对齐)
│ │ └─ 在 payload 中加一个 ret gadget 对齐栈
│ ├─ 坏字符(\x00, \x0a, \x0d)
│ │ └─ 检查输入函数是否截断特定字符
│ ├─ 地址计算错误
│ │ └─ GDB 中 verify: x/gx $rsp, info registers
│ └─ 远程环境差异
│ ├─ libc 版本不同
│ ├─ 栈偏移不同
│ └─ 使用 pwntools 的 DynELF 动态泄露
├─ 远程验证
│ ├─ 调整 payload 适配远程环境
│ └─ 处理网络延迟与 IO 缓冲
└─ 可靠性测试
└─ 多次运行确认成功率
GDB 调试技巧
工具速查
| 工具 | 用途 |
|---|
| checksec | 检查二进制保护机制 |
| pwntools | 利用开发框架 |
| GDB + GEF/pwndbg | 调试分析 |
| ROPgadget | ROP gadget 搜索 |
| one_gadget | libc 中一键 shell gadget |
| Ghidra | 反编译与逆向 |
| ropper | gadget 搜索与 ROP chain 生成 |
| msf-pattern_create | 偏移量计算 |
| readelf / objdump | 二进制信息查看 |