Exploit Development
Turning a bug into a reliable, mitigation-aware weapon.
Status: seed Related: Vulnerability Research, Privilege Escalation, Ghidra, Reading List
What it is
A vulnerability is just a crash until it’s an exploit. Exploit development is the engineering discipline that turns a known bug into a working primitive, chains primitives into a functioning exploit, defeats relevant mitigations, and makes the result reliable across targets.
A useful frame: bug → primitive → exploit → operation. Vulnerability researchers stop at the first arrow; exploit developers carry the work further.
Primitives
The vocabulary of exploit dev. Each one is a building block.
| Primitive | What it gives you | Typical source bug |
|---|---|---|
| Information disclosure | Leak addresses, defeat ASLR/KASLR, find gadgets | OOB read, uninitialized memory, type confusion |
| Arbitrary read (AAR) | Read any address | Controlled pointer dereference |
| Arbitrary write (AAW) | Write to any address | Controlled write (UAF, OOB write, type confusion) |
| Controlled call / RIP control | Redirect execution | vtable corruption, function-pointer overwrite |
| Type confusion | Reinterpret memory | Polymorphic dispatch on attacker-controlled object |
| Use-after-free | Operate on freed memory through a dangling pointer | Refcount bug, lifetime confusion |
| Heap grooming | Shape the allocator state to your advantage | Combined with any of the above |
A real exploit is usually 2-5 primitives chained together.
The mitigation ladder
Each generation of mitigations defeats the prior generation’s techniques. Knowing the ladder tells you what’s still viable on which target.
| Mitigation | What it stops | Typical bypass |
|---|---|---|
| DEP / NX | Executing data | ROP / JOP, return-to-libc, JIT spray |
| ASLR / KASLR | Hardcoding addresses | Info leak, partial overwrite, side channel |
| Stack canaries (/GS, SSP) | Naive stack overflow → RIP | Leak the canary, bypass via SEH/EH, write past it |
| CFG / kCFG | Indirect-call hijacking to non-CFG-valid targets | Find a CFG-valid gadget, use call-site filtering bypasses |
| CET (Shadow Stack + IBT) | ROP and indirect-jump abuse | Find IBT-valid endbr targets; data-only attacks |
| ACG | JIT writing executable pages | Fake JIT request, RPC out, sandbox escape |
| CIG | Loading unsigned modules | Hijack signed binaries, abuse signed loaders |
| SMEP / SMAP | Kernel executing/reading user pages | ROP into kernel image, transient SMAP disable |
| HVCI | Unsigned kernel code | Data-only attacks; bring-your-own-vulnerable-driver |
| VBS / Credential Guard | LSASS secret access | Attack VTL boundary; pre-VBS captures |
| Sandbox (browser, app) | Process-level scope of compromise | Sandbox escape via IPC, kernel bug, sibling-process bug |
Modern exploits are layered: leak → AAR → AAW → indirect-call control → CFG-valid gadget → mitigation bypass → payload. Each link can be its own months of research.
The exploit pipeline
- Trigger. A reliable, deterministic way to reach the buggy state. Often the easiest part.
- Stabilize. Win the race; control the allocator state; survive the crash.
- Leak. Defeat ASLR / KASLR. Find a primitive that gives you a known address.
- Build R/W. Promote the bug into AAR + AAW.
- Pivot to code execution (if needed). RIP control via vtable / function pointer / return address. Or skip this entirely with a data-only attack.
- Defeat mitigations. CFG-valid call sites, ROP chains targeting CET-friendly endpoints, kernel image gadgets that survive HVCI.
- Payload. Token steal (kernel), shellcode + reflective load (user), browser → renderer escape, sandbox escape.
- Reliability. Multi-target. Multi-OS-version. Multi-build. Cleanup so the process / kernel doesn’t crash after.
Data-only attacks
A growing class. Instead of getting RIP control and running shellcode, use the AAW to flip a flag — _KTHREAD.PreviousMode = 0, _TOKEN swap with SYSTEM, ACL replacement, JS engine type confusion that gives you addrof / fakeobj. CET, kCFG, and shadow stacks made code-reuse expensive; data-only routes around all of them.
Worth internalizing: the attacker’s goal is capability, not “running code”. Sometimes the simplest path to capability is a single integer write.
Reliability
A one-shot exploit that works on your test box is a PoC. Useful, but not the work.
What separates an exploit from a PoC:
- Version independence. Resolve symbols and offsets at runtime. Don’t hardcode
+0x478. - Race robustness. Account for timing variance, CPU count, scheduler behavior.
- Graceful failure. If the trigger doesn’t land, don’t blue-screen the box.
- Cleanup. Restore freed objects, unhook callbacks, leave no obvious artifacts.
- Detectability. Sometimes you can’t avoid telemetry, but knowing what you generate is part of the work.
Skills worth investing in
- Read assembly fluently (x86-64 and aarch64).
- Be comfortable in a debugger — gdb, WinDbg, x64dbg, lldb. Time-travel debugging changes how you root-cause.
- Understand the allocator on your target (NT pool / Segment Heap, glibc malloc, jemalloc, JS engine GCs).
- Read kernel and runtime source. Linux kernel, Chromium, V8, WebKit, glibc — the answers are in the code.
References
- The Shellcoder’s Handbook (2nd ed.) — Anley, Heasman, Lindner, Richarte
- A Guide to Kernel Exploitation — Perla, Oldani
- corelan.be — classic stack-overflow / ROP tutorials, still useful as a foundation
- ret2 systems — modern tutorials and CTF-style training
- exploit.education — Phoenix, Nebula, Protostar — hands-on practice
- Connor McGarr’s blog — kernel exploitation walkthroughs
