Detection script for CVE-2026-31431 (Copy Fail) that checks kernel version, patch presence, kernel configs, AF_ALG socket availability, setuid binaries, and mitigations to determine vulnerability status on Linux systems.
Detection only. A working PoC already exists at copy.fail/#exploit. This script is for sysadmins and security teams determining where they are vulnerable — or still vulnerable after patching.
On April 29th 2026, a vulnerability called Copy Fail (CVE-2026-31431) was publicly disclosed by the Xint Code Research Team. It's a logic bug that has been quietly present in the Linux kernel since roughly 2017 — nearly a decade — and lets any unprivileged local user get root.
Not "get root under specific conditions with some luck and a good tailwind." Just... get root. Reliably. On essentially every major Linux distribution.
It affects Ubuntu, Amazon Linux, RHEL, SUSE, and anything else running a mainstream kernel from the last ~8 years. Same script, no recompilation, no per-distro tweaks required.
Yes, it's as bad as it sounds.
The Linux kernel has a crypto subsystem accessible to unprivileged users via AF_ALG sockets. There is a mechanism called splice() that can feed file data directly into that subsystem without copying it — which means the kernel's in-memory cached copy of a file (the "page cache") ends up inside a cryptographic operation.
One specific algorithm — authencesn, used for IPsec Extended Sequence Numbers — has a quirk where it uses the output buffer as scratch space and writes 4 bytes slightly past where it is supposed to. Normally harmless. But when page cache pages from a setuid binary like /usr/bin/su end up chained into that output buffer (thanks to a 2017 "optimisation" in algif_aead.c), those 4 bytes land directly in the kernel's cached copy of the binary.
The operation fails with an error. The kernel never marks that page as dirty. The on-disk file is untouched. File integrity tools checking on-disk checksums see nothing wrong.
But the page cache is what gets executed. And su is setuid root.
The full technical writeup is at xint.io and is genuinely worth reading.
Seven from the original release plus six new checks added to close detection gaps:
| # | Check | What it looks for |
|---|---|---|
| 1 | Kernel version | Is this kernel in the affected range (4.10–6.14)? |
| 2 | Patch presence | Is the fix commit actually in your running kernel? |
| 3 | algif_aead module | Is the vulnerable module loaded or loadable? |
| 4 | CONFIG_CRYPTO_AUTHENC (new) | Is CONFIG_CRYPTO_AUTHENC built-in (=y) or a module (=m)? This single option builds both authenc and authencesn. Built-in means the modprobe blacklist mitigation does nothing. |
| 5 | CONFIG_CRYPTO_USER_API_AEAD (new) | Is the AF_ALG AEAD userspace interface even compiled in? If not, the entire exploit path is closed at compile time. |
| 6 | AF_ALG socket | Can an unprivileged user open one right now? |
| 7 | Python os.splice | Is the pure-Python exploit path available? |
| 8 | Setuid binaries | Extended list of readable setuid-root targets present on the system. |
| 9 | Mitigations | AppArmor, SELinux, seccomp — what's in place? |
| 10 | User namespaces (new) | Are unprivileged user namespaces enabled? (Does not block Copy Fail directly, but affects the broader local privesc surface.) |
| 11 | Transparent hugepages (new) | THP status — can affect page cache alignment and exploit reliability. |
| 12 | Environment detection (new) | Docker/container/VM context — containers share the host kernel; the host is what needs patching. |
| 13 | Root-user warning (new) | Warns if running as root, since several checks give false positives for root regardless of unprivileged restrictions. |
The Bash script covers the same core detection logic but omits three Python-specific items:
| # | Check | Notes |
|---|---|---|
| 1 | Kernel version | |
| 2 | Patch presence | |
| 3 | algif_aead module | |
| 4 | AF_ALG socket | Uses Python as a helper if available; falls back to kernel config inference |
| 5 | Setuid binaries | Extended list, same as Python version |
| 6 | Mitigations | AppArmor, SELinux, seccomp |
| 7 | CONFIG_CRYPTO_AUTHENC | |
| 8 | User namespaces | |
| 9 | Transparent hugepages | |
| 10 | Environment detection |
Not present in shell script (vs Python):
| Missing check | Reason |
|---|---|
| CONFIG_CRYPTO_USER_API_AEAD | Not yet implemented — planned |
| Python os.splice availability | Not applicable to a shell script |
| Root-user warning | Not yet implemented — planned |
Neither script will fix anything or exploit anything. They tell you the truth about your system so you can act on it.
# Clone or download the script, then:
python3 cve-2026-31431-detect.py
That's it. Colour-coded report with a summary at the end.
The script exits with a non-zero code on vulnerable findings, making it suitable for pipeline use:
| Code | Meaning |
|---|---|
0 | No vulnerable conditions found |
1 | One or more vulnerable conditions found |
# Example: fail a CI step if the host is vulnerable
python3 cve-2026-31431-detect.py
rc=$?
if [ $rc -eq 1 ]; then
echo "VULNERABLE — block deployment"
elif [ $rc -ne 0 ]; then
echo "ERROR — script failed to complete (exit $rc)"
fi
CVE-2026-31431 'Copy Fail' — Vulnerability Detection
authencesn page cache corruption / local privilege escalation
Running as uid=1001, euid=1001
=== Kernel Version ===
[VULNERABLE] Kernel version
Reason : Kernel is in the vulnerable range (4.10 – 6.14)
Detail : Release: 6.12.0-124.45.1.el10_1 — patch status must be confirmed
=== CONFIG_CRYPTO_AUTHENC (Kernel Config) ===
[VULNERABLE] CONFIG_CRYPTO_AUTHENC
Reason : Built as module (=m): auto-loads on AF_ALG bind(); modprobe blacklist is the correct mitigation
=== CONFIG_CRYPTO_USER_API_AEAD (Kernel Config) ===
[VULNERABLE] CONFIG_CRYPTO_USER_API_AEAD
Reason : AF_ALG AEAD interface is a loadable module — unprivileged users can access the crypto subsystem via AF_ALG sockets
...
SYSTEM IS LIKELY VULNERABLE TO CVE-2026-31431
Recommended actions:
1. Apply your distribution's kernel update for CVE-2026-31431
2. Until patched, blacklist the module:
echo 'install algif_aead /bin/false' > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null
NOTE: this is ONLY effective when CONFIG_CRYPTO_AUTHENC=m (module).
If CONFIG_CRYPTO_AUTHENC=y (built-in), patching is the only fix.
A companion Bash script (cve-2026-31431-detect.sh) is available for environments where Python is not present or where shell-native tooling is preferred. It performs 10 of the 13 checks — see the check comparison table above for details of what differs.
# Basic run
bash cve-2026-31431-detect.sh
# JSON output — suitable for SIEM ingestion, Ansible facts, log aggregation
bash cve-2026-31431-detect.sh --json > scan-results.json
# Quiet mode — only print the summary (useful in CI logs)
bash cve-2026-31431-detect.sh --quiet
# Disable ANSI colour (for log files)
bash cve-2026-31431-detect.sh --no-colour
The shell script uses the same exit codes (0 = OK, 1 = vulnerable) and produces equivalent JSON output for pipeline consumption. Where Python 3 is available on the system, the shell script uses it to perform the live AF_ALG socket test; otherwise it falls back to kernel config inference.
The real fix is patching your kernel. Check your distribution's security advisories.
| Distribution | Where to look |
|---|---|
| Ubuntu | ubuntu.com/security/CVE-2026-31431 |
| RHEL / Amazon Linux | dnf update kernel |
| SUSE | zypper update kernel-default |
| Debian | apt update && apt upgrade |
If the CONFIG_CRYPTO_AUTHENC check reports =m (built as a module, not built-in), you can blacklist it:
echo 'install algif_aead /bin/false' > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null
Important: this mitigation has no effect if
CONFIG_CRYPTO_AUTHENC=y(built-in). In that case, patching the kernel is the only fix. The CONFIG_CRYPTO_AUTHENC check in the script tells you which situation you're in. Note thatCONFIG_CRYPTO_AUTHENCis the correct kernel config key — it builds both theauthencandauthencesnmodules from a single option.
This may affect IPsec if you are using it — verify before deploying fleet-wide.
The upstream fix is this commit — it reverts the 2017 in-place AEAD optimisation in algif_aead.c, separating the source and destination scatterlists so page cache pages can no longer end up in the writable destination.
If you are running this inside a Docker container, Kubernetes pod, or other container environment, the script will warn you: containers share the host kernel. The vulnerability lives in the kernel, not the container image. You must assess and patch the host.
# GitHub Actions
# The step will naturally fail and block the pipeline when the script exits 1.
# No extra configuration needed — non-zero exit codes fail steps by default.
- name: Check CVE-2026-31431
run: |
python3 cve-2026-31431-detect.py
rc=$?
if [ $rc -eq 1 ]; then
echo "VULNERABLE — pipeline blocked"
exit 1
elif [ $rc -ne 0 ]; then
echo "ERROR — detection script failed to complete (exit $rc)"
exit $rc
fi
# Ansible
# Uses playbook_dir to ensure the script path resolves correctly.
# failed_when checks for any non-zero exit (vulnerability OR script error).
- name: Check for CVE-2026-31431
script: "{{ playbook_dir }}/cve-2026-31431-detect.py"
register: cve_check
failed_when: cve_check.rc != 0
# Nagios / monitoring check (shell script — supports exit codes natively)
bash cve-2026-31431-detect.sh --quiet
# exit 0 = OK, exit 1 = CRITICAL (vulnerable)
# JSON output for SIEM / log aggregation (shell script)
bash cve-2026-31431-detect.sh --json --quiet > /var/log/cve-2026-31431-$(hostname)-$(date +%Y%m%d).json
| Date | Event |
|---|---|
| 2026-03-23 | Reported to Linux kernel security team |
| 2026-03-24 | Acknowledged |
| 2026-03-25 | Patches proposed and reviewed |
| 2026-04-01 | Fix committed to mainline kernel |
| 2026-04-22 | CVE-2026-31431 assigned |
| 2026-04-29 | Public disclosure |
Credit to Taeyang Lee at Theori for the original research insight, and the Xint Code Research Team for the full disclosure writeup.
Found a false positive? A distribution this misses? A kernel config that should be checked? PRs welcome. The goal is accurate signal, not just scary red text.
This tool is provided as-is for defensive security purposes. Point it at systems you are authorised to assess. Don't be weird about it.