
Analysis and mitigation guide for CVE-2026-31431, a Linux kernel local privilege escalation in the crypto algif_aead subsystem, with impact assessment for RHEL and OpenShift, including seccomp and SCC hardening.
Local Privilege Escalation in the Linux Kernel crypto algif_aead subsystem.
CVE-2026-31431, dubbed "Copy Fail", is a logic bug in the Linux kernel's authencesn cryptographic template (algif_aead). It allows an unprivileged local user to perform a controlled 4-byte write into the page cache of any readable file, which can be leveraged to modify a setuid binary and obtain root.
a664bf3d603d| Date | Event |
|---|---|
| 2026-03-23 | Reported to Linux kernel security team |
| 2026-04-01 | Patch committed to mainline |
| 2026-04-22 | CVE assigned |
| 2026-04-29 | Public disclosure |
The exploit requires two things: an AF_ALG socket (allowed by default in all seccomp profiles) and a setuid binary (e.g. /usr/bin/su). The key mitigation is allowPrivilegeEscalation: false — this sets the Linux kernel's no_new_privs flag via prctl(PR_SET_NO_NEW_PRIVS, 1), which causes the kernel to ignore setuid/setgid bits on execve(). Since the exploit relies on executing a modified setuid binary, this blocks the final escalation step.
This is not an OpenShift-specific feature — it works the same way on vanilla Kubernetes (Pod Security Standards Restricted), Docker (--security-opt no-new-privileges), and Podman. OpenShift simply enforces it by default via the restricted-v2 SCC, while other platforms require explicit configuration.
RHEL 8 and RHEL 9 ship kernels that contain the vulnerable code. An unprivileged local user with shell access can exploit this to gain root. Patch immediately.
yum updateinfo list cves CVE-2026-31431
yum update kernel
OpenShift runs on RHCOS, which ships the vulnerable kernel. The practical impact depends on the workload's Security Context Constraints (SCC).
Standard workloads using the default restricted-v2 SCC are not exploitable because allowPrivilegeEscalation: false is enforced.
Pods running with elevated SCCs (anyuid, privileged, or custom SCCs allowing allowPrivilegeEscalation: true) are vulnerable. This commonly includes:
anyuidDirect node access (e.g. via oc debug node/) is always vulnerable — standard local privilege escalation, no container isolation involved.
A test pod is provided to check whether the exploit prerequisites are met in your cluster. It does not attempt to exploit the vulnerability — it only checks:
AF_ALG socket be created? (kernel attack surface reachable)no_new_privs set? (blocks setuid escalation)oc apply -f test-pod.yaml
oc logs cve-2026-31431-check
oc delete -f test-pod.yaml
Use the Deployment variant to test across multiple nodes by scaling replicas or using pod anti-affinity:
oc apply -f test-deployment.yaml
oc logs -l app=cve-2026-31431-check
oc delete -f test-deployment.yaml
| Code | Meaning |
|---|---|
0 | Not exploitable — AF_ALG socket blocked by seccomp |
1 | Partially exposed — AF_ALG reachable but setuid blocked by no_new_privs |
2 | Vulnerable — all exploit prerequisites are met |
On a standard OpenShift cluster with restricted-v2 SCC, you should see exit code 1 (partially exposed): the AF_ALG socket can be created (RuntimeDefault seccomp does not block it), but no_new_privs prevents the setuid escalation step. The published PoC will not work, but the kernel-level vulnerability is still reachable — patching is recommended.
This is the only complete fix. Update the kernel on all nodes and reboot.
For OpenShift, update to a RHCOS version that includes the fix and perform a rolling node reboot.
If algif_aead is compiled as a loadable module (CONFIG_CRYPTO_USER_API_AEAD=m):
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf
rmmod algif_aead 2>/dev/null || true
This does NOT work if algif_aead is built-in (=y), which is the case on RHCOS. Check with:
modinfo algif_aead 2>&1 | grep builtin
grep CONFIG_CRYPTO_USER_API_AEAD /boot/config-$(uname -r)
If the kernel module is built-in, the only pre-patch mitigation for containers is blocking the socket(AF_ALG, ...) syscall via a custom seccomp profile.
Create the MachineConfig to place the profile on all nodes (repeat with role: master for control plane nodes):
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
labels:
machineconfiguration.openshift.io/role: worker
name: 99-worker-seccomp-deny-af-alg
spec:
config:
ignition:
version: 3.2.0
storage:
files:
- path: /var/lib/kubelet/seccomp/deny-af-alg.json
mode: 0644
contents:
source: data:application/json;charset=utf-8;base64,ewogICJkZWZhdWx0QWN0aW9uIjogIlNDTVBfQUNUX0FMTE9XIiwKICAic3lzY2FsbHMiOiBbCiAgICB7CiAgICAgICJuYW1lcyI6IFsic29ja2V0Il0sCiAgICAgICJhY3Rpb24iOiAiU0NNUF9BQ1RfRVJSTk8iLAogICAgICAiYXJncyI6IFsKICAgICAgICB7CiAgICAgICAgICAiaW5kZXgiOiAwLAogICAgICAgICAgInZhbHVlIjogMzgsCiAgICAgICAgICAib3AiOiAiU0NNUF9DTVBfRVEiCiAgICAgICAgfQogICAgICBdCiAgICB9CiAgXQp9
The base64 content decodes to:
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"names": ["socket"],
"action": "SCMP_ACT_ERRNO",
"args": [
{
"index": 0,
"value": 38,
"op": "SCMP_CMP_EQ"
}
]
}
]
}
Note: Applying a MachineConfig triggers a rolling node reboot.
securityContext:
seccompProfile:
type: Localhost
localhostProfile: deny-af-alg.json
To protect all containers without modifying pod specs, override the CRI-O default seccomp profile (/etc/crio/seccomp.json) via MachineConfig by adding the AF_ALG filter rule to the existing profile.
Identify pods running with elevated privileges:
# Find pods not using restricted-v2
oc get pods -A -o json | jq -r '
.items[] |
select(.metadata.annotations["openshift.io/scc"] != "restricted-v2") |
"\(.metadata.namespace)/\(.metadata.name) → \(.metadata.annotations["openshift.io/scc"])"
'
These are the pods where the full exploit chain works. Prioritize patching or seccomp mitigation for nodes running these workloads.
Blocking AF_ALG sockets has negligible impact on most workloads. The following are not affected:
Only applications explicitly configured to use the OpenSSL afalg engine will be affected.
| Environment | allowPrivilegeEscalation | Container Root | Host Root | Risk |
|---|
| RHEL 8 / RHEL 9 (local user) | n/a | n/a | Yes | Critical |
OpenShift Node (shell access, e.g. oc debug node/) | n/a | n/a | Yes | Critical |
OpenShift Pod — restricted-v2 SCC (default) | false | No | No | Low |
OpenShift Pod — anyuid SCC | true | Yes | No (namespace isolation) | High |
OpenShift Pod — privileged SCC | true | Yes | Yes (no isolation) | Critical |
| OpenShift Pod — custom SCC | depends | depends | depends | Audit |
| Kubernetes Pod — PSS Restricted | false | No | No | Low |
| Kubernetes Pod — PSS Baseline / no policy | true (default) | Yes | No | High |
Docker / Podman — --security-opt no-new-privileges | false | No | No | Low |
| Docker / Podman — default | true | Yes | No | High |