DirtyClone: One Dropped Kernel Flag, One More Path to Root

Share
DirtyClone: One Dropped Kernel Flag, One More Path to Root
Illustration of a cloned network packet overwriting a cached Linux binary in kernel memory to gain root.

Another week, another way for a local user to become root on Linux without ever touching the disk. DirtyClone — tracked as CVE-2026-43503, CVSS 8.8 — is the newest member of the DirtyFrag family, and JFrog Security Research published a working exploit walkthrough on June 25, the first public demonstration for this specific variant. The patch reached mainline back on May 21 (Linux v7.1-rc5). If your kernel predates that, you're exposed.

This is the second page-cache copy-on-write root exploit to surface in the same window as pedit COW (CVE-2026-46331). They aren't the same bug, but they rhyme — and DirtyClone is worth understanding on its own because it exposes a deeper problem than any single flawed function.

The One-Flag Vulnerability

When the kernel copies a network packet internally, it's supposed to carry along a metadata flag — SKBFL_SHARED_FRAG — that marks the packet's memory as shared with a file on disk. That flag is a promise: "before you write into this, make a private copy first." It's what forces subsystems doing in-place crypto (like IPsec decryption) to copy-on-write instead of scribbling directly onto file-backed pages.

The entire vulnerability is that two helper functions drop the flag during the copy. Lose the flag, and the kernel no longer knows the memory is shared, so the in-place write lands directly on the cached file. That's it. One missing bit of metadata turns a zero-copy performance optimization into an arbitrary write into the page cache.

How the Exploit Chains Together

The attack is clever in how it assembles ordinary pieces into a root primitive:

  1. Load a privileged binary — /usr/bin/su is the classic target — into memory so its pages sit in the page cache.
  2. Wire those file-backed pages into a network packet's fragment list.
  3. Force the kernel to clone that packet, hitting the code path where the shared-frag flag gets dropped.
  4. Route the cloned packet through a loopback IPsec tunnel the attacker controls. The decryption step performs an in-place transformation — and because the kernel forgot the memory was shared, it overwrites the binary's login checks with attacker-chosen bytes.

The next time anyone runs su, the tampered in-memory copy hands over root.

⚠️ The detail that matters for defenders is the same one that makes pedit COW so nasty: the file on disk is never modified. The poison lives only in the kernel's in-memory copy. File-integrity tooling reads the disk and reports everything clean. There's no audit trail. A reboot restores the original binary — but by then the attacker has had root long enough to establish whatever persistence they wanted, so the reboot just hides the evidence of how they got in. Page cache is shared at the host level too, so a modification made inside an unprivileged namespace affects every process on the machine.

Why It Keeps Happening: A Contract, Not a Bug

Here's the part worth dwelling on, because it explains why this is the fourth variant and probably not the last.

DirtyClone's demonstrated exploit centers on __pskb_copy_fclone(), with skb_shift() also affected, and the broader CVE fix covers still more fragment-transfer helpers where the same flag could leak away. The underlying issue isn't one broken function — it's a contract that the whole networking stack is supposed to honor: every code path that moves socket-buffer fragments must preserve the shared-frag bit, every single time. The kernel's zero-copy networking deliberately lets file-backed memory serve as packet data for performance, and a single dropped flag anywhere along the chain converts that optimization into a write primitive.

The family tree shows the pattern, each fix closing one door while another stayed open:

  • Copy Fail (CVE-2026-31431), late April — the progenitor, a four-byte page-cache write via the algif_aead module.
  • DirtyFrag (CVE-2026-43284 and CVE-2026-43500), May 7 — chained the IPsec ESP and RxRPC paths into a full write primitive. Notably, it triggers regardless of whether the Copy Fail mitigation is applied.
  • Fragnesia (CVE-2026-46300), May 13 — slipped past the DirtyFrag patch through a flag-dropping bug in skb_try_coalesce().
  • DirtyClone (CVE-2026-43503), now — found yet another fragment-transfer path where the contract wasn't honored.

Original DirtyFrag researcher Hyunwoo Kim (@v4bel) submitted a broader multi-site patch covering several remaining frag-transfer helpers on May 16; the combined fix merged May 21 and shipped in v7.1-rc5 on May 24. JFrog's Eddy Tsalolikhin and Or Peles built the DirtyClone PoC by auditing exactly the residual paths those earlier fixes hadn't fully closed.

Who's Exposed

Exploitation needs CAP_NET_ADMIN to configure the loopback IPsec tunnel. On distributions where unprivileged user namespaces are open by default — Debian and Fedora among them — a local user simply creates a new namespace and gets that capability inside it. JFrog confirmed the exploit on Debian, Ubuntu, and Fedora with default namespace configurations.

Ubuntu 24.04 and later restrict namespace creation through AppArmor, which blocks the default exploit path — though, as with pedit COW, the underlying kernel remains vulnerable if an attacker finds another route to the capability.

The hosts that should worry are the familiar ones: multi-tenant servers, CI runners, container hosts, and Kubernetes clusters where untrusted users can create namespaces. On any of those, "local unprivileged user" is a population you don't fully control, and that's exactly who this hands root to.

What To Do

Patch and reboot. The fix is upstream in v7.1-rc5 and backported to stable and LTS branches. Ubuntu, Debian, and SUSE have published advisories; Red Hat has a Bugzilla tracking entry. Check your kernel:

uname -r

Compare against your distro's fixed version, update, and reboot.

If you can't patch yet

Two workarounds each break the chain, both temporary controls rather than fixes.

Restrict unprivileged user namespaces, removing the capability the exploit borrows:

sudo sysctl -w kernel.unprivileged_userns_clone=0

⚠️ This breaks rootless containers, some CI sandboxes, and sandboxed browsers. Test before deploying fleet-wide.

Or blacklist the modules that provide the in-place decryption primitives:

sudo sh -c "printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n' > /etc/modprobe.d/disable-dirtyfrag.conf"

Then unload them if currently loaded:

sudo rmmod esp4 esp6 rxrpc 2>/dev/null; true

⚠️ This breaks IPsec (esp4/esp6) and AFS (rxrpc), and only works when those features are loadable modules rather than compiled into the kernel. Don't apply it on hosts that terminate or transit IPsec / strongSwan / Libreswan tunnels. On typical web-hosting servers, rxrpc is almost never in use, but esp4/esp6 may well be — check before you cut.

If a host may have been hit

⚠️ Treat it as compromised. Because the exploit modifies legitimate binaries in the page cache, mitigation applied after the fact does nothing about an attacker who already ran it. Dropping the page cache flushes the poisoned in-memory binary:

echo 3 | sudo tee /proc/sys/vm/drop_caches

But that only clears the tampered cache image — it does nothing about the root shell, the new SSH keys, the cron job, or whatever else got planted once the attacker had root. The forensically honest move is full incident handling: isolate the host, capture volatile memory before you reboot (the evidence lives in RAM, and a reboot destroys it), consider every credential on the box burned, and rebuild rather than scrub. A clean file-integrity report is meaningless here by design.

The Class Isn't Closed

DirtyFrag is very likely not finished. Any function that moves fragment descriptors without propagating the shared-frag flag is a candidate for the next CVE, and a proper audit has to cover every path that touches skb_shinfo()->flags during fragment transfer. Until the kernel enforces that contract structurally — rather than relying on each helper to remember the flag by hand — defenders should expect more variants and keep kernels current rather than waiting for the specific CVE that hits their fleet.

The practical posture is the same one the whole page-cache COW wave argues for: patch kernels promptly on anything where untrusted users can get a shell, lock down unprivileged user namespaces where your workloads can tolerate it, and stop trusting on-disk integrity checks as your detection backstop for this class. The attack was designed, start to finish, to make those checks lie to you.


References

Read more