CISA added CVE-2026-31431 — nicknamed “Copy Fail” — to its Known Exploited Vulnerabilities catalog on May 1. Eight days later it’s still under-discussed for what it is: a 732-byte Python script that gets you root on every Linux box running a kernel from 2017 onward, by writing arbitrary bytes into the page cache via AF_ALG. If you operate Linux, this is the patch-this-week story.
I’ve been sitting with the writeup for a couple of days. The headline number — 732 bytes — gets the traffic. The headline subsystem — AF_ALG, the kernel crypto socket family — is the more interesting story.
What actually breaks
The page cache is the kernel’s in-memory mirror of file contents. When you read /usr/bin/su, the kernel maps the on-disk file into RAM and returns those pages. The unstated assumption since 2007-or-so is that pages backing a privileged file can only be modified by privileged code paths — write(2) checks against the inode’s permissions, mmap(2) of an executable is shared-readonly. Every kernel reasoning about exec safety leans on that.
Copy Fail breaks the assumption. The flaw is in how AF_ALG sockets — the kernel’s user-facing crypto subsystem, used for things like dm-crypt and IPsec offload — handle a particular gather-write path. Through a sequence of legitimate sendmsg() + recvmsg() calls on a hash transform, an unprivileged user can land bytes inside a page that the kernel has cached for a privileged file. No suspicious syscalls. No exotic primitives. Three subsystem changes that landed in 2011, 2015, and 2017 line up just so, and the page-cache write isn’t checked against the file’s permissions.
The exploit pipeline:
- Open
/usr/bin/suread-only — the kernel populates its page cache. - Open an AF_ALG hash socket. Send a crafted scatter-gather buffer that the kernel “gathers” into the same page-cache page.
- The page cache now contains a crafted shellcode-prefix overwriting
su‘s entry sequence — but only in RAM. - Run
su. The kernel exec path reads from the page cache. Your bytes execute as root. - The dirty pages never hit disk; the on-disk binary is unchanged. Reboot, system looks fine.
Why detection is hard
The researchers’ line — “the exploit uses only legitimate system calls, which are hard to distinguish from normal application behavior” — is the part that should worry you more than the CVSS score. There’s no execve("/tmp/exploit", ...). There’s no SUID-binary copy. There’s no kernel module load. There’s a process, opening a file, doing some hash operations, then running su. Your auditd ruleset doesn’t have a rule for that, because nobody has needed one.
This is the larger pattern with modern Linux LPEs: they are page-cache bugs, prctl bugs, eBPF bugs, io_uring bugs. They live in subsystems that exist to make legitimate workloads faster, and they fire through legitimate API surface. EDR rules built for the 2018-era exploit kit don’t catch them.
What to do this week
- Patch the kernel. Fixed in 6.18.22, 6.19.12, and 7.0. Most distros have backports — Debian-stable, Ubuntu LTS, RHEL, Amazon Linux all have advisories out.
apt-get upgrade linux-image-$(uname -r | cut -d- -f3-)or your distro equivalent, then reboot. Yes, reboot. Live-patching is fine if you have it; this is the kind of bug where you want the new kernel actually running. - If you can’t reboot today, disable AF_ALG. Most production servers don’t need it — it’s used by dm-crypt during boot, and a few crypto-offload userspace clients (kTLS does not need it; it has its own path). Block it at runtime by loading a small bpf-lsm policy, or boot with
algif_hashand friends blacklisted in/etc/modprobe.d/. Test before deploying — your dm-crypt mounts may already have happened by the time the blacklist applies, but don’t take that on faith. - Audit container hosts. Default Docker and containerd configurations don’t restrict AF_ALG. Containers running on a vulnerable host kernel can use this path to escape to root on the host — the bug is in the kernel, and containers share the kernel. This is the multi-tenant Kubernetes nightmare scenario; if you run untrusted workloads on shared nodes, treat this as a node-replacement event, not an in-place patch.
Quick check for AF_ALG exposure
# Is the algif_* family loaded?
lsmod | grep ^algif
# Will it auto-load on use? (Most distros: yes, via netlink request)
ls /lib/modules/$(uname -r)/kernel/crypto/algif_*.ko* 2>/dev/null
# Quick "can an unprivileged user even open it" test:
sudo -u nobody perl -e '
use Socket; socket(S, 38, 5, 0) or die "open: $!"; print "opens OK\n";
'
# AF_ALG is family 38. If "opens OK" prints, an unprivileged user
# has the syscall path the exploit needs.
Run that on every Linux host you operate. If the second command finds modules and the third one prints “opens OK,” patch this week — not next sprint.
The takeaway
Two things stick with me. One: a privilege boundary that depended on three different commits over six years not interacting badly is not really a privilege boundary, and the page-cache assumption is exactly that kind of slow-cooked invariant. The Linux kernel has more of these than anyone wants to admit, and the rate of LPE disclosures over the last three years is the empirical evidence.
Two: 732 bytes of Python is a different thing than a polished CVE writeup with a logo. When the exploit fits in a single tweet’s worth of code, the gap between disclosure and “every actor that wants this has it” is hours, not weeks. The KEV listing on May 1 isn’t the floor of exploitation — it’s the floor of government noticing. Patch as if it’s been in the wild for a month, because it has.
Source: The Hacker News — CISA Adds Actively Exploited Linux Root Access Bug CVE-2026-31431 to KEV. Cover photo by Sejio402 on Pexels.
