This is a loaded service elevator, not a front door
CVE-2026-31431 is a Linux kernel local privilege escalation in the algif_aead path behind the AF_ALG userspace crypto interface. In plain English: a low-privilege local user can abuse AEAD handling plus splice() to place a controlled 4-byte write into page cache for a readable file, then target a setuid binary and turn that transient in-memory corruption into root. Upstream exposure is broadly described as kernels since 4.17+, while vendor backports widen that in places; SUSE explicitly says 4.14+ era kernels and even some older branches with a buggy backport were affected.
The vendor's HIGH 7.8 is basically right on the math, but not on the operational urgency. This is still *post-initial-access* because it needs local code execution and low privileges, so it does not deserve a CRITICAL internet-fire score. But it is KEV-listed, publicly weaponized, reliable, tiny, and broadly applicable across enterprise Linux fleets, so defenders should treat it like a fast follow-on privilege-escalation patch, not a routine local bug.
4 steps from start to impact.
Land local code execution
- Attacker has local code execution or an interactive shell
- Attacker has at least low privileges (
PR:L) - Target runs a vulnerable kernel build
- No unauthenticated remote path is inherent to the bug
- Modern enterprises with strong MFA, PAM controls, and hardened container admission reduce how often attackers get this prerequisite
- Single-purpose appliances and minimal images may not offer a practical user foothold
Abuse AF_ALG AEAD with public tooling
copy_fail_exp.py PoC to drive the vulnerable algif_aead path. The bug is a logic flaw rather than a race, which matters because it makes exploitation unusually stable and easy to reproduce across distributions.AF_ALGinterface is present and reachablealgif_aeadis built-in or loadable and not blocked- Attacker can execute normal userspace syscalls including
socket()andsplice()
- Hosts that explicitly disable
algif_aeadorAF_ALGbreak the exploit chain - Some stripped-down workloads never expose the needed interface because of custom kernels or vendor hardening
- Container sandboxes with aggressive seccomp/LSM policy may constrain the syscall path in some environments
AF_ALG socket creation and unusual splice() usage provides better visibility than generic vuln scanning.Overwrite page cache for a readable setuid target
- A readable target file exists in page cache path
- A practical
setuidroot binary is present - Kernel behavior matches vulnerable copy path
- Some hardened images reduce available
setuidsurface - Custom integrity monitoring that also inspects runtime behavior may still catch the follow-on execution
- Very locked-down container images may limit useful local targets even if host escape remains a concern
setuid binaries, suspicious parent-child chains, and short-lived privilege transitions.Execute the tainted binary and take root
setuid binary from page cache and pivots to root on the host or, in some architectures, from container to host context. Once root is obtained, this stops being a crypto bug and becomes a full host-trust problem: credential theft, agent tampering, kernel persistence, and lateral movement all become possible.- Attacker can execute the chosen
setuidtarget - Exploit completes before page cache state changes
- No control blocks privileged execution
- EDR or strong audit policy may catch the final privilege transition
- Some containerized deployments keep blast radius to one node rather than the entire cluster
- Virtualization contains impact to the guest; this is not a hypervisor escape by itself
su, odd setuid execution paths, unexpected UID 0 transitions, or container processes crossing into host-level administration.The supporting signals.
| In-the-wild status | Yes. NVD marks the CVE as present in CISA KEV, which means confirmed exploitation in the wild. |
|---|---|
| KEV status | Listed by CISA KEV on 2026-05-01 with due date 2026-05-15 per the NVD KEV section. |
| PoC availability | Public and mature. Theori published copy_fail_exp.py in theori-io/copy-fail-CVE-2026-31431; CERT/CC also notes a public PoC. |
| EPSS | 0.02235 from the user-provided intel. Percentile was not independently confirmed in the sources reviewed. |
| CVSS vector | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H — translation: *easy local post-compromise root*, not pre-auth remote. |
| Affected versions | Broad exposure across Linux. CERT/CC describes kernel 4.17 and later; SUSE says 4.14 and newer across many products and notes some older branches were affected by backport behavior. |
| Fixed versions | Upstream/stable backports appear in branches such as 6.18.22, 6.19.12, and 7.0.10; Debian lists fixes including bullseye 5.10.257-1, bookworm 6.1.174-1, trixie 6.12.90-2, sid 7.0.10-1. Ubuntu and Red Hat shipped distro backports rather than requiring those exact upstream numbers. |
| Exposure / scanning reality | Not internet-scannable in any meaningful sense. Shodan/Censys-style exposure counts are not the right model here because the exploit requires local execution. What matters is fleet population of vulnerable kernels, shared multi-user hosts, CI runners, and container nodes. |
| Timeline clarity | Use two dates: the CVE record was published 2026-04-22, while CERT/CC and public exploit discussion point to public exploitation details on 2026-04-29. |
| Researcher / disclosure | Discovered by Taeyang Lee of Theori, with CERT/CC crediting assistance from Theori's AI-based analysis tool Xint Code. |
noisgate verdict.
The decisive factor is that this bug is post-initial-access: the attacker already needs local execution and low privileges before the exploit even starts. KEV listing and a highly reliable public exploit keep it firmly in HIGH, but those facts do not erase the attacker-position requirement that prevents a CRITICAL rating.
Why this verdict
- KEV changes urgency, not attack position — CISA KEV means real-world abuse is happening, so this cannot be treated as a theoretical local bug.
- Exploit friction is unusually low — the public Theori PoC is tiny, the bug is logic-based rather than race-based, and multiple sources describe it as broadly reliable across major distros.
- The reachable population is narrowed by attacker position — it requires local execution and low privileges, which implies the adversary already has a foothold or a hostile workload on the box; that is meaningful downward pressure from CRITICAL.
- Blast radius is host-level and serious — once exploited, the attacker gets root, can tamper with agents and credentials, and can potentially turn container footholds into node compromise.
- Enterprise prevalence keeps the score high — Linux servers, CI runners, jump boxes, Kubernetes nodes, and shared utility hosts make the affected population too large to dismiss as niche.
Why not higher?
This is not an unauthenticated remote exploit and it is not a wormable perimeter event. The attacker must already be on the host or in a relevant container context with low privileges, which means the CVE is a privilege-escalation accelerator rather than an initial-access primitive. That prerequisite is the main reason it stops at HIGH.
Why not lower?
Dropping this to MEDIUM would ignore two real-world amplifiers: KEV-listed exploitation and a public, reliable PoC. On a 10,000-host fleet, any bug that turns ordinary user access into root across common Linux builds deserves fast action, even if it is technically 'local only.'
What to do — in priority order.
- Disable
algif_aeadimmediately — If your distro supports module blocking, preventalgif_aeadfrom loading or unload it where safe. Because this CVE is KEV-listed, deploy this mitigation immediately, within hours, not on a normal 30-day HIGH clock. - Block
AF_ALGinit paths on systems that cannot reboot yet — On platforms like Red Hat where the function may be built-in, use the vendor-documentedinitcall_blacklist=algif_aead_initorinitcall_blacklist=af_alg_initworkaround where operationally acceptable. This is your stopgap for high-value hosts that cannot take a kernel patch today; again, do it within hours because of active exploitation. - Prioritize multi-tenant Linux first — Patch shared bastions, CI runners, jump hosts, VDI-backed Linux, Kubernetes nodes, and any box where untrusted users or workloads can execute code before single-user appliances. These are the systems where the local-access prerequisite is easiest to satisfy, so they get the first emergency wave within hours.
- Tighten container and shell access — Reduce low-privilege footholds while patching: restrict
oc debug/cluster-admin pathways, keep SELinux enforcing, avoid running workloads as root, and review SSH/user access on sensitive nodes. These are compensating controls only, but they meaningfully cut exploitability during the patch window and should be applied within hours on internet-adjacent or shared systems. - Monitor
AF_ALGand anomaloussetuidexecution — Add temporary detections forAF_ALGsocket creation, unusualsplice()usage, and unexpectedsetuidroot binary launches by non-admin accounts. This will not prevent exploitation by itself, but it improves odds of catching live abuse while your fleet rollout proceeds; stand it up within hours where telemetry exists.
- A WAF does nothing here because there is no native web attack surface in the vulnerability.
- External attack-surface management will not find this because the exploit path is local, not internet-exposed.
- Filesystem integrity checks alone are weak because the exploit targets page cache and the underlying file may remain unchanged on disk.
- Secure Boot does not stop runtime exploitation of a vulnerable, already-booted kernel.
- Assuming containers contain it is unsafe; the bug can turn a container foothold into a node-level problem depending on workload and host policy.
Crowdsourced verification payload.
Run this on the target Linux host, not from an auditor workstation. Invoke it as sudo bash ./check-copy-fail.sh; root is recommended so the script can read kernel boot args, module state, and package metadata. The script is conservative: it returns PATCHED when it sees a known mitigation or a known fixed package/kernel on supported Debian/Ubuntu paths, VULNERABLE when it sees a known affected state, and UNKNOWN when vendor backporting prevents a reliable offline verdict.
#!/usr/bin/env bash
# check-copy-fail.sh
# Conservative host-side checker for CVE-2026-31431 (Copy Fail)
# Outputs exactly one of: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
KREL="$(uname -r 2>/dev/null || true)"
OS_ID=""
OS_VER=""
CODENAME=""
CMDLINE="$(cat /proc/cmdline 2>/dev/null || true)"
if [ -r /etc/os-release ]; then
# shellcheck disable=SC1091
. /etc/os-release
OS_ID="${ID:-}"
OS_VER="${VERSION_ID:-}"
CODENAME="${VERSION_CODENAME:-}"
fi
have_cmd() { command -v "$1" >/dev/null 2>&1; }
ver_ge() {
# returns 0 if $1 >= $2 using sort -V
[ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | tail -n1)" = "$1" ]
}
patched() {
echo "PATCHED"
exit 0
}
vulnerable() {
echo "VULNERABLE"
exit 1
}
unknown() {
echo "UNKNOWN"
exit 2
}
# 1) Strong mitigation signals from vendor guidance
if printf '%s' "$CMDLINE" | grep -Eq '(^| )initcall_blacklist=(algif_aead_init|af_alg_init)( |$)'; then
patched
fi
if grep -RqsE '^\s*install\s+algif_aead\s+/bin/false\s*$' /etc/modprobe.d /usr/lib/modprobe.d 2>/dev/null; then
patched
fi
# 2) Ubuntu kmod mitigation versions from Ubuntu USN/blog
if [ "$OS_ID" = "ubuntu" ] && have_cmd dpkg-query; then
KMOD_VER="$(dpkg-query -W -f='${Version}' kmod 2>/dev/null || true)"
case "$OS_VER" in
22.04)
[ -n "$KMOD_VER" ] && dpkg --compare-versions "$KMOD_VER" ge "29-1ubuntu1.1" && patched
;;
24.04)
[ -n "$KMOD_VER" ] && dpkg --compare-versions "$KMOD_VER" ge "31+20240202-2ubuntu7.2" && patched
;;
25.10)
[ -n "$KMOD_VER" ] && dpkg --compare-versions "$KMOD_VER" ge "34.2-2ubuntu1.1" && patched
;;
esac
fi
# 3) Ubuntu generic kernel fixed versions from Ubuntu CVE page
if [ "$OS_ID" = "ubuntu" ]; then
case "$OS_VER" in
26.04) patched ;;
25.10)
case "$KREL" in
6.17.0-*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "6.17.0-29.29" && patched || vulnerable
;;
esac
;;
24.04)
case "$KREL" in
6.8.0-*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "6.8.0-117.117" && patched || vulnerable
;;
esac
;;
22.04)
case "$KREL" in
5.15.0-*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "5.15.0-179.189" && patched || vulnerable
;;
esac
;;
20.04)
case "$KREL" in
5.4.0-*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "5.4.0-230.250" && patched || vulnerable
;;
esac
;;
18.04)
case "$KREL" in
4.15.0-*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "4.15.0-250.262" && patched || vulnerable
;;
esac
;;
16.04|14.04)
# Generic Ubuntu releases are not broadly affected; 4.15 HWE cases are special.
case "$KREL" in
4.15.*) unknown ;;
*) patched ;;
esac
;;
esac
fi
# 4) Debian package/kernel checks from Debian security tracker
if [ "$OS_ID" = "debian" ]; then
case "$CODENAME" in
bullseye)
case "$KREL" in
5.10.*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "5.10.257" && patched || vulnerable
;;
6.1.*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "6.1.174" && patched || vulnerable
;;
esac
;;
bookworm)
case "$KREL" in
6.1.*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "6.1.174" && patched || vulnerable
;;
esac
;;
trixie)
case "$KREL" in
6.12.*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "6.12.90" && patched || vulnerable
;;
7.0.*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "7.0.9" && patched || vulnerable
;;
esac
;;
forky)
case "$KREL" in
7.0.*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "7.0.9" && patched || vulnerable
;;
esac
;;
sid|unstable)
case "$KREL" in
6.19.*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "6.19.12" && patched || vulnerable
;;
7.0.*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "7.0.10" && patched || vulnerable
;;
esac
;;
esac
fi
# 5) Generic upstream heuristics only where backporting ambiguity is low
# If algif_aead is absent/blocked, exposure is materially reduced.
if ! grep -q '^algif_aead ' /proc/modules 2>/dev/null; then
if ! have_cmd modprobe || ! modprobe -n -v algif_aead >/dev/null 2>&1; then
# Could be built-in or truly absent; do not overclaim.
unknown
fi
fi
# Upstream stable branches called out by advisories.
case "$KREL" in
6.18.*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "6.18.22" && patched || vulnerable
;;
6.19.*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "6.19.12" && patched || vulnerable
;;
7.0.*)
BASE="${KREL%%[^0-9.-]*}"
ver_ge "$BASE" "7.0.10" && patched || vulnerable
;;
4.*|5.*|6.*)
# Heavy distro backporting makes a universal offline answer unsafe.
unknown
;;
*)
unknown
;;
esac
If you remember one thing.
algif_aead/AF_ALG where the vendor supports it, starting with shared Linux servers, CI runners, bastions, and Kubernetes nodes; that is your noisgate mitigation SLA here. For the permanent fix, roll the vendor kernel update on an accelerated basis and do not hide behind the ordinary HIGH window just because the noisgate remediation SLA is <= 180 days—KEV means this belongs in the current emergency kernel wave, not the backlog.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.