HVCI in 2026: How Far Down a SYSTEM Shell Actually Gets You

HVCI in 2026: How Far Down a SYSTEM Shell Actually Gets You

Original text: “HVCI, and how far down a SYSTEM shell actually gets you”SiCk, afflicted.sh (2026-06-14). The two ASCII diagrams and the comparison table below are reproduced verbatim with attribution captions.

Executive Summary

A SYSTEM shell on a Windows 11 box is, by historical instinct, the end of the engagement. You own the account that owns the machine, you go reach into kernel memory for a token, you walk away. SiCk’s writeup is a useful reminder that this instinct stopped being correct several Windows builds ago. Hypervisor-Protected Code Integrity (HVCI) splits the machine in two at the hardware level: the regular Windows kernel runs at VTL0, a Secure Kernel runs at VTL1, and the Extended Page Tables (EPT) sitting underneath belong only to the Secure Kernel. The result is that the page-permission word that actually decides what executes is no longer one your kernel-mode code can touch, no matter how thorough your read/write primitive is. The classic “drop shellcode, jump in” ending is gone.

What HVCI does not stop is what makes this writeup worth reading. Two well-documented routes still take a kernel R/W primitive all the way to a full compromise on plain-HVCI hardware: a signed-code ROP chain that reuses the kernel’s own gadgets without ever introducing unsigned bytes, and a Kernel Data Protection (KDP) remap that swaps the physical page behind a protected virtual address instead of writing to the protected page itself. Both are being closed — kernel CET kills the ROP route by faulting on mismatched return addresses, and Intel VT-rp’s HLAT removes the guest-controlled PTE from the translation walk entirely — but both fixes depend on the underlying silicon. The phrase “HVCI is enabled” describes two completely different attack surfaces depending on whether the box is sitting on Tiger Lake / 11th-gen-and-up with 24H2, or on something older. This piece reads as a 2026-correct map of which of those two boxes is in front of you, and what a kernel R/W primitive is actually worth on each.

For there is nothing covered, that shall not be revealed; neither hid, that shall not be known.

Luke 12:2 (KJV) — opening epigraph of the original article

The model

HVCI uses the hypervisor to carve the machine into two trust levels. The normal Windows kernel runs at Virtual Trust Level 0 (VTL0); a small, separate Secure Kernel runs at VTL1, and the Secure Kernel is the only thing allowed to write the EPT — the second set of page tables, sitting underneath the ones VTL0 manages, that the CPU consults to resolve a guest-physical address to a host-physical one. VTL0 can scribble all over its own PTEs and it changes nothing about the EPT entry behind any of them. So every page ends up carrying two independent permission words: the PTE permissions, which the normal kernel controls, and the EPT permissions, which the Secure Kernel owns. When the two disagree, the CPU obeys the EPT entry. Connor McGarr’s way of putting it is that physical memory trumps virtual memory: you can mark a page RWX in the PTE, and that changes nothing whatsoever, because the EPT entry behind that physical frame still says read-only, and that is the word the hardware enforces.

              virtual address
                    |
                    v
            [ PTE  ]  R W X      <- windows controls this, you can edit it
                    |
                    v
            guest-physical page
                    |
                    v
            [ EPTE ]  R - -      <- Secure Kernel controls this, you can't
                    |
                    v
            effective R - -      <- the two get AND'd. the EPTE wins.

Diagram reproduced verbatim from the original article. Source: SiCk, afflicted.sh.

This is the structural change that kills the classic kernel-exploit ending. The usual recipe was to engineer a page that was both writable and executable, drop shellcode into it, and pivot. You cannot, because nothing in the EPT is ever permitted to be writable and executable simultaneously. McGarr walked the demonstration end-to-end: he corrupted a PTE to mark a page executable, wrote a slide of NOPs into it, jumped, and the box bugchecked. Windows believed the page was executable because the PTE said so; the EPT entry behind it still had execute cleared, and there is nothing in VTL0 capable of clearing it. The cousin technique — flipping a page’s user/supervisor bit so that a writable user-mode RWX page can be executed as kernel code — is dead too, because Mode-Based Execute Control (MBEC) marks every user page non-executable whenever the processor is running in kernel mode.

What still gets through

HVCI stops you from running your own code in the kernel. That, as it turns out, is the entire scope of what it stops. On a box that has HVCI and nothing newer underneath it, two routes are still open, and both have been written up in detail.

Route one — reusing the kernel’s own code with ROP

The first is straightforward Return-Oriented Programming against the kernel image itself. Kernel CFG (Control Flow Guard) validates the target of every indirect call and indirect jump against a bitmap of legitimate destinations, but it does not look at return addresses. So instead of bringing code, you reuse the code that is already loaded. With a kernel read/write primitive in hand, you find a thread you control, overwrite a saved return address on its kernel stack, and when the function returns, control walks down a chain of gadgets stitched out of the signed kernel image that is sitting in memory anyway. No unsigned byte is ever introduced, so HVCI has no reason to fire. McGarr’s HVCI writeup builds the whole chain end-to-end, and his phrasing for it captures the technique cleanly: you cannot run your shellcode, so you call the same kernel functions your shellcode would have called.

Route two — data-only attacks on KDP via page-table remap

The second route does not involve executable code at all, and that is what makes it the more interesting half of this picture. HVCI protects code — code cannot be modified, and code cannot be forged. It has nothing to say about whether your data means what you think it means. Kernel Data Protection (KDP) is the feature meant to plug that gap: it marks chosen kernel data regions read-only in the EPT, so that even a kernel-mode write into those pages takes a fault. That part does work. Write the protected page directly and the EPT slaps you with a violation. The problem is what KDP actually protects: the contents of a physical page, not the path your virtual address takes to reach that page. Microsoft said this themselves when they shipped KDP in 2020 — the Secure Kernel only verifies on a periodic basis that a protected region still translates to the physical page it is supposed to.

So you do not touch the protected page at all. You copy its contents somewhere ordinary, edit the copy, and then rewrite the guest page tables so that the protected virtual address now resolves to your unprotected copy instead of the original physical frame. Satoshi Tanda wrote up the three steps in detail. Your copy lives in a perfectly normal writable page, so the EPT raises no objection, and from that point onwards everything that reads the protected virtual address reads your version of it. The hypervisor never witnesses the swap, because witnessing it would require trapping on every write to a guest page table, and a VM exit on every page-table write costs more than anyone is willing to pay.

    before                              after the PFN swap

    prot. VA --PTE--> GPA_a              prot. VA --PTE--> GPA_b
                       |                                    |
                  EPTE R--  locked              EPTE RW-  normal page
                       |                                    |
                  the real data                       your modified copy

    the EPTE on GPA_a never changed. you just stopped pointing at it.
    no write to protected memory, so no EPT violation ever fires.

Diagram reproduced verbatim from the original article. Source: SiCk, afflicted.sh.

The weak point is narrow but it is real. The EPT enforces the permissions of whatever physical page an address resolves to right now. It never verifies that the address still resolves to the physical page it was originally supposed to.

What is closing it

Both of those have answers and they are already arriving in shipping silicon and shipping Windows builds.

Kernel CET closes the ROP route

The ROP route is closed by kernel-mode CET (Control-flow Enforcement Technology). The processor keeps its own hardware-protected copy of every return address in a shadow stack, and on every return it compares the value popped off the regular stack against the value on the shadow stack — if they disagree, it faults. That single check removes the writable return-address slot that the entire technique stood on. Kernel CET has been in Windows since the 22H2 build, and it only runs with HVCI on, so the two travel together as a unit.

Intel VT-rp / HLAT closes the remap route

The remap route is closed in hardware, by Intel’s VT-rp, and specifically by its Hypervisor-managed Linear Address Translation (HLAT) piece. When HLAT is engaged for a range, the CPU stops consulting the guest page tables for translations in that range and instead walks a separate set of paging structures owned by the hypervisor, located through a field in the VMCS. Changing the PFN in the guest PTE has no effect, because the guest PTE is no longer part of the translation. Two companion pieces, paging-write and guest-paging verification, exist to prevent the obvious sidestep of trying to alias the hypervisor’s own page tables. Microsoft ships the whole thing under the name HVPT (Hypervisor-protected Page Tables), and they are explicit about its job: it protects the page-table structures that back KDP, shadow stacks, and CFG. HVPT is on by default in Windows 11 24H2 on Tiger Lake, Intel 11th-gen and newer.

So where does that leave it

Which of these you are actually up against comes down to the underlying hardware, and that is exactly the detail that gets dropped when someone reports “this box has HVCI.” On an older CPU — or on any machine where HVPT is for whatever reason not running — the remap attack works the way Tanda laid it out, KDP comes apart, and a kernel read/write primitive still walks you wherever you want to go. On a 24H2 machine with current silicon, both routes are sealed simultaneously: kernel CET on the ROP side, HLAT on the remap side. The same three words, “HVCI is enabled,” describe both of those boxes, and as targets they have almost nothing in common.

PathPlain HVCI+ kCET / HLAT
Unsigned code (RWX / KRWX)blockedblocked
User/supervisor flipblocked (MBEC)blocked
ROP through signed codeworksclosed by kCET
Data-only remap (KDP)worksclosed by HLAT/HVPT
Comparison matrix of attack paths against plain HVCI vs HVCI plus kCET and HLAT. Table reproduced verbatim from the original article. Source: SiCk, afflicted.sh.

The closing is also not finished. HLAT only locks the ranges the hypervisor has asked it to lock, not all of memory, and it has a documented fallback condition under which it drops back to ordinary paging. The structures HLAT does not currently cover, together with whatever can trigger that fallback, are where SiCk expects the next round of this conversation to come from. Andrea Allievi has been publishing on Secure Kernel internals, Satoshi Tanda documented VT-rp end-to-end across two posts, and McGarr took the control-flow half of this discussion to Black Hat USA 2025. The data-only route is not a theoretical concern either — Lazarus’ FudModule rootkit goes from admin to kernel and then does its entire job through data corruption, with not a single byte of unsigned code anywhere in the chain.

SiCk’s final word on it is honest: there is no clean public route past HLAT on current hardware, and he is not going to pretend otherwise. What he will say is that the cheap, universal version of kernel-read/write-to-SYSTEM is gone, the two routes that are still open are both being closed as the install base turns over, and whether either one still works on the box in front of you is a question about the exact CPU and the exact Windows build, not about whether the HVCI checkbox is ticked.

Key Takeaways

  • HVCI splits the machine across the hypervisor: VTL0 hosts the regular kernel, VTL1 hosts the Secure Kernel, and the EPT belongs only to VTL1. The EPT permission wins over the PTE permission whenever they disagree.
  • Classic shellcode-injection endings are gone. The EPT never permits a page to be writable and executable at the same time, and MBEC kills the user/supervisor flip by marking user pages non-executable when the CPU is in kernel mode.
  • What HVCI alone does not stop: a ROP chain composed entirely of gadgets inside the signed kernel image, because kernel CFG ignores return addresses; and a Kernel Data Protection bypass by remapping the protected virtual address to a copy you control, because KDP guards page contents and not address-to-page mappings.
  • Kernel CET closes the ROP route by enforcing a hardware shadow stack on every return. It shipped in 22H2 and is bound to HVCI.
  • Intel VT-rp’s HLAT, shipped by Microsoft as HVPT, closes the KDP remap route by walking hypervisor-owned page tables for protected ranges. HVPT is on by default in Windows 11 24H2 on Tiger Lake, 11th-gen-and-up Intel processors.
  • “HVCI is enabled” no longer maps to a single attack surface. On a pre-24H2 box without HVPT, a kernel R/W primitive is still close to game-over via ROP or KDP remap. On a 24H2 box on modern Intel silicon, both routes are shut.
  • HLAT is range-scoped and has a fallback condition. The next round of public bypasses is most likely to come from data structures that HLAT does not currently cover, or from whatever can trigger the fallback.

Defensive Recommendations

  • Treat “HVCI on” as a checkbox that needs a hardware audit attached. Inventory which fleet machines are on Intel 11th-gen-and-up (or AMD equivalents with the corresponding paging extensions) and on Windows 11 24H2 or later — that subset is the only one where kCET and HLAT/HVPT are actually carrying the weight you assume.
  • For older fleet hardware where HVPT cannot run, assume kernel R/W primitives still chain to full SYSTEM compromise through KDP remap or signed-kernel ROP. Prioritise driver-attack-surface reduction on those machines: tighten the Microsoft Vulnerable Driver Blocklist policy, audit third-party drivers with kernel NT read/write or arbitrary MSR access primitives, and prefer aggressive uninstallation of legacy or no-longer-shipping vendor drivers (printer, RGB, anti-cheat, “tuning utilities”).
  • Enable Smart App Control / WDAC code-integrity policies in audit-then-enforced mode to constrain which signed drivers can even load. Many published exploit chains start with loading a legitimately signed but exploitable driver to obtain the kernel R/W primitive in the first place — HVCI doesn’t help with that step.
  • Make sure Memory integrity, Kernel-mode Hardware-enforced Stack Protection, and Local Security Authority protection are all on in Windows Security → Device security → Core isolation. The CET-protected shadow stack only protects processes that opt in; ensure the kernel-mode setting is reported as on, not just user-mode.
  • Telemetry: if you have an EDR with kernel-mode visibility, hunt for behaviours typical of KDP remap setup — large kernel-page-table modifications from a single thread, repeated copy-of-protected-region followed by PTE writes, or unexpected calls into MmProtectMdlSystemAddress / MiProtectDriverSection patterns. The actual data corruption produces no signature, but the table manipulation can.
  • Apply the Microsoft Vulnerable Driver Blocklist refresh cadence on a documented schedule. The list lags behind public disclosure; build a process for adding new entries between official releases when an exploit chain becomes notable.
  • For sensitive systems, prefer Secured-core PC hardware as a procurement requirement — this enforces a baseline of TPM 2.0, Pluton, DMA protection, System Guard, and HVCI/HVPT together, rather than relying on individual flags being on or off.
  • Track public research on FudModule and similar data-only kernel rootkits as an indicator of what the post-HVCI offence looks like in the wild, and use it to prioritise hunts for kernel data tampering even on machines where you believe the hypervisor isolation is sound.

Conclusion

SiCk’s framing is the right one to take into a 2026 engagement: SYSTEM is no longer the natural ceiling, but the kernel above it is no longer the natural floor either. HVCI did its job by making “drop shellcode, jump in” structurally impossible, and the two follow-on techniques that survived that change — signed-code ROP and KDP page-table remap — are themselves being closed in silicon and in Windows itself. Whether either route still works against the host in front of you is now a question with a definite answer, but only if you stop asking whether HVCI is on and start asking which CPU it is sitting on and which Windows build is exposed to it. The same three words describe two very different targets.

Original text: “HVCI, and how far down a SYSTEM shell actually gets you” by SiCk at afflicted.sh.

Comments are closed.