Dirty Frag: A New Linux Kernel Zero-Copy Privilege Escalation Vulnerability
Dirty Frag: A vulnerability chain on the Linux kernel’s zero-copy path that poisons page cache writes, chaining together the xfrm-ESP and RxRPC vulnerabilities. It affects nearly all mainstream distributions since 2017 and allows passwordless privilege escalation to root.
Here We Go Again
To be honest, I never thought I’d be writing another kernel privilege escalation post so soon.
It’s only been eight days since the Copy Fail article. Eight days! The algif_aead blacklist from Copy Fail had barely been typed into the terminal, the patch wasn’t even warm, and then—Dirty Frag dropped.
What’s even more absurd is that Copy Fail’s mitigations are completely ineffective against Dirty Frag. This time, the attack targets entirely different kernel subsystems—the xfrm-ESP and RxRPC encryption paths—so whether you disable algif_aead or not makes absolutely no difference.
Dirty Pipe (2022), Copy Fail (2026.04), Dirty Frag (2026.05)… Every time it’s “affects nearly all distributions since 2017.” Do you know what that means? It means for nearly the past decade, anyone who logged into any unprivileged account on your system had a chance to quietly become root.
But thinking about it, is this entirely the kernel developers’ fault? Not necessarily.
The explosive growth of AI-assisted code auditing tools in recent years allows security researchers to scan out vulnerabilities lurking for nearly a decade within hours. Bugs that previously required manual line-by-line review are now swept out in batches by AI. Copy Fail was located by the AI tool Xint Code within one hour. Although Dirty Frag came from manual auditing by Korean researcher Hyunwoo Kim, the code paths it exploits belong to the same family as Copy Fail—page cache writes on the zero-copy path—indicating that this type of design flaw is far from an isolated case. Better tools, higher efficiency, and naturally more vulnerabilities are “discovered.”
What’s even more disturbing is the disclosure process this time. The researcher originally submitted vulnerability details to the kernel security team with an agreed 5-day embargo to give distributions time to patch. But on the same day, an unrelated third party directly published the complete details and exploit for the ESP(xfrm) vulnerability. No CVE, no patch, but the PoC was available to everyone. This isn’t responsible disclosure—it’s stabbing every Linux user in the back. The patching window was blown away, and everyone was forced to run naked.
Alright, rant over. Let’s take a serious look at what this vulnerability is all about.
Timeline
| Date | Event |
|---|---|
| 2017-01 | xfrm-ESP vulnerability introduced with commit cac2661c53f3 (lurked for 9 years) |
| 2023-06 | RxRPC vulnerability introduced with commit 2dc334f1a63a |
| 2026-04-29 | Korean researcher Hyunwoo Kim (@v4bel) reports RxRPC vulnerability and full exploit to security@kernel.org |
| 2026-05-07 | Vulnerability details submitted to linux-distros mailing list with agreed 5-day embargo |
| 2026-05-07 | Same day, third party publicly releases ESP(xfrm) vulnerability details and exploit, embargo broken immediately |
| 2026-05-07 | After consultation with distribution maintainers, full Dirty Frag documentation publicly released. At this point no CVE, no official patch |
| 2026-05-08 | At time of writing, major distributions are still waiting for upstream patch merge |
Impact Analysis
| Metric | Details |
|---|---|
| CVE | None yet (NVD had no time to assign before embargo broke) |
| Vulnerability Type | Deterministic logic flaw, not a race condition |
| Exploitation Success Rate | 100%, guaranteed success on first execution |
| Affected Range | Nearly all mainstream Linux distributions since 2017 (as of May 2026, latest kernel 7.0.3 also affected) |
| Exploitation Method | 192-byte payload, assembling a root-shell ELF through 48 4-byte writes |
| Disk Footprint | No persistent modification — only pollutes in-memory page cache, bypasses inotify; restored on reboot |
| Bypasses Copy Fail Mitigation | Copy Fail’s algif_aead disable is completely ineffective as it attacks different subsystems |
Comparison with historical kernel LPE vulnerabilities:
| Feature | Dirty Cow (2016) | Dirty Pipe (2022) | Copy Fail (2026) | Dirty Frag (2026) |
|---|---|---|---|---|
| Race condition | Required | Not required | Not required | Not required |
| Affected range | Specific versions | 5.8+ | All mainstream distros since 2017+ | All mainstream distros since 2017+ |
| Exploit code complexity | Complex | Complex | 10 lines Python | Single-file C program |
| Disk footprint | Yes | Yes | No | No |
| Patch status | Fixed | Fixed | Fixed | No official patch yet |
Confirmed affected distributions:
| Distribution | Tested Kernel Version |
|---|---|
| Ubuntu 24.04.4 | 6.17.0-23-generic |
| RHEL 10.1 | 6.12.0-124.49.1 |
| CentOS Stream 10 | 6.12.0-224 |
| AlmaLinux 10 | 6.12.0-124.52.3 |
| Fedora 44 | 6.19.14-300 |
| openSUSE Tumbleweed | 7.0.2-1 |
| Arch Linux | 7.0.3 |
From 6.12 to 7.0, from Ubuntu to Arch—full platform coverage. Yes, pretty much any mainstream distribution you have installed is likely affected.
Technical Principles: Why “Dirty Frag”?
The Core in One Sentence
splice() implants a read-only file’s page cache reference into the network send buffer’s (skb) frag slot → the receiving kernel performs in-place cryptographic operations on the frag (in-place crypto, src == dst pointing to the same memory) → what should have been a decryption operation on ciphertext becomes a direct STORE primitive writing to read-only page cache → replacing su’s machine code with a root-shell ELF.
“Dirty” refers to polluting the page cache, “Frag” refers to exploiting the skb (socket buffer) fragment mechanism. Together—Dirty Frag.
Zero-Copy Path skb Frag Pollution
This part is key to understanding the entire vulnerability, so let’s expand on it.
Normally, when you write data to a socket, the kernel copies the data from userspace into its skb. But splice() takes the zero-copy path—it directly stuffs the pointer to the file page cache page (page struct + offset) into the skb’s frag array without copying the data itself:
struct skb_shared_info {
struct sk_buff *frag_list; // frag linked list
skb_frag_t frags[MAX_SKB_FRAGS]; // frag array, each is {page, offset, size}
// ...
};
The key point here: the page passed to splice() is your file’s (e.g., /usr/bin/su) in-memory page cache page—you only have read permissions, but a reference to this page has already been stuffed into the network protocol stack’s skb.
Next, it depends on whether the receiving network protocol stack “gets itchy hands” and writes to this page.
Vulnerability 1: xfrm-ESP Page-Cache Write
The first vulnerability lies on the IPsec ESP (Encapsulating Security Payload) decryption path.
The esp_input() function is used to decrypt ciphertext data. Normally, if the skb’s data region would be shared with a frag, the kernel should first call skb_cow_data() (Copy-on-Write) to copy the shared page before operating on it. However, there exists a code path in esp_input() that bypasses COW:
static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
{
if (!skb_cloned(skb)) {
if (!skb_is_nonlinear(skb)) {
// [1] Linear skb: only head data, no frag, safe
nfrags = 1;
goto skip_cow;
} else if (!skb_has_frag_list(skb)) {
// [2] Has frag, but no frag_list → directly skips cow!
nfrags = skb_shinfo(skb)->nr_frags;
nfrags++;
goto skip_cow; // The bomb is here
}
}
// Normal path: copy data, safe
err = skb_cow_data(skb, 0, &trailer);
}
When the skb is nonlinear (has frags holding the page cache passed by splice) but frag_list is empty, the code directly jumps to skip_cow. Then, the subsequent crypto_authenc_esn_decrypt() performs in-place AEAD decryption—src and dst point to the same scatterlist, which is the page cache page implanted by the attacker.
During the decryption process, there’s one particularly critical line of code:
// Move the high 4 bytes of sequence number to the end of dst SGL
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1);
This line writes 4 bytes to dst (your su page cache page) at offset assoclen + cryptlen. The value of tmp + 1 comes from the high 32 bits of the ESP header’s sequence number—something the attacker fully controls through XFRMA_REPLAY_ESN_VAL.seq_hi when registering the XFRM SA (Security Association).
So the attacker simultaneously controls:
- Where to write (file offset, positioned by adjusting payload length)
- What value to write (4 bytes, specified via
seq_hi)
By registering 48 different XFRM SAs, each with its seq_hi storing one 4-byte fragment of the ELF, and looping 48 times, a complete root-shell ELF is assembled into su’s page cache.
However, this vulnerability has one limitation: registering XFRM SAs requires CAP_NET_ADMIN privileges. Attackers can obtain this by creating user namespaces (unshare(CLONE_NEWUSER | CLONE_NEWNET))—but Ubuntu’s AppArmor happens to prevent unprivileged users from creating network namespaces.
This is why a second vulnerability is needed.
Vulnerability 2: RxRPC Page-Cache Write
The second vulnerability lies on the RxRPC protocol’s Kerberos authentication decryption path.
The rxkad_verify_packet_1() function performs in-place pcbc(fcrypt) decryption on the first 8 bytes of received data packets:
skcipher_request_set_crypt(req, sg, sg, 8, iv.x);
// ^^ ^^
// src==dst → in-place operation!
ret = crypto_skcipher_decrypt(req); // 8-byte write happens here
skb_to_sgvec() directly converts the skb’s frag (which contains the page cache page spliced in by the attacker) into a scatterlist, with src and dst being the same sg. So the decryption operation directly writes the 8-byte “decryption result” back to that read-only page cache page.
Compared to xfrm-ESP:
| Feature | xfrm-ESP | RxRPC |
|---|---|---|
| Write size | 4 bytes | 8 bytes |
| Value control | Direct control (seq_hi) | Indirect (requires fcrypt key brute-force) |
| Privilege required | User namespace | No privilege required |
| Introduction time | 2017-01 | 2023-06 |
The values written via the RxRPC path cannot be directly controlled—they are the result of fcrypt_decrypt(C, K). The attacker needs to first register a key K via add_key("rxrpc", ...), then brute-force in userspace to find the K that produces the target 8-byte plaintext. Fortunately, fcrypt is a 56-bit key, 8-byte block cipher dedicated to the Andrew File System, so brute-forcing isn’t a big problem.
The most critical point: the RxRPC path requires absolutely no privileges. No need to create user namespaces, no need for network namespaces, no need for CAP_NET_ADMIN. And Ubuntu loads the rxrpc.ko module by default.
Chaining Logic
The two vulnerabilities complement each other, providing full coverage:
| Scenario | Vulnerability Used | Reason |
|---|---|---|
| Ubuntu (AppArmor blocks namespaces) | RxRPC | rxrpc.ko loaded by default, no privilege required |
| RHEL / Fedora / openSUSE | xfrm-ESP | Namespaces available, ESP writes precisely controllable |
| Other distributions | xfrm-ESP or RxRPC | Choose based on module loading, at least one path available |
This is what makes Dirty Frag terrifying—no matter how you configure it, there’s always a path to root.
PoC Analysis
The complete exploit is open-sourced at github.com/V4bel/dirtyfrag. Compiling and running requires just one line:
git clone https://github.com/V4bel/dirtyfrag.git
cd dirtyfrag && gcc -O0 -Wall -o exp exp.c -lutil && ./exp
The core idea isn’t complicated:
- Prepare a 192-byte minimal ELF—a root-shell that executes with privilege escalation
- Split this 192 bytes into 48 4-byte chunks (if using the xfrm-ESP path)
- Register one XFRM SA for each 4-byte chunk, with its
seq_hiset to that chunk’s value - Each time, use
splice()to sendsu’s page cache into the skb, triggering one in-place write - Loop 48 times, and
su’s page cache region (first 192 bytes) is completely replaced with the root-shell ELF execve("/usr/bin/su")→ root shell
The entire process never touches disk files. The on-disk /usr/bin/su remains untouched, its md5 still the original md5. But in the kernel’s page cache, it has been secretly replaced.
When any process initiates execve on su, the kernel reads from the page cache—oops, it runs the binary you injected. Root obtained.
Emergency Response: Temporarily Disable Vulnerable Modules
As of May 8, 2026, official kernel patches have not yet been released. Until patches are available, the only temporary mitigation is to unload and blacklist the three vulnerable modules.
First, confirm whether your system is affected:
lsmod | grep -E 'esp4|esp6|rxrpc'
If there’s any output, you’re affected.
Execute the following commands immediately (no reboot required):
sudo sh -c "printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n' > /etc/modprobe.d/dirtyfrag.conf"
sudo rmmod esp4 esp6 rxrpc 2>/dev/null
Verify:
lsmod | grep -E 'esp4|esp6|rxrpc'
Important: If you suspect the PoC has already been executed, you must clear the page cache:
echo 3 | sudo tee /proc/sys/vm/drop_caches
If you don’t do this, even after disabling the modules, the polluted page cache remains in memory—running su still gives a root shell.
Side effects:
- Disabling
esp4/esp6will interrupt IPsec VPN tunnels. Desktop users and most servers are unaffected, but if you rely on IPsec VPN (such as strongSwan, Libreswan), please evaluate the impact first. - Disabling
rxrpchas minimal impact unless you’re using AFS (Andrew File System).
AI-Accelerated Vulnerability Arms Race
The exposure of Dirty Frag forces us to think about a deeper question: Why are kernel LPE vulnerabilities popping up one after another recently?
| Vulnerability | Discovery Time | Key Tool | From Report to Public |
|---|---|---|---|
| Dirty Pipe | 2022 | Manual audit | Standard process |
| Copy Fail | 2026-04 | Xint Code (AI) | About one month |
| Dirty Frag | 2026-05 | Manual audit | 8 days (embargo broken same day) |
Kernel LPE vulnerabilities went from one per year to one per month, and now from one per month to… one per week?
Behind this is the explosive growth of AI-assisted code auditing tools. Previously, relying on human line-by-line review, a vulnerability sitting undiscovered for ten years was normal. Now AI can scan an entire subsystem within hours, pulling out all potential zero-copy paths, in-place operations, and shared references. Better tools, higher efficiency, and naturally more vulnerabilities are “discovered”—the kernel hasn’t suddenly gotten worse, it’s just that old debts hidden in corners are being uncovered one by one by AI.
What’s even more alarming is the change in the disclosure ecosystem. Dirty Frag’s embargo was deliberately broken by a third party, with vulnerability details and PoC released on the same day. What does this mean? It means from the researcher reporting the vulnerability to security@kernel.org to hackers worldwide getting their weapons, only eight days passed. The patching window—gone.
We can blame the person who broke the embargo for being unethical. But the reality is, this will only happen more and more. AI makes finding vulnerabilities faster, and weaponization faster too. You can’t always count on everyone abiding by embargo agreements.
And don’t forget, the same vulnerability family is still being dug out:
| Vulnerability | Subsystem | Status |
|---|---|---|
| Copy Fail | AF_ALG + authencesn | Fixed |
| Dirty Frag | xfrm-ESP + RxRPC | No patch |
| Copy Fail 2 | ESP-in-UDP | Publicly disclosed |
| ZCRX Freelist | io_uring ZCRX | Publicly disclosed |
The core principles of these four vulnerabilities are strikingly similar—splice() stuffs read-only page cache references into kernel subsystems, and the subsystems write in-place on the frags. What they expose is actually the same class of design problem:
| Component | Reason for Introduction | Side Effect |
|---|---|---|
splice() | Zero-copy, performance optimization | Read-only page cache references sent into kernel subsystems |
AF_ALG | Expose kernel crypto capabilities | Unprivileged users can directly initiate crypto sessions |
| xfrm-ESP | IPsec acceleration | In-place decryption, using read-only pages as output buffers |
| RxRPC | AFS network protocol support | Same as above, doesn’t even need namespace privileges |
Each design, taken individually, is a reasonable performance optimization or functional requirement. But pieced together, they form a vulnerability chain where any local user can become root without a password.
Unless upstream kernel thoroughly re-examines the paradigm of “in-place operations on zero-copy paths,” I guarantee you—this won’t be the last one.
For ordinary users, my advice is simple:
- Execute the blacklist commands above right now. Change them back when the official patch arrives.
- Closely monitor kernel updates from your package manager. Once a fixed version is available, upgrade and reboot immediately.
- Regularly
lsmod | grepto check if these modules have been accidentally loaded.
References
- GitHub PoC Repository: https://github.com/V4bel/dirtyfrag
- LWN Report: https://lwn.net/Articles/1071719/
- Phoronix Report: https://www.phoronix.com/news/Dirty-Frag-Linux
- Korean Technical Analysis (GeekNews): https://news.hada.io/topic?id=29275
- Copy Fail Disclosure Site: https://copy.fail/
- Copy Fail 2 (Electric Boogaloo): https://github.com/0xdeadbeefnetwork/Copy_Fail2-Electric_Boogaloo
Infographics

Figure 1: Dirty Frag vulnerability overview — how zero-copy page cache references are exploited through skb frags