Primitives for mitigated targets
TL;DR: Modern Windows targets don’t fall to “smash the stack, jump to shellcode.” Exploitation now means engineering a series of data-only primitives (info leak → AR → AW → token swap) that respect ASLR, CFG, CET, HVCI, and segment-heap hardening.
What it is
A mental model for what an exploit looks like in 2024+: not a single payload but a pipeline of primitives, each derived from the bug. Each stage answers one question — where are things? (leak), what can I read? (AR), what can I write? (AW), how do I become SYSTEM? (data corruption). Old PoCs that splat shellcode on the stack and ret into it are obsolete on protected targets.
Preconditions / where it applies
- Target with full mitigation set: ASLR + PIE-equivalent (
/DYNAMICBASE), DEP, CFG, CET (where available), kCFG/kCET in kernel, HVCI/VBS, segment heap. - A memory-safety bug with at least one of: linear OOB, UAF, type confusion, integer underflow on a size, or partial pointer overwrite.
Technique
Stage 0 — info leak. Without a leak there is no reliable exploit. Common sources: uninitialised structure fields returned to user mode (uninitialised-memory-disclosures), pointer fields in objects you can spray and re-read, Spectre-style transient reads, or sibling bugs in the same process.
Stage 1 — turn the bug into a controlled corruption of a useful object.
- For OOB write: heap groom so a sprayable object with an attacker-readable field is adjacent.
- For UAF: spray reclaims; the field overlay determines what you control (vtable, length, pointer).
- For type confusion: choose the target type whose layout aliases the controlled fields onto pointers/lengths.
Stage 2 — arbitrary read. Overwrite a size field; trigger a read; recover adjacent memory. Iterate until you can read any kernel/user address.
Stage 3 — arbitrary write. Overwrite a data pointer; trigger a write; deposit bytes at an address of choice. See arbitrary-read-write-primitives.
Stage 4 — escalation.
- User-mode RCE: locate
kernelbase, findVirtualProtect/LoadLibrary, JIT page or stomp module bytes (CFG-aware: use a CFG-valid call target). - Kernel LPE: walk
EPROCESSlist, copy System token, restore. - Cross-tenant cloud / sandbox escape: find the broker process and abuse its handles.
Stage 5 — cleanup. Reverse corruption so the kernel doesn’t bug-check on shutdown. This step is what separates a one-shot crash from a reliable exploit shipped in a chain.
What changed vs. old playbooks.
- Stack shellcode → dead (DEP + CFG).
- ROP chain to
VirtualAlloc→ still possible in user mode if no CET; kernel ROP further constrained by kCFI/HVCI. - “Just spray vtables” → segment heap and pool isolation make groom unreliable; bespoke object choice matters.
Detection and defence
- Push every layer: HVCI, MBEC, kCFI, CET, Win32k filtering, AppContainer, sandboxing.
- Microsoft’s Exploit Guard / Attack Surface Reduction blocks several common second-stage actions (LoadLibrary from script hosts, code-injection patterns).
- EDR pattern: rapid syscall storms against
NtFsControlFile,NtAlpcCreatePort, suspect token field reads.
References
- Microsoft: mitigation history — mitigation engineering posts
- Connor McGarr — Windows exploitation series — modern primitive engineering
- Project Zero — Issue tracker — case studies of full chains
See also: arbitrary-read-write-primitives, control-flow-guard, hvci-vbs.