Published 2026-05-20 · 12 min read

Dirty Frag (CVE-2026-43284 / CVE-2026-43500) — what homelab operators need to know

On 2026-05-07 a Linux kernel local privilege escalation went public ahead of its coordinated-disclosure date, with broader discussion landing across oss-security and r/hetzner the following day. By the time the official advisories were out, two CVEs were assigned, a working proof-of-concept was on GitHub, and the bug had picked up a second name — "Copy Fail 2" — from a separate disclosing entity. The bug — in two different kernel subsystems, the same root cause — affects essentially every stable Linux kernel from 4.11 onwards. Per Red Hat's record, the practical exploitation primitive is corrupting page-cache contents of readable system files like /etc/passwd — the same broad pattern Dirty COW established in 2016. Here's what it is, who's exposed, and how to confirm your fleet is patched.

The TL;DR

Why "Dirty Frag" is the name

The first hot take on the embargo-break thread was that "Frag" stood for IP fragmentation reassembly, or maybe memory fragmentation. Neither was right. The "Frag" in Dirty Frag is paged skb fragments — the frags[] array on a Linux socket buffer that holds page references for zero-copy networking.

That single naming pun encodes the entire bug. When userspace does splice() from a pipe into a UDP socket (or uses sendmsg() with MSG_SPLICE_PAGES), the kernel attaches the pipe's pages directly to the outgoing skb's frags[] array. The skb doesn't own those pages — they're still backed by the pipe. The standard signal for "these pages aren't ours, copy before mutating" is the SKBFL_SHARED_FRAG flag. TCP sets that flag correctly. The IPv4 / IPv6 datagram (UDP) append path… did not. That single oversight made every code path downstream that trusted the flag a potential vulnerability site.

The name follows the "Dirty" naming convention (Dirty COW, Dirty Pipe, Dirty Cred) — kernel bugs that turn an existing low-privilege foothold into full root by exploiting a shared memory / shared resource pattern.

The technical mechanism (one paragraph per CVE)

CVE-2026-43284 — xfrm ESP-in-UDP

IPsec's ESP-in-UDP mode (RFC 3948, used for NAT-traversal on port 4500) decrypts incoming UDP-encapsulated ESP packets via the xfrm framework. ESP input takes a fast path when the receiving skb is "uncloned and has no frag_list" — it decrypts in place, assuming exclusive page ownership. With the SKBFL_SHARED_FRAG flag missing on UDP datagram splice frags, an ESP-in-UDP packet whose payload was assembled from spliced pipe pages looks like a normal uncloned skb. ESP input decrypts it in place — over pages that the attacker still has a userspace handle to via the pipe. CISA-ADP classified this as CWE-123 (Write-what-where Condition), which is the strongest memory-corruption primitive on the spectrum. The fix marks IPv4 / IPv6 splice frags with SKBFL_SHARED_FRAG (matching TCP) and makes ESP input fall back to skb_cow_data() when the flag is present.

CVE-2026-43500 — rxrpc DATA / RESPONSE

RX RPC is the protocol underlying AFS. Its DATA and RESPONSE packet handlers (rxrpc_input_call_event and rxrpc_verify_response) linearise the skb before calling into the security ops — but only when skb_cloned() is true. An uncloned skb that still carries externally-owned paged frags fell through to the in-place decryption path, where skb_to_sgvec() bound the frag pages directly into the AEAD / skcipher scatter-gather list. Same root cause as 43284, different downstream subsystem. CWE-787 (Out-of-bounds Write). The fix extends the gate to also unshare when skb_has_frag_list() or skb_has_shared_frag() is true.

For the upstream commit messages quoted verbatim, see the reference card — kernel-stable commit messages are the authoritative narrative for what changed, and I'd rather quote them than paraphrase.

Why both CVEs got a single bug name

Strictly speaking, these are sibling bugs in different subsystems, not the same bug. But they share:

Treating them as one disclosure is the right operational call. Distros are publishing a single advisory that covers both — Debian's DSA-6253-1 (Trixie) and DSA-6258-1 (Bookworm) both list CVE-2026-43284 and CVE-2026-43500 in the same announcement.

Detect: is my kernel vulnerable?

Three checks, in order from cheapest to most thorough:

1. Compare uname -r to the per-branch fix versions

$ uname -r
6.6.135-1-pve

# Compare to the per-branch fix versions:
#   stable 5.10.x  → fixed in 5.10.255 (or newer)
#   stable 5.15.x  → fixed in 5.15.205
#   stable 6.1.x   → fixed in 6.1.171
#   stable 6.6.x   → fixed in 6.6.138
#   stable 6.12.x  → fixed in 6.12.87
#   stable 6.18.x  → fixed in 6.18.29
#   stable 7.0.x   → fixed in 7.0.6

Important caveat: distros do not necessarily ship the upstream stable point releases verbatim. Ubuntu's 6.8.0-58-generic may include a backport of the Dirty Frag fix even though the upstream 6.8 branch is not maintained. Use the distro tracker as the authoritative answer.

2. Distro security tracker

Authoritative answer for any major distro:

3. Live scan

For a fleet of more than two hosts, comparing uname -r by hand stops scaling. Tools that audit installed kernel-package versions against published CVE feeds (including Noxen) report both CVE IDs directly when a vulnerable kernel package is on disk — and surface a separate "kernel updated but not yet running" finding when the package has been upgraded but the host hasn't rebooted. That second flag is the one that catches the realistic failure mode here: patch shipped on Tuesday, host still running Monday's kernel because nobody had time for the reboot window.

Fix per distro

# Debian / Ubuntu
sudo apt update
sudo apt full-upgrade -y
sudo reboot

# Rocky / RHEL / AlmaLinux
sudo dnf upgrade -y kernel
sudo reboot

# Alpine
sudo apk update
sudo apk upgrade
sudo reboot

# Verify the *running* kernel matches the *installed* kernel:
uname -r
ls /boot/vmlinuz-* 2>/dev/null | sort -V | tail -1
rpm -q --last kernel 2>/dev/null | head -1

The reboot is non-optional. apt full-upgrade / dnf upgrade on its own only changes the installed kernel package — the running kernel is whatever was loaded at last boot. A kernel-LPE fix that isn't running is no fix at all. On Debian / Ubuntu, [ -f /var/run/reboot-required ] tells you a reboot is needed.

Defence in depth — what actually buys you something

Patch first, harden second. But once the patch is in, these narrow the window for the next kernel LPE in the same class:

Blacklist unused network modules (Canonical's pre-patch mitigation)

Canonical's "Dirty Frag" advisory publishes /etc/modprobe.d/dirty-frag.conf as the official pre-patch mitigation. The principle: most homelabs don't use AFS, most homelabs don't use IPsec NAT-T, and neither subsystem needs to be loaded by default. Audit what's actually loaded first:

lsmod | grep -E '^(rxrpc|kafs|esp|xfrm|ah|af_rxrpc)\b'

# If none are used, blacklist them via Canonical's recommended file:
cat << 'EOF' | sudo tee /etc/modprobe.d/dirty-frag.conf
blacklist rxrpc
blacklist kafs
# Only blacklist xfrm/esp if you do not run IPsec:
# blacklist xfrm_user
# blacklist esp4
# blacklist esp6
EOF

# Regenerate initramfs so the blacklist applies at next boot:
sudo update-initramfs -u   # Debian / Ubuntu
sudo dracut --force         # RHEL / Rocky / AlmaLinux

The xfrm blacklist is more aggressive — only do it if you've confirmed nothing on the host uses IPsec (check ip xfrm policy). Most homelabs use WireGuard, OpenVPN, or Tailscale instead of IPsec, and for those the blacklist is safe.

Tighten container isolation

The trigger condition (splice() into a UDP socket) doesn't require elevated capabilities, but constrained namespaces narrow what untrusted container workloads can actually reach. If you run Docker / Podman / Incus, audit:

Reduce who has shell access at all

The realistic exploit chain is: (1) attacker compromises a service running as a low-privilege user — say, a Plex instance with a stale CVE; (2) attacker drops a copy of the Dirty Frag PoC; (3) PoC escalates to root via the local kernel; (4) attacker now owns the host. Step 3 is the new entry. Step 2 is where defence-in-depth used to live — and where the exposed admin surfaces post spends most of its energy. Both still matter.

What this incident taught us

  1. Embargo discipline broke first, the kernel team responded second. Public disclosure landed 2026-05-07 ahead of the planned coordinated date; the r/hetzner thread and oss-security mailing list followed on 2026-05-08, with NVD publishing CVE-2026-43284 the same day and CVE-2026-43500 three days later. That's not the kernel team's fault — embargo breaks happen — but it does mean the first wave of public information is invariably wrong (a second naming, "Copy Fail 2", spawned in parallel), and the first wave of fix advice is invariably premature. For about 72 hours, the most accurate single source was the kernel-stable mailing list, not any vendor blog.
  2. Bug-class fixes ship as bug clusters. The Dirty Frag root cause (missing SKBFL_SHARED_FRAG on UDP datagram splice frags) was always going to require auditing every downstream code path that trusts that flag. Two CVEs landed initially; expect more to follow as auditors comb other subsystems for the same pattern. Keeping your kernel patched is not a one-time event.
  3. The Mac-as-control-plane stays out of the blast radius. A Linux kernel LPE is meaningful only on Linux hosts. Your macOS scanner / dashboard / fleet manager is not exposed to this CVE — it's running a different kernel with a different network stack. That's the architectural point of running the scanner on a different OS than the scanned: the scanner's foothold is not the scanned's foothold.

How Noxen handles it

Dirty Frag shows up in Noxen's daily-rebuilt feed as two finding rows — one per CVE — against any host whose installed kernel package is older than the per-distro fix version. The package name varies by distro (linux-image-* on Debian / Ubuntu, kernel on RHEL-family), but Noxen's distro-aware matcher normalises both to the same CVE record.

Hosts that have the fix installed but haven't rebooted are flagged separately as "kernel updated but not yet running" — surfaced as a distinct finding so a reboot is queued explicitly rather than implicitly assumed. For Dirty Frag specifically, this is the failure mode to watch: the package upgrade is easy, the reboot is the friction.

The scheduled-scan agent picks up newly-published kernel CVEs within 24 hours of the next feed rebuild — so an embargo-break event like this one shows up the morning after disclosure rather than on whatever cadence you remember to run a manual scan.

Scan your Linux fleet from your Mac

Noxen runs nightly agentless audits over SSH and shows only what changed since the last scan — new CVEs, config drift, newly exposed admin services. Mac-native control plane, no SaaS round-trip.

Buy Noxen — $79 one-time Download free trial