SECURITYKVM

Stopping A Two-Faced Greedy Guest: How Stealthium Protects Against Januscape.

JUL 2026By Branislav Brzak, Chris Hosking. Stealthium Core Team.
Stopping A Two-Faced Greedy Guest: How Stealthium Protects Against Januscape.

This article covers Januscape (CVE-2026-53359): a shadow-MMU bug in open source Linux virtualization technology Kernel-based Virtual Machine (KVM), that enables a malicious guest to achieve full escape to the host, and how Stealthium stops its exploitation before impact.


Januscape, named after Janus, the Roman god of entries, gates and transitions with two faces, is a nightmare for multi-tenanted accelerator environments leveraging KVM.

For customers of rent-a-GPU providers or neo-clouds that offer access to GPU and AI chips via nested virtualisation, the Januscape vulnerability means an attacker can spin up a VM in their own tenant on the shared host, break its isolation and seize control of the machine underneath it, and now have access to your tenant. Training runs, inference workloads, sensitive data and proprietary models within reach of an uninvited greedy guest.

Introduction to Januscape

Januscape is a demonstrated guest-to-host escape in the Linux KVM hypervisor, tracked as CVE-2026-53359 and closed upstream by commit 81ccda30b4e8. It was discovered by security researcher Hyunwoo Kim (known as v4bel) and disclosed on the 6th of July after a successful bounty submission to Google's kvmCTF programme. Fascinatingly, as the vulnerability relates to shadow MMU, the vulnerability affects Intel (VMX/EPT) and AMD (SVM/NPT) alike, (hence the two-faced Janus!) making it the first publicly documented KVM guest-to-host escape known to work on both architectures from the same trigger.

The danger of Januscape for rent-a-GPU or neo-clouds offering access virtualized accelerators

Its applicability across the two chip makers is unique, but it's not why the vulnerability is as dangerous as it is. Because Januscape executes inside the cloud provider's host kernel's KVM code, customer environments are blind to the attack. Januscape sits entirely beyond the reach of traditional endpoint security.

It doesn't matter how well an EDR scored in the last Mitre ATT&CK or how popular a CNAPP might be. In this kind of multi-tenanted environment, your in-guest security solution is sitting on the wrong side of the hypervisor boundary, watching syscalls and processes, not the shadow-MMU internal where Januscape lives. No signal, no log entry, nothing in your guest to correlate. On an unpatched kernel the only native trace is a single kernel WARN - fired after the damage, doing precious little (nothing) to stop the exploit.

While the hypervisor is the trust boundary in the multi-tenant compute for some rent-a-GPU providers and certain neo-clouds, it's often uninstrumented. Security tooling watches the guests and it watches host userspace. The KVM code paths that actually enforce guest/host isolation are a blind spot.

Stopping Januscape is the responsibility of the cloud / GPU virtualisation providers. However, that in itself is challenging.

What an attacker does to trigger Januscape: load a kernel module, manipulate page table mappings, race a timing window, is indistinguishable from completely legitimate GPU-tenant behaviour. Rent-a-GPU customers routinely load custom kernel modules for drivers and CUDA components, tune memory mappings for performance, and run timing-sensitive training and inference workloads as a matter of course. Flagging "suspicious guest activity" from inside the tenant runs headfirst into a signal-to-noise problem baked into the product itself: the same freedom that makes rent-a-GPU valuable is exactly what makes an attacker's setup look identical to a real customer's.

Stealthium exists to protect the AI Factory and your access to accelerated compute, and is able to detect and stop the Januscape attack in real-time.

To skip ahead to how Stealthium prevents Januscape, click here!

If you're keen to understand how the attack unfolds, read on.

The Attack in Three Phases

Januscape unfolds in three phases. Each turns a legitimate KVM mechanism against itself, and each leaves a distinct trace at the shadow-MMU level - if you are watching the right place.

Foundational to this attack is an understanding of how KVM's shadow MMU translates guest memory, and how it tracks those translations in reverse maps. Click each drop down for more information.

The KVM Shadow MMU

KVM gives every guest the illusion of its own physical memory. To do that it maintains shadow page tables: host-side page tables that translate a guest physical frame number (a gfn) to a real host page.

Each shadow page carries a role - a small descriptor recording what the page is: its level in the paging hierarchy, and crucially whether it is direct. A direct page maps a contiguous, linear slice of guest memory, so KVM can compute the gfn for any entry in it arithmetically, as sp->gfn + (index << ((level - 1) * 9)). A non-direct page shadows the guest's own page tables, so KVM stores the real translation for each entry separately instead of computing it.

Reverse Maps (rmaps)

For every guest frame, KVM keeps a reverse map - the rmap - listing every shadow page-table entry that currently points at that frame. When a page is swapped, migrated, or freed, KVM walks the rmap to find and zap every entry that references it. This is how KVM guarantees no stale translation is ever left pointing at memory that has moved or been released.

The catch: an entry for a leaf translation is filed under the gfn that translation is for. File it under the wrong gfn and the guarantee breaks - when the real frame is freed, the mis-filed entry is never found and never zapped. It survives as a live pointer into freed memory.

Phase 1: Priming the Shadow Page Cache

The attacker first gets KVM to build the shadow page they intend to abuse. From inside the guest, they drive a memory-access pattern that makes KVM create a direct shadow page - a large, linear 2MB mapping (direct=1) - over a region of guest physical memory they control.

This is to be expected. Every running VM builds direct shadow pages constantly; it is the fast path for mapping guest RAM. The attacker is simply seeding KVM's shadow-page cache with a page whose role they will exploit next. Nothing here is anomalous in isolation.

Phase 2: Forcing the Role-Confused Reuse

Now the attacker changes how they touch that same guest memory, forcing a fault that needs a finer, 4KB mapping - a walk whose role requires direct=0.

When KVM services that fault, kvm_mmu_get_child_sp() reaches for a shadow page and reuses the cached direct page from Phase 1, even though its role does not match the walk. KVM then records the leaf translation through kvm_mmu_page_set_translation(). Because the page it reused is direct, KVM computes the gfn arithmetically as sp->gfn + index and files the rmap entry under that value - the gfn of the original direct parent - instead of the real translation the walk was actually for.

The rmap entry is now filed under the wrong guest frame. On a patched kernel, kvm_mmu_page_set_translation() catches this exact mismatch and refuses. On an unpatched kernel it emits a WARN and carries on. The corruption is in place.

Phase 3: Weaponizing the Stale rmap

Everything so far has produced one mis-filed rmap entry. The attacker now turns it into a use-after-free.

Because the entry is filed under a gfn it does not belong to, KVM will not find it when the real frame is freed. The attacker frees that frame and lets KVM hand the underlying host page back to the kernel - while the dangling shadow entry still points straight at it. They then steer the host into reclaiming that page for a structure of their choosing and drive writes through the still-live entry.

That is an arbitrary write into freed, reallocated host kernel memory - a use-after-free with the guest holding the pen. From there it is a well-worn path to controlling a host kernel structure, executing code in the host kernel, and stepping out onto the machine every other tenant is sharing. The VM boundary is gone.

The entire sequence runs on the host's vCPU thread emulating the guest, in the time it takes to service a handful of page faults.

Where Stealthium Breaks the Chain

Every phase of Januscape converges on a single instant: when KVM files a leaf translation under a gfn that does not match the shadow page's role. That instant already has a name in the kernel - it is the condition KVM's own WARN checks for inside kvm_mmu_page_set_translation(). On an unpatched host that check fires and is ignored. Stealthium treats it as a kill signal.

Stealthium instruments the host kernel directly. The Stealthium agent attaches to kvm_mmu_page_set_translation() at fentry and replicates KVM's own invariant: for any direct or passthrough shadow page, the gfn being recorded must equal sp->gfn + (index << ((level - 1) * 9)). The instant a translation violates it - the precise Januscape signature - Stealthium sends SIGKILL to the offending vCPU thread, before the mis-filed rmap entry can be freed and reclaimed. The corruption never becomes a use-after-free. It is, in effect, a proactive KVM_BUG_ON for hosts that do not yet carry the fix.

{
	"type": "AgentAutoAction",
	"ts": 1783428154242561500,
	"data": {
		"pid_tgid": 914261098577819,
		"nsproxy": 18446744072523153000,
		"cgroup": 42,
		"action": "Kill",
		"reason": "Januscape",
		"comm": "qemu-system-x86"
	}
}

An example of the auto-action event Stealthium emits the instant it blocks a Januscape attempt.

This is not a heuristic, nor a signature that needs tuning. It's Stealthium listening for the kernel's own correctness invariant and turning detection insight into response action. A legitimate guest never trips it; the check exists in KVM precisely because tripping it means memory corruption. This means the Stealthium detection is explicitly designed as false positive free. Just an attacker's VM that has just tried to escape, already stopped.

The Class Behind the Bug

Januscape is one bug for two chip makers. However, it demonstrates the danger in the hypervisor trust boundary. The instrumentation Stealthium uses to catch it - a live view into the kernel code paths that enforce guest/host isolation - is the same instrumentation that surfaces the rest of the class: other shadow-MMU and nested-virtualization escapes, malicious device emulation, cross-VM side channels, and the privileged host processes an escape reaches for next. These are not theoretical. They are the attacks that matter most for customer's accessing multi-tenant accelerated compute for their AI needs. Attacks that are invisible to every tool that watches only guests and host userspace.

The reason is architectural, as your existing stack has no instrumentation inside the hypervisor. From its perspective, a healthy VM and a VM corrupting host kernel memory look identical - until the host is already gone.

The window between a KVM CVE landing and a fleet-wide patch is measured in weeks. During that window, and at any point a zero-day is in play, providers of multi-tenanted accelerated compute have a responsibility to enforce security at runtime.

AI is only as secure and trustworthy as the layer it runs on, and for many that is currently invisible and indefensible. Stealthium exists to make AI Accelerated Compute observable, secure, and controlled.

If you run multi-tenant VMs - for customers, for internal isolation, for confidential workloads - you are trusting a boundary you cannot see. Januscape is the crossing we caught. The uncomfortable question is how many others never showed up at all.

See Stealthium in action. Book a demo.


Reference: CVE-2026-53359, a use-after-free in the KVM shadow MMU (kvm_mmu_get_child_sp() / kvm_mmu_page_set_translation()), closed upstream by commit 81ccda30b4e8. Stealthium enforces the kernel's invariant at runtime on hosts that have not yet been patched.