
BPF LSM блокировщик для CVE-2026-31431 (Copy Fail) — устранение без перезагрузки для OpenShift 4
CVE-2026-31431 («Copy Fail») — это уязвимость повышения привилегий в ядре Linux
в криптографическом интерфейсе algif_aead. Злоумышленник использует сокеты AF_ALG с
алгоритмом authencesn и splice() для повреждения произвольных файлов в кэше страниц
ядра — включая setuid-бинарники, такие как /usr/bin/su.
В этом документе представлено исправление без перезагрузки с использованием DaemonSet на основе BPF LSM,
который блокирует все привязки AF_ALG AEAD — подсистему, эксплуатируемую Copy Fail. Это
предотвращает обходы через вложенность криптошаблонов (например, pcrypt(authencesn(...))).
Другое использование AF_ALG (hash, skcipher) не затрагивается. Протестировано комплексно на трёх
отдельных кластерах OCP 4.22.
# 1. Проверьте, что BPF LSM включён (в RHEL CoreOS 9.8 он включён по умолчанию)
oc debug node/<любой-узел> -- chroot /host cat /sys/kernel/security/lsm
# Должен содержать "bpf"
# 2. Разверните namespace и предоставьте привилегированный SCC
oc apply -f daemonset.yaml
# 3. Pod'ы DaemonSet запустятся автоматически на всех узлах
# 4. Проверка
oc get pods -n block-copyfail # На всех узлах должен быть статус Running
oc logs -n block-copyfail -l app=block-copyfail
# Ожидается: "block-copyfail: blocker active — all AF_ALG AEAD binds blocked"
Без перезагрузок. Без drain узлов. Без перезапусков pod'ов. Защита действует немедленно и охватывает все процессы на всех узлах (100% покрытие).
Эксплойт объединяет три функции ядра:
socket(AF_ALG, SOCK_SEQPACKET, 0)authencesn(hmac(sha256),cbc(aes)), конкретному
алгоритму аутентифицированного шифрованияЗлоумышленник повреждает /usr/bin/su в кэше страниц (без права записи в
файл), а затем выполняет его для получения root.
Создайте новый namespace cve-2026-31431-test в вашем кластере и запустите тестовый скрипт, применив манифесты из каталога test:
oc apply -f test
Проверьте результаты:
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
В уязвимом кластере вы увидите:
=== 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
Подход BPF LSM перехватывает socket_bind на уровне ядра и блокирует все
привязки AF_ALG AEAD независимо от вложенности шаблонов. Он основан на
block-copyfail, переписан на C
с использованием libbpf для развёртывания в OCP.
BPF LSM должен быть включён. В RHEL CoreOS 9.8 (OCP 4.22) он включён по умолчанию. Проверьте с помощью:
oc debug node/<любой-узел> -- chroot /host cat /sys/kernel/security/lsm
Ожидаемый вывод включает bpf:
lockdown,capability,landlock,yama,selinux,bpf
Если bpf отсутствует, потребуется одноразовый MachineConfig (это
единственный сценарий, требующий перезагрузки):
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
Создайте новый namespace block-copyfail, предоставьте SCC и разверните DaemonSet, применив манифест daemonset.yaml.
Привилегированный SCC должен быть предоставлен до создания pod'ов DaemonSet,
иначе создание pod'ов завершится ошибками проверки SCC.
oc apply -f daemonset.yaml
oc get pods -n block-copyfail -o wide
Ожидается: по одному pod'у на узел, все 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
Ожидается:
block-copyfail: blocker active — all AF_ALG AEAD binds blocked
Повторно запустите тот же тест эксплойта из раздела Подтверждение уязвимости.
После развёртывания DaemonSet на основе BPF LSM вывод будет следующим:
=== 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
Журналы DaemonSet покажут заблокированную попытку:
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
Запустите verify-algos.py на узле, чтобы подтвердить, что все алгоритмы AEAD заблокированы,
а другие типы AF_ALG (hash, skcipher) продолжают работать:
oc debug 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()
"
Ожидаемый вывод:
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
Это подтверждает, что BPF LSM блокирует все привязки AEAD, оставляя другие типы AF_ALG функциональными.
Исходный код блокировщика BPF LSM находится в block-copyfail/:
block-copyfail/
block_copyfail.bpf.c # Программа ядра BPF (хук LSM)
block_copyfail.c # Загрузчик пользовательского пространства (скелет libbpf)
block_copyfail.h # Общая структура событий
Makefile # Конвейер сборки
Dockerfile # Многоэтапная сборка
daemonset.yaml # Манифест Namespace + DaemonSet
trigger-test.py # Быстрый скрипт проверки
Сборка и публикация:
cd block-copyfail/
podman build -t quay.io/<org>/block-copyfail:latest .
podman push quay.io/<org>/block-copyfail:latest
Dockerfile использует многоэтапную сборку: Fedora с clang/bpftool/libbpf-devel для компиляции, UBI 9 minimal для образа выполнения (~122 МБ).
Удаление DaemonSet немедленно снимает смягчение на всех узлах:
oc delete -f daemonset.yaml
# или
oc delete namespace block-copyfail
Программа BPF автоматически отсоединяется при завершении процесса загрузчика. Перезагрузка или перезапуск pod'а не требуются.