Dirty Pipe, Copy Fail, Dirty Frag: What Linux Kernel Exploits Keep Teaching Us
A defender's reading of the kernel's recurring privilege-escalation bugs

Every year or two, a Linux kernel privilege-escalation bug gets a catchy name, a logo, and a flurry of breathless coverage. Dirty COW. Dirty Pipe. DirtyCred. The branding is silly, but the pattern underneath is deadly serious and worth studying — not so you can write exploits, but so you understand why your boxes keep being vulnerable to the same shape of bug, and what actually reduces the blast radius.
This is a defender’s read. No working exploit code here, and none needed. The lessons are in the categories, not the payloads.
1 The recurring shape: a local user becomes root
The thing these bugs have in common is the outcome: an unprivileged local user — a low-rights service account, a process inside a container, a compromised web app running as www-data — turns into root. That’s “local privilege escalation,” and it’s the second half of nearly every real-world compromise. The attacker rarely lands as root. They land as something small and clamber up. Kernel LPE bugs are the ladder.
Dirty Pipe (CVE-2022-0847) is the cleanest teaching example. Disclosed in early 2022 by Max Kellermann, it was a flaw in the kernel’s pipe handling where, due to an uninitialised flag, data could be written into the page cache backing a file the attacker only had read access to. Read-only files you couldn’t normally touch — including ones owned by root — became writable. The proof-of-concept everyone reached for overwrote a line in a setuid binary or a system file, and from there, root. The bug was elegant precisely because it didn’t need to defeat any memory-corruption defences. It just abused correct-looking logic.
2 Why memory-safety mitigations didn’t save us
Here’s the first hard lesson. The kernel has accumulated a serious arsenal of exploit mitigations over the years — KASLR to randomise addresses, SMEP and SMAP to stop the kernel running or reading userspace memory, stack canaries, the lot. They make memory-corruption exploits genuinely harder.
Dirty Pipe sailed past all of it, because it wasn’t a memory-corruption bug. It was a logic flaw. You can’t randomise your way out of a function that does the wrong correct-looking thing. This is why “we have all the mitigations enabled” is necessary but nowhere near sufficient — a whole class of these bugs lives in logic the mitigations never look at.
DirtyCred, presented at Black Hat in 2022 by a team from Northwestern, drove a related point home from the other direction. Rather than corrupting data, it swapped an unprivileged credential structure for a privileged one — exploiting how the kernel manages the cred objects that decide who you are. It generalised a technique so that many otherwise-unrelated bugs could be escalated to root the same way. The lesson: attackers don’t just find bugs, they find reusable exploitation primitives that turn a dozen mediocre bugs into a dozen root shells.
3 The defensive lessons that actually transfer
So what do you, running real machines, take from all this? Not “panic at every CVE.” These:
Patch latency is the whole game. Dirty Pipe was fixed almost immediately; the people who got burned were running unpatched kernels weeks later. The exposure window is entirely about how fast you reboot into a fixed kernel. A disciplined reboot cadence isn’t hygiene theatre — it’s the single biggest lever you have.
# the unglamorous question that decides everything:
uname -r # what kernel are you ACTUALLY running?
# vs what's installed and waiting for a reboot:
apt list --installed 2>/dev/null | grep linux-imageIf those two disagree, you have a fixed kernel on disk and a vulnerable one in memory. That gap is where people get owned.
Assume local code can become root. Every one of these bugs reframes “but they only have a low-privilege shell” as “they have root, give it twenty minutes.” Design as if local access equals total compromise. That means:
- Don’t run untrusted code on hosts that matter. A kernel LPE inside a container is, very often, root on the host. Containers share the host kernel — that’s the bargain, and it’s exactly the surface these bugs attack.
- Reduce who has local access at all. Fewer accounts, fewer running services, fewer setuid binaries lying around to be hijacked.
- Use the kernel’s own seatbelts. seccomp profiles that block the syscalls an exploit needs, and Mandatory Access Control (AppArmor, SELinux) that confines a compromised process, can turn “instant root” into “blocked, and logged.” They don’t fix the bug; they shrink what a single bug buys the attacker.
Defence in depth, because the kernel will fail again. This is the honest one. There will be a Dirty Something in 2026, and 2027. The kernel is twenty-odd million lines of C, and C plus that much surface area guarantees more of these. Planning as though the kernel is a perfect security boundary is planning to be surprised. Planning as though it will eventually be breached — and ensuring that breach is contained, detected, and short-lived — is planning that survives contact with reality.
4 The verdict
These exploits aren’t trivia. Each one is a free lesson, paid for by other people’s incident response. The takeaway isn’t to memorise CVE numbers. It’s three habits: patch fast and reboot, treat any local foothold as a probable root compromise, and confine everything so that when the kernel does fail — and it will — the damage is bounded and noisy rather than total and silent.
Who is this for? Anyone running multi-tenant boxes, containers, or anything that executes code they didn’t fully write. Which, again, is everyone. The kernel is doing its best. Your job is to plan for the day its best isn’t enough.




