
BPF LSM bloqueur pour CVE-2026-31431 (Copy Fail) - remédiation sans redémarrage pour OpenShift 4
CVE-2026-31431 (« Copy Fail ») est une vulnérabilité d'élévation de privilèges dans le noyau Linux
présente dans l'interface cryptographique algif_aead. Un attaquant utilise des sockets AF_ALG avec
l'algorithme authencesn et splice() pour corrompre des fichiers arbitraires dans le cache de pages
du noyau — y compris les binaires setuid comme /usr/bin/su.
Ce document fournit une correction sans redémarrage utilisant un DaemonSet BPF LSM
qui bloque toutes les liaisons AEAD AF_ALG — la sous-couche exploitée par Copy Fail. Cela
empêche les contournements via l'imbrication de modèles cryptographiques (par ex. pcrypt(authencesn(...))).
Les autres utilisations d'AF_ALG (hash, skcipher) ne sont pas affectées. Testé de bout en bout sur trois
clusters OCP 4.22 distincts.
# 1. Vérifier que BPF LSM est activé (RHEL CoreOS 9.8 l'active par défaut)
oc debug node/<any-node> -- chroot /host cat /sys/kernel/security/lsm
# Doit contenir "bpf"
# 2. Déployer le namespace et accorder le SCC privilégié
oc apply -f daemonset.yaml
# 3. Les pods DaemonSet démarreront automatiquement sur tous les nœuds
# 4. Vérifier
oc get pods -n block-copyfail # Tous les nœuds doivent afficher Running
oc logs -n block-copyfail -l app=block-copyfail
# Attendu : "block-copyfail: blocker active — all AF_ALG AEAD binds blocked"
Aucun redémarrage. Aucun drain de nœuds. Aucun redémarrage de pods. La protection est immédiate et couvre tous les processus sur tous les nœuds (couverture à 100 %).
L'exploit enchaîne trois fonctionnalités du noyau :
socket(AF_ALG, SOCK_SEQPACKET, 0)authencesn(hmac(sha256),cbc(aes)), un algorithme
de chiffrement authentifié spécifiqueL'attaquant corrompt /usr/bin/su dans le cache de pages (sans accès en écriture au
fichier), puis l'exécute pour obtenir root.
Créez un nouveau namespace cve-2026-31431-test sur votre cluster et exécutez le script de test en appliquant les manifests dans le répertoire test :
oc apply -f test
Vérifiez les résultats :
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
Sur un cluster vulnérable, vous verrez :
=== 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
L'approche BPF LSM accroche socket_bind au niveau du noyau et bloque toutes
les liaisons AEAD AF_ALG quelle que soit l'imbrication de modèles. Elle est basée sur
block-copyfail, réécrit en C
avec libbpf pour le déploiement OCP.
BPF LSM doit être activé. RHEL CoreOS 9.8 (OCP 4.22) l'active par défaut. Vérifiez avec :
oc debug node/<any-node> -- chroot /host cat /sys/kernel/security/lsm
La sortie attendue inclut bpf :
lockdown,capability,landlock,yama,selinux,bpf
Si bpf n'est pas présent, un MachineConfig ponctuel est nécessaire (c'est le
seul scénario nécessitant un redémarrage) :
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
Créez un nouveau namespace block-copyfail, accordez le SCC et déployez le DaemonSet en appliquant le manifest daemonset.yaml.
Le SCC privilégié doit être accordé avant la création des pods DaemonSet,
sinon la création des pods échouera avec des erreurs de validation SCC.
oc apply -f daemonset.yaml
oc get pods -n block-copyfail -o wide
Attendu : un pod par nœud, tous 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
Attendu :
block-copyfail: blocker active — all AF_ALG AEAD binds blocked
Relancez le même test d'exploit de la section Confirmer la vulnérabilité.
Après le déploiement du DaemonSet BPF LSM, la sortie sera :
=== 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
Les journaux du DaemonSet montreront la tentative bloquée :
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
Exécutez verify-algos.py sur un nœud pour confirmer que tous les algorithmes AEAD sont bloqués
tandis que les autres types AF_ALG (hash, skcipher) continuent de fonctionner :
oc debug node/<any-node> -- 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()
"
Sortie attendue :
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
Cela confirme que le BPF LSM bloque toutes les liaisons AEAD tout en laissant les autres types AF_ALG fonctionnels.
Le code source du bloqueur BPF LSM se trouve dans block-copyfail/ :
block-copyfail/
block_copyfail.bpf.c # BPF kernel program (LSM hook)
block_copyfail.c # Userspace loader (libbpf skeleton)
block_copyfail.h # Shared event struct
Makefile # Build pipeline
Dockerfile # Multi-stage build
daemonset.yaml # Namespace + DaemonSet manifest
trigger-test.py # Quick validation script
Construisez et publiez :
cd block-copyfail/
podman build -t quay.io/<org>/block-copyfail:latest .
podman push quay.io/<org>/block-copyfail:latest
Le Dockerfile utilise une construction multi-étapes : Fedora avec clang/bpftool/libbpf-devel pour la compilation, UBI 9 minimal pour l'image d'exécution (~122 Mo).
La suppression du DaemonSet retire immédiatement l'atténuation sur tous les nœuds :
oc delete -f daemonset.yaml
# ou
oc delete namespace block-copyfail
Le programme BPF se détache automatiquement lorsque le processus de chargement se termine. Aucun redémarrage ou redémarrage de pod n'est nécessaire.