re-exploit
Installation
Paste this into Claude Code, Cursor, or any agent that can run commands.
What this skill does
What it does:
- Takes a known bug in a program and turns it into a working attack
- Builds chains of computer instructions to bypass security features
- Exploits heap memory problems to read or write data
- Finds ways around sandboxes that block dangerous actions
- Tests attacks in a safe environment before using them
When to use it:
- When you have found a crash or bug and want to make it do something useful
- When you need to bypass protections like no-execute memory or address randomization
- When you need to chain multiple small tricks together to get full control
- When you want to read or write memory in ways the program does not allow
SKILL.mdShow the author's original SKILL.md (not in English)
---
name: re-exploit
description: >
利用开发:ROP 链构造、堆利用。
触发词:ROP、堆利用、fastbin、tcache、利用开发、exploit
---
# 利用开发(ROP 链构造 / 堆利用)
## 何时使用 / 何时不用
- 用:拿到已定位的漏洞(越界读写 / 崩溃 / 危险输入)要升级为 RCE / 任意读写——漏洞分析([[re-crash-triage]] / [[re-vuln]])移交的利用开发环节
- 用:ROP 链构造——gadget 搜索、栈布局、ret2csu / SROP / ret2dlresolve
- 用:堆利用——fastbin / tcache 攻击、unsorted bin 泄露、double free 系列
- 用:seccomp 沙箱绕过(ORW:open / read / write 链)
- 不用:入门级 pwn(ret2win / 简单 ret2libc / 格式化字符串)→ [[re-pwn]]
- 不用:只有崩溃还没有定位(→ [[re-crash-triage]]);还没崩溃找 bug(→ [[re-fuzzing]])
- 注意:利用验证是动态执行,默认在沙箱内跑([[platform-tips]] 最高原则,见 [[re-sandbox]]);**先规划再写**(坑 4),组合绕过分步验证
## 工具准备
参考 [[platform-tips]]——利用验证默认沙箱;静态准备(gadget 搜索 / 规划)可免沙箱,动态验证进沙箱。
### pwntools(pip 安装,Python 3.8+)—— 利用脚本主力
- `pip install pwntools`(官方 PyPI,4.15.x;官方文档声明支持 Python 3.8+,64 位系统支持最好)
- 发行版包:Debian/Ubuntu `sudo apt install python3-pwntools`、Fedora `sudo dnf install python3-pwntools`、Arch `sudo pacman -S python-pwntools`(Extra 仓库官方包)
- **Python 3.12+(Ubuntu 24.04 自带 3.12)直接 pip 装报 PEP 668 `externally-managed-environment`**——对策:venv(`python3 -m venv ~/venvs/pwn && source ~/venvs/pwn/bin/activate`)或发行版包或 `--break-system-packages`
- 验证: `pwn --version` / `python3 -c "import pwn; print(pwn.version)"`
### ropper / ROPgadget —— gadget 搜索
- ropper:`pip install ropper`(官方 PyPI);Arch `sudo pacman -S ropper`(Extra 官方包);Debian/Ubuntu/Fedora 无官方包 → pip
- ROPgadget:`pip install ROPgadget`(官方 PyPI);Debian/Ubuntu `sudo apt install python3-ropgadget`、Fedora `sudo dnf install python3-ROPGadget`、Arch `sudo pacman -S ropgadget`(均为官方包)
- 验证: `ropper --version`;`ROPgadget --binary ./target | head`;常用搜索:`ROPgadget --binary ./target | grep 'pop rdi'`、`ropper -f ./target --search "pop rdi"`
### gdb + gef/pwndbg(见 [[re-gdb]])—— 栈/堆布局验证
- gdb: Debian/Ubuntu `sudo apt install gdb`、Fedora `sudo dnf install gdb`、Arch `sudo pacman -S gdb`、macOS `brew install gdb`;32 位目标 Debian/Ubuntu 补 `gdb-multiarch` + `libc6-i386`
- **堆利用调试建议装 pwndbg**:`heap` / `bins`(tcachebins / fastbins / unsorted)命令直接看 chunk 布局与 bin 链表(gef 也有 `heap bins`,功能略弱);验证: gdb 里 `heap bins` 有输出
- pwndbg 内置 `checksec`(见 [[re-pwn]] 工具准备)
### angr(可选,见 [[re-angr]])—— 溢出大小 / 路径约束自动化
- `pip install angr`——注意 [[re-angr]] 的 Python 版本兼容性说明(9.x 支持 Python 3.9–3.11 系,系统 3.12+ 建议独立 venv)
- 用:溢出偏移不确定(符号化输入求覆盖返回地址的约束)、利用路径校验逻辑复杂
- 验证: `python3 -c "import angr; print(angr.__version__)"`
## 操作步骤
按顺序执行;每步产物(利用面结论、gadget 清单、链布局、PoC)记录证据路径 + sha256(存证方法见 [[re-triage]]),供报告引用。
1. **利用面分析(崩溃点 / 可控数据)**:
- 输入:[[re-crash-triage]] 的最小 PoC / 漏洞报告(崩溃点 + 输入通道)——确认**可控字节范围与偏移**(`cyclic` 定位崩溃时 rip 偏移,见 [[re-pwn]] 步骤 2)
- `pwn checksec --file ./target`:NX / PIE / Canary / RELRO——每项决定一条限制:栈不可执行 → 不能直接跑 shellcode;PIE → 地址要泄露 base;RELRO full → GOT 不可写(改函数指针 / 栈地址 / libc 钩子替代)
- **seccomp 检查**:`seccomp-tools dump ./target`(Gem: `gem install seccomp-tools`,官方途径;Arch 官方仓库无此包——仅 AUR 有 `seccomp-tools`,直接用 gem 即可)或静态找 `prctl` / `seccomp` 调用——若禁 `execve` 则拿 shell 路线作废,改 ORW 链(步骤 4)
- 记录:崩溃偏移、保护矩阵、seccomp 规则、可利用原语(任意写 / 栈溢出 / 堆越界)
2. **ROP 链(gadget 搜索、栈布局)**:
```sh
ROPgadget --binary ./target | grep -E 'pop rdi|pop rsi|pop rdx' # 64 位传参 gadget
ROPgadget --binary ./target | grep 'ret' | head # 对齐用 ret
ropper -f ./target --search "syscall" # SROP / shellcode 跳板
```
- 布局模板(64 位,先泄露后执行两段式):
`padding + pop_rdi_ret + arg1 + func_addr + [对齐 ret] + 下一段...`
- 参数顺序:rdi → rsi → rdx(SysV ABI);缺 rdx gadget 时用 **ret2csu**(`__libc_csu_init` 里的 `pop rbx; pop rbp; pop r12; pop r13; pop r14; pop r15; ret` + 搬运 mov 段)或 ret2dlresolve
- **16 字节对齐**:`call` 时 `$rsp % 16 == 0` 才满足 `movaps`(glibc 新版本 `system`/`printf` 内部会用),不对齐在 gadget 链前补一个 `ret`
- 栈布局产物:链注释图(偏移 + gadget 地址 + 参数)+ 算好的偏移表
3. **堆利用(fastbin / tcache 攻击模式)**:
- 先看 glibc 版本(`ldd ./target | grep libc`)决定攻击面(坑 3):
- **tcache(glibc ≥ 2.26)**:tcache poisoning——double free 后改 `fd` 到目标地址附近 → 下一次分配拿到任意地址(2.32+ 指针被 safe-linking 异或,坑 3);UAF 直接改 fd 更快
- **fastbin(glibc < 2.26 / 老题)**:fastbin dup——double free 同一 chunk 两次 → 同一地址连续分配两次 → 伪造堆上任意写
- **unsorted bin 泄露**:free 大 chunk(> fastbin 上限)进 unsorted bin,fd/bk 指向 main_arena —— `show`/格式化读泄露 libc base(`libc_base = leaked - main_arena_offset`)
- 用 pwndbg 逐 chunk 验证:
```
(gdb) heap # 看 chunk 布局与地址
(gdb) bins # 看各 bin 链表(tcachebins/fastbins/unsorted)
```
- 模式清单:double free → tcache dup;UAF → fd 覆写 / 泄露;off-by-one(单字节溢出)→ 改 next chunk size 造重叠 chunk;house of 系列(高版本利用)标注复杂度再上
- 目标落地:任意地址分配 → 改 `__free_hook`(≤2.33)/ `__malloc_hook` / GOT 可写项 / 栈返回地址 → 触发 get shell 或任意读写
4. **沙箱 / 缓解绕过(seccomp)**:
- 若步骤 1 发现禁 `execve`:**ORW 链**——ROP 依次调 `open(flag_path, 0)` → `read(fd, buf, n)` → `write(1, buf, n)`:
```sh
ROPgadget --binary ./target | grep -E 'syscall|pop rdi|pop rsi|pop rdx' # 或 libc 里的 gadget
```
- 找不到现成 `syscall` gadget 时用 libc 的(先泄露 libc base)或 SROP(`rt_sigreturn` 构造寄存器帧)
- 缓解绕过思路:PIE → 先泄露 base(格式化 / 堆泄露 / puts 泄露,见 [[re-pwn]] 步骤 3-4);Canary → 泄露后再溢出(payload = pad + canary + pad + ret,canary 低 1 字节为 `\x00` 可直接截断处理);RELRO full → 不打 GOT,改栈 / `__free_hook` / `environ` 泄栈地址
- 组合顺序纪律:先泄露(canary / libc base / heap base)→ 再覆盖(坑 4)
5. **PoC 验证**:
```python
from pwn import *
context.binary = './target'; context.log_level = 'debug'
p = process('./target') # 沙箱内本地;远程: remote(host, port)
p.sendline(payload)
p.interactive()
```
- 验证纪律:**逐步验证每段链**(泄露段先打,看到泄露值对得上再打覆盖段)——一次全链失败无法定位是哪段错(坑 4);gdb 断在链入口逐条 `si` 确认跳转与栈布局
- 本地跑通 → 记录 PoC(文件 + sha256 + 运行输出)→ 远程最终验证;PoC hash 与漏洞特征可进 [[re-ioc]]
## 内核利用
与用户态同框架:fuzz([[re-fuzzing]])→ crash([[re-crash-triage]])→ 利用(本路径)。
- **提权原语**:modprobe_path 覆写(触发内核执行任意路径)、cred 结构覆写(uid/gid 置 0)、io_uring / BPF 子系统利用面
- **堆喷与对象布局**:堆喷策略(同尺寸对象占位)、对象重叠(UAF 后伪造对象)、SLUB 分配器行为(per-CPU 缓存)
- **内核 UAF 利用路径**:漏洞触发(悬垂引用)→ 对象重用(占位伪造)→ 控制流劫持(函数指针 / ops 表覆写)
- **环境**:调试内核(KASAN 开、KASLR 关闭或绕过)、gdb/kgdb 断点、模块化测试(只读分析 + 沙箱验证)
## 跨域联合
- [[re-vuln]]:本技能被漏洞挖掘网关引用——网关 4-5 步(逆向定位 → 报告)之后,可利用的漏洞进入本技能做利用开发(漏洞挖掘域"从崩溃到 PoC"的收口)
- [[re-crash-triage]]:崩溃分析 / 最小 PoC 输入交接本技能(利用面分析第一步的输入);本技能产出的 PoC 反过来验证漏洞可达性
- [[re-pwn]]:入门衔接——栈溢出 / 格式化字符串 / ret2libc 基础在本技能扩展为复杂 ROP 与堆利用
- [[re-ctf]]:pwn 赛题进阶(堆题 / 高难度 ROP 题)经网关调度本技能
- [[re-angr]](可选):溢出偏移求约束、利用路径校验自动化
- [[re-gdb]]:栈 / 堆布局动态验证(pwndbg `heap`/`bins`);[[re-binary-core]] 底座定位原语与后门
- [[re-sandbox]] / [[platform-tips]]:利用验证默认沙箱内跑(最高原则);远程目标仅做最终验证
## 常见坑与陷阱
- **gadget 地址受 ASLR / PIE 影响(泄漏为先)**:现象——按静态地址(`objdump` 出的 gadget / 函数地址)写链,本地偶发成功远程必挂,或泄露后算出的 base 对不上;原因——PIE 开启时二进制基址随 ASLR 变化(低 12 位页内偏移不变),libc 基址同理;静态地址只在无 PIE(或已算 base)时成立;对策——**泄漏为先**:第一段链先泄露(puts/printf 打 GOT 或堆/格式化泄露),`libc_base = leaked - offset`、`binary_base = leaked - offset`,后续所有 gadget/函数地址 = base + 静态偏移(低 12 位直接拼);每次泄露后重新计算再发覆盖段,别复用上一轮的 base
- **tcache 双链表检查(safe-linking 异或 / key 检查)**:现象——glibc 2.32+ 上 classic tcache poisoning 崩(`malloc(): unaligned tcache chunk detected`)或 double free 直接 abort(`free(): double free detected in tcache 2`);原因——2.32 起 tcache fd 被 safe-linking 异或(`PROTECT_PTR(pos, ptr) = ptr ^ (pos >> 12)`,pos 是**该 chunk 的 fd 字段自身存储地址**(常规 chunk 即 `chunk_addr + 0x10`),不是 heap_base);2.29 起 double free 检查 key 字段:free 进 tcache 时把 key 写成 tcache 结构地址,再 free 同 chunk 时发现 key 仍等于 tcache 指针就**遍历整个 bin** 找重复(命中直接 `malloc_printerr` abort)——"中间插一次 free 别的 chunk"不清除首个 chunk 的 key 字段,绕不过;对策——写新 fd 前先**泄露堆地址**再编码:`stored_fd = new_fd ^ (fd_field_addr >> 12)`(UAF 读到的 fd 是已解密指针,但**新值必须重新编码**——`PROTECT_PTR` 作用于每次写入,只有原样写回才免编码);double free 绕 key 检查只有两条路:① UAF 改写 key 字段(改成非 tcache 指针值)② 改 size 使 chunk 落入**不同 bin**(key 检查只看目标 bin 的链头,落别的 bin 不触发)
- **glibc 版本差异(check 变化)**:现象——本地(如 glibc 2.31)打通的堆利用,题目远程(2.35 / 2.27)行为完全不同:tcache 不存在 / 数量上限不同 / `__free_hook` 没了 / safe-linking 出现;原因——堆管理器每版本收紧检查:2.26 引入 tcache、2.29 加 key 检测、2.32 加 safe-linking、2.34 删 `__malloc_hook`/`__free_hook`(2.34+ 用 `__libc_malloc` 指针或环境劫持)、2.37 起 `global_max_fast` 收窄为 `uint8_t`(fastbin 上限随之收紧)、tcache 初始化改用 `__getrandom_nocancel`;对策——`ldd ./target | grep libc` 先确认版本,攻击面按版本选(步骤 3 清单);本地环境与远程不一致时用 `patchelf --set-interpreter ./ld-2.35.so --replace-needed libc.so.6 ./libc-2.35.so` 换 libc 本地复现;2.34+ 换目标(`__malloc_hook` 不存在 → 打 `exit` 函数指针 / `setcontext` / vtable 思路);每个 check 收紧点(key / safe-linking / count)列检查清单逐步验证
- **绕过组合复杂(先规划再写)**:现象——防护多个叠加时(NX+PIE+Canary+seccomp+高版本 glibc)脚本越改越乱,动不动全崩,调试半天发现是某一小段逻辑错;原因——每个保护是独立约束,组合后依赖链长(泄露 canary → 泄露 libc → 泄露堆 → ORW 链),一步错全盘错,边写边试没有全局规划;对策——**先写利用规划表再写代码**:列清(1)需要哪些原语(栈溢出 / UAF / 格式化写)(2)需要泄露什么、泄露顺序与依赖(canary 必须在覆盖前、libc base 必须在调 libc 函数前)(3)每段链的入口与出口;然后**分步实现分步验证**:泄露段单独跑通(值对得上)→ 覆盖段单独跑通(gdb 确认返回地址命中)→ 合链;gdb 断点逐段 `si`;每改一步先确认上一步仍通
- **对齐问题(movaps / 栈对齐)**:现象——ROP 链到 `system` 或 `printf` 就段错误,gdb 看指令在 `movaps` 处崩,链前半段明明全对;原因——x86-64 ABI 要求 `call` 时栈 16 字节对齐,GCC 生成的 libc 函数(`printf`/`system`/`execve`)内部 `movaps` 假设对齐,不对齐直接 SIGSEGV;对策——在调用 gadget 链里、目标函数前补一个 `ret` gadget(`ret` 使 `$rsp += 8` 调整对齐);或选对齐版 gadget(`pop rdi; ret` + 偶数字节 padding);64 位所有 libc 函数调用前都按此检查
Mirrored from the author's public source. Install counts from the open skills registry.