SuperAPI AI Directory

exploiting-memory-corruption

Security & auditing

View repository

Updated 10-16 03:18

About this skill

Exploiting Memory Corruption

Reverse engineering tells you a binary is buggy. Exploitation is the separate craft of turning that bug into a controlled primitive and then into code execution, against the mitigations the target actually ships. The work is a chain of conversions — bug to primitive, primitive to leak, leak to control — and each link is chosen by what the binary's protections allow, not by rote.

When to Use

  • You have a crash or a known bug class in an ELF/PE and need to assess exploitability and build a working exploit
  • Constructing a ROP/JOP chain, a ret2libc, or a heap-grooming sequence
  • Defeating a specific mitigation set (ASLR, NX/DEP, canary, PIE, RELRO) on a lab or CTF target
  • Working an authorized binary-exploitation task or a CTF pwn challenge

When NOT to Use

  • Recovering what the binary does — disassembly, decompilation, algorithm recovery — is analyzing-binaries; do that first if the bug is not yet understood.
  • Unpacking or de-virtualizing a protected binary before you can even see the code — use unpacking-protected-binaries.
  • Web, deserialization, or injection bugs — those are their own skills (testing-web-applications, exploiting-deserialization); this skill is native memory corruption only.
  • Kernel/driver exploitation specifically — the primitives differ enough (no libc, SMEP/SMAP/KASLR, syscall context) that it is out of scope here; the userland method still informs it.
  • Fuzzing to find the bug — that is a discovery step; this skill starts once you have a crash.

Enumerate the Mitigations First

The exploit you can build is dictated by what is turned on. Read them before planning anything (checksec, or pwntools' checksec/ELF):

Mitigation What it blocks The usual answer
NX / DEP Executing your shellcode on the stack/heap ROP / ret2libc — reuse existing executable code
ASLR Hardcoding addresses An info leak to recover a base, then compute offsets
Stack canary Naive stack-return overwrite Leak the canary, overwrite something else, or brute a forking server
PIE Hardcoding the binary's own addresses Leak a binary address; until then only relative/partial overwrites
Partial RELRO Little — GOT is still writable GOT overwrite is on the table
Full RELRO GOT overwrite Target __malloc_hook/__free_hook (pre-2.34), or a ROP-only path

The two questions that order the work: do I have an info leak (ASLR/PIE), and where can I write (RELRO). Everything else follows from those.

Bug Class to Primitive

Name what you have before reaching for a technique:

  • Stack buffer overflow → direct control of saved return address; the cleanest path to a ROP chain, canary permitting.
  • Heap overflow / off-by-one → corrupt adjacent chunk metadata or an adjacent object's pointers; primitive depends on the heap layout you can groom.
  • Use-after-free → reallocate the freed slot with attacker data so a stale pointer is used under your control; classic route to a fake vtable/object.
  • Type confusion → treat one type's fields as another's, often yielding a read/write or a controlled call directly.
  • Format string → %n for a targeted write and %p/%s for a leak; often both a leak and a write from one bug.
  • Integer overflow / OOB → usually a means to one of the above (a short-computed size producing a heap overflow).

The goal of this step is one of three primitives: control flow hijack, an arbitrary read, or an arbitrary write. Get to a primitive, then build.

ROP and Code Reuse

With NX on, you execute the program's own bytes in an order you choose:

  • Find gadgets with ROPgadget or ropper; look for the pop rdi; ret-style register controls that set up a call.
  • ret2libc — return into system("/bin/sh") once you have a libc base; the most direct chain when libc is available and leaked.
  • ret2csu to control rdx/rsi when no clean gadget exists, using __libc_csu_init's tail on pre-2.34 binaries.
  • SROP (sigreturn-oriented) when gadgets are scarce but you can stage a fake signal frame and reach a sigreturn.
  • ret2dlresolve to call a function by forcing the dynamic linker to resolve it, useful with no leak and partial RELRO.
  • one_gadget to find a single libc address that execs a shell, collapsing the final stage when its constraints hold.

Match the leaked libc to a build with libc-database so the offsets are right — a chain built against the wrong libc fails silently.

Heap Exploitation (glibc ptmalloc)

Heap work is layout work: you arrange allocations so a corruption lands on something useful. The technique depends on the glibc version, because the allocator's checks have tightened steadily.

  • tcache poisoning (2.26+) — the highest-leverage modern primitive: corrupt a freed tcache chunk's next to return an arbitrary pointer from the next malloc. On 2.32+ the pointer is mangled by safe-linking (XORed with the address it is stored at, shifted right by 12), so you need a heap leak to forge it, and 2.34 removed the __free_hook/__malloc_hook targets that used to end the chain.
  • fastbin dup — double-free into the fastbin to return the same chunk twice; watch the tcache-first behaviour on modern glibc.
  • unsorted-bin leak — a freed chunk in the unsorted bin holds a main_arena pointer; reading it leaks a libc address to defeat ASLR.
  • House of * techniques (Force, Orange, Einherjar, and newer) — each abuses a specific allocator invariant for an arbitrary write when the simple primitives are blocked; reach for them only when the layout demands it.

Drive all of this in pwndbg or GEF — heap, bins, and tcache commands show the state so you groom against reality, not a mental model.

The Loop

Build the exploit incrementally with pwntools, and verify each conversion before adding the next: reach the crash, prove control of the instruction pointer, land a leak and confirm the computed base, then the write, then the shell. When a stage misbehaves, attach with gdb.attach() at exactly that point rather than staring at the whole chain. A working local exploit that fails remotely is almost always an environment gap — libc version, stack alignment (a stray ret for movaps), or an env-dependent leak.

Scope and Authorization

Exploit development belongs on binaries you own, CTF targets, lab VMs, or software you are explicitly engaged to test — the lab_only egress mode is the default here, since a working exploit run against production is an intrusion, not research. Two specific edges:

  • A 0-day in third-party software puts you in coordinated disclosure, not publication. Build the proof, report it through the vendor's channel, and do not drop a weaponized exploit publicly ahead of a fix.
  • Never test an exploit against a system you do not control to "see if it works." A memory-corruption exploit that lands crashes the service when it does not; that is an availability impact on someone else's estate.

Rationalizations to Reject

  • "It crashes on EIP/RIP, so it's exploitable." Control of the instruction pointer is the start, not the finish. Modern mitigations mean a crash may be a dead end without a leak; assess reachability of a primitive before claiming exploitability.
  • "It works locally, ship it." Remote libc, ASLR entropy, and stack alignment differ. Match the target libc and test against the real environment before reporting it as working.
  • "Just spray shellcode on the stack." NX has been default for over a decade. If you are writing shellcode to the stack and jumping to it, confirm NX is actually off before spending time on it.
  • "one_gadget will handle the last step." Its constraints (specific registers null, specific stack state) frequently do not hold. Check them; keep a ret2libc as the fallback.
  • "The heap technique from the writeup will just work." Allocator checks are version-specific — tcache keys, count checks, safe-linking. Confirm the target glibc version and that the technique still applies to it.

ATT&CK Coverage

Generated from secskills-core/ttp-index.json — edit that file, then run python3 scripts/sync_attack.py --write. Re-verify IDs against the current ATT&CK release before citing them in a report.

Execution (TA0002)

  • T1203 Exploitation for Client Execution — see also performing-social-engineering, analyzing-malware

Privilege Escalation (TA0004)

  • T1068 Exploitation for Privilege Escalation — see also escalating-linux-privileges, escalating-windows-privileges

Lateral Movement (TA0008)

  • T1210 Exploitation of Remote Services — see also enumerating-network-services

Detection content for any of these: engineering-detections. Proactive search: hunting-threats. Post-compromise: responding-to-incidents.

References

  • analyzing-binaries — recovering the code and understanding the bug first
  • unpacking-protected-binaries — when the binary must be unpacked/de-virtualized
  • reviewing-cryptography — when the target is a crypto primitive, not memory
  • maintaining-engagement-state — egress modes and the lab-only default here