exploit-pwn-chain

Engineer a reliable exploit from a known memory-corruption bug — stack, heap, or kernel — taking a local crash to a stable remote exploit: ret2libc/ret2csu/one_gadget, tcache/fastbin/unsorted-bin tech

SKILL.md

GitHub

Pwn chain: bug to working exploit

When it applies

You already know where it crashes — reverse-eng-binary-triage or exploit-memory-corruption found the overflow/UAF/double-free — and now you must produce a stable exploit against the real remote target, not a script that only works on your laptop. Covers userspace stack pwn, glibc heap exploitation, and Linux kernel/driver pwn (e.g. an ioctl bug for privesc). Pentest-only; note the crash risk in the RoE (tradecraft-scope-roe).

Why it works

A local win and a remote win differ by engineering, not by the bug: the remote libc version, ASLR slide, stack alignment, network buffering, and heap layout all shift. The chain is always the same — model the protections, leak what randomization hides, build the primitive (ROP/poisoned bin/sprayed object), then iterate against the real environment until the success rate is measured, not assumed.

Method

Stack / heap / kernel technique reference (templates, glibc version table, spray objects, privesc paths): see cheatsheet.md next to this file.

  1. Classify bug + protections. checksec ./vuln (NX / canary / PIE / RELRO / FORTIFY) decides the whole strategy. Bug class: stack overflow / format string / heap (UAF, double-free, overflow) / integer / race / kernel.
  2. Pick the strategy. NX off + no ASLR → shellcode. NX + libc provided → ret2libc / one_gadget. NX + no libc → leak, then libc-database lookup. Heap → technique per glibc version (tcache / fastbin / unsorted / large bin). Kernel → commit_creds ROP, modprobe_path, or core_pattern.
  3. Stage libc + gadgets. ./find puts 0x6f0 in libc-database; ROPgadget --binary ./libc.so.6 --only "pop|ret"; one_gadget ./libc.so.6; base = leaked - libc.sym['puts']. Run the target under the exact remote libc locally: patchelf --set-interpreter ./ld-linux-x86-64.so.2 --set-rpath . ./vuln.
  4. Write the pwntools skeleton. context.binary = ELF('./vuln'); iterate with process() / gdb.debug('./vuln', 'b *main+xx'); cyclic() / cyclic_find() for the offset; finish at p.interactive().
  5. Make it work locally — attach, watch registers, tune offsets with pwndbg/GEF (vmmap, heap, bins, telescope), then switch to remote().
  6. Stabilize remotely. Never guess libc offsets — leak and reverse-lookup. Fix movaps stack alignment with an extra ret gadget. Anchor reads with recvuntil(b'exact marker'), never sleep; prefer sendlineafter over sendline. For heap sprays, oversize the spray and leave padding chunks against consolidation. Then loop the exploit (while True) until it succeeds ≥ 95% over 20+ remote runs.

Gotchas

  • "Local works, remote segfaults in system" — almost always 16-byte stack misalignment (movaps xmm0, [rsp]); add one ret gadget. Second cause: wrong libc version — leak first.
  • Heap techniques are glibc-version-locked — tcache appeared in 2.27, safe-linking in 2.32, hooks removed in 2.34. Same binary + different libc = completely different exploit path. Check with ./libc.so.6 | head -1.
  • Kernel pwn: confirm CPU flags first — +smep +smap +pku in the qemu line (or the real kernel's config) dictates the ROP chain; KPTI forces the swapgs_restore_regs_and_return_to_usermode trampoline for the return to userland.
  • One KASLR leak is enough — compute everything as offsets from it; repeated leaking just adds failure surface.
  • canary on forking servers — child processes share the canary; brute-force it byte by byte (1/256 per byte).
  • Static binaries have no GOT — use SROP or raw syscalls instead of ret2libc.

Verify success

The exploit runs end-to-end against the real remote target ≥ 95% of the time across 20+ consecutive runs, from the same pwntools script, with the libc identified by leak — not assumed. A shell (or root shell, for kernel pwn) that survives reconnection is the proof.

References

pwntools docs; GEF (bata24 fork for kernel structs) / pwndbg; ROPgadget / ropper; one_gadget; libc-database. Find the bug with reverse-eng-binary-triage; userspace basics in exploit-memory-corruption; post-exploitation via exploit-chaining and payloads-reverse-shells.


Portions adapted from reverse-skill by zhaoxuya520, MIT License.