
对 CVE-2026-31431 的复现分析、C 改编的 exp。
.
├── container
│ ├── container.md # Идеи эскалации из контейнера
│ ├── expenv # Окружение контейнера
│ │ ├── bin
│ │ │ ├── gscontainer
│ │ │ └── share
│ │ ├── Dockerfile
│ │ ├── run.sh
│ │ └── src
│ │ ├── gscontainer.c
│ │ └── share.c
│ ├── images
│ │ ├── container.png
│ │ ├── getshell.png
│ │ ├── host.png
│ │ ├── payload.png
│ │ └── share.png
│ ├── recode.bin # Одностадийный shellcode
│ └── stager
│ ├── shell.elf # Встроенный в эксплойт многостадийный shellcode
│ ├── stager1.bin
│ ├── stager2.bin
│ ├── stager3.bin
│ └── stager4.bin
├── doc
│ ├── cve-2026-31431.md # Анализ xint.io
│ └── shellcode.md # Что такое shellcode
├── exp.py # curl http://copy.fail/exp -o exp.py
├── README.md
├── success
│ ├── code.py # Извлечённый из exp.py shellcode
│ └── exploit.c # Упрощённый эксплойт на C с нативным shellcode для повышения привилегий от msf, требуется обход антивируса
└── test
├── exptest.c # Без комментариев
└── test.c # Корректный с комментариями, для понимания
Три отдельные безобидные правки, объединившись, создали уязвимость:
splice(файл → pipe → AF_ALG)
↓
af_alg_sendmsg: страница кэша файла → TX SGL (zero-copy)
↓
_aead_recvmsg: in-place оптимизация
├─ memcpy_sglist: копирование AAD+шифротекста в RX SGL
├─ sg_chain: теговая страница (всё ещё указывает на кэш файла) встраивается в RX SGL
└─ req->src = req->dst (= scatterlist, содержащий страницу кэша файла)
↓
crypto_authenc_esn_decrypt: перестановка ESN
└─ scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1)
↓ обход dst scatterlist до позиции тега
↓ kmap_local_page() отображает страницу кэша файла
↓ memcpy: запись seqno_lo в страницу кэша файла
↓
HMAC проверка не пройдена → возврат -EBMSG
↓
Страница кэша файла изменена, ядро не пометило страницу как грязную, файл на диске не изменён
В статье утверждается, что с 2017 года «почти все дистрибутивы Linux» затронуты. Страничный кэш разделяется между контейнерами, поэтому эта уязвимость является как локальным повышением привилегий (LPE), так и вектором эскалации из контейнера.
Исправление в основной ветке ядра (коммит a664bf3d603d) возвращает операции AF_ALG AEAD в режим out-of-place (разделение src и dst):
Before: aead_request_set_crypt(..., rsgl_src,
areq->first_rsgl.sgl.sgt.sgl, ...)
^ src == dst ^
After: aead_request_set_crypt(..., tsgl_src,
areq->first_rsgl.sgl.sgt.sgl, ...)
^ src ≠ dst ^ (TX SGL для src, RX SGL для dst)
Сообщение коммита: «Нет смысла работать in-place в algif_aead, поскольку источник и назначение имеют разные отображения».
Временное смягчение:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead
Или через политику seccomp запретить создание сокета AF_ALG (socket(AF_ALG, ...)).
| Время | Коммит | Влияние |
|---|
| 2011 | a5079d084f8b | Добавлен authencesn, использующий dst scatterlist как временное пространство для перестановки ESN. Безвредно — старый интерфейс AEAD сохранял AAD отдельно, а единственным вызывающим кодом было ядерное xfrm. |
| 2015 | 104880a6b470 | authencesn переведён на новый интерфейс AEAD, что привело к записи seqno_lo «за границу вывода» по адресу assoclen+cryptlen. Всё ещё неэксплуатируемо — AF_ALG работал в режиме out-of-place, req->src и req->dst разделены, страница кэша файлов присутствовала только в src (только для чтения). |
| 2017 | 72548b093ee3 | Оптимизация in-place для AF_ALG. AAD+шифротекст копируется из TX SGL в RX buffer, но теговая страница встраивается через sg_chain. Устанавливается req->src = req->dst. Теперь страница кэша файлов, полученная через splice, оказывается в доступном для записи dst scatterlist. Уязвимость сформирована. |
| Дистрибутив | Версия ядра | Затронут |
|---|
| Ubuntu 24.04 LTS | 6.17.0-1007-aws | Да |
| Amazon Linux 2023 | 6.18.8-9.213.amzn2023 | Да |
| RHEL 10.1 | 6.12.0-124.45.1.el10_1 | Да |
| SUSE 16 | 6.12.0-160000.9-default | Да |
| Характеристика | Проверка по исходному коду | Пояснение |
|---|
| authencesn запись seqno_lo в конец | authencesn.c:134 | По адресу assoclen+cryptlen записывается seqno_lo (младшие 32 бита). Описание пользователя верно. |
| Соглашение о перестановке ESN | Подтверждено в блоге (xint.io) | Байты AAD 0-3 = seqno_hi, байты 4-7 = seqno_lo. tmp[1] (=байты 4-7=seqno_lo) записываются в конец. |
| Путь расшифровки не восстанавливает перезаписанные байты | authencesn.c:270-273 vs 215-217 | decrypt_tail восстанавливает ESN в смещении 0-7, но никогда не восстанавливает исходные байты по адресу assoclen+cryptlen. Путь шифрования (genicv_tail) перед записью ICV восстанавливает их. |
| AF_ALG шифрование/дешифрование in-place | algif_aead.c:189-252 | rsgl_src и areq->first_rsgl.sgl.sgt.sgl указывают на один и тот же RX SGL. |
| Встраивание тега в AF_ALG | algif_aead.c:238-244 | При расшифровке тег вставляется через sg_chain, а не копируется — в сценарии splice теговая страница всё ещё указывает на кэш файла. Коренная причина уязвимости. |
| Передача данных через splice (zero-copy) | fs/splice.c:876, af_alg.c:1049, lib/scatterlist.c:1167 | Прямая ссылка на страницу pipe, zero-copy. |
| CVE-2026-31431 | Цепочка из трёх коммитов | authencesn(2011/2015) + in-place(2017) + zero-copy через splice = контролируемая запись в страничный кэш. |