On April 29, 2026, the offensive security firm Theori published a full write-up of a local privilege escalation vulnerability they had been sitting on just long enough to coordinate a patch. The vulnerability is tracked as CVE-2026-31431, carries a CVSS score of 7.8 (HIGH), and has been given the name Copy Fail by Theori and the Xint Code research team. A working exploit ships as a 732-byte Python script and reliably roots Ubuntu, Amazon Linux, RHEL, and SUSE on essentially any kernel built since mid-2017. No race condition. No kernel offset required. Same script, every distro.
This article follows the same two-part format we use for all our CVE breakdowns: a plain-language executive summary first, then a full technical dissection for readers who want to understand the bug from first principles. At the end, we discuss what this class of vulnerability means in practice for teams running Linux workloads in cloud and containerized environments, and how the Pragma Core platform addresses the underlying detection and prevention challenges.
Part I. Executive breakdown
What happened
Researchers at Theori, using an AI-assisted code analysis tool called Xint Code, spent roughly one hour scanning the Linux kernel's crypto/ subsystem before surfacing a logic flaw introduced in August 2017. The flaw lives in algif_aead, a kernel module that exposes AEAD (Authenticated Encryption with Associated Data) ciphers to unprivileged userspace programs through AF_ALG sockets.
The short version: when a 2017 performance optimization switched AEAD operations from out-of-place to in-place processing, it accidentally allowed page-cache pages from userspace-controlled files to end up inside a kernel-writable scatterlist. An unprivileged process can then splice() a file it only has read access to into the crypto socket, trigger a decrypt operation, and watch the kernel write four controlled bytes directly into the page cache of that file, bypassing the file's permissions entirely.
Four bytes sounds modest. It is more than enough.
Who is affected
| Distribution / Component | Status |
|---|---|
| Ubuntu (all versions shipping kernel >= 4.14) | Vulnerable. Apply available kernel updates. |
| Amazon Linux 2023 | Vulnerable. AWS has issued patched kernel packages. |
| RHEL 10.1 | Vulnerable. Red Hat has published an advisory. |
| SUSE 16 | Vulnerable. SUSE has issued patched packages. |
| Debian / downstream distros on affected kernels | Vulnerable. Check distribution advisories. |
| Linux kernel >= 6.18.22 or >= 6.19.12 | Patched. |
| Kernels built with AF_ALG disabled | Not exploitable via this path. |
Any Linux system running a kernel between 4.14 and the patched versions above is potentially vulnerable if AF_ALG is enabled. The vast majority of general-purpose Linux installations have AF_ALG enabled.
Why this matters more in cloud and Kubernetes environments
The page cache is shared at the host level. A process running inside a container does not get its own page cache: it reads from and writes to the same page cache pages the host kernel uses. This has two concrete implications.
First, a compromised container can use this vulnerability to corrupt files on the host that it would normally have zero write access to, including setuid binaries owned by root. Second, in a multi-tenant environment running multiple containers on a single node, a write from inside one container can affect the page cache seen by processes running in other containers on the same host.
This takes a "local privilege escalation" vulnerability and turns it into something that behaves closer to a container escape or lateral movement primitive in the right environment.
Recommended actions
-
Patch immediately. Update to kernel 6.18.22 or 6.19.12 or later. Check your distribution's security advisories for backported fixes to older LTS kernels. This is the only complete remediation.
-
Check if AF_ALG is accessible. On systems where you control the build, verifying that
CONFIG_CRYPTO_USER_API_AEADis not enabled eliminates the attack surface entirely. On standard distribution kernels this is almost always enabled. -
Detect with runtime rules. The exploit requires opening an
AF_ALGsocket of typeSOCK_SEQPACKETand binding toauthencesn(hmac(sha256),cbc(aes)). No legitimate application outside a narrow set of disk-encryption tools (cryptsetup, veritysetup, and a few others) does this. Any process outside that known list creating anAF_ALG SEQPACKETsocket is a high-confidence detection signal worth treating as critical. See the detection section below for a full Falco rule. -
Disable unprivileged namespace creation where possible. While this is not a primary mitigation for this specific vulnerability, it narrows the broader unprivileged-user attack surface on systems where kernel features like AF_ALG are accessible to all users.
-
Inventory your container workloads. In Kubernetes and cloud environments, identify which nodes are running unpatched kernels and prioritize them for rolling restarts or live patching.
Part II. Technical breakdown
Background: what algif_aead is and why it exists
The Linux kernel exposes its cryptographic primitives to userspace through the AF_ALG socket family. Programs can open an AF_ALG socket, bind it to a named algorithm (e.g., authencesn(hmac(sha256),cbc(aes))), and then perform crypto operations entirely in kernelspace, without having to ship their own crypto implementation in userspace. This is used by tools like cryptsetup and dm-crypt for disk encryption.
Within AF_ALG, algif_aead handles AEAD (Authenticated Encryption with Associated Data) operations. AEAD ciphers produce a ciphertext plus an authentication tag; decryption fails with EBADMSG if the tag does not verify. The important word there is "fails": decryption may fail from the caller's perspective, but the question Copy Fail asks is whether any work was already done before the failure was returned.
The 2017 in-place optimization and where it went wrong
Before August 2017, algif_aead performed AEAD operations out-of-place: source and destination scatterlists were distinct allocations. Commit 72548b093ee3 changed this to set req->src = req->dst and chain the source's tag pages into the destination scatterlist with sg_chain().
The intent was reasonable: avoid a copy by operating on the same memory in place. The problem is that when userspace uses splice() to feed a file into the crypto socket, the pages that end up in the source scatterlist are not a private copy. They are the actual page-cache pages backing the spliced file. With in-place processing, those same page-cache pages now end up in the writable destination scatterlist.
authencesn(hmac(sha256),cbc(aes)) includes logic to rearrange Extended Sequence Numbers. During a decrypt operation, it writes four bytes of scratch data at offset assoclen + cryptlen in the output. Because the output scatterlist now extends into the chained page-cache pages, that four-byte scratch write lands inside the cached data of the spliced file, in kernel memory, without going through any permission check, without waking up any inotify watcher, without triggering any write audit event.
The authentication tag check fails after this write, not before. The write has already happened by the time EBADMSG is returned to the caller.
The exploit primitive and how it becomes root
The raw primitive is: write four controlled bytes at a known offset inside the page cache of any file the attacker can open for reading, regardless of whether the attacker has write permission on that file.
The public proof of concept (theori-io/copy-fail-CVE-2026-31431) chains three steps to turn this into an interactive root shell:
Step 1: Identify the target offset in /etc/passwd.
The UID field of a user entry in /etc/passwd is a short decimal string. For a standard user with UID 1000, the entry looks like:
username:x:1000:1000::/home/username:/bin/bash
The exploit finds the byte offset of the four characters 1000 within the file using a straightforward search. The target is to overwrite that four-character field with 0000.
Step 2: Perform the page cache write via AF_ALG + splice.
# Open AF_ALG socket and bind to AEAD algorithm
sock_fd = socket(AF_ALG, SOCK_SEQPACKET, 0)
bind(sock_fd, {ALG_TYPE="aead", ALG_NAME="authencesn(hmac(sha256),cbc(aes))", ...})
# Set key, IV, and other parameters
setsockopt(sock_fd, SOL_ALG, ALG_SET_KEY, key)
op_fd = accept(sock_fd) # operations socket
# Send 8 bytes of AAD with seqno_lo set to the target marker (b"0000")
sendmsg(op_fd,
data=[8-byte AAD],
cmsg=[ALG_SET_OP=DECRYPT, ALG_SET_IV, ALG_SET_AEAD_ASSOCLEN=8],
flags=MSG_MORE
)
# Splice the target file's page cache into the pipe and into the crypto socket
pipe_r, pipe_w = pipe()
splice(target_fd, pipe_w, nbytes=32, offset=file_offset) # page cache page enters pipe
splice(pipe_r, op_fd, nbytes=32) # pipe content enters AF_ALG
# Drive the decrypt operation. EBADMSG is expected; the write has already fired.
recv(op_fd) # returns EBADMSG
At this point, the four bytes 0000 have been written into the page-cache page of /etc/passwd at the offset corresponding to the user's UID field. The on-disk file is untouched. Only the in-memory page cache has been modified.
Step 3: Gain root via su.
The exploit calls pwd.getpwnam(username) to confirm that libc is now reading UID 0 for the target user (because getpwnam reads from /etc/passwd, which is served from the modified page cache). Then it calls execvp("su", ["su", username]). The user enters their own password. PAM validates it against /etc/shadow (which is untouched). Then PAM calls setuid(getpwnam(username).pw_uid), which resolves to 0. The shell opens as root.
The page cache modification persists until either the page is evicted (the exploit can trigger this itself via POSIX_FADV_DONTNEED) or the system is rebooted.
The complete exploit flow end-to-end
unprivileged process
|
v
Open AF_ALG SEQPACKET socket, bind to authencesn(hmac(sha256),cbc(aes))
|
v
Find byte offset of UID "1000" in /etc/passwd page cache
|
v
sendmsg(8-byte AAD with PWND marker, MSG_MORE)
|
v
splice(/etc/passwd page_cache_page -> pipe -> AF_ALG op socket)
|
v
recv() -> EBADMSG [auth fails AFTER 4-byte scratch write fires]
|
v
/etc/passwd page cache now shows UID 0000 for target user
|
v
su <username> -> enter own password -> PAM resolves UID 0 -> root shell
What makes this reliable and cross-distribution
Three properties make Copy Fail substantially more dangerous than the average kernel LPE:
No race window. The write happens on the execution path of a single-threaded crypto operation. There is no concurrent state to win, no TOCTOU to exploit, no retry loop needed. The primitive either works or it does not, and on a vulnerable kernel it works every time.
No kernel offsets or ASLR bypass required. The exploit works entirely through documented kernel interfaces (AF_ALG, splice()). It does not require knowing any kernel addresses, does not attempt to corrupt kernel data structures, and does not use any ROP chain. The only address it needs is a file offset within a userspace file it can already read.
Single script, all major distributions. The same Python script targets Ubuntu 24.04, Amazon Linux 2023, RHEL 10.1, and SUSE 16 without modification. Any distribution shipping a kernel between 4.14 and the patched versions is affected.
Detection
The mandatory first step of the exploit is opening an AF_ALG socket of type SOCK_SEQPACKET. Normal AF_ALG users (hashing, symmetric crypto) use SOCK_DGRAM. The SEQPACKET type is required only for AEAD operations, which is a small, well-known set of disk-encryption binaries.
The following Falco rule detects any process outside that known list creating an AF_ALG SEQPACKET socket, and treats it as a critical alert:
- list: known_af_alg_binaries
items: [cryptsetup, systemd-cryptsetup, veritysetup, integritysetup,
cryptsetup-reencrypt, kcapi-enc, kcapi-dgst, kcapi-rng, kcapi-sym]
- macro: successful_af_alg_seqpacket_socket
condition: >
evt.type = socket and evt.rawres >= 0
and (evt.arg.domain contains AF_ALG or evt.rawarg.domain = 38)
and (evt.arg.type = 5 or evt.arg.type = 2053
or evt.arg.type = 524293 or evt.arg.type = 526341)
- rule: Unexpected Process Using Kernel AEAD Crypto Socket
desc: >
Detects creation of an AF_ALG SEQPACKET socket from a process outside
the known disk-encryption toolchain. This is the mandatory first step
of CVE-2026-31431 (Copy Fail).
condition: >
successful_af_alg_seqpacket_socket
and not proc.name in (known_af_alg_binaries)
output: >
Unexpected process %proc.name opened AF_ALG AEAD kernel crypto socket
(socket.domain=%evt.arg.domain socket.type=%evt.arg.type
proc.pid=%proc.pid container.name=%container.name
image=%container.image.repository:%container.image.tag
proc.cmdline=%proc.cmdline)
priority: CRITICAL
tags: [host, container, kernel, cve, CVE-2026-31431,
MITRE_TA0004_privilege_escalation, MITRE_T1068]
The patch
The upstream fix (commit fafe0fa2995a) reverts the 2017 in-place optimization. AEAD operations go back to using separate source and destination scatterlists. Page-cache pages spliced into the source scatterlist no longer appear in the writable destination scatterlist, and the four-byte scratch write can no longer reach files the caller does not own.
The regression was introduced in commit 72548b093ee3 in August 2017 and went undetected for nearly nine years.
A note on how this was found
Theori states in their write-up that Copy Fail was surfaced by Xint Code, their AI-assisted code analysis system, in roughly one hour of scan time against the Linux crypto/ subsystem. The disclosure describes it as "one operator prompt, no harnessing." Whether or not that characterization holds up to scrutiny in every detail, the core message is significant: a focused AI scan of a well-audited open-source subsystem produced a high-severity bug that had survived nine years of manual review, multiple rounds of fuzzing, and continuous integration testing. The cost of this class of audit is dropping, and the Linux kernel is not the last target where similar approaches will work.
Timeline
| Date | Event |
|---|---|
| August 2017 | Commit 72548b093ee3 introduces in-place AEAD optimization in algif_aead. |
| Early April 2026 | Upstream patch series ending with commit fafe0fa2995a is merged. |
| April 29, 2026 | Theori publicly discloses CVE-2026-31431 (Copy Fail). Working PoC released. |
| April 29, 2026 | Distribution advisories published by Red Hat, SUSE, Canonical, Amazon. |
| April 30, 2026 | Working exploits confirmed on Ubuntu 24.04, Amazon Linux 2023, RHEL 10.1, SUSE 16. |
| May 1, 2026 | Microsoft publishes cloud impact analysis. Kubernetes workload guidance issued. |
What we should take away from Copy Fail
A few patterns emerge from this vulnerability, and they recur across the class of bugs that are hardest to catch with conventional static analysis:
An optimization can be a vulnerability. The 2017 change was a legitimate performance improvement. It was correct in isolation. The problem was that it introduced a new data flow path that connected userspace-spliced page-cache pages to a kernel-writable scatterlist, and that connection was not visible to anyone reviewing the change in isolation without tracing the full execution path through authencesn.
Errors-before-checks are underappreciated. A lot of security thinking about crypto operations focuses on "does the auth tag verify?". Copy Fail demonstrates that asking only that question misses the possibility that writes happen before the check fails. Any code path where work is done prior to a validation that may return an error deserves scrutiny for what that work touches.
Page cache sharing is an underappreciated multi-tenancy boundary. Container isolation is commonly discussed in terms of namespace and cgroup separation. The page cache does not appear in most threat models as a potential data flow between containers and the host. It should.
AI-assisted code analysis is now a practical offensive research tool. The cost of auditing large, well-maintained open-source codebases for this class of subtle data-flow bug is no longer limited to organizations with large dedicated research teams. This shifts the risk calculation for any codebase that has not been audited with modern tooling.
How Pragma Core addresses this class of problem
Copy Fail was not found by reading a single function and spotting a bad input. It was found by tracing a data flow: a page-cache page enters a userspace API, passes through an optimization that redirected it to a writable scatterlist, and ends up modified by a crypto scratch operation. Conventional SAST tools almost never trace data flows across kernel subsystem boundaries. Even for application-level code, most scanners do not trace flows across function call chains of this depth.
Here is how the Pragma Core platform connects to the problems this CVE represents.
Autonomous AI agents for deep data flow investigation
The autonomous research agents in Pragma Core do not stop at individual function analysis. They reason about how data moves across code boundaries: where does this value come from, where does it end up, what assumptions does each component along the path make about the other? That is precisely the thinking that exposed Copy Fail. For your own codebase, the same approach applied to data flows crossing trust boundaries, or to code paths where output of an operation is used before a validation is checked, surfaces the kind of vulnerability that survives conventional audits.
Continuous SCA and vulnerability tracking across your fleet
For teams running Linux workloads in cloud or Kubernetes environments, the immediate question after a disclosure like this is: which hosts are running a vulnerable kernel, and how fast can we roll out the patch? Pragma Core tracks package and dependency versions across all connected repositories and surfaces CVE matches the moment they enter the catalog, with remediation guidance and CVSS context. No manual monitoring of distribution security mailing lists required.
Runtime-aware findings, not just static findings
A vulnerability like Copy Fail is only exploitable if AF_ALG is accessible from the attacker's context. Pragma Core connects static findings to runtime context: whether a component is actually reachable, whether the conditions for exploitation are met in your specific deployment. That means fewer "theoretically vulnerable" findings and more findings you can act on.
Infrastructure penetration testing with full host coverage
The Pragma Core infrastructure pentest module runs agentic tests against live environments, including Linux hosts, with coverage for local privilege escalation primitives. A post-disclosure check against your own environment can confirm whether Copy Fail is exploitable in your specific kernel and configuration before you rely on the patch alone.
Closing thoughts
CVE-2026-31431 is a reminder that the Linux kernel's attack surface is both enormous and largely trusted. Nine years is a long time for a bug to sit in a production subsystem used on almost every Linux server in the world, including every major cloud provider's VM fleet and essentially every Kubernetes node. The fact that it was found by an AI scan in one hour, rather than by a human reviewer in nine years, is not an indictment of the kernel security team. It is a signal about what automated reasoning over large codebases can now do, and what that means for the offense-defense balance in vulnerability research.
The patch is out. Apply it. If you want to talk about what proactive coverage looks like for the rest of your stack, you can reach us at pragma-core.com.
Sources
- Theori / Xint Code, Copy Fail: CVE-2026-31431 Linux Kernel Local Privilege Escalation, April 29, 2026.
- Sysdig Threat Research Team, CVE-2026-31431: "Copy Fail" Linux kernel flaw lets local users gain root in seconds, April 29, 2026.
- Bugcrowd / David Brumley, What we know about Copy Fail (CVE-2026-31431), April 30, 2026.
- Microsoft Security Blog, CVE-2026-31431: Copy Fail vulnerability enables Linux root privilege escalation across cloud environments, May 1, 2026.
- The Hacker News, New Linux "Copy Fail" Vulnerability Enables Root Access on Major Distributions, April 30, 2026.
- Help Net Security, Nine-year-old Linux kernel flaw enables reliable local privilege escalation (CVE-2026-31431), April 30, 2026.
- GitHub, rootsecdev/cve_2026_31431: Exploit PoC for CVE-2026-31431.
- NVD, CVE-2026-31431 Detail.
- Red Hat Security Advisory, CVE-2026-31431.