
Структурированное объяснение CVE-2026-31431 (Copy Fail), связывающее три изменения ядра, которые ввели уязвимость и позволили ее эксплуатировать.
Логическая ошибка в authencesn, передаваемая через AF_ALG и splice(), приводящая к контролируемой записи 4 байтов в кэш страниц любого читаемого файла в системе. Нет состояния гонки, нет смещений, нет скомпилированного полезного груза. Тот же самый скрипт размером 732 байта получает root на каждом дистрибутиве Linux, начиная с 2017 года.
CVE-2026-31431 - Ошибка копирования — это логическая ошибка в криптографическом шаблоне authencesn ядра Linux. Она позволяет непривилегированному локальному пользователю выполнить контролируемую запись 4 байтов в кэш страниц любого читаемого файла в системе, не изменяя файл на диске.
Ошибка не существует ни в одном из трех компонентов по отдельности. Она возникает из их взаимодействия:``` 2011 ────────────────────────────────────────────────────────────────────── - authencesn added to the kernel (a5079d084f8b). - Uses the caller's destination scatterlist as scratch space. - Reorder ESN bytes before HMAC computation. - Only caller: internal xfrm layer. Harmless.
2015 ────────────────────────────────────────────────────────────────────── - algif_aead.c gains AEAD support with splice() path (104880a6b470). - splice() can deliver page cache pages to the TX scatterlist. - AF_ALG uses out-of-place operation: req->src != req->dst. - Page cache pages remain read-only. Not exploitable.
2017 ────────────────────────────────────────────────────────────────────── - In-place optimization in algif_aead.c (72548b093ee3). - Copies AAD+CT to RX buffer but chains authentication tag pages via sg_chain(). - Sets req->src = req->dst. - Page cache pages now reside in WRITABLE dst. - authencesn writes past boundary → page cache corruption.
2026 ────────────────────────────────────────────────────────────────────── - Copy Fail - CVE-2026-31431. Discovered by Theori / Xint Code. - Exploitable across all distros since 2017.
---
---
---
<div id='root-cause'/>
## ***🧬 Анализ первопричин***
<div id='primitive'/>
### ***Примитив AF_ALG + splice()***
AF_ALG (*[AF_ALG = 38](https://docs.kernel.org/crypto/userspace-if.html#user-space-api-general-remarks)*) — это тип сокета, который предоставляет криптографический API ядра непривилегированному пользовательскому пространству. Непривилегированный процесс может:
1. Открыть сокет AF_ALG / SOCK_SEQPACKET.
2. Выполнить bind() к любому доступному шаблону AEAD, предоставляемому криптографическим API ядра.
3. Установить криптографический ключ через setsockopt(SOL_ALG, ALG_SET_KEY, ...) на настроенном алгоритме.
4. Вызвать accept() для получения выделенного сокета операций, который будет обрабатывать запросы на шифрование и дешифрование.
5. Отправлять подготовленные данные с помощью sendmsg() и получать обработанный результат через recvmsg(), полностью взаимодействуя с криптографической подсистемой ядра.
Он включён по умолчанию в конфигурации ядра всех основных дистрибутивов (CONFIG_CRYPTO_USER_API_AEAD=y).
**[splice(2)](https://man7.org/linux/man-pages/man2/splice.2.html)** передаёт данные между файловыми дескрипторами без копирования — он передаёт ссылки на страницы, а не копии. Соответствующий поток:```
open("/usr/bin/su") -> fd_file
pipe() -> pipe_rd, pipe_wr
# moves N bytes from the file into the pipe
# the pipe buffer now contains a reference to the same physical page in the page cache
splice(fd_file, pipe_wr, N)
# delivers that reference to the AF_ALG socket
# the TX scatterlist of algif_aead now points to the page cache page of /usr/bin/su
splice(pipe_rd, alg_fd, N)
TX-список разброса (scatterlist) сокета AF_ALG содержит прямые ссылки на те же физические страницы, которые используются ядром для каждого read(), mmap() и execve() файла. Копирование не выполняется.
Коммит 72548b093ee3, algif_aead.c. Для расшифровки реализация:
In-place operation: RX SGL (req->dst): [ user buffer: AAD (copy) || CT (copy) ] --sg_chain--> [ Tag (page cache pages) ] req->src = req->dst = RX SGL
Result: page cache pages from /usr/bin/su are now part of the WRITABLE scatterlist passed to the crypto algorithm.
<div id='authencesn'/>
### ***Выход за границы записи в authencesn***
authencesn — это обёртка ядра AEAD, используемая IPsec с расширенными порядковыми номерами (RFC 4303). IPsec использует 64-битные порядковые номера:
- seqno_hi — старшие 32 бита (байты 0-3 AAD)
- seqno_lo — младшие 32 бита (байты 4-7 AAD)
Только seqno_lo передаётся по сети; seqno_hi является неявным контекстом. Для вычисления HMAC authencesn необходимо переставить эти байты: seqno_hi в начале и seqno_lo в конце хэш-входа.
Оно выполняет эту перестановку, используя целевой scatterlist вызывающего объекта в качестве рабочего пространства:```c
/* crypto/authencesn.c - crypto_authenc_esn_decrypt() */