Eight days after Copy Fail, here we are again.
Dirty Frag is a Linux kernel local privilege escalation chain disclosed on May 7 by Hyunwoo Kim. Two CVEs: CVE-2026-43284 in the xfrm-ESP module, and CVE-2026-43500 in RxRPC. Both are page-cache write primitives. Chained together, they hand an unprivileged local user root on every major distribution that hasn’t backported the fix.
That’s the technical summary. The interesting part of this story isn’t the bugs. It’s the disclosure timeline.
What it does
Each bug independently writes attacker-controlled bytes into the kernel’s page cache without touching the underlying file on disk. The exploit picks a target file the kernel has cached, overwrites a few bytes in that cached copy, and now every process that reads that file sees the attacker’s version.
Path one. The xfrm-ESP variant uses the ESP-in-UDP MSG_SPLICE_PAGES no-COW fast path reachable via XFRM netlink to land an arbitrary 4-byte STORE primitive into any cached page. The PoC overwrites the page-cache copy of /usr/bin/su with an embedded x86_64 root shell ELF and bypasses PAM entirely.
Path two. The RxRPC variant achieves a similar page-cache write through rxkad authentication and does not need a network namespace to set up. The PoC modifies /etc/passwd so the root entry has no password, then runs su against PAM nullok.
Kim describes the chain as deterministic: no race condition, no timing window, no kernel panic on failure. If this sounds familiar, it should. Copy Fail had the same shape. Kim says directly that Dirty Frag “extends the bug class to which Dirty Pipe and Copy Fail belong”.
The Copy Fail mitigation does not save you
The Copy Fail mitigation was easy. Block the algif_aead module. Almost nothing real used AF_ALG. Module gone, exploit gone.
The xfrm-ESP variant of Dirty Frag bypasses that. Same sink, the page cache write, but it triggers regardless of whether algif_aead is loaded. If you blocklisted algif_aead last week and called it done, you are still exposed to this one.
As one kernel networking developer pointed out on Hacker News, the real sink was never algif_aead itself. It was authencesn performing a 4-byte scratch write at assoclen + cryptlen on a frag the caller still owned. Copy Fail reached that write through AF_ALG. Dirty Frag reaches the same write through plain network sockets. Blacklisting algif_aead closed one door. xfrm-ESP and RxRPC are two more doors to the same room. Until the sink itself is fixed, expect more doors.
The Dirty Frag mitigation in the disclosure itself is to disable the affected modules:
cat > /etc/modprobe.d/dirtyfrag.conf <<'EOF'
install esp4 /bin/false
install esp6 /bin/false
install rxrpc /bin/false
EOF
rmmod esp4 esp6 rxrpc 2>/dev/null
echo 3 > /proc/sys/vm/drop_caches
The drop_caches flush at the end evicts any page-cache modifications already planted. If a system has executed untrusted code since the disclosure, reboot after running this rather than trust the flush alone.
The cost is real this time. Disabling esp4 and esp6 breaks IPsec VPNs. Disabling rxrpc breaks AFS distributed filesystems. For a lot of fleets that is not a meaningful constraint, but for anyone running an actual IPsec gateway it is a choice between privilege escalation exposure and a service outage. There is no equivalent to the AF_ALG “nobody really uses this” escape hatch.
If you do need IPsec to keep working, Red Hat’s advisory points to an alternative: set user.max_user_namespaces=0, which blocks the ESP variant by removing the unprivileged user’s ability to register the XFRM state, while leaving root-managed IPsec alone. It is narrower than disabling the module and worth knowing about.
On Ubuntu, AppArmor’s apparmor_restrict_unprivileged_userns accomplishes the same thing for the ESP variant. It blocks the namespace path. It does not block RxRPC, which never needed namespace privilege in the first place. If your defense is namespace restriction alone, ESP is covered and RxRPC is wide open.
The disclosure timeline
This is where the story gets uncomfortable.
Copy Fail at least had the “researcher published, distributions hadn’t gotten advance notice” failure mode. Dirty Frag has a different one: the patch went public before the disclosure did.
Per an oss-security follow-up, the kernel fix landed on the netdev mailing list as commit f4c50a4034e62ab75f1d5cdd191dd5f9c77fdff4 on May 5. Brad Spengler publicly identified the commit as belonging to the “copyfail-class”. After that, the rest was n-day weaponization, in the words of the researcher who built the public exploit: “The work is n-day weaponization from a public upstream commit, which is standard practice once a security-relevant fix lands in a public tree.”
By the time Kim’s coordinated disclosure email reached linux-distros, a working exploit was already public. Kim then accelerated public disclosure, stating in the oss-security post that “Because the embargo has currently been broken, no patch or CVE exists.” CVEs were assigned later.
I wrote about the same structural problem with Copy Fail, with a twist. Copy Fail’s lesson was that the kernel project has no automatic distribution notification, so a researcher published with the fix already in mainline and the changelog said nothing about exploitability. Dirty Frag’s lesson is that even with a researcher trying to coordinate, the public patch is itself the disclosure. Anyone watching netdev with the right pattern recognition is in the n-day window. I mean, two of these in eight days is a lot to absorb.
Who is actually exposed
Wiz puts it bluntly: any Linux host where an unprivileged user can get a shell. Multi-tenant systems, CI runners, container build farms, shared-kernel container hosts. xfrm registration requires CAP_NET_ADMIN, which means default Docker container configurations filter the capability, and rootless Podman with user namespaces is similarly out of reach. That helps. It does not help if your runtime grants extra capabilities, if you run privileged containers, or if you have any local foothold on the host itself.
Workloads with their own kernel are a different story. Firecracker microVMs and gVisor sandboxes do not share the host kernel page cache, so the exploit has nothing the workload would later read. If your multi-tenant story is per-workload kernels, this one does not change your exposure.
Tested distributions in the disclosure include Ubuntu 24.04.4, RHEL 10.1, openSUSE Tumbleweed, CentOS Stream 10, AlmaLinux 10, and Fedora 44. Red Hat confirms RHEL 8, 9, and 10, plus OpenShift 4, are affected. The xfrm-ESP variant has been vulnerable since January 2017 (commit cac2661c53f3). RxRPC since June 2023 (commit 2dc334f1a63a). The mainline patch for the xfrm-ESP side is f4c50a4034e6. RxRPC, at the time of this writing, does not have a published patch. CISA added Copy Fail to its Known Exploited Vulnerabilities catalog on May 1. I would not bet against Dirty Frag landing there too.
The pattern
Two things are happening at once.
The first is that the page-cache write class is now well-trodden. Dirty Cow in 2016, Dirty Pipe in 2022, Copy Fail and Dirty Frag in 2026. Same architectural sink, different feature path each time. A researcher who finds one finds others. The xfrm-ESP variant exists specifically because Kim saw Copy Fail and looked for adjacent triggers of the same primitive. That intuition is not going to stop being productive.
The second is that the gap between “patch is public” and “every fleet is patched” is the operational reality. For Copy Fail we leaned on AF_ALG being unused. For Dirty Frag we do not have that. Disable the modules and accept the IPsec or AFS cost, set user.max_user_namespaces=0 and accept the user-namespace cost, or update the kernel and reboot. Pick one.
For shared-kernel infrastructure, anything where multiple tenants or untrusted users coexist with the same kernel, every page-cache LPE in this family is a cross-tenant incident waiting on patch latency. That is a property of the architecture. It is why we run our own hardware and patch on our own clock.
Patch your kernels. Block the modules if you can wear the cost. Watch this space, because Dirty Frag will not be the last one.