July 24, 2026
Windows Exploit Dev in 2026: ROP Is Dying, Data-Only Attacks Are Winning, and Your Debugger Got a…
Modern exploit chains can go from a sandboxed browser tab to NT AUTHORITY\SYSTEM without hijacking a single branch, without one byte of…

By Pratham Shah
9 min read
- 1 The playbook, then and now
- 2 The kernel gained hardware-enforced shadow stacks, and classic kernel ROP became dramatically harder
- 3 User mode: CFG grew up into XFG, and the forward edge closed too
- 4 So what actually works now? You stop hijacking control flow and start corrupting data
- 5 Case study: one bug, deterministic SYSTEM, zero hijacked branches
Modern exploit chains can go from a sandboxed browser tab to NT AUTHORITY\SYSTEM without hijacking a single branch, without one byte of shellcode, and without tripping a single control-flow mitigation.
That should unsettle you more than any individual zero-day. It means the playbook most of us learned smash a buffer, pivot the stack, ROP your way to executable memory is no longer the playbook. It still works in a lab with mitigations switched off. It does not work on the machines you're actually paid to break.
This is the delta. What died, what replaced it, and the debugging workflow that keeps you effective on the far side of the shift.
The one-paragraph version:_ Windows 11 24H2 introduced kernel-mode hardware-enforced shadow stacks by default on supported hardware when VBS/HVCI requirements are met, while continuing to strengthen control-flow integrity in user mode. Together they make classic ROP and pointer-hijacking dramatically less practical on properly configured default installs. Modern Windows exploit chains increasingly succeed by building read/write primitives and corrupting_ data the kernel already trusts no gadgets, no shellcode, nothing for the control-flow mitigations to catch. And the single highest-leverage skill right now is Time Travel Debugging, because data-only bugs break thousands of instructions away from where you corrupt them.
The playbook, then and now
Here's the whole shift on one page.
The old playbook (what the classic tutorials teach):
- Find a memory-corruption bug.
- Overwrite a saved return address or a function pointer.
- Pivot the stack, build a ROP chain to defeat DEP.
- Land in your shellcode. Win.
Why it's largely obsolete on a fully hardened 2026 default install: shadow stacks catch step 2–3 in hardware, control-flow integrity kills the function-pointer overwrite in step 2, and DEP was never the real wall anyway.
The 2026 playbook:
- Find a bug that yields a read and/or write primitive.
- Leak a base address increasingly through a side channel, because the easy leaks got locked down.
- Corrupt data the kernel already trusts: tokens, privilege bitmasks, page tables, dispatch tables.
- Avoid touching control flow whenever possible. There's far less for the mitigations to catch.
Everything below is the detail behind that table.
The kernel gained hardware-enforced shadow stacks, and classic kernel ROP became dramatically harder
The biggest single change: Kernel-mode Hardware-enforced Stack Protection, shipping by default on capable hardware in Windows 11 24H2 (build 26100) when VBS/HVCI is enabled (Microsoft docs).
Every kernel-mode stack now has a corresponding shadow stack: a separate, read-only copy of return addresses. On every ret, the CPU compares the return address on the normal stack against the shadow copy. Mismatch, or a stack pivot that lands without a valid token, and you eat a STATUS_STACK_BUFFER_OVERRUN (0xC0000409). In VTL0 the shadow stack is read-only only call (and wrssq in audit mode) can write to it.
Sit with what that does to your reflexes: you can no longer overwrite a saved return address and expect the ret to take you anywhere useful. The backward edge is enforced in silicon.
The two references worth your time, because the official docs are thin:
- A hands-on investigation of kernel-mode shadow stacks (Connor McGarr, Feb 2025) that drives a JTAG debugger to watch CET actually run the best practical treatment that exists.
- An SSTIC 2025 conference paper with the deep structural analysis, including audit-mode transitions and where the enforcement boundaries sit.
And note the dependency chain: kernel CET requires HVCI, and HVCI requires VBS. The mitigation stack is layered on purpose you don't get to quietly disable one brick.
User mode: CFG grew up into XFG, and the forward edge closed too
While the backward edge got shadow stacks, the forward edge indirect calls kept tightening.
Control Flow Guard has been around for years: a bitmap marks valid indirect-call targets and a lightweight check runs before each indirect call. Its weakness was always granularity. CFG only cares that a target is a valid function start, not which function, so overwriting a pointer with the address of any CFG-approved function got you through.
Xtended Flow Guard (XFG) closes most of that. It derives a type-based hash from the function prototype return type plus argument types into roughly a 55-bit value checked at the call site (community-maintained mitigation reference). Now your overwritten pointer has to reference a function with a matching prototype, not merely any function. The usable target pool collapses.
Stack them: XFG on the forward edge, CET shadow stacks on the backward edge, and the "overwrite a pointer, jump to a gadget" era is increasingly impractical on well-configured targets. This two-part macro overview of where exploit development is heading captures how far it has pushed attackers.
So what actually works now? You stop hijacking control flow and start corrupting data
This is the mental shift that matters most, and the classic tutorials can't teach it because it barely existed when they were written.
If you can't redirect execution, you don't. You build arbitrary read and write primitives and use them to corrupt data the kernel already trusts tokens, privilege bitmasks, page tables, the SSDT without introducing new code or new control-flow edges. This is data-oriented exploitation, and it has become one of the dominant directions in modern Windows exploitation research. A recent conference talk, Subverting the Windows Kernel with VBS/HVCI (Juan Sacco, EuskalHack 2026), makes the point directly: controlled R/W primitives and dispatch-table manipulation, demonstrated live on a fully updated Windows 11 box with VBS/HVCI and kernel CET enabled. None of it trips code integrity or CFI, because none of it adds code or edges.
Case study: one bug, deterministic SYSTEM, zero hijacked branches
If you study one vulnerability this year, make it CVE-2026–40369. Ori Nimron built it for Pwn2Own Berlin 2026, got turned away on venue capacity, and publicly dropped the whole chain (NVD). Microsoft patched it in the May 2026 update. It's the perfect teaching bug: pure data corruption, and fully deterministic.
The bug lives in ExpGetProcessInformation in ntoskrnl.exe, reached via NtQuerySystemInformation with info class 253 (SystemProcessInformationExtension). Two failures compound:
- Call with
Length = 0and theProbeForWriteguard becomes a no-op its whole body is wrapped inif (Length). Any pointer you pass, including a raw kernel address, sails through validation. - Class 253 dispatches through a
gotothat skips the pointer sanitization the neighboring class 5 and 252 paths perform correctly.
The result: an arbitrary increment primitive at an attacker-chosen kernel address. For every running process, the kernel bumps three DWORDs process count at +0, thread count at +4, handle count at +8.
Watch an increment-only bug become a SYSTEM shell:
- Leak
ntoskrnlbase via a user-mode prefetch side channel no kernel interaction. (Why a side channel? Because 24H2 tightened the oldNtQuerySystemInformationaddress leaks behindSeDebugPrivilege. One mitigation closed the easy leak, so the technique moved to microarchitecture. Watch for that pattern it repeats endlessly.) - Use the increment to corrupt
CmpLayerVersionCount, expanding the valid index range of theCmpLayerVersionsarray, then grow a null entry into a controlled allocation to bootstrap an arbitrary read. - Walk the
EPROCESSlist with that read to find the current process token. - Use the increment again on
token+0x42to set bit 20SeDebugPrivilege. - With debug rights, open
winlogon.exe,CreateRemoteThreada 272-byte payload, and you're SYSTEM.
No ROP. No shellcode on a corrupted stack. No indirect call. Control flow was never hijacked — only data was. That pattern captures where modern Windows exploitation is increasingly headed.
One more detail from Nimron's writeup every Windows dev should internalize: Windows does not currently enable SMAP as a general kernel protection. Linux and macOS fault the instant kernel code touches user memory without explicitly disabling the protection. Windows leans on __try/__except software guards instead, so corrupted kernel pointers happily dereference attacker-controlled user pages without crashing. If Windows adopts broader SMAP enforcement in the future, a whole category of user-pointer techniques would change overnight. It's a development worth watching.
BYOVD is still the cheat code
Before you spend three weeks on a novel data-only chain, remember that a signed vulnerable driver often just hands you kernel read/write. Recent Milan0day 2026 research is a clean example: abusing ThrottleStop.sys (CVE-2025-7771), which exposes arbitrary physical memory R/W through MmMapIoSpace, then patching kernel structures to blind and kill EDR. Bring-your-own-vulnerable-driver is still the highest-ROI kernel access path on real engagements, and the driver blocklist is perpetually behind the actual set of abusable signed drivers. If your goal is "kernel on a real box this quarter," On many real-world engagements, BYOVD offers a higher ROI than burning a kernel 0-day.
The tooling caught up: WinDbg + TTD is your research instrument now
Here's the half the mitigation posts skip. Data-only exploitation makes debugging harder the bug is "I incremented a DWORD here and something breaks four thousand instructions later." Live debugging that is misery. This is exactly where Time Travel Debugging stops being a party trick and becomes the center of the workflow.
If you take one habit from this post: record a trace, then interrogate it like a database. Stop single-stepping. Start querying.
Recent WinDbg releases made TTD materially better for this (release notes):
- A new Overwrite data-access type, and
dx @$cursession.TTD.Memory()now shows the old value of every write. For corruption bugs this is enormous: you instantly answer "what was here before it got clobbered, and which instruction clobbered it." - Position bookmarks, so you stop pasting trace positions into Notepad.
- A 64-bit indexing engine, which kills most of the out-of-memory pain on large traces.
The queries that earn their keep all analytical, reconstructing what happened:
* Explore what TTD exposes for this trace
dx -r1 @$cursession.TTD
* Every write to an 8-byte range, with old + new values and timestamps
dx @$cursession.TTD.Memory(0xADDR, 0xADDR+8, "w")
* Who wrote here? Break on write, then run the trace BACKWARDS
ba w8 0xADDR
g-
* Enumerate every call to a function across the entire recorded run
dx @$cursession.TTD.Calls("nt!ExpGetProcessInformation")
* Filter those calls with the data model (LINQ over process state)
dx @$cursession.TTD.Calls("nt!NtQuerySystemInformation").Where(c => c.ReturnValue != 0)* Explore what TTD exposes for this trace
dx -r1 @$cursession.TTD
* Every write to an 8-byte range, with old + new values and timestamps
dx @$cursession.TTD.Memory(0xADDR, 0xADDR+8, "w")
* Who wrote here? Break on write, then run the trace BACKWARDS
ba w8 0xADDR
g-
* Enumerate every call to a function across the entire recorded run
dx @$cursession.TTD.Calls("nt!ExpGetProcessInformation")
* Filter those calls with the data model (LINQ over process state)
dx @$cursession.TTD.Calls("nt!NtQuerySystemInformation").Where(c => c.ReturnValue != 0)The debugger data model (dx plus LINQ-style queries) is the other skill to grind. dx with no arguments now drops you at the root namespace so you can browse what's queryable, and same-named variables get disambiguated with @n (foo@0, foo@1). If you never went past k, dd, and bp, you're leaving most of the debugger's power on the table. The deepest treatment of the model is still this debugger data model writeup old, but the model has only grown.
Why TTD specifically beats live debugging in 2026: the bug classes that survive modern mitigations races, use-after-free, "corrupt now, crash later" data-only bugs are precisely the ones where a recorded, reversible trace can turn a multi-day guessing game into a twenty-minute backward walk. The first time it saves you a week, you stop thinking of it as optional.
Read the scoreboard: Pwn2Own tells you where the surface is
Want to know where the exploitable surface actually is? Watch Pwn2Own, not vendor marketing. Berlin 2026 closed with 47 unique zero-days and $1,298,250 paid over three days (official results, event recap); Berlin 2025 had 28.
The Windows LPE bugs keep coming from the same neighborhoods, which tells you where to hunt:
- Win32k / DirectComposition e.g. CVE-2025–50168 in
Win32kbase, a 2025 Pwn2Own LPE (PoC). Win32k lockdown killed a lot of this from sandboxed contexts, but it's far from dead. ntoskrnlinfo-class handlers CVE-2026-40369 is the poster child.- Races and out-of-bounds writes both showed up as Windows 11 SYSTEM wins.
- Hypervisor / VM escapes — this VMware Workstation escape writeup is worth reading for how the guest-to-host surface is evolving.
Don't forget the layer above the kernel
A reality check so you don't over-index on the kernel: in early-2026 engagements, the combination of AMSI patching and ETW tampering techniques continue to appear in public offensive tooling and remain relevant against some environments, although their effectiveness increasingly depends on Defender configuration (cloud protection on), EDR products, and detection maturity (overview). Telemetry keeps improving, but in-process user-mode instrumentation is still soft. Sometimes the easier win is blinding the sensor, not out-engineering the kernel.
Objections I already hear
Because I know the comments:
- "Just disable the mitigations." Sure in your lab. Your targets increasingly can't or won't, and 24H2 enables these protections by default on supported hardware and configurations. Develop where your targets live, not where they're comfortable.
- "Most orgs still run 22H2 with mitigations off." True today, shrinking fast. Skate to where the puck is going. An exploit designed for modern mitigations is generally more likely to remain useful on older configurations than the reverse.
- "Data-only attacks aren't new." Correct the concept is old. What's new is that it's no longer optional. Once CET and XFG are enabled, data-oriented techniques become one of the most practical remaining approaches.
- "BYOVD makes all of this moot." Often, yes. But blocklists tighten and mature targets watch driver loads. Run both tracks; don't bet the engagement on one.
What to actually do with this
If you write exploits for a living and want to stay dangerous:
- Rebuild your lab on Windows 11 24H2/25H2 with VBS/HVCI and kernel CET ON. If you're developing against 22H2 with mitigations off, you're training for a war that already ended.
- Learn TTD properly. Highest-ROI skill jump available right now, and the new old-value/overwrite features make it genuinely better than a year ago. Record by default; query the trace.
- Rewire from control-flow hijack to primitive-building. Your questions become: what read primitive, what write primitive, and what trusted structure do I corrupt with them? Study CVE-2026–40369 until that pattern feels automatic.
- Keep a BYOVD track. Unglamorous, but it's what works on real engagements.
- Watch the SMAP question. When Windows enforces it, a large class of user-pointer tricks changes, and whoever saw it coming adapts first.
The fundamentals never moved: find a bug, build a primitive, reach your goal. What changed is the middle the part where you used to reach for a stack pivot and a ROP chain. Start questioning that reflex. The people who stay effective in 2026 aren't the ones who memorized the most techniques. They're the ones who think in primitives instead of payloads, and treat the debugger as a research instrument instead of a crash viewer.
Sources & further reading
- Microsoft — Kernel-mode Hardware-enforced Stack Protection
- Investigating Kernel Mode Shadow Stacks on Windows (Connor McGarr)
- SSTIC 2025 — Analyzing the Windows kernel shadow stack mitigation
- Windows exploit mitigations reference (community-maintained)
- The State of Exploit Development, Part 2
- CVE-2026–40369 — technical breakdown · NVD · researcher blog
- Subverting the Windows Kernel with VBS/HVCI (Juan Sacco, EuskalHack 2026)
- Milan0day 2026 — ThrottleStop.sys / CVE-2025–7771 kernel patching
- WinDbg — Release notes (TTD & data-model updates) · TTD object model
- Debugger data model, JavaScript & x64 exception handling
- Pwn2Own Berlin 2026 — official results · CVE-2025–50168 PoC · VMware Workstation escape writeup
A note for readers: everything here references publicly disclosed, patched vulnerabilities and published research. Test only on systems you own or are authorized to assess.