Primitives for mitigated targets

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, find VirtualProtect / LoadLibrary, JIT page or stomp module bytes (CFG-aware: use a CFG-valid call target).
  • Kernel LPE: walk EPROCESS list, 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

See also: arbitrary-read-write-primitives, control-flow-guard, hvci-vbs.