## https://sploitus.com/exploit?id=095F87FD-8A9C-5F44-A765-7FB67BF585A3
# CVE-2026-31431: Copy Fail - AF_ALG Page Cache Corruption
A 587-byte static ELF that exploits CVE-2026-31431 to get root from any local
user. No heap spray, no race, no ROP. Writes shellcode directly into a suid
binary's page cache through a kernel bug in the AF_ALG splice path.
| | |
|------------|-----------------------------------------------------------------|
| CVE | CVE-2026-31431 |
| Bug class | Page cache corruption via splice aliasing |
| Root cause | `af_alg_sendpage` / splice into AEAD request aliases the page cache of the source file |
| Component | `crypto/af_alg.c` + `crypto/algif_aead.c` |
| Impact | Arbitrary 4-byte write to any file's page cache (bypasses all permissions) |
| Required | Local user. No capabilities needed. |
| Exploit | 587-byte static ELF (x86_64), single file, no dependencies |
## The Vulnerability
The AF_ALG interface lets userspace perform kernel crypto operations over
sockets. For AEAD ciphers (like `authencesn`), the kernel accepts plaintext via
`sendmsg` with `MSG_MORE`, then additional data can be spliced in from a file
descriptor.
The bug: when you splice a file into an AF_ALG AEAD request, the kernel pins the
file's page cache pages directly into the crypto scatter-gather list. The AEAD
decryption operation then writes its output - the "decrypted" ciphertext -
back into those same pages. The kernel treats the splice source pages as a
read buffer, but the crypto layer treats them as a write buffer.
The result: the AEAD output overwrites the file's page cache. Any subsequent
read of that file (by any process, any user, including suid execution) sees
the corrupted data. The file on disk isn't modified - only the in-memory page
cache - so filesystem integrity checks won't catch it.
The attacker controls what gets written by controlling the "ciphertext" that
the AEAD operation "decrypts". With `authencesn(hmac(sha1),cbc(aes))`, the
first 4 bytes of the AAD (Associated Authenticated Data) in the sendmsg are
written verbatim to the splice offset after the AEAD processes the request.
## What is "Copy Fail"
"Copy Fail" (sometimes "Kopy Fail") refers to this class of Linux kernel
vulnerability where the copy-on-write / page cache isolation semantics fail.
The kernel is supposed to ensure that splicing from a file gives you a
read-only reference to the page. Instead, it hands the crypto subsystem a
writable reference to the original page cache entry.
The name is a play on copy-on-write ("COW") bugs like Dirty COW
(CVE-2016-5195) - same category of "the kernel let you write to something
you should only be able to read", different mechanism.
## Exploitation
The target is `/bin/su` - a suid-root binary present on every Debian-based
system. Its ELF entry point is at file offset `0x3910` (consistent across
Debian Bookworm builds, including kernelCTF rootfs).
The exploit writes 28 bytes of shellcode to that offset:
- `setuid(0)` - 7 bytes
- `execve("/bin/sh", NULL, NULL)` - 21 bytes
Since the AEAD primitive writes 4 bytes per operation (one 32-bit chunk of the
AAD lands at the splice offset), the exploit loops 7 times, writing 4 bytes
each iteration to offsets `0x3910` through `0x3928`.
Each iteration:
1. `socket(AF_ALG)` + `bind` with `authencesn(hmac(sha1),cbc(aes))`
2. `setsockopt` to set the key and auth tag size
3. `accept` to get the request fd
4. `sendmsg` with `MSG_MORE` - the 8-byte iov contains 4 bytes of AAD filler + 4 bytes of shellcode
5. `splice` from `/bin/su` through a pipe into the request fd (positions the page cache pages)
6. `recvfrom` - triggers AEAD processing, corrupts the page cache
7. Close everything (critical - stale AF_ALG state corrupts subsequent splices)
After all 7 iterations, the exploit `execve`s `/bin/su`. The kernel loads it
from the corrupted page cache. Execution jumps to the overwritten entry point.
`setuid(0)` + `execve("/bin/sh")` runs. Root shell.
## The Binary
587 bytes. Static ELF, no libc, no dynamic linker, no dependencies. Runs on
any x86_64 Linux kernel with the vulnerable AF_ALG code.
The size comes from:
- Custom ELF header with overlapping fields (e.g., `/bin/su\0` string stored
in the `e_shoff` field, crypto key material stored in `e_ident` padding)
- Single RWX PT_LOAD segment, BSS for the sockaddr_alg struct
- All syscalls via `push imm8 / pop rax / syscall` (3 bytes each)
- Shellcode stored as data, read by the main loop
- Register preservation across syscalls to avoid redundant setup
## Building
```
nasm -f bin -o copy_fail_v3 copy_fail_v3.asm && chmod +x copy_fail_v3
```
Requires NASM. Produces the exploit binary directly - no linking step.
## Usage
As any unprivileged user on a vulnerable system:
```
$ id
uid=1000(user) gid=1000(user) groups=1000(user)
$ ./copy_fail_v3
# id
uid=0(root) gid=0(root) groups=0(root)
```
The exploit takes under a second. No output on success - you're just dropped
into a root shell.
## Affected Kernels
Any kernel with the vulnerable `af_alg` splice path. The bug has been present
since the `algif_aead` splice support was added. Check whether your kernel
config includes `CONFIG_CRYPTO_USER_API_AEAD=y` (or `=m` with the module
loaded).
## Fix
The fix ensures splice source pages are properly copied (not aliased) when
used as AEAD output buffers, restoring the expected page cache isolation.
## Disclaimer
Published for educational and defensive research purposes. Do not use against
systems you do not own or have explicit authorization to test.
---
Daniel Wade - [GitHub](https://github.com/Rat5ak) · [Twitter/X](https://x.com/Nadsec11) · [Bluesky](https://bsky.app/profile/nadsec.online) · [nadsec.online](https://nadsec.online/)