Гонка в AF_ALG: 14-летняя дыра в ядре Linux принесла $113 337

· Безопасность
Исследователь STAR Labs нашёл в криптоподсистеме ядра Linux состояние гонки, позволяющее обычному пользователю стать root и выйти за пределы Docker-контейнера. Уязвимость CVE-2025-39964 просуществовала около четырнадцати лет.

cover.svg

Мухаммад Алифа Рамдхан, специалист по поиску уязвимостей из STAR Labs, опубликовал разбор того, как он обнаружил и превратил в рабочий эксплойт уязвимость CVE-2025-39964 в ядре Linux. Отправленная в программу Google kernelCTF, она принесла награду в 113 337 долларов; завершить цепочку помог коллега автора Бинг-Джон Билли Дженг.

Привлекательная поверхность атаки

Подсистема AF_ALG открывает пользовательским программам доступ к криптографическому API ядра — шифрованию и расшифровке. Автора привлекло то, что до неё можно дотянуться из непривилегированного кода через обычный сокетный интерфейс, без каких-либо особых прав или настроек. К тому же, насколько он мог судить, ни одна прежняя заявка в kernelCTF не использовала AF_ALG как точку входа.

Заметить это следует не путать с уязвимостью Copy Fail, раскрытой в 2026 году и отмеченной на Pwnie Awards: та тоже касается AF_ALG, но представляет собой прямую логическую ошибку в пути AEAD. Здесь же речь о состоянии гонки между двумя потоками, работающими с одним сокетом.

Суть ошибки

При обработке данных ядро складывает пользовательский ввод в список scatter-gather и хранит состояние в контексте af_alg_ctx. Флаг merge означает, что на последней странице осталось место и следующую порцию можно дописать в неё. Код неявно предполагал: если merge истинен, то в последнем блоке гарантированно есть хотя бы одна запись.

Автор проверил это допущение на двух потоках. Пока поток ждёт освобождения буфера отправки, блокировка сокета намеренно снимается. Аккуратно чередуя два вызова sendmsg() и заставив один из них завершиться ошибкой копирования, удаётся привести контекст в состояние, которое считалось невозможным: merge истинен, но счётчик записей равен нулю. Тогда выражение sgl->sg + sgl->cur - 1 обращается к sg[-1] — за пределы массива.

От чтения за границей до записи в память

Сам по себе выход за границу даёт лишь аварию KASAN. Ключевым стало наблюдение Дженга: путь memcpy_from_msg() копирует данные через copy_from_user, который проверяет только адрес-источник в пространстве пользователя, но не адрес-приёмник. Неверный адрес назначения возвращает ошибку EFAULT, а не роняет ядро. Это превращает уязвимость в оракул: перебирая значения, можно нащупать нужный адрес, не перезагружая систему при каждой промашке.

Получив произвольную запись, эксплойт перезаписывает core_pattern — параметр, задающий обработчик аварийных дампов. Если его значение начинается с символа |, ядро запускает указанную программу от имени root. Намеренно уронив дочерний процесс, автор получает root, а поскольку AF_ALG доступен изнутри контейнера, тем же путём выходят за пределы Docker на хост.

Возраст и исправление

Уязвимый код восходит к коммиту 2011 года, вошедшему в Linux 2.6.38, — ошибка пролежала около четырнадцати лет. Проблема была не в отсутствии одной проверки, а в допущении, что ввод собирает лишь один поток. Патч превращает это допущение в правило: пока один sendmsg() выполняется, второй писатель на том же сокете получает -EBUSY.


Источник: Lobsters

Комментарии

Войдите, чтобы комментировать.

  • Пока нет комментариев.