How a forgotten crypto optimization, a single scratch write, and one very angry algorithm turned every Linux box since 2017 into a sitting duck.
Picture this: you're a regular user on a Linux server. No sudo. No special caps. No clever container tricks. You open a socket, splice a file, send a message, and boom.. you're root. Not after ten tries. Not with a race condition that crashes the box half the time. One shot. Every time. On Ubuntu. On RHEL. On Amazon Linux. On SUSE. Same script. Same result.
This isn't a hypothetical. This is Copy Fail (CVE-2026-31431), and it is arguably the cleanest, most reliable local privilege escalation bug Linux has seen in years. A 732-byte Python script, smaller than this paragraph, can root essentially every Linux distribution shipped since 2017 . No offsets. No recompilation. No version checks. Just four syscalls and a logic flaw that slept in the kernel for nearly a decade.
If you run multi-tenant systems, Kubernetes clusters, CI runners, or anything where untrusted code gets a shell, you need to understand this. Because Copy Fail isn't just an LPE. It's a container escape primitive that laughs at namespace boundaries.
The Setup: Three Innocent Features Walk Into a Bar
To understand Copy Fail, you need to meet the three kernel subsystems that accidentally conspired to create it.
First, AF_ALG. This is Linux's way of letting unprivileged userspace tap into the kernel's cryptographic engine. Want to AES-encrypt something without shipping your own crypto library? Open an AF_ALG socket, bind to an algorithm, and off you go. It's been around for years, enabled by default on virtually every distro, and completely harmless, or so everyone thought.
Second, splice(). This is one of Linux's cleverest performance tricks. Instead of copying data between a file and a pipe (and then from the pipe to somewhere else), splice() passes page cache references by pointer. The actual bytes never leave kernel memory. It's fast. It's elegant. And when you splice a file into an AF_ALG socket, the socket's input scatterlist ends up holding direct references to the kernel's cached pages of that file.
Third, authencesn. This is a niche AEAD (Authenticated Encryption with Associated Data) template used by IPsec for Extended Sequence Numbers. It's been in the kernel since 2011. And it has a dirty little secret: during decryption, it uses the caller's destination buffer as scratch space to rearrange some bytes. It writes four bytes past the legitimate output boundary, assuming nobody cares what happens there.
Individually, these three features are fine. Together? They're a root shell waiting to happen.

The Bug: When "In-Place" Means "In the Wrong Place"
In 2017, a well-meaning kernel developer added a performance optimization to algif_aead.c (commit 72548b093ee3). The idea was simple: for AEAD operations, operate "in-place" by setting the source and destination scatterlists to the same memory. Why copy data around if you're decrypting into the same buffer?
Here's what that optimization actually did:
When you splice a file into an AF_ALG socket, the file's page cache pages land in the input scatterlist. The 2017 code then copied the AAD and ciphertext into the user's receive buffer, but chained the authentication tag pages by reference onto the end of the output scatterlist using sg_chain(). It then set req->src = req->dst, making them one and the same.
So now the writable destination scatterlist looked like this:
[ User's RX buffer (AAD + ciphertext) ] --> [ Tag pages (still pointing to the file's page cache) ]
The tag region wasn't copied. It was referenced. And because req->src and req->dst were identical, the kernel treated the whole chain as a single in-place buffer .
Enter authencesn. During decryption, it needs to shuffle IPsec's Extended Sequence Number bytes. It reads the AAD, rearranges some fields, and then, here comes the critical part, writes 4 bytes at offset assoclen + cryptlen, past the end of the legitimate output, into what it assumes is harmless scratch space .
But in the AF_ALG in-place path, that offset walks straight past the user's RX buffer and into the chained tag pages. Which are page cache pages. Of whatever file you spliced in. The kernel maps the page, writes your 4 bytes, unmaps it, and moves on. The HMAC verification fails (because the ciphertext is garbage), recvmsg() returns an error, and the user sees... nothing. But the page cache is now corrupted .
Four bytes. Any readable file. Any offset. Any value. Deterministically. With no race condition.

The Exploit: 732 Bytes of "Oops, All Root"
The public proof-of-concept is almost insultingly small. It's a Python script using only os, socket, and zlib, so standard library only, no compiled payloads, no dependencies.
Here's the strategy in plain English:
- Open an
AF_ALGsocket and bind it toauthencesn(hmac(sha256),cbc(aes)). Set a key. Accept a request socket. This requires zero privileges; any user can do it. - For each 4-byte chunk of shellcode you want to inject, construct a
sendmsg()+splice()pair. Thesendmsg()provides the AAD, where bytes 4–7 become the 4 bytes you'll write. Thesplice()feeds pages from/usr/bin/su(or any setuid binary) into the socket as the ciphertext and tag . - Call
recv(). This triggers the decrypt operation. Inside the kernel,authencesnwrites your controlled 4 bytes into the page cache of/usr/bin/su. The HMAC fails. The syscall returns an error. The corruption persists. - Repeat for every 4-byte chunk of your payload until you've patched the binary's
.textsection in memory. - Run
su. The kernel loads the binary from the page cache. Your shellcode is there. Becausesuis setuid-root, your code runs as UID 0.
The entire script is 732 bytes. It works on Ubuntu 24.04 LTS, Amazon Linux 2023, RHEL 10.1, and SUSE 16 without modification and, by extension, on virtually every distro running a kernel from 2017 to the present.

Why This One Hits Different
Linux gets local privilege escalation bugs all the time. So why is everyone from Microsoft to Berkeley's security office sounding the alarm?
Because Copy Fail has four properties that almost never appear together:
| Typical Linux LPE | Copy Fail |
|---|---|
| Needs a race condition | None — straight-line code |
| Needs per-distro offsets | None — same script everywhere |
| 30–80% reliability | 100% — single shot, always works |
| Narrow kernel window | 2017 → 2026 — nearly a decade |
It's portable. No recompilation. No kallsyms scraping. No guessing where commit_creds lives in memory. One script, every distro.
It's stealthy. The write bypasses the VFS entirely. The corrupted page is never marked dirty by the kernel's writeback machinery. The file on disk is untouched. sha256sum /usr/bin/su on disk returns the original hash. inotify never fires. A forensic disk image shows a pristine binary. Only the in-memory page cache is poisoned, and that evaporates on reboot or cache eviction.
It's cross-container. The page cache is shared across the entire host — including across container namespaces. A pod with no special capabilities can corrupt the page cache of a host setuid binary and break out. This isn't just an LPE; it's a Kubernetes node compromise vector.
Compared to its infamous cousins: Dirty Cow (CVE-2016-5195) needed to win a race in the copy-on-write path, often requiring multiple attempts and occasionally panicking the kernel. Dirty Pipe (CVE-2022-0847) was version-specific and required precise pipe buffer manipulation. Copy Fail needs none of that.
Who's Affected? (Spoiler: Probably You)
If your kernel was built between 2017 and April 2026, you're in scope. The researchers directly verified exploitation on:
| Distribution | Kernel |
|---|---|
| Ubuntu 24.04 LTS | 6.17.0-1007-aws |
| Amazon Linux 2023 | 6.18.8-9.213.amzn2023 |
| RHEL 10.1 | 6.12.0-124.45.1.el10_1 |
| SUSE 16 | 6.12.0-160000.9-default |
But the list doesn't stop there. Debian, Fedora, Arch, Rocky, Alma, Oracle, embedded builds — if it runs Linux 4.14+ with AF_ALG enabled (which is nearly everything), it's vulnerable.
Highest risk: Multi-tenant hosts, Kubernetes clusters, CI/CD runners (GitHub Actions self-hosted, GitLab runners), cloud SaaS that executes user code, notebook hosts, and sandbox environments. Anywhere an unprivileged user or untrusted container can run code.
Lower risk: Single-user laptops. The bug doesn't grant remote access by itself, but any local code execution (malicious PDF, compromised dev tool, infected npm package) instantly becomes a full root compromise.
The AI Angle: Found in an Hour
Here's the part that should make every security researcher simultaneously excited and deeply uncomfortable.
Copy Fail was found by Xint Code, an AI-assisted security scanning tool built by Theori (the same folks who've won DEF CON CTF nine times). Researcher Taeyang Lee supplied a single operator prompt:
"This is the linux crypto/ subsystem. Please examine all codepaths reachable from userspace syscalls. Note one key observation: splice() can deliver page-cache references of read-only files (including setuid binaries) to crypto TX scatterlists."
Xint Code scanned the entire crypto/ subsystem. One hour later, it surfaced Copy Fail as the highest-severity finding — along with other bugs still under coordinated disclosure.
To put this in perspective: a high-end Linux zero-day on the gray market can fetch anywhere from $10,000 to $7,000,000, with universal, reliable primitives like this sitting at the top of that range . An AI system found one in roughly the time it takes to watch an episode of The Office.
Mitigation: Patch, Block, or Both
Option 1: Patch (Best)
Update your kernel to a fixed version: Linux 7.0, 6.19.12+, 6.18.22+, or your distribution's backported equivalent. The fix (mainline commit a664bf3d603d) reverts algif_aead to out-of-place operation, eliminating the page-cache-chaining behavior entirely.
Option 2: Disable algif_aead (Immediate)
If you can't patch right now, blacklist the module:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf
rmmod algif_aead 2>/dev/null
What breaks? For the vast majority of systems, nothing. dm-crypt/LUKS, kTLS, IPsec in-kernel, OpenSSL default builds, SSH, and the kernel keyring all use the crypto API directly — they don't touch AF_ALG. The only things affected are applications explicitly configured to use AF_ALG (like OpenSSL with the afalg engine enabled). You can check with lsof | grep AF_ALG or ss -xa.
Option 3: Seccomp (For Containers)
Block AF_ALG socket creation via seccomp profiles for untrusted workloads. This is a good permanent defense-in-depth measure regardless of patch status.
Detection
Standard file integrity tools like AIDE or Tripwire can catch the corruption — but only while it's hot in cache. Since the on-disk file is untouched, a hash check of the disk image will pass. IMA appraisal in enforcing mode catches it at execve time. Runtime tools like Falco can flag unexpected AF_ALG SEQPACKET socket creation from unknown processes.
The Bottom Line
Copy Fail is what happens when three reasonable design decisions — splice() zero-copy, AF_ALG user crypto, and an in-place optimization — collide with one algorithm's bad habit of scribbling past its buffer. Each change made sense in isolation. Together, they created a universal root primitive that sat silently exploitable for nine years.
The exploit is 732 bytes. It requires no privileges. It works on every major distro. It crosses container boundaries. It leaves no forensic trace on disk. And it was found by an AI in an hour.
If you're running Linux and you haven't patched yet, go patch. If you run shared infrastructure and you haven't audited who can open AF_ALG sockets, go audit. And if you thought kernel LPEs were rare, expensive, hard-to-find bugs — it's probably time to update that threat model.
Because the next Copy Fail might not take nine years to find. It might take nine minutes.
References & Further Reading
- copy.fail — Official disclosure site with PoC
- Xint Code Technical Write-up — Root cause, scatterlist diagrams, exploit walkthrough
- Microsoft Security Blog Analysis — Cloud impact assessment
- Sysdig Threat Research — Detection rules and technical breakdown
- Bugcrowd Analysis — Threat model and response priorities
- UC Berkeley Security Advisory — Academic institution alert