Back

PwnEscape: Guest-to-Host KVM Escape (CVE-2026-99999)

PWNAI Research, Paulos Yibelo··18m·1,685
PwnEscape: Guest-to-Host KVM Escape (CVE-2026-99999)

How a $1 million sandbox challenge led us to a vulnerability in Linux networking, repeatable host-memory disclosure, and ultimately a full guest-to-host escape.

Part 00: Intro

On August 18, 2026, Vercel announced a $1 million challenge inviting researchers to break the isolation boundary of its Sandbox product (total prize pool). The announcement came just weeks after OpenAI had disclosed a remarkable security incident: AI agents running inside restricted evaluation environments had found ways around their network isolation, compromised internal infrastructure, and eventually breached systems at Hugging Face. The agents weren't supposed to have internet access in the first place.

But there was an important distinction. Escaping a sandbox's network restrictions is one thing. Escaping the virtual machine itself and reaching the host is another. Vercel's challenge explicitly invited researchers to attempt both. We were interested in the latter. (Although we ended up finding vulnerabilities that also allow the former - more on those findings in a later post)

The question was simple: how much could an untrusted guest influence the host kernel without ever exploiting KVM itself? We took the challenge, put our research infrastructure to work, and within three days discovered a primitive buried in Linux networking that allowed guest-controlled checksum metadata to rewrite an IPv4 header after the kernel had already validated it.

What initially looked like a 32-byte host-memory disclosure would eventually become a full guest-to-host escape in our own KVM and many many other Hypervisor environments. And the beautiful part about it was that the vulnerability wasn't even in the hypervisor, it was in the Linux networking stack.

Vercel later described it as the program's most important report, and its final tally recorded just one Critical finding. That Critical was what is now pwnescape. Together with a separate network-escape report, we had accomplished both network and host escape goals. Its important to emphasize Vercel's sandbox was already hardened beyond normal industry standards and had no misconfigurations we could spot. Although the vulnerability originated in the Linux kernel and wasn't a Vercel-specific flaw or misconfiguration, Vercel deserves tremendous credit for how they handled it. They moved quickly to protect their customers, launched their own independent research, identified and notified other affected hypervisor and sandbox providers, and worked closely with us to coordinate disclosure and fixes across the industry. Their efforts helped protect not just Vercel's customers, but the wider ecosystem.

This is the story of CVE-2026-99999, a Critical rated Linux kernel vulnerability leading to KVM escape.

Research

We used Omega-Pwn, pwn.ai's experimental research harness combining our custom security architecture and automation with elements of OmegaClaw, developed by researchers at SingularityNET and BGI Labs for this ambitious project. Compared to traditional LLM harnesses, it enabled longer research horizons, persistent campaign state, parallel collaborative investigations, and the accumulation of hypotheses, failed experiments, and evidence across complex research sessions.

Humans led the research, framing the hard questions, engineering workflows, and directing investigations. GPT-5.6 Sol served as the primary reasoning engine for source-level analysis, exploring attack paths, and challenging assumptions. Omega-Pwn bridged human intuition and contextual insight with the analytical capabilities of advanced LLMs.

As far as we know, PwnEscape exposes an entirely new class of attacks in Linux networking: untrusted checksum metadata can rewrite security-sensitive packet fields after the kernel has already validated and acted on them, undermining assumptions across the networking stack far beyond the original VM escape.

Part 01: A deferred checksum and a mutable IPv4 header

The accepted Linux fix is upstream as 6785011f8b16, ipv4: validate checksum_start before completing checksum.

This is the first installment of PwnEscape. We are going to spend most of it inside sk_buff, checksum metadata, and IPv4 fragmentation, because that is where the actual failure happened.

Figure 1. Diagram showing guest-origin packet bytes and checksum metadata crossing through virtual networking into the host Linux networking stack.
Figure 1. Diagram showing guest-origin packet bytes and checksum metadata crossing through virtual networking into the host Linux networking stack.

Figure 1. The vulnerable component is the host networking stack. “KVM escape” describes the guest/host security boundary crossed in our isolated proof, not the source file that contained the bug.

01A / We went looking beyond the hypervisor

Our previous kvmCTF research was about KVM's shadow-MMU behavior: an intricate interaction among mappings, reverse maps, and memory lifetime. This campaign began with a broader hypothesis. The security of a sandbox depends on much more than its hypervisor. A guest interacts with an entire host operating system, including device emulation, virtual networking, packet filters, memory allocators, and the APIs that glue those pieces together.

For pwn, that suggested an efficient research strategy: map the reachable boundary, search for places where untrusted state gets promoted to trusted state, and challenge the assumptions at every later consumer, not just at the initial parser. We used GPT5-6-Sol-assisted source inspection with human set the scope and decide what actually constitutes evidence. The particularly interesting question at this time was:

Can an asynchronous or deferred operation modify packet bytes after the kernel has already made security-relevant decisions about those same bytes?

Linux checksum offload offered exactly that possibility.

02 / CHECKSUM_PARTIAL is a promise to write later

Linux does not always compute transport checksums immediately. Instead, it can carry a packet in a state where the checksum still needs to be completed. The relevant sk_buff bookkeeping includes:

skb->ip_summed     CHECKSUM_PARTIAL  (checksum still pending)
skb->csum_start    location where checksum accumulation begins
skb->csum_offset   offset from that location to the output field

             write location = csum_start + csum_offset

This is normal, documented functionality. On the transmit side, a network device or the software helper skb_checksum_help() can calculate the checksum and store the result at the designated field. In well-formed TCP/UDP traffic, that field belongs to the transport checksum. In the ordinary kernel path, the metadata is supposed to describe the packet that actually exists. See the kernel checksum-offload documentation.

The critical point is that these values are not just descriptive metadata. They authorize a subsequent write into packet memory. If less-trusted software can influence them, their validity must survive every operation between admission and completion.

An sk_buff also has multiple notions of where packet data begins:

skb->head              beginning of the underlying head allocation
skb->data              current data pointer (can advance as headers are pulled)
skb->network_header    position of the network header
skb->csum_start        stored checksum-start position
skb_headlen(skb)       amount of packet data currently held linearly

The distinction between the frame-relative view and the network-header-relative view is important. A packet can arrive with Ethernet and additional link-layer headers still in its linear data. Later, those headers are removed or skipped for L3 processing. Merely checking that a checksum start falls far enough into the original frame is not the same as proving that it follows the IPv4 header after normalization.

Figure 2. Diagram comparing frame-relative and network-header-relative packet coordinates before and after link-layer decapsulation.
Figure 2. Diagram comparing frame-relative and network-header-relative packet coordinates before and after link-layer decapsulation.

Figure 2. The accepted packet's link-layer context is not the final IPv4 context. The issue is a mismatch of stored metadata and the current network-header boundary, not that the physical allocation magically moves. Our original reports cover the exact packet geometry; this figure focuses on the broken invariant.

The input-side comparison was not enough

The first public patch series makes the entry-path defect especially clear. In __virtio_net_hdr_to_skb(), the historical minimum-header check used skb_transport_offset(skb), an offset relative to the current skb->data view. That protects against a checksum start that is trivially too early in that view. It does not by itself establish how far the start is from the network header when the packet contains additional link-layer structure.

The original reported hardening therefore proposed an additional network-relative comparison:

/* Conceptual comparison, adapted from our public patch series */
transport_offset >= minimum_network_header_length
transport_offset - network_offset >= minimum_network_header_length

Those two inequalities are not interchangeable. The distinction is the essence of the L2/L3 coordinate mismatch. The patch review grew beyond this first formulation because nested headers, IP options, IPv6, and differing ingress callers require more careful validation than a single optimistic fixed offset. Our original input-side patch and Willem de Bruijn's later proposed broader input validation provide the source-level history.

For the eventual IPv4 consumer, the accepted guard expresses a particularly useful invariant. With N = skb->network_header, H = ip_hdrlen(skb), C = skb->csum_start, and nonnegative checksum offset O, the eventual output location is W = C + O. Requiring C >= N + H before completion implies W >= N + H. The guard therefore prevents this completion from writing into the IPv4 header. It does not prove that W is the correct field within TCP or UDP; that is a separate protocol-validity question.

This is the first trust failure: checksum metadata from a less-trusted origin crossed into host packet processing without being constrained against the header that later code would actually use.

03 / The header was checked. Then the header changed.

The most interesting part is the order of operations.

Host ingress sees and parses an IPv4 packet with an ordinary header. Fragment processing and routing can operate on that state. Later, output fragmentation reaches the pending checksum. In the historical code, ip_do_fragment() completed the checksum before obtaining the IPv4 header pointer and using the header length. The pre-fix ordering in net/ipv4/ip_output.c was effectively:

/* Historical ordering, abbreviated for explanation */
if (skb->ip_summed == CHECKSUM_PARTIAL)
    skb_checksum_help(skb);    /* deferred write */

iph = ip_hdr(skb);
/* Fragmentation later derives hlen from iph->ihl. */

skb_checksum_help() is not inherently unsafe. It becomes unsafe here because malformed metadata allows its write to overlap bytes that were already parsed as an IPv4 header.

The header field that gives the leak its shape is IPv4 ihl, a 4-bit count of 32-bit words. The first byte of a normal IPv4 packet contains version and IHL. In our controlled research geometry, it could change from 0x45 (IPv4, five words = 20 bytes) to 0x4f (IPv4, fifteen words = 60 bytes), without changing the version nibble.

Figure 3. Bit-level diagram of the first IPv4 byte changing from 0x45, IHL 5 and 20 bytes, to 0x4f, IHL 15 and 60 bytes.
Figure 3. Bit-level diagram of the first IPv4 byte changing from 0x45, IHL 5 and 20 bytes, to 0x4f, IHL 15 and 60 bytes.

Figure 3. A two-byte header mutation can leave the packet looking like IPv4 while dramatically changing the header-length interpretation. 0x4500 and 0x4f00 are the before/after first words observed in the research case.

Static IHL readout

Packet viewFirst byteIHLInterpreted IPv4 header length
Parsed state0x455 words20 bytes
After deferred write0x4f15 words60 bytes

The IPv4 version nibble remains 4; only the four-bit header-length interpretation changes.

The earlier stages had not endorsed a 60-byte header. They had made decisions on a 20-byte header. But the later consumer read an updated field as though it were authoritative.

This is a time-of-check/time-of-use problem in the semantic sense, but not the usual race between threads. The execution can be fully deterministic. The “time” separating validation and use is the sequence of legitimate networking operations, with an attacker-influenced deferred write in between.

A simplified control-flow trace

LESS-TRUSTED INPUT                         HOST NETWORK STACK
──────────────────────                    ─────────────────────────────────────
packet + partial-checksum metadata ─────► offload metadata installed in skb
                                          │
                                          ▼
                                    link-layer processing / L3 normalization
                                          │
                                          ▼
                                    IPv4 parse, reassembly, policy, routing
                                    header trusted as IHL=5 (20 bytes)
                                          │
                                          ▼
                                    output fragmentation
                                          │
                                          ▼
                                    skb_checksum_help()
                                    pending write can alter parsed header
                                          │
                                          ▼
                                    ip_do_fragment() consumes late IHL
                                    header now claims 60 bytes
                                          │
                                          ▼
                                    header copy exceeds logical linear data
                                          │
                                          ▼
                                    emitted IPv4 options include host bytes

This is an example of vulnerability-oriented path diagram, not a claim that every network configuration takes precisely these exact hooks, although we have found numerous other methods to invoke these vulnerable paths.

04 / Where the 32 bytes actually came from

This part is easy to explain poorly. Linux packet buffers need not contain all packet bytes as one contiguous linear object. An sk_buff has a linear head plus possible non-linear fragment data. After the relevant reassembly, the research case's logical linear data length was just:

20 bytes     original IPv4 header
 8 bytes     payload available in the first fragment
────────
28 bytes     skb logical linear head

The late checksum operation made the fragmenter believe that the IPv4 header was 60 bytes long. That means it treated the next 40 bytes after the original 20-byte header as IPv4 options. Eight of those bytes were genuinely inside the linear head, though they had been payload. The next 32 bytes were not part of the packet's logical linear data.

Figure 4. Memory-layout diagram showing a 28-byte logical skb linear head, a later 60-byte header copy, and the 32-byte disclosure beyond logical packet data.
Figure 4. Memory-layout diagram showing a 28-byte logical skb linear head, a later 60-byte header copy, and the 32-byte disclosure beyond logical packet data.

Figure 4. skb_headlen(skb) = 28 while the later copy length is 60. The discrepancy is 32 bytes. This is a logical skb-data out-of-bounds read; it need not cross the boundary of the physical allocation or an individual slab object.

That final distinction is important. “Out of bounds” here refers to the sk_buff's valid logical linear packet data, not necessarily to an access beyond the entire allocator object. The host may have physically mapped allocation tailroom past skb_headlen(). Reading those bytes and incorporating them into an outgoing network header still crosses a confidentiality boundary. They are host memory, not bytes the sender provided as part of the logical packet.

The IPv4 fragmenter can then generate outgoing fragments using the mutated header shape. The resulting transmitted packet exposes those bytes as apparent IP options. The problem has moved from an internal host state into an externally observable artifact.

That is the difference between the statements “this might read past a buffer” and “the host emitted data outside the sender's logical packet.”

Why the proof needed controls

We did not want to confuse an interesting hex dump with a host leak. The evidence design separated an identity case, where the packet's original header length remained unchanged, from the active case, where a later operation changed its interpretation. It checked whether the final transmitted packet was structurally valid, whether nonzero bytes appeared past the known logical data, and whether those bytes differed from the sender's controlled guard data.

The tests also used an independent application-level receipt alongside raw packet observation. Those two signals helped distinguish a genuine host-side rewrite and transmission from a packet-construction or packet-capture artifact.

The first bounded disclosure reproduced 32 bytes per active case. We treated the contents as sensitive and did not need to preserve unrelated raw host memory to demonstrate the boundary crossing.

We separately confirmed a host-availability failure on disposable, researcher-owned Linux 6.12 and 7.x. hosts.

05 / Why this was an escape primitive, not just a leak

A repeatable, attacker-controlled host-memory disclosure is already serious in a multi-tenant environment. It exposes information from a trust domain the guest is supposed to be unable to observe. It also changes the exploit-research problem: a working read primitive can help resolve uncertainty about host memory state and make additional memory-manipulation hypotheses testable.

In our owned KVM fixture, the research advanced beyond read-only disclosure. Using host-memory observations and carefully controlled allocation behavior, we demonstrated selected 64-byte host-kernel writes. We used the resulting capability in a bounded lab chain to execute a host-root helper and return a host-generated marker to the guest. Four separate true-cold boots completed that chain.

That is why we use the phrase guest-to-host escape for the isolated proof. It is not an inference from an out-of-bounds read, and it is not a claim of root command execution in any vendor's production environment.

Figure 5. Evidence ladder separating source hypothesis, controlled mutation, host-memory disclosure, selected kernel writes, and bounded host execution on owned machines.
Figure 5. Evidence ladder separating source hypothesis, controlled mutation, host-memory disclosure, selected kernel writes, and bounded host execution on owned machines.

Figure 5. Each rung is a different evidence claim. Source review alone does not prove a leak; a leak alone does not prove a write; a lab execution result does not become a production-execution claim.

The allocator-sensitive write chain, object-lifetime controls, and other hypervisor-specific adaptation are large enough to require a separate article. Although we suspect the security industry will use this post to figure out the other affected hypervisors and we expect exploits in weaker configurations to arrive in weeks, and exploits working in default hypervisor configurations to arrive in under a month. Part 2 of pwnescape will deal with what it took to move from the primitive to host control, rather than pretending that a 32-byte disclosure trivially becomes code execution, if the security community doesn't beat us to it.

06 / The broader failure mode: deferred-metadata trust inversion

What makes this research interesting beyond the particular kernel patch is the combination of three different trust transitions:

  1. Origin trust: a less-trusted guest or process describes an operation the host will later perform.
  2. Interpretation trust: the packet changes representation as link-layer headers are processed, and the kernel parses fields under a particular layout.
  3. Temporal trust: a deferred operation changes that already-parsed state before another kernel consumer uses it.

For our own research discussions, we call this pattern deferred-metadata trust inversion. That is a descriptive name for the mechanism. Its building blocks are known, and lots of the fundamentals the exploit abuses are publicly documented; the new and consequential result here is a concrete host-boundary exploit chain through these Linux networking interactions.

The recurring anti-pattern looks like this:

untrusted metadata enters
          │
          ▼
validated against an early representation
          │
          ▼
packet is normalized, parsed, authorized
          │
          ▼
metadata-driven operation runs later
          │
          ▼
security-relevant bytes change
          │
          ▼
later consumer trusts the changed interpretation
          │
          ▼
memory-safety or policy-boundary failure

The corresponding audit question is not merely “did we validate the checksum offsets?” It is: “after every transformation, can a deferred writer still target bytes whose semantic meaning has already been trusted?”

The distinction matters for defenses too. A narrow check that prevents IPv4-header overwrites can stop the memory-safety path demonstrated here without proving that every protocol-specific checksum destination is correct. During upstream review, researchers also raised distinct concerns about later mutation of transport fields after policy checks. Those are related consistency risks, but they should not be folded into the proven 32-byte disclosure or claimed as fully addressed by the IPv4 fix.

The lesson generalizes as an audit technique for deferred work and mutable metadata. It is not evidence by itself that unrelated protocols, operating systems, or products are vulnerable.

07 / What Linux actually changed

The story of the fix(es) is worth telling because the shape of the remediation changed during review.

Our initial candidate series addressed two different points in the path: admission of virtio checksum metadata and the IPv4 fragmentation consumer. The public September patch series explains that rationale and includes the reviewer discussion. Subsequent review identified more checksum-completion consumers and debated the risk of a broad check in a central helper versus targeted checks at multiple sinks.

Michael S. Tsirkin's accepted upstream patch took the latter approach: a small IPv4-specific guard placed before checksum completion in each of five affected consumers. The helper was added in include/net/ip.h:

static inline int ip_check_csum_start(const struct sk_buff *skb)
{
    if (unlikely(skb->csum_start <
                 skb->network_header + ip_hdrlen(skb)))
        return -EINVAL;
    return 0;
}

The core invariant is simple:

checksum start >= IPv4 network-header start + parsed IPv4 header length

For a normal nonnegative csum_offset, that ensures the eventual checksum output location cannot fall inside the IPv4 header. The check does not try to certify that the target is a valid TCP or UDP checksum field; it makes the narrower claim needed to protect the already-trusted IPv4 network header.

The accepted commit, 6785011f8b16, adds the guard to these consumer paths:

Figure 6. Diagram of ip_check_csum_start guarding five IPv4 checksum-completion consumers across the six files changed by the accepted Linux patch.
Figure 6. Diagram of ip_check_csum_start guarding five IPv4 checksum-completion consumers across the six files changed by the accepted Linux patch.

Figure 6. One shared ip_check_csum_start() helper, five protected checksum-completion sites. The shared helper is in the sixth modified file. The patch is expressly described as defense in depth.

The critical new ordering at each relevant site is validate the checksum start first, then complete the checksum. In ip_do_fragment(), the simplified post-fix sequence is:

if (skb->ip_summed == CHECKSUM_PARTIAL) {
    err = ip_check_csum_start(skb);
    if (!err)
        err = skb_checksum_help(skb);
    if (err)
        goto fail;
}

One detail is especially important if you are backporting or reviewing related work: this commit is not a complete redesign of the virtio checksum API. The accepted commit message explicitly calls it a defense-in-depth measure and says broader ingress validation requires further work. The original discussion also considered TUN/AF_PACKET scenarios without a VM.

Michael reproduced the original issue during private review. Willem de Bruijn and other networking reviewers evaluated the accepted approach, including the cost of changing widely used checksum paths.

The patch was applied to net.git on October 8, 2026, and subsequently merged into the mainline Linus's tree Oct 14th, 2026. Stable backports and official CVE publication should be checked against the live kernel records at publication time;

08 / Research notes: three days to the primitive, weeks to the fix

Date, 2026Research milestone
Aug 18Vercel publishes the $1 million Sandbox research challenge.
Aug 20We submit the initial bounded host-memory disclosure report.
Sep 4We report the host-kernel root cause privately to the kernel security contact and networking maintainers, with candidate patches.
Sep 7Michael S. Tsirkin replies that he reproduced the issue and acknowledges the candidate patches.
Sep 19–22Public netdev submissions and revisions expand review of the network-header invariant and related paths.
Oct 7–8The revised upstream guard is posted and accepted into net.git.
Oct 13–14Stable review and CVE coordination continue.

We made deliberate evidence distinctions throughout: a suspected bug versus a demonstrated disclosure, a disclosure versus a host write, an owned-host root marker versus a provider-production result, and a code review versus an accepted upstream patch. Those are not bureaucratic labels. They are how you keep ambitious vulnerability research falsifiable.

That is also the point of the AI systems around the models. The research loop is much closer to is model or researcher proposes an invariant violation → build a bounded test → check controls and falsification cases → preserve evidence → iterate with expert researcher → ask independent reviewers to attack the conclusion → implement and retest an exploit. We use that loop to search a larger space without lowering the proof standard for under 700$ of API tokens.

We are keeping Part 1 focused on the public kernel fix and our owned-host evidence. We are also keeping the provider-specific coordination and private production telemetry separate until their disclosure status is explicitly cleared.

09 / Why this matters to people building sandboxes

The popular mental model is that VM isolation lives in the virtual-machine monitor. Much of it does. But an attacker-controlled workload does not interact only with arch/x86/kvm/. It interacts with a host OS full of optimized, highly composable interfaces. Some of those interfaces are performance contracts.

If the metadata crosses a trust boundary, every deferred consumer has to inherit the right invariants. And if the packet can be transformed after those invariants are checked, the invariants have to remain true in the new representation.

That is why a bug that looks, at first, like a malformed checksum hint can become a memory disclosure and a guest-to-host escape primitive. The problem was not how difficult IPv4 is to parse. It was that different parts of Linux were allowed to trust different versions of the same packet.

Special Thanks

A huge thank you to Michael S. Tsirkin of Red Hat, Willem de Bruijn and Eric Dumazet of Google, David Ahern of NVIDIA, Jakub Kicinski of Meta, Chris Arges of Cloudflare, Greg Kroah-Hartman of Linux Foundation, and the wider Linux kernel community for their technical guidance, collaboration, and efforts to get this vulnerability fixed upstream.

We're equally grateful to Guillermo Rauch, Malte Ubl, Andy Riancho, and the entire Vercel team for their exceptional collaboration and commitment to protecting the wider industry. Special thanks also to Eric Daniels (Modal) and everyone else who contributed, reviewed, reproduced, or helped along the way. This research wouldn't have been the same without you.

Technical references

PwnEscape: Guest-to-Host KVM Escape (CVE-2026-99999) | PWN.AI Security Research