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
1---2name: exploiting-memory-corruption3description: Develop working exploits from memory-corruption bugs in native binaries — turning a stack/heap overflow, use-after-free, or type confusion into control flow, an arbitrary read/write, and a shell, and defeating ASLR, NX, stack canaries, PIE, and RELRO along the way. Use when you have a crashing input or a known bug class in an ELF/PE and need a primitive, when building a ROP chain with pwntools and ROPgadget, or when working a CTF pwn challenge or authorized binary exploitation.4---56# Exploiting Memory Corruption78Reverse engineering tells you a binary is buggy. Exploitation is the separate9craft of turning that bug into a controlled primitive and then into code10execution, against the mitigations the target actually ships. The work is a11chain of conversions — bug to primitive, primitive to leak, leak to control —12and each link is chosen by what the binary's protections allow, not by rote.1314## When to Use1516- You have a crash or a known bug class in an ELF/PE and need to assess17 exploitability and build a working exploit18- Constructing a ROP/JOP chain, a ret2libc, or a heap-grooming sequence19- Defeating a specific mitigation set (ASLR, NX/DEP, canary, PIE, RELRO) on a20 lab or CTF target21- Working an authorized binary-exploitation task or a CTF pwn challenge2223## When NOT to Use2425- **Recovering what the binary does** — disassembly, decompilation, algorithm26 recovery — is `analyzing-binaries`; do that first if the bug is not yet27 understood.28- **Unpacking or de-virtualizing a protected binary** before you can even see29 the code — use `unpacking-protected-binaries`.30- **Web, deserialization, or injection bugs** — those are their own skills31 (`testing-web-applications`, `exploiting-deserialization`); this skill is32 native memory corruption only.33- **Kernel/driver exploitation** specifically — the primitives differ enough34 (no libc, SMEP/SMAP/KASLR, syscall context) that it is out of scope here;35 the userland method still informs it.36- **Fuzzing to *find* the bug** — that is a discovery step; this skill starts37 once you have a crash.3839## Enumerate the Mitigations First4041The exploit you can build is dictated by what is turned on. Read them before42planning anything (`checksec`, or `pwntools`' `checksec`/ELF):4344| Mitigation | What it blocks | The usual answer |45| --- | --- | --- |46| **NX / DEP** | Executing your shellcode on the stack/heap | ROP / ret2libc — reuse existing executable code |47| **ASLR** | Hardcoding addresses | An info leak to recover a base, then compute offsets |48| **Stack canary** | Naive stack-return overwrite | Leak the canary, overwrite something else, or brute a forking server |49| **PIE** | Hardcoding the binary's own addresses | Leak a binary address; until then only relative/partial overwrites |50| **Partial RELRO** | Little — GOT is still writable | GOT overwrite is on the table |51| **Full RELRO** | GOT overwrite | Target `__malloc_hook`/`__free_hook` (pre-2.34), or a ROP-only path |5253The two questions that order the work: *do I have an info leak* (ASLR/PIE), and54*where can I write* (RELRO). Everything else follows from those.5556## Bug Class to Primitive5758Name what you have before reaching for a technique:5960- **Stack buffer overflow** → direct control of saved return address; the61 cleanest path to a ROP chain, canary permitting.62- **Heap overflow / off-by-one** → corrupt adjacent chunk metadata or an63 adjacent object's pointers; primitive depends on the heap layout you can64 groom.65- **Use-after-free** → reallocate the freed slot with attacker data so a stale66 pointer is used under your control; classic route to a fake vtable/object.67- **Type confusion** → treat one type's fields as another's, often yielding a68 read/write or a controlled call directly.69- **Format string** → `%n` for a targeted write and `%p`/`%s` for a leak; often70 both a leak and a write from one bug.71- **Integer overflow / OOB** → usually a means to one of the above (a72 short-computed size producing a heap overflow).7374The goal of this step is one of three primitives: **control flow hijack**, an75**arbitrary read**, or an **arbitrary write**. Get to a primitive, then build.7677## ROP and Code Reuse7879With NX on, you execute the program's own bytes in an order you choose:8081- **Find gadgets** with ROPgadget or ropper; look for the `pop rdi; ret`-style82 register controls that set up a call.83- **ret2libc** — return into `system("/bin/sh")` once you have a libc base; the84 most direct chain when libc is available and leaked.85- **ret2csu** to control `rdx`/`rsi` when no clean gadget exists, using86 `__libc_csu_init`'s tail on pre-2.34 binaries.87- **SROP** (sigreturn-oriented) when gadgets are scarce but you can stage a88 fake signal frame and reach a `sigreturn`.89- **ret2dlresolve** to call a function by forcing the dynamic linker to resolve90 it, useful with no leak and partial RELRO.91- **one_gadget** to find a single libc address that execs a shell, collapsing92 the final stage when its constraints hold.9394Match the leaked libc to a build with `libc-database` so the offsets are right —95a chain built against the wrong libc fails silently.9697## Heap Exploitation (glibc ptmalloc)9899Heap work is layout work: you arrange allocations so a corruption lands on100something useful. The technique depends on the glibc version, because the101allocator's checks have tightened steadily.102103- **tcache poisoning** (2.26+) — the highest-leverage modern primitive: corrupt104 a freed tcache chunk's `next` to return an arbitrary pointer from the next105 `malloc`. On 2.32+ the pointer is mangled by **safe-linking** (XORed with the106 address it is stored at, shifted right by 12), so you need a heap leak to107 forge it, and 2.34 removed the `__free_hook`/`__malloc_hook` targets that used108 to end the chain.109- **fastbin dup** — double-free into the fastbin to return the same chunk twice;110 watch the tcache-first behaviour on modern glibc.111- **unsorted-bin leak** — a freed chunk in the unsorted bin holds a `main_arena`112 pointer; reading it leaks a libc address to defeat ASLR.113- **House of \* techniques** (Force, Orange, Einherjar, and newer) — each abuses114 a specific allocator invariant for an arbitrary write when the simple115 primitives are blocked; reach for them only when the layout demands it.116117Drive all of this in `pwndbg` or GEF — `heap`, `bins`, and `tcache` commands118show the state so you groom against reality, not a mental model.119120## The Loop121122Build the exploit incrementally with pwntools, and verify each conversion123before adding the next: reach the crash, prove control of the instruction124pointer, land a leak and confirm the computed base, then the write, then the125shell. When a stage misbehaves, attach with `gdb.attach()` at exactly that126point rather than staring at the whole chain. A working local exploit that127fails remotely is almost always an environment gap — libc version, stack128alignment (a stray `ret` for `movaps`), or an env-dependent leak.129130## Scope and Authorization131132Exploit development belongs on binaries you own, CTF targets, lab VMs, or133software you are explicitly engaged to test — the `lab_only` egress mode is the134default here, since a working exploit run against production is an intrusion,135not research. Two specific edges:136137- **A 0-day in third-party software** puts you in coordinated disclosure, not138 publication. Build the proof, report it through the vendor's channel, and do139 not drop a weaponized exploit publicly ahead of a fix.140- **Never test an exploit against a system you do not control** to "see if it141 works." A memory-corruption exploit that lands crashes the service when it142 does not; that is an availability impact on someone else's estate.143144## Rationalizations to Reject145146- **"It crashes on EIP/RIP, so it's exploitable."** Control of the instruction147 pointer is the start, not the finish. Modern mitigations mean a crash may be148 a dead end without a leak; assess reachability of a primitive before claiming149 exploitability.150- **"It works locally, ship it."** Remote libc, ASLR entropy, and stack151 alignment differ. Match the target libc and test against the real152 environment before reporting it as working.153- **"Just spray shellcode on the stack."** NX has been default for over a154 decade. If you are writing shellcode to the stack and jumping to it, confirm155 NX is actually off before spending time on it.156- **"one_gadget will handle the last step."** Its constraints (specific157 registers null, specific stack state) frequently do not hold. Check them; keep158 a ret2libc as the fallback.159- **"The heap technique from the writeup will just work."** Allocator checks are160 version-specific — tcache keys, count checks, safe-linking. Confirm the target161 glibc version and that the technique still applies to it.162163<!-- attack:start -->164165## ATT&CK Coverage166167_Generated from `secskills-core/ttp-index.json` — edit that file, then run168`python3 scripts/sync_attack.py --write`. Re-verify IDs against the169current ATT&CK release before citing them in a report._170171**Execution** (TA0002)172173- [T1203](https://attack.mitre.org/techniques/T1203/) Exploitation for Client Execution — see also `performing-social-engineering`, `analyzing-malware`174175**Privilege Escalation** (TA0004)176177- [T1068](https://attack.mitre.org/techniques/T1068/) Exploitation for Privilege Escalation — see also `escalating-linux-privileges`, `escalating-windows-privileges`178179**Lateral Movement** (TA0008)180181- [T1210](https://attack.mitre.org/techniques/T1210/) Exploitation of Remote Services — see also `enumerating-network-services`182183Detection content for any of these: `engineering-detections`. Proactive search: `hunting-threats`. Post-compromise: `responding-to-incidents`.184185<!-- attack:end -->186187## References188189- `analyzing-binaries` — recovering the code and understanding the bug first190- `unpacking-protected-binaries` — when the binary must be unpacked/de-virtualized191- `reviewing-cryptography` — when the target is a crypto primitive, not memory192- `maintaining-engagement-state` — egress modes and the lab-only default here