29.06.2022

Состояние гонки в сборщике мусора ядра Linux, способное привести к повышению привилегий

Янн Хорн (Jann Horn) из команды Google Project Zero, в своё время выявивший уязвимости Spectre и Meltdown, опубликовал технику эксплуатации уязвимости (CVE-2021-4083) в сборщике мусора ядра Linux. Уязвимость вызвана состоянием гонки при чистке файловых дескрипторов unix-сокетов и потенциально позволяет локальному непривилегированному пользователю добиться выполнения своего кода на уровне ядра.

Проблема интересна тем, что временное окно, в течение которого проявляется состояние гонки, оценивалось как слишком незначительное для создания реальных эксплоитов, но автор исследования показал, что даже подобные изначально скептически рассматриваемые уязвимости могут стать источником реальных атак, если у создателя эксплоита есть необходимые навыки и время. Янн Хорн показал, как при помощи филигранных манипуляций можно свести состояние гонки, возникающее при одновременном вызове функций close() и fget(), к полноценно эксплуатируемой уязвимости класса use-after-free и добиться обращения к уже освобождённой структуре данных внутри ядра.

Состояние гонки возникает в процессе закрытия файлового дескриптора при одновременном вызове функций close() и fget(). Вызов close() может отработать до выполнения fget(), что введёт сборщик мусора в замешательство так как в соответствии со счётчиком refcount у структуры file не будет внешних ссылок, но она останется прикреплена к файловому дескриптору, т.е. сборщик мусора посчитает, что имеет эксклюзивный доступ к структуре, но фактически небольшой промежуток времени остающаяся в таблице файловых дескрипторов запись ещё будет указывать на освобождаемую структуру.

Для увеличения вероятности попадания в состояние гонки использовано несколько трюков, которые позволили довести вероятность успеха эксплуатации до 30% при внесении специфичных для конкретной системы оптимизаций. Например, для увеличения времени обращения к структуре с файловыми дескрипторами на несколько сотен наносекунд выполнено вытеснение данных из процессорного кэша через замусоривание кэша активностью на другом ядре CPU, что позволило добиться отдачи структуры из памяти, а не из быстрого кэша CPU.

Второй важной особенностью стало использование для увеличения времени состояния гонки прерываний, генерируемых аппаратным таймером. Подбирался такой момент, чтобы обработчик прерывания срабатывал во время возникновения состояния гонки и на какое-то время прерывал выполнение кода. Для дополнительного затягивания возвращения управления при помощи epoll генерировалось около 50 тысяч записей в waitqueue, требующих перебора в обработчике прерываний.

Техника эксплуатации уязвимости раскрыта после 90-дневного периода неразглашения. Проблема проявляется начиная с ядра 2.6.32 и устранена в начале декабря. Исправление вошло в состав ядра 5.16, а также перенесено в LTS-ветки ядра и пакеты с ядром, поставляемые в дистрибутивах. Примечательно, что уязвимость была выявлена в ходе анализа похожей проблемы CVE-2021-0920, проявляющейся в сборщике мусора при обработке флага MSG_PEEK.

Источник.