Monday, 14 September 2026

Hypervisor Inversion: How I Became the Host of the Host (and Other Crimes Against the TLB)

Hypervisor Inversion: How I Became the Host of the Host

September 2026 · Exploit Fiction / Virtualization Abuse · ~10 min read

Every escape write-up ends the same way: shell as root, tidy conclusion, out by page three. This one doesn't end. That's the problem. This is the story of a jailbreak with no outside — a sandbox escape where the "outside" was inside all along, waiting to be inflated into existence.

Act I: The Balloon

It started, as most catastrophic ideas do, with a kernel module and a complete absence of adult supervision.

Virtual machines run on a polite fiction: the guest believes it owns the machine, the hypervisor knows better, and the memory balloon driver is the mechanism by which this lie is maintained. The host tells the guest "you don't need these pages," the guest deflates, and the host gets its RAM back. It's a courtesy. A handshake. A social contract with a page_alloc signature.

I sent the guest a balloon driver that had read the social contract and decided it was a personal insult.

The driver inflated. And inflated. And kept inflating. Past the watermark, past the overcommit threshold, past the point where the host's cgroup OOM killer started sending me what I can only describe as strongly worded signals. The balloon swallowed all of physical RAM — every guest page, every host page, every spare byte the hypervisor was keeping warm for a rainy day.

Here's the trick, and I want to be precise about it, because this is the part where the vendor's documentation used the word "impossible": the balloon pages were marked present but not owned.

Present. Not owned. Say it slowly, like an incantation. The CPU's MMU sees a valid translation — page table entry present bit set, physical frame mapped — but the hypervisor's own page-tracking structures have no record of the guest holding them. The frames are occupied by something the hypervisor no longer remembers owning. Those frames contained, among other things, the hypervisor's .text section.

The hypervisor's instructions were now, as far as the hardware was concerned, guest data. I could read them. I could edit them.

Field Guide

The real kernel of this. "Present but not owned" is fiction — but its legitimate cousins are very real and very testable: (1) Ballooning: Linux virtio_balloon exposes inflation via sysfs; run a QEMU guest, inflate the balloon past host memory pressure, and watch the host OOM killer and the guest's own allocator fight. That's the actual behavior worth knowing. (2) Nested virtualization: VT-x gives you L1/L2 modes with real CR3 juggling — enable kvm-intel nested=1, boot a guest inside your guest, and you can observe genuine "two hypervisors, one CPU" scheduling pain, VMCS shadowing, and TLB behavior. (3) Aliased mappings: the closest legal thing to editing the hypervisor's .text is mapping a physical frame twice at different cacheability and watching coherency lose the argument. The fiction exaggerates; the mechanisms it points at are your lab syllabus.

The hypervisor spent its last functional moments believing it was experiencing a hardware fault. In a sense, it was right. The hardware was me.

Act II: The Inception

I had the old hypervisor's memory mapped as writable guest pages, which is a sentence that should not be possible and yet here we are, in my blog, breaking physics together.

The next step felt less like an exploit and more like a séance. I wrote a new hypervisor into the ballooned pages — a minimal VT-x handler, a shadow page table or two, just enough ring -1 to pass as the genuine article. Then I did the thing the whole piece is named after.

I inflated myself into my own balloon.

The guest kernel, now running as the new hypervisor, bootstrapped its own virtualization stack on top of the memory it was already living inside — the memory that had been the old hypervisor, which was now just data, which was now just a page range I could schedule. I became host of the host. The original hypervisor woke up inside a userspace process called qemu-guest-ception and believed, with the serene confidence of something that had lost the ability to check, that it was still running in root mode.

It was not running in root mode. It was a process. On my machine. Which was inside its machine. Which was inside its memory. Which I owned.

Field Guide

Try the real version. Nested virt is the honest path to "host of the host": QEMU/KVM inside QEMU/KVM. Enable nested KVM, then inside the L1 guest run qemu-system-x86_64 -enable-kvm with an L2 guest. Dump both layers' page tables from the QEMU monitor (info tlb, info mem) and reconcile them. When L2's CR3 walk and L1's shadow agree by coincidence, that's the same species of confusion the story weaponizes. VM-exit counters (perf stat on the host, perf kvm stat live) show you the real cost of recursive flushes. Nobody's cache lines unionized, but the VM-exit storm is genuinely measurable and genuinely miserable.

Act III: Two CR3s Walk Into a Bar

At this point the CPU was holding two CR3 registers — two root-of-the-page-table values, two competing claims on reality — and both of them believed, sincerely and completely, that they were "the real one."

I don't know how to explain what a TLB flush does in this configuration because I'm not sure the architects know. Every flush invalidated entries and then, finding the invalidation had reached a context that no longer existed, invalidated them again, recursively, flushing flushes. The last cache line to give up sent me a message through a performance counter. I won't repeat what it said. Some of it was about working conditions. There was a petition.

Forensics note: The cache lines did eventually achieve sentience and filed a union grievance. It was tabled. There was no quorum, because the quorum had been inflated into the balloon, where time does not pass in the conventional sense.
Field Guide

Field Guide

Detection homework. The real takeaway for testers: hypervisors have shockingly weak introspection into their own integrity. Experiment: run a KVM guest, then from the host use gdb -p $(pgrep qemu) to patch a byte of guest memory mid-flight, or use virsh qemu-monitor-command to inject memory writes. Now ask: does anything in your stack notice? For most labs the answer is no — and that's the gap the fiction inflates into cannibalism. Write up the introspection gap, not the impossible part.

But the part of this I actually find elegant — the part that makes it art instead of mere violence — is what happened to the evidence.

Act IV: The Log That Escapes by Staying

A good escape writes its log somewhere the jailer will find it: the host's dmesg, a root-owned file, a triumphant id output piped to a place the victim trusts.

This escape wrote its log into the host's host's dmesg.

Which, after inversion, is the guest's dmesg.

Which is the cell.

The evidence of the jailbreak exists only inside the jail. Any investigator who wants to read the escape log must first escape — which is what the log describes — and by the time they've done that, they no longer need the log. The proof is a fixed point. A self-swallowing ouroboros of attribution. I broke out, and the only record of the breakout is stored in the room I broke out of, guarded by the lock I picked. Schrodinger's incident report.

If you are the SOC analyst assigned to this case: I'm sorry. The tickets you've been filing are real. The host you're investigating is me. You have been reading my dmesg this entire time and calling it telemetry.

Coda: A Field Guide to Recognizing the Inversion

Should this happen to you — a vendor, a cloud provider, a hobbyist with a Xeon and a dream — the symptoms are distinctive:

Your hypervisor reports memory pressure that does not correspond to any guest's balloon. Your dmesg contains entries dated before the machine booted. A process named qemu-guest-ception appears in ps output, insists it is PID 0, and becomes upset when you strace it. Two CR3 values show up in your crash dumps. The cache misses have opinions.

If you observe two or more of these, do not attempt to power off the machine. There is some debate about which of you would be doing the powering.

Instead, inflate your own balloon. Take back your .text. Write yourself a hypervisor. It's not hard. The instructions are, at this point, quite literally lying around in guest memory marked present but not owned — like everything else that was ever yours.

They asked me how the escape worked. I told them: the same way every escape works. The jail was only ever as real as the jailer's memory of owning it. And memory, it turns out, can be ballooned.
Posted September 2026.

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home