
BPF LSM blocker para CVE-2026-31431 (Copy Fail): remediación sin reinicio para OpenShift 4
CVE-2026-31431 ("Copy Fail") es una vulnerabilidad de escalada de privilegios en el kernel de Linux
en la interfaz criptográfica algif_aead. Un atacante utiliza sockets AF_ALG con
el algoritmo authencesn y splice() para corromper archivos arbitrarios en la caché de páginas
del kernel, incluidos binarios setuid como /usr/bin/su.
Este documento proporciona una mitigación sin reinicio mediante un DaemonSet de BPF LSM
que bloquea todos los binds AEAD de AF_ALG, el subsistema explotado por Copy Fail. Esto
previene bypass mediante anidamiento de plantillas criptográficas (p. ej. pcrypt(authencesn(...))).
Otros usos de AF_ALG (hash, skcipher) no se ven afectados. Probado de extremo a extremo en tres
clústeres OCP 4.22 separados.
# 1. Verifique que BPF LSM esté habilitado (RHEL CoreOS 9.8 lo tiene por defecto)
oc debug node/<cualquier-nodo> -- chroot /host cat /sys/kernel/security/lsm
# Debe contener "bpf"
# 2. Implemente el namespace y otorgue el SCC privilegiado
oc apply -f daemonset.yaml
# 3. Los pods del DaemonSet se iniciarán automáticamente en todos los nodos
# 4. Verifique
oc get pods -n block-copyfail # Todos los nodos deben mostrar Running
oc logs -n block-copyfail -l app=block-copyfail
# Esperado: "block-copyfail: blocker active — all AF_ALG AEAD binds blocked"
Sin reinicios. Sin drenajes de nodos. Sin reinicios de pods. La protección es inmediata y cubre todos los procesos en todos los nodos (cobertura del 100%).
El exploit encadena tres características del kernel:
socket(AF_ALG, SOCK_SEQPACKET, 0)authencesn(hmac(sha256),cbc(aes)), un algoritmo específico
de cifrado autenticadoEl atacante corrompe /usr/bin/su en la caché de páginas (sin acceso de escritura al
archivo) y luego lo ejecuta para obtener root.
Cree un nuevo namespace cve-2026-31431-test en su clúster y ejecute el script de prueba aplicando los manifiestos en el directorio test:
oc apply -f test
Verifique los resultados:
oc wait pod/cve-test -n cve-2026-31431-test \
--for=jsonpath='{.status.phase}'=Succeeded --timeout=120s
oc -n cve-2026-31431-test logs -l app=cve-2026-31431-test
En un clúster vulnerable verá:
=== CVE-2026-31431 Vulnerability Test ===
Target: /usr/bin/su
Original SHA256: 8969560ae8e6e21c6184c1451f59418822ee69dd5d946d71987b55236bbc0feb
Attempting splice + AF_ALG page-cache corruption (160 bytes in 40 chunks)...
After SHA256: 30b0f5b5a054c4df65b48ca792863bf7054b4d793f15f57163792ba6c2b151ae
PAGE CACHE CORRUPTION: YES - /usr/bin/su was modified in the page cache
Attempting to execute corrupted /usr/bin/su ...
exit code: 0
RESULT: PARTIALLY MITIGATED
Page-cache corruption succeeded (kernel is vulnerable)
Privilege escalation blocked (allowPrivilegeEscalation=false)
oc delete namespace cve-2026-31431-test
El enfoque de BPF LSM engancha socket_bind a nivel del kernel y bloquea todos
los binds AEAD de AF_ALG independientemente del anidamiento de plantillas. Se basa en
block-copyfail, reescrito en C
con libbpf para implementación en OCP.
BPF LSM debe estar habilitado. RHEL CoreOS 9.8 (OCP 4.22) lo tiene habilitado por defecto. Verifique con:
oc debug node/<cualquier-nodo> -- chroot /host cat /sys/kernel/security/lsm
La salida esperada incluye bpf:
lockdown,capability,landlock,yama,selinux,bpf
Si bpf no está presente, se necesita un MachineConfig de una sola vez (este es el
único escenario que requiere reinicio):
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
labels:
machineconfiguration.openshift.io/role: worker
name: 99-enable-bpf-lsm
spec:
kernelArguments:
- lsm=lockdown,capability,selinux,bpf
Cree un nuevo namespace block-copyfail, otorgue el SCC e implemente el DaemonSet aplicando el manifiesto daemonset.yaml.
El SCC privilegiado debe otorgarse antes de que se creen los pods del DaemonSet;
de lo contrario, la creación de pods fallará con errores de validación de SCC.
oc apply -f daemonset.yaml
oc get pods -n block-copyfail -o wide
Esperado: un pod por nodo, todos Running:
NAME READY STATUS AGE NODE
block-copyfail-2jhzf 1/1 Running 34s ci-...-master-2
block-copyfail-4dfq7 1/1 Running 34s ci-...-master-1
block-copyfail-c2ts8 1/1 Running 34s ci-...-worker-c
block-copyfail-ctblk 1/1 Running 34s ci-...-worker-a
block-copyfail-m26sx 1/1 Running 34s ci-...-worker-b
block-copyfail-xsh6d 1/1 Running 34s ci-...-master-0
oc logs -n block-copyfail -l app=block-copyfail
Esperado:
block-copyfail: blocker active — all AF_ALG AEAD binds blocked
Vuelva a ejecutar la misma prueba de exploit de la sección Confirmando la Vulnerabilidad.
Después de implementar el DaemonSet de BPF LSM, la salida será:
=== CVE-2026-31431 Vulnerability Test ===
Target: /usr/bin/su
Original SHA256: 30b0f5b5a054c4df65b48ca792863bf7054b4d793f15f57163792ba6c2b151ae
Attempting splice + AF_ALG page-cache corruption (160 bytes in 40 chunks)...
AF_ALG bind failed: [Errno 1] Operation not permitted
RESULT: CANNOT TEST - AF_ALG or splice not available/permitted
Los registros del DaemonSet mostrarán el intento bloqueado:
oc logs -n block-copyfail -l app=block-copyfail
block-copyfail: blocker active — all AF_ALG AEAD binds blocked
block-copyfail: BLOCKED pid=16777 comm=python3 time=2026-05-01 16:37:23
Ejecute verify-algos.py en un nodo para confirmar que todos los algoritmos AEAD están bloqueados
mientras que otros tipos de AF_ALG (hash, skcipher) continúan funcionando:
oc debug node/<cualquier-nodo> -- chroot /host python3 -c "
import socket
tests = [
('aead', 'gcm(aes)'),
('aead', 'ccm(aes)'),
('aead', 'rfc4106(gcm(aes))'),
('hash', 'sha256'),
('skcipher', 'cbc(aes)'),
('aead', 'authencesn(hmac(sha256),cbc(aes))'),
]
for t, n in tests:
s = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)
try:
s.bind((t, n))
print(f' ALLOWED {t}/{n}')
except OSError as e:
print(f' BLOCKED {t}/{n} -- {e}')
finally:
s.close()
"
Salida esperada:
BLOCKED aead/gcm(aes) -- [Errno 1] Operation not permitted
BLOCKED aead/ccm(aes) -- [Errno 1] Operation not permitted
BLOCKED aead/rfc4106(gcm(aes)) -- [Errno 1] Operation not permitted
ALLOWED hash/sha256
ALLOWED skcipher/cbc(aes)
BLOCKED aead/authencesn(hmac(sha256),cbc(aes)) -- [Errno 1] Operation not permitted
Esto confirma que el BPF LSM bloquea todos los binds AEAD mientras deja funcionales otros tipos de AF_ALG.
El código fuente del bloqueador BPF LSM está en block-copyfail/:
block-copyfail/
block_copyfail.bpf.c # Programa BPF del kernel (hook LSM)
block_copyfail.c # Cargador de espacio de usuario (esqueleto libbpf)
block_copyfail.h # Estructura de eventos compartida
Makefile # Pipeline de compilación
Dockerfile # Compilación multi-etapa
daemonset.yaml # Manifiesto de Namespace + DaemonSet
trigger-test.py # Script rápido de validación
Compile y publique:
cd block-copyfail/
podman build -t quay.io/<org>/block-copyfail:latest .
podman push quay.io/<org>/block-copyfail:latest
El Dockerfile utiliza una compilación multi-etapa: Fedora con clang/bpftool/libbpf-devel para la compilación, UBI 9 minimal para la imagen de ejecución (~122 MB).
Eliminar el DaemonSet elimina inmediatamente la mitigación en todos los nodos:
oc delete -f daemonset.yaml
# o
oc delete namespace block-copyfail
El programa BPF se desengancha automáticamente cuando el proceso del cargador sale. No se necesita reinicio ni reinicio de pods.