re-exploit

Installation
Run `npx skills add "https://github.com/dslsdzc/rev-skills" --skill "re-exploit"` to install this skill, then follow its SKILL.md instructions for my next request.

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.

The systems behind these skills get built for partners every week.

Partner with us