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.

PrimitiveWhat it gives youTypical source bug
Information disclosureLeak addresses, defeat ASLR/KASLR, find gadgetsOOB read, uninitialized memory, type confusion
Arbitrary read (AAR)Read any addressControlled pointer dereference
Arbitrary write (AAW)Write to any addressControlled write (UAF, OOB write, type confusion)
Controlled call / RIP controlRedirect executionvtable corruption, function-pointer overwrite
Type confusionReinterpret memoryPolymorphic dispatch on attacker-controlled object
Use-after-freeOperate on freed memory through a dangling pointerRefcount bug, lifetime confusion
Heap groomingShape the allocator state to your advantageCombined 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.

MitigationWhat it stopsTypical bypass
DEP / NXExecuting dataROP / JOP, return-to-libc, JIT spray
ASLR / KASLRHardcoding addressesInfo leak, partial overwrite, side channel
Stack canaries (/GS, SSP)Naive stack overflow → RIPLeak the canary, bypass via SEH/EH, write past it
CFG / kCFGIndirect-call hijacking to non-CFG-valid targetsFind a CFG-valid gadget, use call-site filtering bypasses
CET (Shadow Stack + IBT)ROP and indirect-jump abuseFind IBT-valid endbr targets; data-only attacks
ACGJIT writing executable pagesFake JIT request, RPC out, sandbox escape
CIGLoading unsigned modulesHijack signed binaries, abuse signed loaders
SMEP / SMAPKernel executing/reading user pagesROP into kernel image, transient SMAP disable
HVCIUnsigned kernel codeData-only attacks; bring-your-own-vulnerable-driver
VBS / Credential GuardLSASS secret accessAttack VTL boundary; pre-VBS captures
Sandbox (browser, app)Process-level scope of compromiseSandbox 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

  1. Trigger. A reliable, deterministic way to reach the buggy state. Often the easiest part.
  2. Stabilize. Win the race; control the allocator state; survive the crash.
  3. Leak. Defeat ASLR / KASLR. Find a primitive that gives you a known address.
  4. Build R/W. Promote the bug into AAR + AAW.
  5. Pivot to code execution (if needed). RIP control via vtable / function pointer / return address. Or skip this entirely with a data-only attack.
  6. Defeat mitigations. CFG-valid call sites, ROP chains targeting CET-friendly endpoints, kernel image gadgets that survive HVCI.
  7. Payload. Token steal (kernel), shellcode + reflective load (user), browser → renderer escape, sandbox escape.
  8. 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