
Образовательная переработка PoC для Copy Fail (CVE-2026-31431) — повышение привилегий в Linux kernel через algif_aead in-place crypto + запись в page-cache через splice()
Образовательная переработка proof-of-concept от Theori/Xint для CVE-2026-31431 —
локальной уязвимости повышения привилегий в интерфейсе криптографических сокетов
algif_aead ядра Linux. Затрагивает все основные ветки ядра с 4.14 (июль 2017)
по 6.18.21, а также стабильную ветку 6.19 вплоть до 6.19.11.
Только для образовательных целей и авторизованных исследований безопасности. НЕ запускайте на системах, которыми вы не владеете или на тестирование которых у вас нет явного письменного разрешения. Запуск на уязвимом хосте даёт любому непривилегированному локальному пользователю немедленный root-доступ. Авторы не несут ответственности за неправомерное использование.
Ядро Linux предоставляет доступ к внутриядерному криптографическому API из
пользовательского пространства начиная с ядра 3.2 через семейство сокетов
AF_ALG (<linux/if_alg.h>). Пользовательское пространство открывает сокет
SOCK_SEQPACKET, привязывает его к шаблону алгоритма (например,
"aead" / "authencesn(hmac(sha256),cbc(aes))") и отправляет данные для
шифрования или дешифрования.
В 2017 году была внедрена оптимизация производительности, которая позволяет алгоритмам AEAD шифровать или дешифровать данные на месте (in-place), когда ядро обнаруживает, что источник и назначение используют одни и те же нижележащие страницы. Это позволяет избежать избыточного копирования памяти при каждой операции.
Шаблон authencesn (который добавляет поддержку расширенных порядковых номеров
(ESN) к аутентифицированному шифрованию) содержит запись в черновую область
(scratch-write) при дешифровании: он записывает байты ESN в определённое
смещение в буфере данных операции до выполнения проверки аутентификации. Когда
исходные страницы операции были получены из кэша страниц (page cache)
обычного файла через splice(), и активна оптимизация in-place, эта черновая
запись попадает напрямую обратно в эти кэшированные страницы.
Поскольку кэш страниц ядра является общим отображением — любой процесс, открывающий тот же файл, видит те же физические страницы — непривилегированный пользователь может:
/usr/bin/su) с флагом O_RDONLY.AF_ALG.authencesn; черновая запись ESN повреждает
страницу по контролируемому смещению байтами, контролируемыми атакующим.Сканеры целостности вне хоста (AIDE, Tripwire, IMA/EVM), которые сравнивают контрольные суммы файлов на диске, полностью слепы к этой атаке. Запись исчезает после вытеснения из кэша страниц или перезагрузки, не оставляя криминалистических следов на диске.
Нацелившись на /usr/bin/su (двоичный файл с setuid root) и перезаписав
ветвление, которое контролирует проверку аутентификации PAM, атакующий получает
root-оболочку без пароля.
Коммит оптимизации, который внёс ошибку, появился в цикле разработки 4.14
(июль 2017). Любое ядро, скомпилированное с CONFIG_CRYPTO_USER_API_AEAD=y
и CONFIG_CRYPTO_AUTHENC=y (оба параметра обычно включены по умолчанию)
в указанных выше диапазонах, уязвимо.
Проверьте трекер безопасности вашего дистрибутива для получения точного статуса исправления.
os.splice добавлен в Python 3.10)/usr/bin/su,
доступен для чтения всем, так как является setuid)# Показать, что было бы записано, не затрагивая ядро (безопасно):
python3 copy_fail_exploit.py --dry-run
# Патч кэша страниц /usr/bin/su (требуется уязвимое ядро):
# ВНИМАНИЕ: требуется уязвимое ядро — используйте только на системах, которыми вы владеете
python3 copy_fail_exploit.py
# Патч и немедленное повышение привилегий до root:
python3 copy_fail_exploit.py --spawn-shell
# Использование пользовательского целевого двоичного файла и полезной нагрузки:
python3 copy_fail_exploit.py --target /usr/bin/sudo --payload-file ./custom_patch.bin
# Подробный вывод (печать каждой 4-байтовой записи):
python3 copy_fail_exploit.py --verbose --dry-run
По умолчанию --spawn-shell выключен. После патчинга скрипт выводит:
[+] Патч применён к кэшу страниц '/usr/bin/su' (диск не изменён).
Запустите `su` для повышения привилегий или перезапустите с --spawn-shell.
Для восстановления: echo 3 | sudo tee /proc/sys/vm/drop_caches
Вы должны явно согласиться на запуск оболочки с помощью --spawn-shell. Это
сделано намеренно: это заставляет вас понять, что сделал эксплойт, прежде чем
выполнять результат.
Настройка примитива.
Эксплойт открывает сокет AF_ALG и привязывает его к шаблону
authencesn(hmac(sha256),cbc(aes)) с фиктивным нулевым ключом. Затем он
настраивает размер тега аутентификации на 4 байта через ALG_SET_AEAD_AUTHSIZE.
accept() на управляющем сокете даёт операционный сокет, который может
отправлять отдельные запросы на дешифрование.
Инициирование записи в кэш страниц.
Для каждого 4-байтового фрагмента полезной нагрузки патча эксплойт вызывает
sendmsg() на операционном сокете с установленным флагом MSG_MORE, передавая
8 байт ассоциированных данных (AAD): 4 байта заполнителя, за которыми следуют
4 байта полезной нагрузки. MSG_MORE сообщает ядру удерживать операцию, пока
не поступят дополнительные данные. Затем он создаёт канал (pipe) и выполняет
два вызова splice() — file_fd → pipe → op_sock — передавая страницы из
кэша страниц самого файла в качестве входных данных для операции дешифрования.
Когда наконец вызывается recv(), ядро выполняет дешифрование authencesn.
Срабатывает оптимизация in-place (источник и назначение — одни и те же страницы),
и черновая запись ESN копирует наши байты полезной нагрузки в страницу кэша по
целевому смещению. recv() возвращает EBADMSG, потому что тег аутентификации
не может быть проверен — это ожидаемо и безвредно. Запись уже произошла.
Патчинг двоичного файла.
Встроенная полезная нагрузка (взятая дословно из PoC Theori) перезаписывает
инструкцию ветвления в /usr/bin/su из стандартной сборки util-linux,
превращая условный переход, который обеспечивает аутентификацию PAM, в
безусловный переход (fall-through). После патчинга запуск su без пароля
успешен и даёт root-оболочку. Двоичный файл на диске не тронут; сброс записей
кэша страниц (echo 3 > /proc/sys/vm/drop_caches) мгновенно отменяет изменение.
Для более глубокого технического анализа см. ссылки ниже, в частности блог-пост Xint и репозиторий PoC Theori.
- rule: AF_ALG socket opened by unprivileged process
desc: >
CVE-2026-31431 — непривилегированный процесс открыл сокет AF_ALG
(семейство 38), что является первым шагом эксплойта Copy Fail.
condition: >
evt.type = socket and
evt.arg.domain = 38 and
not user.uid = 0
output: >
AF_ALG socket opened (user=%user.name uid=%user.uid
pid=%proc.pid comm=%proc.name)
priority: WARNING
tags: [host, network, privilege_escalation, CVE-2026-31431]
rule CopyFail_CVE_2026_31431 {
meta:
description = "Detects Copy Fail exploit script in memory or on disk"
cve = "CVE-2026-31431"
strings:
$template = "authencesn(hmac(sha256),cbc(aes))" ascii
$splice = "os.splice" ascii
$aflag = "AF_ALG" ascii
condition:
all of them
}
Добавьте в /etc/audit/rules.d/cve-2026-31431.rules:
-a always,exit -F arch=b64 -S socket -F a0=38 -k cve_2026_31431_afalg
Затем просматривайте журналы с помощью:
ausearch -k cve_2026_31431_afalg --interpret
1. Примените патч ядра (рекомендуется) Обновитесь до ядра 6.18.22, 6.19.12, 7.0-rc7 или новее, либо примените обновление безопасности вашего дистрибутива.
2. Отключите CONFIG_CRYPTO_USER_API_AEAD при сборке Если вы собираете собственное ядро, установите:
# CONFIG_CRYPTO_USER_API_AEAD is not set
Это удаляет весь интерфейс AEAD AF_ALG и устраняет поверхность атаки.
Большинству встраиваемых/усиленных (hardened) развёртываний он не нужен.
3. seccomp — запрет семейства сокетов AF_ALG
Добавьте фильтр seccomp для чувствительных процессов (или системно через
политику LSM), который отклоняет socket(AF_ALG, ...):
// Deny socket() when domain == AF_ALG (38)
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[0])),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 38, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EACCES),
4. Снижение привилегий кэша страниц через усиление ядра
vm.unprivileged_userfaultfd=0 и аналогичные параметры уменьшают поверхность
атаки пользовательских интерфейсов памяти ядра, хотя напрямую не блокируют
данную CVE.
Эксплойт изменяет только кэш страниц в памяти; двоичный файл на диске никогда не затрагивается. Чтобы восстановить исходное содержимое файла в памяти, сбросьте кэш страниц ядра:
echo 3 | sudo tee /proc/sys/vm/drop_caches
Это вытесняет все чистые страницы из кэша. Следующее чтение /usr/bin/su
(или любого другого пропатченного двоичного файла) перезагрузит исходные байты
с диска.
Перезагрузка также полностью очищает кэш страниц.
MIT — см. LICENSE.
MIT была выбрана вместо специальной лицензии «только для образовательных целей»,
поскольку такой лицензии не существует как признанного идентификатора OSI/SPDX.
MIT — разрешительная, юридически хорошо понятная и совместимая с репозиториями
ответственного раскрытия. Образовательное намерение выражено в docstring модуля,
разделе «Отказ от ответственности» этого README и выводе --help — а не в
юридически сомнительном пункте пользовательской лицензии.
| Ветка | Первая уязвимая | Последняя уязвимая | Исправлено в |
|---|
| mainline | 4.14 | 6.18.21 | 6.18.22 |
| 6.19 stable | 6.19.0 | 6.19.11 | 6.19.12 |
| 7.0-rc | 7.0-rc1 | 7.0-rc6 | 7.0-rc7 |
| Дистрибутив | Поставляемое ядро (прибл.) | Исправлено? |
|---|
| Debian 12 (Bookworm) | 6.1.x | Да (бэкпорт) |
| Debian 13 (Trixie) | 6.12.x / 6.14.x | Да (бэкпорт) |
| Ubuntu 24.04 LTS | 6.8.x | Да (USN-7xxx) |
| Ubuntu 24.10 | 6.11.x | Да |
| RHEL 9.x | 5.14.x (перебазировано) | Да (RHSA) |
| Alpine 3.20 | 6.6.x | Да (бэкпорт) |
| Arch Linux | rolling (≥ 6.18.22 сейчас) | Да |
| Kali Linux 2026.1 | 6.18.12 | Уязвимо |
| Kali Linux 2026.2+ | 6.18.22+ | Исправлено |
| Linux Mint 22 | 6.8.x (на базе Ubuntu) | Да (Ubuntu USN) |