Mondoo

Dirty Frag: Universal Linux LPE via Page Cache Writes

Dirty Frag chains two Linux kernel page-cache write vulnerabilities in the xfrm ESP and RxRPC subsystems to achieve deterministic root privileges on every major Linux distribution. Like Copy Fail and Dirty Pipe before it, the exploit requires no race conditions and no timing windows. A patch has been merged upstream, but no distribution has shipped updated packages yet. Apply the kernel module mitigation immediately.

Christoph Hartmann
Christoph Hartmann
·8 min read·
Dirty Frag: Universal Linux LPE via Page Cache Writes

A new Linux kernel vulnerability class called Dirty Frag lets any unprivileged local user gain root on every major Linux distribution. Discovered and disclosed by security researcher Hyunwoo Kim (@v4bel), it chains two independent page-cache write primitives in the kernel's xfrm ESP and RxRPC subsystems into a single, deterministic exploit. No race conditions are involved, the kernel does not panic on failure, and the success rate is very high.

No CVE has been assigned. The disclosure embargo was broken by a third party before distributions could prepare patches or request CVE identifiers. The upstream kernel patch has been merged, but the vulnerability currently has no CVE ID, no official CVSS score, and no distribution security advisories. Based on the comparable Copy Fail (CVE-2026-31431) scoring, the expected CVSS v3.1 score is 7.8 (High) with vector AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H. A public exploit with a one-line build command is already available.

Dirty Frag is a direct descendant of Dirty Pipe and Copy Fail. All three share the same fundamental pattern: tricking the kernel into performing an in-place write operation on a page cache page that an unprivileged user should only be able to read.

How Dirty Frag works

Both vulnerabilities in the chain exploit the same class of bug. On a zero-copy send path, splice() plants a reference to a page cache page into the frag slot of a network socket buffer (sk_buff). The receiving kernel code then performs in-place cryptographic operations directly on that frag. Because the page cache page was never copied into a private buffer, the write modifies the cached version of the file in RAM, and every subsequent read sees the modified data.

Variant 1: xfrm ESP page-cache write

The first vulnerability lives in esp_input(), the function that handles incoming ESP (Encapsulating Security Payload) packets. Before performing in-place AEAD decryption, esp_input() is supposed to allocate a private buffer with skb_cow_data() when the socket buffer is non-linear. However, a flawed branch skips this copy when the socket buffer has frags but no frag list:

C
if (!skb_cloned(skb)) {
if (!skb_is_nonlinear(skb)) {
nfrags = 1;
goto skip_cow;
} else if (!skb_has_frag_list(skb)) {
nfrags = skb_shinfo(skb)->nr_frags;
nfrags++;
goto skip_cow; // frag preserved — page cache exposed
}
}

When the skip_cow path is taken, the attacker's spliced page cache page becomes both the source and destination of an AEAD decryption. During preprocessing, crypto_authenc_esn_decrypt() writes 4 bytes of the ESN (Extended Sequence Number) high bits to the end of the destination scatterlist. The attacker controls both the value (set freely via XFRMA_REPLAY_ESN_VAL at SA registration) and the position (tuned by payload length). The AEAD authentication check runs after the write, so even though it fails, the page cache modification persists.

The exploit uses this 4-byte write primitive 48 times to assemble a 192-byte minimal root-shell ELF directly in the page cache of /usr/bin/su. When the parent process executes /usr/bin/su, the setuid-root bit elevates to euid=0, and the shellcode at the entry point runs setgid(0); setuid(0); setgroups(0, NULL); execve("/bin/sh").

This variant requires CAP_NET_ADMIN to register XFRM SAs, which means it needs the ability to create a user namespace.

Variant 2: RxRPC page-cache write

The second vulnerability is in rxkad_verify_packet_1(), which verifies data packets at the RXRPC_SECURITY_AUTH level. It performs an in-place pcbc(fcrypt) single-block decryption on the first 8 bytes of the RxRPC payload. The same pattern applies: skb_to_sgvec() converts the frag directly into the scatterlist, and the in-place decryption writes 8 bytes onto the attacker's pinned page cache page.

Unlike the ESP variant, the write value is the output of fcrypt_decrypt(ciphertext, key) rather than a directly controlled value. The attacker controls the key via an RxRPC session key registered through add_key(), which requires no privileges. By brute-forcing the key in userspace (fcrypt has a 56-bit key space), the attacker can produce the desired 8-byte output.

This variant targets /etc/passwd, replacing the root entry's password field with an empty string so that pam_unix.so nullok grants root access without a password prompt.

Critically, this variant does not require user namespace creation, making it effective on Ubuntu systems that restrict unprivileged user namespaces through AppArmor.

Why chain two variants?

Each variant has a blind spot that the other covers:

  • xfrm ESP provides a powerful, directly controlled 4-byte write, but requires user namespace creation. Ubuntu can block this via AppArmor.
  • RxRPC works without namespaces, but the rxrpc.ko module is not included in most distributions. However, on Ubuntu, rxrpc.ko is included by default and is autoloaded when user code opens an AF_RXRPC socket.

By chaining them, the exploit achieves root on every major distribution regardless of namespace restrictions or module availability.

Relationship to Copy Fail

The ESP variant of Dirty Frag shares the same vulnerability sink as Copy Fail (CVE-2026-31431): both trigger the authencesn scratch write that stores 4 bytes onto a page cache page via scatterwalk_map_and_copy(). The difference is the entry point. Copy Fail reaches this sink through the algif_aead userspace crypto interface, while Dirty Frag reaches it through the ESP networking path. The RxRPC variant uses an entirely different sink (rxkad + pcbc(fcrypt) in-place decryption). Systems where the previously published Copy Fail mitigation (blacklisting algif_aead) has been applied remain vulnerable to both Dirty Frag variants.

Why this one matters

Dirty Frag is the third major page-cache write vulnerability in the Linux kernel in recent years. Each iteration raises the bar for defenders:

  • Deterministic, no race conditions. The exploit succeeds reliably on every attempt. The kernel does not panic on failure, so the attacker can retry silently.
  • Bypasses the Copy Fail mitigation. The previously recommended algif_aead blacklist does nothing against Dirty Frag, which triggers through entirely different kernel paths (ESP and RxRPC).
  • Universal coverage through chaining. Where prior exploits had distribution-specific gaps (module availability, namespace restrictions), the two-variant chain covers every major distribution.
  • No CVE, no advisories, no patches. The broken embargo means security teams cannot track this through normal vulnerability management workflows. Scanners that rely on CVE IDs will not flag it.
  • Page cache corruption leaves no trace on disk. The modification lives in the page cache (RAM), not on the underlying filesystem. The corrupted page is never marked dirty, so the kernel's writeback mechanism never flushes it to disk. A disk image, an offline forensic analysis, or a reboot produces clean data with no evidence of tampering. Runtime integrity tools like AIDE will detect the corruption if they run while the modified page is still cached, but after a reboot or drop_caches the evidence is gone.
  • Crosses container boundaries. Containers share the host kernel's page cache. An attacker inside a container can corrupt cached files used by the host or other containers, making this a threat to multi-tenant Kubernetes clusters and cloud platforms.
  • Public exploit available. A working proof-of-concept with a one-line build command was published the same day the embargo broke.

Affected systems

The xfrm ESP vulnerability has existed since commit cac2661c53f3 (January 2017), and the RxRPC vulnerability since commit 2dc334f1a63a (June 2023). The effective lifetime of these vulnerabilities is about 9 years for ESP and 3 years for RxRPC.

Dirty Frag has been confirmed on the following distributions. Because no CVE has been assigned, most distribution security trackers do not yet have entries for this vulnerability.

DistributionTested kernelPatch statusNotes
Ubuntu 24.04.46.17.0-23-genericNot yet patchedBoth variants work: ESP via user namespaces, RxRPC via default rxrpc.ko. Monitor Ubuntu Security
RHEL 10.16.12.0-124.49.1.el10_1.x86_64Not yet patchedESP variant confirmed. Monitor Red Hat CVE database
openSUSE Tumbleweed7.0.2-1-defaultNot yet patchedMonitor SUSE security
CentOS Stream 106.12.0-224.el10.x86_64Not yet patchedFollows RHEL kernel updates
AlmaLinux 106.12.0-124.52.3.el10_1.x86_64Not yet patchedFollows RHEL kernel updates
Fedora 446.19.14-300.fc44.x86_64Not yet patchedMonitor Fedora Bodhi
DebianNot explicitly tested, but expected vulnerableNot yet patchedesp4/esp6 modules present in default config. Monitor Debian security tracker
Amazon LinuxNot explicitly tested, but expected vulnerableNot yet patchedESP modules present. Monitor Amazon ALAS

Patch status

The upstream patch was merged into the netdev tree on May 7, 2026. It takes the approach of setting the SKBFL_SHARED_FRAG flag on page frags that arrive via splice() in the IPv4/IPv6 datagram append paths. The ESP input fast path then checks this flag, routing socket buffers with externally pinned pages through the skb_cow_data() copy path instead of the vulnerable skip_cow shortcut.

No distribution has shipped updated kernel packages yet. The disclosure timeline was complicated by the embargo being broken by a third party, forcing early publication of the full exploit before distributions could prepare patches.

What does "the embargo was broken" mean? When a security researcher finds a serious vulnerability, the standard practice is coordinated disclosure: the researcher privately reports the bug to the affected vendor (in this case, the Linux kernel security team and the linux-distros mailing list), and both sides agree on an embargo period, typically 7 to 14 days, during which the vulnerability is kept confidential. This window gives distribution maintainers time to develop, test, and ship kernel patches to users before attackers learn the details. When a third party independently discovers and publishes the same vulnerability during that window, the embargo is "broken": the details are now public, but the patches are not ready. That is what happened here. The researcher had agreed to a 5-day embargo with linux-distros, but an unrelated party published exploit details on May 7, leaving distributions without time to prepare updates. The researcher then published the full Dirty Frag writeup the same day, in coordination with the linux-distros maintainers, because withholding it no longer provided any defensive benefit.

What to do now

1. Apply the module mitigation immediately

Since no kernel patches are available from distributions, block the vulnerable modules:

Bash
printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n' \
> /etc/modprobe.d/dirtyfrag.conf
rmmod esp4 esp6 rxrpc 2>/dev/null || true

This prevents the vulnerable code paths from being loaded. Systems that actively use IPsec ESP tunnels will lose that functionality. The rxrpc module is used by kafs, the in-kernel Andrew File System (AFS) client. For most environments, disabling these modules has no impact.

2. Patch the kernel as soon as updates are available

Monitor your distribution's security advisories. When kernel packages containing the upstream fix are released, update immediately. The patch is small and targeted (adds a flag check in the ESP input path and marks splice-originated frags), so the risk of regression is low.

3. Restrict user namespaces where possible

The ESP variant requires user namespace creation. On systems where unprivileged user namespaces are not needed, restricting them blocks the more powerful of the two variants:

Bash
# Debian/Ubuntu: restrict unprivileged user namespaces
sysctl -w kernel.unprivileged_userns_clone=0
# RHEL/Fedora/CentOS: restrict via max_user_namespaces
echo 0 > /proc/sys/user/max_user_namespaces

Ubuntu also restricts unprivileged user namespaces through AppArmor profiles, which is why the RxRPC variant exists as a fallback. Note that restricting namespaces does not mitigate the RxRPC variant, which works without namespaces on systems where rxrpc.ko is available (notably Ubuntu).

4. Block exploitation in container workloads

Dirty Frag crosses container boundaries because containers share the host kernel's page cache. An attacker inside a container can corrupt cached files used by the host or other containers. Apply defenses at both the host and pod level.

On every node, apply the module mitigation from step 1. This is the most effective control because it prevents the vulnerable code paths from being loaded kernel-wide, regardless of what containers do.

In Kubernetes pods, a seccomp profile can block the RxRPC variant by denying AF_RXRPC (family 33) and AF_ALG (family 38) sockets. The RxRPC exploit needs both: AF_RXRPC for the trigger and AF_ALG for cksum computation. Create a file on each node at /var/lib/kubelet/seccomp/block-dirtyfrag.json:

JSON
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"names": ["socket"],
"action": "SCMP_ACT_ERRNO",
"args": [
{
"index": 0,
"value": 33,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": ["socket"],
"action": "SCMP_ACT_ERRNO",
"args": [
{
"index": 0,
"value": 38,
"op": "SCMP_CMP_EQ"
}
]
}
]
}

Reference it in the pod spec:

YAML
securityContext:
seccompProfile:
type: Localhost
localhostProfile: block-dirtyfrag.json

For Docker, pass the profile directly: docker run --security-opt seccomp=block-dirtyfrag.json.

Important limitation: Seccomp is less effective against the ESP variant. The ESP exploit creates its own user and network namespace via unshare(CLONE_NEWUSER | CLONE_NEWNET) and opens sockets inside that new namespace. Blocking unshare via seccomp would prevent this, but that also breaks many legitimate container runtimes. The module blacklist on the host (step 1) is the most reliable defense for container environments because it prevents the vulnerable kernel code from loading regardless of what syscalls containers make.

Prioritize by environment

  • Critical (mitigate immediately): Multi-tenant hosts, shared development servers, Kubernetes clusters, CI/CD runners executing untrusted code. The deterministic nature and high success rate make this trivially exploitable.
  • High: Single-tenant production servers. Any local user, including compromised application accounts, can escalate to root.
  • Moderate: Single-user workstations where the threat model already assumes the user has code execution.

Detect with Mondoo

Because no CVE has been assigned, traditional vulnerability scanners that rely on CVE IDs will not flag Dirty Frag. Mondoo customers can take two approaches:

  1. Kernel version detection. Mondoo continuously scans your infrastructure and identifies kernel versions across cloud instances, containers, and on-premises servers. Any Linux system running a kernel older than the upstream fix (commit f4c50a4034e6, merged May 7, 2026) is vulnerable. Mondoo has published its own advisory, MONDOO-ADV-2026-up91c, so affected systems are already flagged even without a CVE assignment.

  2. Mitigation verification. Use Mondoo policies to verify that the module blacklist is in place across your fleet. Check that /etc/modprobe.d/dirtyfrag.conf exists and contains the install esp4 /bin/false, install esp6 /bin/false, and install rxrpc /bin/false directives on every host, including Kubernetes nodes.

Disclosure timeline

DateEvent
Apr 29, 2026RxRPC vulnerability and patch submitted
Apr 30, 2026ESP vulnerability and exploit submitted to security@kernel.org
Apr 30, 2026ESP patch submitted to netdev mailing list
Apr 30, 2026Independent ESP report submitted to security@kernel.org by Kuan-Ting Chen
May 4, 2026Shared-frag patch submitted
May 7, 2026Patch merged into netdev tree
May 7, 2026Embargo broken by third-party publication
May 7, 2026Full Dirty Frag disclosure published after agreement with distribution maintainers

References

About the Author

Christoph Hartmann

Christoph Hartmann

Co-Founder & CTO

Christoph Hartmann, co-founder and CTO at Mondoo, wants to make the world more secure. He's long been a leader in security engineering and DevOps, creating widely adopted solutions like Dev-Sec.io and Chef InSpec. For fun, he builds everything from custom operating systems to autonomous robots.

Ready to Get Started?

See how Mondoo can help secure your infrastructure.