On May 8, 2026 (May 7 UTC), security researcher Hyunwoo Kim (@v4bel) disclosed Dirty Frag on the oss-security mailing list — two chained Linux kernel vulnerabilities (later tracked as CVE-2026–43284 and CVE-2026–43500) that allow any unprivileged local user to gain root on virtually every major Linux distribution. The disclosure was an embargo break — "Because the embargo has now been broken, no patches or CVEs exist for these vulnerabilities." Microsoft reported limited in-the-wild activity shortly after disclosure. The observed attack chain: SSH-based initial access, privilege escalation via Dirty Frag, modification of GLPI LDAP authentication files, and forceful wiping of PHP session evidence to cover tracks.
The PoC was detonated on a default Ubuntu 22.04 GCP instance (kernel 6.8.0-1054-gcp, unpatched); Path A captured first, Path B separately via ./exp --force-rxrpc -v. Every audit record below is real. The detection approach is deliberately atomic — one single audit event per rule, no time windows, no correlation. The central insight: Dirty Frag cannot hide its kernel primitives. What we map below is what the exploit class structurally requires from the kernel, and the audit telemetry each step leaves behind.
What is Dirty Frag?
Dirty Frag belongs to a class of Linux LPE techniques — alongside DirtyCOW, DirtyPipe, and CopyFail — that share one trick: convince the kernel to write attacker-controlled data into memory it believes is read-only, then turn that write into root. The mechanism targets the page cache — the RAM region holding copies of files currently in use. The kernel trusts cached pages implicitly: it will execute code from them without re-reading from disk and authenticate users against them without rechecking the underlying file. Dirty Frag uses this trust two different ways.
Two paths, two targets, one shared sink. Both paths splice a page-cache page into a kernel crypto pipeline that performs an in-place decrypt without checking memory ownership. The cipher and subsystem differ; the structural flaw is identical.
- Path A — ESP (CVE-2026–43284) — corrupts
/usr/bin/su. Poisons the page-cache copy ofsuwith a 192-byte shellcode stub. When the unmodified-on-disk SUID binary executes, the kernel grants root EUID based on disk metadata, then runs the poisoned RAM copy. Shellcode callssetgid(0)→setuid(0)→setgroups(0, NULL)→execve("/bin/sh"). PAM never involved. - Path B — RxRPC (CVE-2026–43500) — corrupts
/etc/passwd. Rewrites bytes 4-15 of the root entry to produceroot::0:0:(empty password field)./usr/bin/suis unmodified. The exploit runssunormally and PAM'spam_unix.so nullok(default in/etc/pam.d/common-authon Ubuntu) accepts the blank password.
What separates Dirty Frag from prior bugs in the class:
- No race condition. Both paths are deterministic — same result every run.
- Nearly universal reach for Path A. The xfrm-ESP flaw was introduced in 2017. Per Tenable, Linux distros released in the last nine years are likely affected. Path B has been present since 2023.
- Asymmetric prerequisites. Path A needs a user namespace (
unshare(CLONE_NEWUSER|CLONE_NEWNET)) to obtainCAP_NET_ADMIN. Path B runs entirely unprivileged in the host namespace. (Some vendor coverage describes Path B as also requiring a namespace; the public PoC's Path B does not — unshare is opt-in viaPOC_UNSHARE=1, exp.c:1238-1241.) - Bypasses CopyFail mitigations. Per Elastic Security Labs, "DirtyFrag does not depend on the algif_aead module, meaning systems that only applied the Copy Fail mitigation may still be exposed" — same in-place-decrypt sink, different kernel subsystems (
algif_aeadvsxfrm/rxkad).
Detection asymmetry — the most important takeaway of this post. Path A leaves an unconditional kernel-native audit event (
MAC_IPSEC_EVENT SA-icv-failure) that fires regardless of auditd rule configuration — once per 4-byte write, 48 times per exploitation. Path B leaves no equivalent:rxkademits nothing on verification failure. Detection of Path B depends entirely on rules deployed before the attack. If you take one thing from this post: assume Path A will catch itself; pre-deploy rules for Path B.
How It Works
Two paths reach the same primitive through different kernel subsystems. Both abuse the kernel performing in-place crypto on splice-targeted page-cache pages without checking memory ownership. Path A uses the xfrm/ESP receive pipeline; Path B uses the rxkad authentication subsystem.
Step 1 — Borrow a network key (Path A only). The ESP path needs CAP_NET_ADMIN to register XFRM Security Associations. unshare(CLONE_NEWUSER|CLONE_NEWNET) creates an isolated sandbox that grants it. Path B doesn't need a namespace — AF_RXRPC, add_key, AF_ALG, and splice triggers all work unprivileged in the host namespace.
Step 2 — Arm
Step 3 — Target the page cache
Step 4 — Write
Step 5 — Escalate
Lab Setup
Telemetry below was captured on a default Ubuntu 22.04 GCP instance running kernel 6.8.0-1054-gcp (unpatched, auditd loaded) with the audit backlog raised to 8192 (auditctl -b 8192 — without this, Path B captures lose records to the syscall burst). Eight auditd rules cover both paths' kill-chain primitives (the SA-icv-failure and SAD-add events emit kernel-side without needing rules — see the note below the ruleset). Add to /etc/audit/rules.d/dirtyfrag.rules, reload with sudo augenrules --load:
# 1. Path A — Namespace escape (earliest indicator)
# Three flag values: NEWUSER alone, NEWUSER|NEWNET (V4bel), NEWUSER|NEWPID|NEWNET (defensive variant)
-a always,exit -F arch=b64 -S unshare -F a0=268435456 -k net_userns_escape
-a always,exit -F arch=b64 -S unshare -F a0=1342177280 -k net_userns_escape
-a always,exit -F arch=b64 -S unshare -F a0=1879048192 -k net_userns_escape
-a always,exit -F arch=b32 -S unshare -F a0=268435456 -k net_userns_escape
-a always,exit -F arch=b32 -S unshare -F a0=1342177280 -k net_userns_escape
-a always,exit -F arch=b32 -S unshare -F a0=1879048192 -k net_userns_escape
# 2. Path A — XFRM SA registration channel
-a always,exit -F arch=b64 -S socket -F a0=16 -F a2=6 -k dirtyfrag_xfrm_netlink
-a always,exit -F arch=b32 -S socket -F a0=16 -F a2=6 -k dirtyfrag_xfrm_netlink
# 3. Both paths — page-cache write primitive
-a always,exit -F arch=b64 -S vmsplice -k pagecache_write_primitive
-a always,exit -F arch=b64 -S splice -k pagecache_write_primitive
-a always,exit -F arch=b32 -S vmsplice -k pagecache_write_primitive
-a always,exit -F arch=b32 -S splice -k pagecache_write_primitive
# 4. Both paths — privilege escalation confirmation
-a always,exit -F arch=b64 -S setuid -S setreuid -S setresuid -F auid!=4294967295 -k priv_esc
-a always,exit -F arch=b64 -S setgid -S setregid -S setresgid -F auid!=4294967295 -k priv_esc
-a always,exit -F arch=b32 -S setuid -S setreuid -S setresuid -F auid!=4294967295 -k priv_esc
-a always,exit -F arch=b32 -S setgid -S setregid -S setresgid -F auid!=4294967295 -k priv_esc
# 5. Path B — AF_RXRPC socket (anchor)
-a always,exit -F arch=b64 -S socket -F a0=33 -k dirtyfrag_rxrpc_socket
-a always,exit -F arch=b32 -S socket -F a0=33 -k dirtyfrag_rxrpc_socket
# 6. Path B — RxRPC session-key registration
-a always,exit -F arch=b64 -S add_key -k dirtyfrag_add_key
-a always,exit -F arch=b32 -S add_key -k dirtyfrag_add_key
# 7. Both paths — kernel module load monitoring
-a always,exit -F arch=b64 -S init_module -S finit_module -k kernel_module_load
-a always,exit -F arch=b32 -S init_module -S finit_module -k kernel_module_load
# 8. Path B — AF_ALG socket (pcbc(fcrypt) checksum prep)
-a always,exit -F arch=b64 -S socket -F a0=38 -k dirtyfrag_af_alg_socket
-a always,exit -F arch=b32 -S socket -F a0=38 -k dirtyfrag_af_alg_socketAudit records below show the key= value from the -k flag in each rule above; these are defender-chosen tags, not anything the exploit sets.
MAC_IPSEC_EVENT records require no rule. SAD-add and SA-icv-failure are mandatory kernel audit events emitted directly by the XFRM subsystem via xfrm_audit_state_add() and xfrm_audit_state_icvfail() (net/xfrm/xfrm_state.c), using audit type AUDIT_MAC_IPSEC_EVENT (1415, include/uapi/linux/audit.h). They appear independent of any rule configuration. Suppressing them requires auditctl -a exclude,always -F msgtype=MAC_IPSEC_EVENT.
For the nerds — full telemetry walkthrough
For detection engineers who want the receipts — every captured audit record from both paths, phase by phase, syscall by syscall, cross-referenced to the exp.c line that produced it and the kernel mechanism that emitted it — there's a companion post that walks the full audit trail:
→ I'll Catch a Grenade for You — Dirty Frag Telemetry Walkthrough
Covered there:
- Path A (Phases 1–4): namespace trick, XFRM SA registration, splice chain to page cache, in-place ESP decrypt poisoning the page, MAC_IPSEC_EVENT loopback ICV failure, privileged execution
- Path B (Phases 1–5): rxrpc.ko module autoload, /etc/passwd open + mmap, offline pcbc(fcrypt) brute-force, three-trigger A→B→C kernel chain with AF_ALG checksum prep, PAM nullok escalation
Detection
The detection approach is atomic — every rule below matches a single audit event in isolation. No time windows, no event-count thresholds, no cross-event correlation. The exploit's syscall sequence is structural; the timing is fully attacker-controlled, and single-event invariants survive arbitrary pacing.
Each rule covers a kernel mechanism the public DirtyFrag exploit invokes during its run — namespace escape, SA registration, page-cache routing, in-place crypto setup, write trigger. Rules 4 and 9 match MAC_IPSEC_EVENT records (kernel-native, no auditd rule required); the rest need their auditd rules deployed before the attack.
Recommendations
Prevention
Disable unprivileged user namespaces (blocks Path A only). Removes the unshare(CLONE_NEWUSER) prerequisite without touching IPsec or kernel modules (Red Hat RHSB-2026-003):
# Debian/Ubuntu
echo "kernel.unprivileged_userns_clone=0" | sudo tee /etc/sysctl.d/99-dirtyfrag.conf && sudo sysctl --system
# RHEL/Fedora
echo "user.max_user_namespaces=0" | sudo tee /etc/sysctl.d/99-dirtyfrag.conf && sudo sysctl --systemRed Hat's bulletin notes the sysctl approach blocks the ESP variant only; CVE-2026–43500 (the rxrpc variant) is reported as not affecting Red Hat products (because rxrpc.ko isn't shipped on RHEL by default). The RxRPC path runs in the host namespace and is unaffected by these sysctls regardless of distribution. To close both paths on systems where rxrpc IS affected, pair with the module blacklist below. Cost: rootless Docker/Podman, Flatpak, Firefox/Chrome sandboxes break.
Blacklist vulnerable modules. If IPsec (esp4/esp6) and AFS (rxrpc) are not in active use:
printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n' \
| sudo tee /etc/modprobe.d/dirtyfrag.conf
sudo rmmod esp4 esp6 rxrpc 2>/dev/null
lsmod | grep -E 'esp4|esp6|rxrpc' # confirm what's loaded before committingesp4/esp6 break IPsec VPNs. rxrpc breaks AFS. Confirm neither is in use.
Container environments. Coverage varies sharply by orchestrator default:
Any container running with --privileged or seccomp=unconfined bypasses these defaults entirely — both paths permitted regardless of orchestrator. Sysdig recommends restricting AF_KEY, AF_RXRPC, and XFRM netlink syscalls in container runtimes via seccomp. For Kubernetes, deploy an explicit SeccompProfile covering both paths — Docker's defaults are NOT inherited by K8s pods.
Conclusion
Dirty Frag is the second universal Linux LPE in eight days — following CopyFail's algif_aead page-cache write — and both share the same root pattern: a kernel performance optimization that skips a safety check, turned into a deterministic write primitive. DirtyCOW, DirtyPipe, CopyFail, Dirty Frag — separate bugs, discovered years apart, all reducing to the same question: can an attacker get the kernel to write attacker-controlled data into a page-cache page it was never supposed to touch? The detection approach here focuses on what is architecturally required: unshare, XFRM SA registration, the vmsplice/splice chain, AF_RXRPC from a non-AFS process, AF_ALG pcbc(fcrypt), and the loopback SA-icv-failure. These are structural requirements of the vulnerability class and surface in the kernel's own audit infrastructure regardless of how the exploit is packaged.
References
- oss-security — Dirty Frag disclosure, Hyunwoo Kim
- Microsoft — Active Attack: Dirty Frag
- PoC — github.com/V4bel/dirtyfrag
- Kernel commit cac2661c53f3 — esp4: Avoid skb_cow_data (2017 — introducing optimization)
- Kernel commit f4c50a4034e6 — referenced as 2026 fix in Hyunwoo Kim disclosure
- Wiz Research — Dirty Frag
- Elastic Security Labs — Copy Fail + Dirty Frag
- Sysdig — Detecting Dirty Frag
- Red Hat RHSB-2026–003
- Tenable — Dirty Frag FAQ
- Docker seccomp documentation
- net/xfrm/xfrm_state.c (kernel source)
- net/rxrpc/rxkad.c (kernel source)
- vmsplice(2) · splice(2) · netlink(7) · capabilities(7) · pam_unix(8) — Ubuntu Jammy