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
- What it is: a class of bug where an skb
(socket buffer) carries paged fragments the kernel does
not own privately (because userspace
splice()'d pipe pages into a UDP socket), and downstream crypto code performs in-place decryption on those externally-owned pages. Two manifestations — one inxfrmESP input (CVE-2026-43284, CVSS 8.8 HIGH), one inrxrpcDATA / RESPONSE handlers (CVE-2026-43500, CVSS 7.8 HIGH). Both are local privilege escalations. - Who's exposed: every stable Linux kernel from 4.11 onwards for the xfrm bug; from 5.3 onwards for the rxrpc bug. If your host runs an unpatched mainline-derived kernel released since 2017, you are in the affected range — independent of whether you "use" IPsec or AFS.
- What it costs an attacker: an existing
local foothold and the ability to open a UDP socket and call
splice()/sendmsg(MSG_SPLICE_PAGES). No remote exploitation. No special capabilities. A public PoC at github.com/V4bel/dirtyfrag is already in NVD's references, tagged "Exploit, Third Party Advisory". - The fix: apply your distro's latest kernel security update and reboot. The patched kernel must actually be running. The fix has landed in every maintained stable branch — kernel.org stable 5.10.255, 5.15.205, 6.1.171, 6.6.138, 6.12.87, 6.18.29, 7.0.6 (and newer).
- If you can't patch yet: blacklist
rxrpcif AFS isn't in use (kills the 43500 surface entirely). The xfrm side does not have a clean disable-the-feature workaround if you're running IPsec NAT-T — patch is the only fix.
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:
- The same root cause class (externally-owned paged frags bypassing the cloned-skb fast-path gate).
- The same trigger (userspace
splice()/MSG_SPLICE_PAGESinto a UDP socket). - The same fix surface (mark the frags + widen the unshare gate).
- The same backport schedule (both fixed in the same kernel-stable point releases on every supported branch).
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:
- Ubuntu: cross-reference ubuntu.com/security/CVE-2026-43284 and CVE-2026-43500. Canonical published a "Dirty Frag" advisory blog post on ubuntu.com/blog/ as the human-readable summary (search "Dirty Frag" on the Ubuntu blog for the current URL). At the time of writing (2026-05-20) the per-release status reads "Needs evaluation" on every LTS — backports are in progress.
- Debian 13 Trixie: fixed in
linux 6.12.86-1(CVE-2026-43284) andlinux 6.12.88-1(CVE-2026-43500), bundled in DSA-6253-1. - Debian 12 Bookworm: fixed in
linux 6.1.170-3(CVE-2026-43284) andlinux 6.1.172-1(CVE-2026-43500), bundled in DSA-6258-1. - Debian 11 Bullseye LTS: DLA-4572-1 ships
linux 5.10.251-5; DLA-4574-1 shipslinux-6.1 6.1.172-1~deb11u1. See the security tracker for the announcement links. - RHEL / Rocky / AlmaLinux 8, 9, 10 (and OpenShift): CVE-2026-43284 is patched across every supported major via 37 RHSAs (32 at first publication on 2026-05-20, +5 additional backports landed 2026-05-21) — headline advisories RHSA-2026:16195 (RHEL 8), RHSA-2026:16206 / 16312 (RHEL 9.x), RHSA-2026:16062 / 19074 (RHEL 10.x). Live-patches via kpatch are available for the E4S / EUS streams (no reboot needed). The companion CVE-2026-43500 is "Not affected" on RHEL / RHCOS because the RxRPC module is not shipped or supported in those binaries — confirmed by Red Hat's CVE page.
- Alpine: pending — track security.alpinelinux.org/vuln/CVE-2026-43284.
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:
- User namespaces are enabled and remapping is in place.
- Containers drop
CAP_NET_RAWandCAP_NET_ADMINunless they specifically need them. - Sandboxed workloads (CI runners, untrusted code execution
services) use seccomp profiles that allow
spliceandsendmmsgonly where strictly required.
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
- 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.
- Bug-class fixes ship as bug clusters. The
Dirty Frag root cause (missing
SKBFL_SHARED_FRAGon 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. - 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.