pedit COW and DirtyClone: Two Linux Page-Cache LPEs and Why 'Local' Means Root in 2026
Two Linux page-cache kernel bugs — pedit COW (CVE-2026-46331) and DirtyClone (CVE-2026-43503) — turn a container foothold into root. Why local privilege escalation is the 2026 breakout.
In one June window, two unrelated Linux kernel teams shipped fixes for two unrelated bugs that do the same thing: corrupt the shared page cache from kernel code and walk an unprivileged user up to root. CVE-2026-46331, nicknamed pedit COW, got a CVE at merge time on 16 June 2026 and a working public exploit a day later. CVE-2026-43503, DirtyClone, was quietly patched on 21 May and got a public exploit walkthrough from JFrog Security Research on 25 June. Neither is a remote code execution bug. Neither will show up in the breach headlines the way a CitrixBleed or a Splunk pre-auth RCE does. And both matter more than most of the appliance CVEs that did, because of where Linux actually runs in 2026.
The page cache became the Linux privilege escalation highway
For most of the last decade, "local privilege escalation" sat at the bottom of every triage queue. The reasoning went: an attacker already needs code execution on the box, so a LPE only "upgrades" an existing foothold — patch it when convenient. That reasoning died with the container.
The modern Linux estate is multi-tenant by construction. A Kubernetes node runs workloads from a dozen teams. A CI/CD runner executes pull-request code from anyone with a fork. A shared build host, a managed-database fleet, a serverless backend — all of them give partially trusted code a local shell by design. On those systems there is no meaningful gap between "I have a foothold" and "I have arbitrary code as a normal user." The only barrier left is the one the kernel enforces between that user and root. A reliable LPE removes the last wall in the building.
The reliable LPEs of 2026 all attack the same structure: the page cache — the kernel's in-memory copy of file contents shared across every process that opens the same file. Corrupt a page that backs a root-owned binary or a privileged config and you own the system without ever touching the bytes on disk. This is the lineage the security community has been naming bug by bug:
- Dirty COW (CVE-2016-5195) — the original copy-on-write race, 2016.
- Dirty Pipe (CVE-2022-0847) — page-cache write via pipe buffers, 2022.
- Copy Fail (April 2026), DirtyFrag (May 2026), then DirtyClone and pedit COW in the same June.
Five entries in the same family in 2026 alone is not a coincidence. It is a class being industrialised, and the two June bugs show how the same root can be reached from opposite ends of the kernel.
pedit COW: a tc packet-editing bug that rewrites the page cache
pedit COW lives in net/sched, the traffic-control subsystem. The act_pedit action is what tc uses to rewrite header bytes on packets in flight — the kind of thing a firewall or a load balancer config does routinely. Inside tcf_pedit_act(), the kernel computes a copy-on-write range once, before the per-key loop, using a value called tcfp_off_max_hint. The problem: that hint does not account for the runtime header offset added by typed keys. Some writes land outside the privately copied region and fall onto shared page-cache pages. The patch moves the skb_ensure_writable() call inside the per-key loop, where the real offset is known, and adds overflow checking on the offset arithmetic (TuxCare analysis).
The exploit prerequisites are the part that matters operationally. According to the TuxCare write-up and the SentinelOne advisory, an attacker needs all of:
- a kernel carrying the vulnerable
act_peditpath; - the
act_peditmodule loaded, or auto-loadable; - unprivileged user namespaces enabled, which hand the attacker a namespace-local
CAP_NET_ADMIN; - the ability to run code as an untrusted or partially trusted local user.
Condition (3) is the quiet one. Unprivileged user namespaces are open by default on RHEL and Debian; on Ubuntu they are gated behind kernel.apparmor_restrict_unprivileged_userns. They are also the mechanism most container runtimes lean on. The same feature that makes rootless containers possible is what hands the exploit its capability. Red Hat rated the bug Important rather than Critical because the attack vector is local — but "local" is doing a lot of work in that sentence.
DirtyClone: the same root, from the other end of the kernel
DirtyClone (CVE-2026-43503, CVSS 8.8) never touches tc. It abuses missing flag propagation in __pskb_copy_fclone() when a socket buffer is cloned through netfilter's TEE target. The clone loses its SKBFL_SHARED_FRAG safety marker, and the now-unmarked buffer proceeds through IPsec in-place decryption with no protection — corrupting the same shared page-cache pages, reaching the same root. One bug enters through the packet-editing path; the other through packet-cloning and IPsec. They meet at the page cache.
That convergence is the point. Defenders who think in terms of "patch the tc CVE" are fighting the symptom. The structural weakness — kernel paths that assume a buffer is private when it is shared — is what keeps producing these. Both bugs were fixed in their respective subsystems; the class is not.
"We patched the appliance the same week. We were clean."
The appliance was patched. The Kubernetes nodes the appliance's workloads schedule onto were running a kernel from before either fix, with unprivileged user namespaces on by default. The pre-auth RCE got someone a pod.
pedit COWgot them the node.
Why "local" is the 2026 container escape
Here is the chain the last two weeks of headlines keep handing attackers. A pre-auth RCE — pick any of the Splunk, UniFi, Cisco Unified CM, or PTC Windchill bugs that hit CISA KEV this month — drops the attacker into a process or a container as a service account. That is a foothold, not a takeover. What turns it into a takeover is the answer to one question: can this foothold reach root, escape the container, and pivot to the node? A page-cache LPE is the cleanest yes available.
| pedit COW | DirtyClone | |
|---|---|---|
| CVE | CVE-2026-46331 | CVE-2026-43503 |
| Subsystem | net/sched act_pedit (tc) |
netfilter TEE → __pskb_copy_fclone() |
| Root cause | COW range computed before per-key offsets resolved | cloned skb loses SKBFL_SHARED_FRAG marker |
| Severity | Red Hat Important | CVSS 8.8 |
| CVE / patch | assigned 16 Jun 2026 | patch merged 21 May 2026 |
| Public exploit | 17 Jun 2026 | 25 Jun 2026 (JFrog) |
| Key prerequisite | unprivileged user namespaces | netfilter TEE + IPsec path |
| Result | page-cache corruption → root | page-cache corruption → root |
Two properties make this class hard to catch after the fact. First, the highest-risk environments — multi-tenant servers, CI/CD runners, container hosts, Kubernetes clusters where untrusted users can create namespaces — are exactly the ones that expect untrusted local code, so a local exploit raises no anomaly on its own (CyberSecGuru analysis). Second, the exploits operate entirely in RAM. They corrupt cached pages, not files. File-integrity monitoring sees nothing, because nothing on disk changes. An EDR agent watching for dropped binaries has nothing to drop on.
And the scanner gap is structural, not a tuning problem. A vulnerability scanner reads a kernel version banner and reports "Important CVE present." It cannot tell you whether act_pedit is actually loadable on your build, whether unprivileged user namespaces are enabled, or whether a foothold on this specific host would actually reach root. The CVE being "present" and the host being exploitable are different facts, and only the second one matters when you are deciding what to fix at 2 a.m.
Remediation
A complete runbook for both bugs. Patch first; the compensating controls below are for fleets that cannot reboot kernels on demand.
1. Am I affected?
# Kernel version vs your distro's fixed build
uname -r
# Is act_pedit loaded or auto-loadable? (pedit COW)
lsmod | grep -q act_pedit && echo "act_pedit LOADED"
modprobe -n -v act_pedit # dry-run: does it resolve?
# Are unprivileged user namespaces enabled? (the pedit COW capability source)
sysctl kernel.unprivileged_userns_clone 2>/dev/null # Debian/Ubuntu
sysctl user.max_user_namespaces # value > 0 = enabled
cat /proc/sys/kernel/apparmor_restrict_unprivileged_userns 2>/dev/null # Ubuntu gate
If you run multi-tenant nodes, CI runners, or any host where partially trusted users get a shell, treat the answer as "affected until proven patched."
2. Patch — exact fixed builds. Take your distro's current stable kernel; the relevant errata for pedit COW are:
- RHEL 10 — RHSA-2026:27288
- RHEL 9 — RHSA-2026:27789
- RHEL 8 / AlmaLinux 8 — RHSA-2026:27353 (ALSA-2026:27353)
- Debian 13 (trixie) — DSA-6355-1 (issued 21 Jun 2026)
- Mainline and stable: backports merged.
DirtyClone's fix merged upstream on 21 May 2026; any distro kernel built after your vendor's late-May/June errata carries it. Verify the running kernel after reboot — a patched package on disk that has not been booted is not a patched system.
3. Can't patch now? Compensating controls. Kernel patches need a reboot, and fleets defer reboots for weeks. Until you can:
- Disable unprivileged user namespaces where you do not need them. This removes pedit COW's capability source outright:
sysctl -w user.max_user_namespaces=0 # RHEL/EL family sysctl -w kernel.unprivileged_userns_clone=0 # Debian # Ubuntu: keep kernel.apparmor_restrict_unprivileged_userns=1 - Blacklist
act_peditif you run notc peditrules:echo 'install act_pedit /bin/true' | tee /etc/modprobe.d/disable-act_pedit.conf - Isolate untrusted workloads with a sandbox that does not expose the host kernel's network paths — gVisor or Kata Containers for CI runners and multi-tenant pods.
4. Hunt for compromise. These are RAM-only exploits, so disk-based hunting fails by design. Look at behaviour:
- unexpected auto-load of
act_pediton a host that never usestcpacket editing; - unprivileged users creating user/network namespaces on production nodes (
auditonclone/unsharewithCLONE_NEWUSER); - a process acquiring
uid 0with no precedingsetuid/sudo path in the audit trail; - anomalous
tc filtercreation tied to a non-network workload.
Map detections to MITRE ATT&CK T1068 (Exploitation for Privilege Escalation) and T1611 (Escape to Host). On Kubernetes, alert on pods that gain node-level capabilities they were never granted.
5. Eradicate and verify. Patch, reboot (or apply a vendor live-patch where available), then rotate every secret reachable from the compromised host or node — service-account tokens, kubelet credentials, mounted cloud keys, CI secrets. A root foothold on a node reads them all. Confirm the booted kernel version, re-check the userns posture, and only then call the host clean.
Where Zero Hunt fits
Everything above reduces to one operational question a vulnerability scanner cannot answer and a quarterly pentest does not test: if an attacker gets a foothold here, does it become root? That is an assumed-breach question, and answering it on every host, continuously, is what the Zero Hunt 10-agent swarm is built to do.
The Post-Exploit and Pivot agents do exactly the chain these bugs enable — take a foothold, attempt local privilege escalation, then lateral movement — and they generate the attempt per target. Instead of reporting "Important kernel CVE present," the engine reconstructs whether your kernel build, your act_pedit state, and your user-namespace posture actually combine into a working root primitive. A scanner asserts the CVE exists; the swarm proves the host is, or is not, exploitable — which is the only version of that fact worth acting on. The privilege-escalation skills it uses are backtested in the AI Gym against the Vulhub corpus before any of them touch a production target, so a new page-cache technique is validated in the gym, not improvised against your fleet.
The containment is the same story read backwards. Zero Hunt runs every generated exploit inside an ephemeral Docker container with optional gVisor hardening, and a hardened host the attack never reaches. That design exists precisely because kernel LPEs like pedit COW are built to escape weak container boundaries — gVisor's user-space kernel does not expose the host's net/sched or skb code paths, so a pedit-COW-style write has nothing of the host's to corrupt. The engine that hunts the class is sandboxed against the same class. And because campaigns are change-triggered, a new container host or a freshly imaged node on the perimeter is re-tested within the hour — which is the window that matters when a weaponised PoC ships the day after the CVE.