❗️
Copy Fail: 732 байта до root на любом LinuxНайдена уязвимость в ядре Linux, которая позволяет непривилегированному пользователю получить права root. Никакого брутфорса, никаких гонок, а всего один Python-скрипт.
Что происходитУязвимость живёт на пересечении трёх компонентов ядра.
AF_ALG — сокет, который открывает доступ к криптоподсистеме для любого пользователя без прав.
splice() — системный вызов, который передаёт данные между файловыми дескрипторами без копирования, напрямую через ссылки на страницы кеша страниц. И
authencesn — AEAD-шаблон для IPsec с поддержкой 64-битных порядковых номеров.
В 2017 году в
algif_aead.c добавили оптимизацию: операции AEAD стали выполняться in-place, то есть
req->src и
req->dst указывают на один и тот же scatterlist. Когда пользователь через
splice() подаёт файл в AF\_ALG сокет, страницы с кэшем страниц этого файла попадают прямо в записываемый scatterlist.
authencesn при расшифровании использует буфер назначения как временное хранилище для перестановки байт ESN. Конкретно — пишет 4 байта по смещению
dst[assoclen + cryptlen], то есть за пределами легитимного буфера вывода. Эти байты он никогда не восстанавливает.
В итоге
scatterwalk_map_and_copy доходит до страниц кэша страниц целевого файла и пишет туда 4 байта, которые контролирует атакующий. Операция возвращает ошибку (контрольная сумма не сходится — данные подделаны), но запись в кэш страниц уже произошла.
Что контролирует атакующийТри параметра полностью в руках атакующего:
Первый — какой файл. Любой файл, доступный для чтения текущему пользователю.
Второй — какое смещение. Через параметры
splice() и
assoclen можно выбрать конкретные 4 байта внутри кэша страниц нужного файла.
Третий — какое значение. Записываемые байты берутся из байт 4–7 AAD, которые атакующий формирует сам через
sendmsg().
Как выглядит эксплойтЦель по умолчанию —
/usr/bin/su, исполняемый файл с битом смены идентификатора пользователя, который есть на всех основных дистрибутивах:
a = socket.socket(38, 5, 0) # AF_ALG, SOCK_SEQPACKET
a.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
# ... установить ключ, принять request socket u ...
u.sendmsg([b"A"*4 + payload_chunk], [cmsg_headers], MSG_MORE)
os.splice(target_fd, pipe_wr, offset)
os.splice(pipe_rd, alg_fd, offset)
u.recv(...) # вызывает decrypt → запись в page cache
Скрипт итерируется по 4-байтовым чанкам шелл-кода, записывает их в кэш страниц
/usr/bin/su. Затем
execve("/usr/bin/su") — ядро читает бинарь из кэша, запускает инжектированный код с UID 0.
Как закрытьПатч убирает оптимизацию 2017 года и возвращает out-of-place операции для AEAD в
algif_aead.c. Теперь
req->src указывает на TX SGL,
req->dst на RX SGL. Страницы page cache больше не попадают в записываемый scatterlist.
Для немедленного отключения вектора атаки, до обновления ядра, можно заблокировать модуль:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null
Уязвимость существовала почти десять лет. Каждое из трёх изменений 2011, 2015 и 2017 годов было разумным по отдельности. Проблема возникла на их пересечении, и никто не связал их вместе.
Найдена уязвимость с помощью AI-инструмента Xint Code: исследователь сформулировал поверхность атаки, инструмент за час проанализировал криптоподсистему и выдал CVE-2026-31431 как наиболее критичную находку.
➡️
Источник📍 Навигация:
Вакансии •
Задачи •
Собесы🐸 Библиотека devops'a#разбор_полётов