
Detector and proof-of-concept for a Linux kernel page-cache write vulnerability (CVE-2026-31431) in algif_aead, including a non-destructive scanner and a local privilege escalation exploit.
This repository contains detectors and a PoC for the Linux algif_aead / authencesn page-cache scratch-write vulnerability CVE-2026-31431.
Disclosure article: https://xint.io/blog/copy-fail-linux-distributions
Only use this on hosts you own or have explicit authorization to test. The detector is non-destructive and only operates on sentinel files in a temporary directory; the PoC and LPE modify in-memory page-cache state and are real local privilege escalation techniques — running them without authorization is typically illegal.
algif_aead performs AEAD operations in place, i.e. req->src == req->dst. When input data is fed from a regular file via splice(), the destination scatterlist contains references to that file's page-cache pages, allowing the kernel to write to the file's page-cache copy.
authencesn(hmac(sha256),cbc(aes)) performs a 4-byte scratch write to the seqno_lo field in the AAD (AAD bytes 4 through 7). Combined with the in-place AEAD and splice() path above, an attacker can cause these 4 bytes to land in the page-cache pages of a readable file.
This modification is not written back to disk; it only affects the current kernel page cache. The on-disk file content remains unchanged, but readers sharing that page cache will see the tampered content. World-readable files such as /etc/passwd and /usr/bin/su are potential targets.
| File | Description |
|---|---|
test_cve_2026_31431.py | Python non-destructive detector. Only operates on temporary sentinel files; does not touch system files. |
src/main.rs | Rust non-destructive detector. Detection logic and exit codes match the Python detector. |
src/bin/poc.rs | Rust version of poc.py. Pollutes the page cache of /usr/bin/su and executes su. |
poc.py | Python PoC. |
exploit_cve_2026_31431.py | Python LPE. Attempts to obtain a root shell by polluting the /etc/passwd page cache. |
Requires a local Rust toolchain and Cargo.
Build all Rust binaries:
cargo build --release --bins
After building, the following are produced:
target/release/cve_2026_31431_detector
target/release/cve_2026_31431_poc
If precompiled binaries are to be committed to the repository, place them at:
dist/linux-x86_64/cve_2026_31431_detector
dist/linux-x86_64/cve_2026_31431_poc
dist/linux-x86_64/SHA256SUMS
Do not commit the entire target/ directory; it contains Cargo intermediate artifacts and local caches.
Build only the non-destructive detector:
cargo build --release --bin cve_2026_31431_detector
Build only the Rust version of poc.py:
cargo build --release --bin cve_2026_31431_poc
The Rust non-destructive detector is recommended:
./target/release/cve_2026_31431_detector
echo $?
It can also be run directly via Cargo:
cargo run --release --bin cve_2026_31431_detector
echo $?
Python detector usage:
python3 test_cve_2026_31431.py
echo $?
Exit code meanings:
| Exit code | Meaning |
|---|---|
0 | No vulnerability detected, or prerequisites not met, e.g. AF_ALG / authencesn unavailable. |
2 | Detected as vulnerable to CVE-2026-31431. |
1 | Error during testing; the result cannot be used to determine vulnerability. |
Common output interpretation:
| Output | Conclusion |
|---|---|
检测结论:未发现漏洞 | No vulnerability detected, or trigger prerequisites not met. |
检测结论:存在 CVE-2026-31431 漏洞 | The marker PWND successfully landed in the spliced page-cache page; the vulnerability is present. |
检测结论:存在疑似 CVE-2026-31431 漏洞 | The page cache was modified; even if the marker did not land in the expected position, the system should be considered affected. |
测试过程出错,无法判断是否存在漏洞 | Detection failed with exit code 1; investigate the error message and rerun. |
Operations performed by the detector:
AF_ALG and authencesn(hmac(sha256),cbc(aes)) are available.sendmsg, setting the AAD's seqno_lo to the marker PWND.splice().recv() to drive algorithm execution.The detector does not modify /usr/bin/su, /etc/passwd, or other system files. The temporary sentinel file is deleted on exit.
Rust version of poc.py:
./target/release/cve_2026_31431_poc
Or:
cargo run --release --bin cve_2026_31431_poc
Note: Following the logic of poc.py, this PoC opens /usr/bin/su, pollutes its page-cache copy via the vulnerability primitive, and then executes su. This is not a non-destructive detection flow and should only be run in an authorized test environment.
python3 exploit_cve_2026_31431.py
python3 exploit_cve_2026_31431.py --shell
This script locates the current user's UID field in /etc/passwd and attempts to change the UID in the page cache to 0000. The on-disk /etc/passwd is not modified, but system processes reading the page cache may see the tampered content.
Limitations:
1000 to 9999.nscd, sssd, and systemd-userdbd may mask page-cache changes to /etc/passwd./etc/passwd page cache must remain present between patching and executing su.Clearing the page cache:
python3 -c "import os; fd=os.open('/etc/passwd', os.O_RDONLY); os.posix_fadvise(fd, 0, 0, os.POSIX_FADV_DONTNEED); os.close(fd)"
If a root shell has already been obtained, you can also run:
echo 3 > /proc/sys/vm/drop_caches
A reboot will also clear the page-cache state.
Until the distribution kernel is patched, algif_aead can be disabled:
sudo tee /etc/modprobe.d/disable-algif-aead.conf <<<'install algif_aead /bin/false'
sudo rmmod algif_aead 2>/dev/null
After applying the mitigation, the detector should typically output 检测结论:未发现漏洞 and state that the trigger prerequisites are not met, with exit code 0.
The upstream fix approach is to restore AEAD operations from in-place to non-in-place processing, preventing page-cache pages from entering the writable destination scatterlist.