Skip to content
hekke.io

Copy Fail: A Ten-Year-Old Path to Linux Root

03 MAY 20267 MIN READ
Copy Fail: A Ten-Year-Old Path to Linux Root

An unprivileged user. A 732-byte Python script. A setuid binary. Root.

That is the essence of CVE-2026-31431, disclosed on April 29 by the team at Xint Code and dubbed "Copy Fail." The vulnerability lets any local user escalate to root through a deterministic exploit that works across distributions, requires no race conditions, and fits in under 800 bytes of code. Microsoft's Defender team published an independent analysis two days later, confirming the same primitives and adding container-escape detail.

This code path has been in the Linux kernel since 2017. Every major distribution shipped it. Ubuntu 24.04, Amazon Linux 2023, Red Hat Enterprise Linux 10.1, SUSE 16. For nearly a decade, a single optimization in the kernel's cryptographic subsystem quietly became a 4-byte page-cache write primitive that any unprivileged process could trigger.

01. THE FLAW

The vulnerability lives in AF_ALG, the kernel's mechanism for exposing cryptographic algorithms to userspace through a socket interface. Instead of linking a userspace crypto library, applications can ask the kernel to AEAD-encrypt buffers directly through socket operations. It is a clean abstraction that avoids duplicating crypto implementations across userspace and kernel, and it has been part of mainline Linux since the early 4.x days.

In 2017, the algif_aead implementation gained an in-place optimization. When the source and destination buffers for a crypto operation could alias (point to the same memory), the code would skip an unnecessary copy. The assumption was that if source equals destination, you can safely encrypt or decrypt the data in place.

That assumption breaks when the destination buffer comes from splice(). The splice() system call can move data between file descriptors and pipes without copying through userspace, often by sharing the same physical pages. When an attacker constructs a pipe whose pages are also page-cache pages of an open file, the in-place operation writes directly into the page cache at an attacker-controlled offset.

The result is a controlled 4-byte write into the page cache for any file the attacker can open for reading. The disk content is untouched, but the kernel's view of the file changes, and every process that maps those pages afterward sees the modified bytes. Since the page cache also backs executables, this becomes arbitrary code execution the next time a privileged process loads the modified binary.

Why did this slip past review for nearly a decade? Because it was an optimization commit, not an obviously dangerous primitive. The vulnerability only emerges through the specific interaction between AF_ALG, splice(), and the page cache, a combination that requires understanding three separate subsystems to see the full attack surface.

02. THE EXPLOIT

The proof-of-concept from copy.fail targets /usr/bin/su by default, though any setuid root binary works. The script is 732 bytes of Python 3 using nothing outside the standard library, and it is deterministic. No timing attacks, no KASLR bypasses, no privileged setup on the kernel side.

The attack sequence is straightforward. First, open the target binary so its pages enter the page cache. Then construct a splice() setup where the destination pages are the same physical pages that back the executable. Use AF_ALG to request an in-place AEAD operation that writes into those shared pages, overwriting a few instructions in the binary with attacker-controlled data.

The next time any process executes the binary, the kernel maps the modified cached pages and runs the patched code. Since /usr/bin/su runs with setuid root privileges, the modified instructions execute as root. From there, the attacker can spawn a shell, copy /bin/bash into /tmp/, or write an SSH key into root's authorized_keys.

Three properties make this particularly dangerous. The write is deterministic, landing at the intended offset every time with no race to lose. It does not depend on kernel-build-specific information like symbol addresses or gadget offsets. And it works from inside unprivileged containers, writing to the host's page cache and compromising host binaries that any privileged host process might later execute.

03. THE BLAST RADIUS

The vulnerable code shipped in upstream Linux from 2017 onward, which means every active LTS branch and every recent stable release inherited it. Xint Code published verified results against Ubuntu 24.04 LTS (kernel 6.17), Amazon Linux 2023 (6.18.8), Red Hat Enterprise Linux 10.1 (6.12), and SUSE 16 (6.12). Other mainstream distributions running upstream-derived kernels in that window are reasonable to assume vulnerable until patched.

In practice that covers most server deployments, desktop installations, and developer machines. Roughly a decade of Linux releases, all sharing the same buggy algif_aead path.

Android escapes the bug, but not by deliberate design against this attack. The platform's SELinux policy denies AF_ALG socket creation to application UIDs as part of the standard untrusted_app profile, a hardening choice that predates Copy Fail by years. It happens to neutralize the attack vector entirely. A textbook example of defense in depth doing the job it was put in for.

The container angle is what makes this particularly damaging for cloud environments. Because the exploit writes to the host kernel's page cache, it works from inside unprivileged containers and affects host binaries. In shared-kernel multi-tenant environments, which includes most container orchestration platforms, a compromised container can use Copy Fail to compromise the host, then pivot to other tenants or the control plane.

CISA added CVE-2026-31431 to its Known Exploited Vulnerabilities catalog within days of public disclosure, indicating either active exploitation in the wild or high confidence that exploitation will follow quickly. The deterministic nature of the exploit and the universal 732-byte proof-of-concept make weaponization straightforward for any attacker with local access.

04. THE PATCH

Mainline commit a664bf3d603d removes the in-place optimization that gave attackers their write primitive. The fix is surgical. It forces the crypto operation to use separate source and destination buffers, eliminating the alias condition that triggered the bug.

For systems that cannot patch immediately, the recommended mitigation is blacklisting the algif_aead kernel module:

bash
echo "blacklist algif_aead" > /etc/modprobe.d/disable-algif-aead.conf

WARNING

Disabling algif_aead may break legitimate applications that depend on the kernel's AEAD crypto interface, including some TLS implementations and file encryption tools. Test thoroughly in non-production environments before deploying this mitigation.

Distribution patch availability is moving fast but uneven. Debian shipped fixed kernels first, with most other major distributions following in the days after disclosure. Older enterprise LTS lines like Red Hat Enterprise Linux and the older Ubuntu LTS releases tend to lag a little, since downstream kernels carry extensive backported patches that need integration testing before a security update lands.

AppArmor and SELinux profiles can provide interim hardening by constraining access to socket(AF_ALG, ...) for untrusted workloads. The approach takes careful policy work to avoid breaking legitimate applications, but it can effectively contain the attack surface while waiting for a kernel update.

05. WHERE IT STANDS

Page-cache attack primitives are not new. Dirty COW in 2016 exploited a race condition in copy-on-write handling to write to read-only memory mappings. Dirty Pipe in 2022 used pipe buffer reuse to splice arbitrary data into file pages. Copy Fail continues this lineage, and it might be the most ergonomic of the lot. No races to win, no offsets to calculate, one syscall chain to execute.

The pattern points to a deeper issue with the AF_ALG interface. The socket abstraction gives userspace far richer access to kernel-internal crypto state than most workloads actually need. Each new feature expands the attack surface, and the complexity of interactions between crypto operations and memory management creates opportunities for subtle bugs that can persist for years.

The honest question is whether AF_ALG should be enabled by default on systems that do not specifically require kernel crypto acceleration. The interface serves real use cases, including high-performance TLS termination, hardware crypto offload, and filesystem encryption, but most desktop and server workloads can use userspace crypto libraries without a meaningful performance penalty.

Credit belongs to Xint Code, with Brian Pak first announcing the bug publicly. The thoroughness of their disclosure (working proof-of-concept, affected version range, mitigation guidance, and CISA coordination) is the kind of careful work the field depends on. The fix is a single commit, but the path to root was a decade in the making.

SHAREX / TWITTERMASTODON