Вредоносный Python-пакет, который запускается без импорта: как скомпрометировали библиотеку LiteLLM

3 мин
Вредоносный Python-пакет, который запускается без импорта: как скомпрометировали библиотеку LiteLLM

21 октября разработчики библиотеки LiteLLM сообщили о компрометации релиза 1.107.0. Все семь wheel-пакетов этой версии содержали инструмент для похищения секретов и загрузчик удалённого кода. Для запуска хватало старта Python в окружении с установленным пакетом, даже если приложение не использовало LiteLLM. Одновременно официальный Git-тег оставался чистым, а заражённые артефакты выпустил и подписал штатный процедура сборки.

Версия была доступна в PyPI с 11:39 до 13:23 UTC. Пострадали и контейнеры в Docker Hub и GHCR. По предварительной версии команды, атакующий получил GitHub CLI-токены из логов внутреннего сервиса, доступного из интернета. Токен владельца организации позволил отключить защиту ветки и перенаправить сборку на подставной репозиторий Berri-AI/litellm вместо BerriAI/litellm. 

Механизм компрометации пакета заслуживает внимания. Для начала в настройках сборщика maturin появилась строка data = "cook". Она добавляла каталог с закладкой в служебный раздел wheel. Установщик переносил файлы в корень site-packages, где находятся установленные модули. В исходном архиве каталога cook/ не было: там видна только строка конфигурации, а сам вредоносный код содержится в wheel.

Первым срабатывал файл cook.pth со строкой import cook. Обычно файлы .pth добавляют пути поиска модулей, но строки с командой import Python выполняются при обычной инициализации. Загвоздка в том, что проверка достижимости кода через импорты приложения в таком случае не отвечает на вопрос об исполнении, так как точка входа находится в настройке интерпретатора.

Второй точкой запуска стал файл sitecustomize.py, Python автоматически импортирует такой компонент настройки среды. Он запускал отдельный фоновый процесс, который раз в час получал адрес сервера из файла на GitHub, повторял сбор секретов и загружал исходник для исполнения. Похититель искал SSH-ключи, облачные токены, секреты CI/CD и ИИ-инструментов, а при доступе к базе прокси LiteLLM равным образом крал ключи LLM-провайдеров. Важно понимать, что удаление пакета в этом сценарии не останавливает уже запущенный процесс и не отзывает украденные ключи.

Сам приём автоматического запуска вредоноса уже встречался в предыдущих атаках. Анализируя базу CodeScoring, мы видим этот механизм в разных кампаниях по краже секретов. В частности, вредоносный пакет openai-mcp 2.41.1 через файл .pth загружал среду исполнения JavaScript Bun и запускал JS-код. У пакета claudedashbord 0.1.0 загрузка удалённого кода из такого хука была отложена на 12 часов. Но в данном случае алгоритм запуска попал в официальный выпуск LiteLLM, а заражённые wheel-пакеты собрал и подписал штатный процедура CI. Отсюда следует вывод для команд разработки: доверие к имени библиотеки, корректной подписи и чистому Git-тегу не заменяет проверки содержимого артефакта, который устанавливается в окружение.

При расследовании подобного инцидента нужно сверять версию в метаданных установок и кэшах, а контейнеры стоит проверять по digest из отчёта. Важно инсталлировать, запускался ли Python в заражённом окружении. При этом начинать с команды pip show опасно, так как pip тоже запускает интерпретатор. После изоляции нужны остановка фонового процесса, пересоздание окружений и отзыв доступных им секретов. Отсутствие файлов закладки не доказывает, что среда оставалась чистой. Сами разработчики в свою очередь рекомендуют закрепить версию 1.106.2.

База CodeScoring содержит сведения о вредоносности LiteLLM версии 1.107.0. Для пользователей CodeScoring это означает возможность выявить эту версию при композиционном анализе зависимостей и блокировать её загрузку с настроенным модулем CodeScoring.OSA и политикой «Зависимость опасна».

Поделиться

TelegramVK

Продолжить читать