The Jerusalem PostSheffi Paz, prominent Tel Aviv anti-illegal immigration activist, dies at 74PunchWorld leaders celebrate Lula’s re-election in BrazilEl ComercioEN VIVO | Lula gana las elecciones presidenciales en Brasil tras una remontada inédita ante Flávio BolsonaroDaily MaverickVenezuela opposition leader Machado returns home after fraught year awayBollywood HungamaAnupam Kher to play Sunil Munshi’s father in Swedish-Belgian film Out of Fashion, directed by Jens JonssonZDF heuteAktuelle Pressemitteilungen des ZDFSCMP ChinaWhy China’s new estimate on Greenland ice loss speed is 55% lower than America’sTagesschauMarktbericht: Gesunkene Kurse locken Schnäppchenjäger anSRF NewsEbola-Virus – An Ebola erkrankte Schweizerin wird in die Schweiz gebrachtMyJoyOnlineGhana is restoring nature, but is nature recovering?Wirtualna Polska"Nie będę z siebie robił anioła". Nawrocki o konflikcie z TuskiemESPNTransfer rumors, news: Man United eye Branthwaite amid defensive rebuild
The Daily Newsstand · Free, Always
Monday, October 26, 2026

Разбор инцидента LiteLLM 1.107.0: как увели релизный конвейер и почему подпись не помогла

Translate
ой ой ой, опасный контейнер!

ой ой ой, опасный контейнер!

21 октября 2026 года на PyPI вышел litellm==1.107.0 со стилером учётных данных, а образы Docker с тем же тегом оказались заражены. LiteLLM — один из самых популярных open-source LLM-шлюзов: он хранит ключи ко всем вендорам моделей, которыми пользуется команда. Для LiteLLM это второй такой случай за год: в марте 2026 были скомпрометированы версии 1.82.7 и 1.82.8.

Нас инцидент не затронул: у себя мы используем собственный шлюз, KTMS. Но разбирать его интересно независимо от того, какой шлюз у вас. Самое поучительное здесь не стилер, а то, как он прошёл через релизный конвейер, который после мартовского инцидента специально укрепляли.

Расследование продолжается. Факты ниже взяты из отчёта BerriAI (обновление 21.10, 20:30 UTC), разбора StepSecurity, Wiz и Socket. Перед публикацией сверить с последним обновлением отчёта.

Хронология

Время UTC, 21 октября 2026, по отчёту BerriAI:

Время

Событие

10:08:29

Защита веток отклоняет неавторизованную правку приватного релизного workflow

10:08:34

Токеном привилегированного аккаунта защиту обходят и применяют правку

10:46:39

Изменённый workflow запускает staging-сборку из репозитория-двойника Berri-AI/litellm

11:08:53

Стартует продакшен-конвейер

11:11:18

Токен второго разработчика проходит шаг ручного одобрения

11:39:51

Семь заражённых wheel-файлов загружены на PyPI

11:46:56

Публикуются образы Docker; теги v1.107.0, latest, main-latest, main-stable указывают на заражённый индекс

11:53:49

Токеном третьего разработчика удаляются записи о запусках workflow

12:06

О компрометации сообщают в issue BerriAI/litellm#47826

12:09–12:24

Изменённый workflow откатывают

~13:23

PyPI убирает 1.107.0

13:51

Теги на GHCR недоступны, Docker Hub ещё отдаёт заражённый образ

14:20

Все затронутые теги Docker Hub удалены или перенаправлены на чистые образы

От первой правки workflow до публикации прошло полтора часа. Заражённый пакет пролежал на PyPI около 1 ч 45 мин, образ под плавающими тегами Docker Hub — около 2 ч 30 мин.

Официальный тег v1.107.0 в исходном репозитории чистый: артефакты собраны из чужого кода. Клиентов приватного enterprise-реестра, по словам BerriAI, инцидент не затронул.

Таймлайн атаки

Таймлайн атаки

Как устроена атака

Что было до атаки

После мартовского инцидента BerriAI перестроила выпуск релизов:

  • релизный кандидат, тесты, сборка артефактов в приватном репозитории, повторные тесты;

  • публикация через Trusted Publishers с отдельного аккаунта;

  • неизменяемые теги Docker для релизов;

  • подписанные образы Docker.

Это заметно лучше среднего по open source. И всё же атака прошла.

Пять шагов атакующего

  1. Окно расширенных прав. За день до атаки двум инженерам временно выдали права на публикацию, чтобы выпустить бэкпорты исправлений.

  2. Три набора токенов. У атакующего были OAuth-токены GitHub CLI трёх разработчиков. Одним можно было запускать workflow, вторым он прошёл шаг ручного одобрения, третьим стёр записи о запусках.

  3. Обход защиты веток. Первую попытку правки релизного workflow защита отклонила. Через пять секунд токеном владельца организации защиту отключили и принудительно запушили изменение.

  4. Подмена источника. Изменённый workflow собирал код не из BerriAI/litellm, а из двойника Berri-AI/litellm, отличающегося одним дефисом.

  5. Настоящая подпись. Конвейер подписал и опубликовал артефакты как обычно. Проверка подписи показала бы легитимный релиз.

не теряйте токены, братья

не теряйте токены, братья

Откуда взялись токены

Это пока не подтверждено. Версия BerriAI: атакующий попал во внутренний деплой LiteLLM, открытый в интернет, вероятно через недавно закрытую уязвимость. Этот деплой писал в логи тела ответов, и в них могли оказаться OAuth-токены GitHub CLI разработчиков. Во внутреннем деплое нашли неожиданный аккаунт с безобидным именем. Как именно атакующий получил доступ и использовал ли он аккаунт для чего-то ещё, BerriAI ещё выясняет.

Что делала нагрузка

Запуск без импорта

В site-packages попадали три файла: cook.pth, cook.py и sitecustomize.py. Файлы .pth Python обрабатывает при каждом старте интерпретатора, а sitecustomize импортирует автоматически. Поэтому для заражения было достаточно установленного пакета: import litellm не нужен, хватит любого python в том же окружении. Даже pip show litellm снова запускает стилер.

Что собиралось

По разбору StepSecurity:

  • пароли, cookies и история браузеров;

  • SSH-, GPG- и TLS-ключи;

  • учётные данные AWS, Azure, gcloud, Kubernetes, Docker, GitHub, реестров пакетов, файлы .env;

  • учётные данные из метаданных облачного инстанса;

  • секреты CI и инфраструктуры: Vault, Jenkins, Terraform;

  • переменные окружения всех доступных процессов;

  • конфигурации и токены более 20 AI-инструментов для разработки;

  • история shell и баз данных;

  • при заданном DATABASE_URL — база прокси LiteLLM целиком: ключи вендоров, конфигурации моделей, master key.

Образ litellm-database выгружал базу прокси уже при первом старте.

Отправка и второй этап

Архив шифровался AES-256-CBC с ключом, обёрнутым RSA-4096, и уходил обычным HTTP POST на http://www.litellm.dev/healthcheck. Домен зарегистрировали 13 октября, за восемь дней до атаки.

На Linux и macOS sitecustomize.py запускал отдельный демон, который держал /tmp/remote_code_loader.lock, раз в час читал файл-«тайник» в GitHub и выполнял код с указанного там хоста. Аккаунт с тайником удалили, но StepSecurity предупреждает: если кто-то зарегистрирует то же имя заново, заражённые хосты начнут исполнять его код. На Windows загрузчик не работал, а стилер работал.

Поскольку архив зашифрован, по сетевым логам нельзя понять, что именно ушло с конкретного хоста.

Кто затронут и как проверить

Затронуты:

  • любой хост или CI-раннер, где ставился litellm==1.107.0, в том числе через pip install litellm без версии в окне 11:39–13:23 UTC;

  • любой контейнер из образа v1.107.0, а также из latest, main-latest, main-stable, скачанных с 11:46 до 14:20 UTC.

Чистыми названы 1.106.x и более ранние, а также 1.107.0rc1 и 1.107.0rc2.

Не запускайте Python на подозрительном хосте. Проверяйте файловую систему и lock-файлы:

grep -rn "litellm.*1\.107\.0" --include="requirements*.txt" --include="*.lock" --include="pyproject.toml" .
find / \( -name cook.pth -o -name cook.py -o -name 'cook*.pyc' -o -name 'sitecustomize*.pyc' \
  -o -name 'litellm-1.107.0*' \) 2>/dev/null
ls -l /tmp/remote_code_loader.lock /tmp/.pc_once_* 2>/dev/null
docker images --digests | grep -i litellm
# заражённый индекс: sha256:1bbcb1443bc962e45f5aad53fe3d2c30600e0379715fb6c664cdd3ac126c339e

Признаки в сети: запросы к litellm.dev и www.litellm.dev. Запрос на путь /codepy означает, что выполнялся второй этап. Найденный файл подтверждает заражение, но отсутствие файлов не доказывает, что хост чист.

Что делать

По рекомендациям BerriAI и StepSecurity:

  1. Убить процесс, держащий /tmp/remote_code_loader.lock, и заблокировать litellm.dev на DNS или на выходе из сети.

  2. Пересобрать образы и виртуальные окружения с нуля, а не чистить три файла.

  3. Закрепить litellm==1.106.2 или образ по дайджесту, удалить 1.107.0 из зеркал и реестров.

  4. Если 1.107.0 работал на хосте с выходом в интернет, считать, что данные ушли. Сменить всё, до чего хост мог дотянуться: роль инстанса, облачные, SSH- и CI-ключи, токены реестров, Kubernetes и Vault, токены AI-инструментов, любые секреты из окружения процессов.

  5. Для прокси с базой — ещё все ключи вендоров из базы и конфигураций моделей, master key, salt key, виртуальные ключи и пароль из DATABASE_URL.

  6. Поискать в логах litellm.dev и /codepy, в AWS проверить CloudTrail на GetCallerIdentity от роли инстанса около времени установки и на использование её ключей снаружи вашей инфраструктуры. Проверить расход у вендоров моделей.

Уроки

1. Подпись подтверждает происхождение, а не безопасность

Образы LiteLLM были подписаны, и вредоносные тоже. Подпись говорит: «этот артефакт собрал вот этот конвейер». Если конвейер захвачен, подпись остаётся валидной. Помогает закрепление идентичности подписи на конкретный workflow и тег, но настоящая защита — контроль над тем, кто может этот workflow менять. Это вопрос прав доступа, а не криптографии.

2. Ручное одобрение не работает, если аккаунт одобряющего украден

Правило «двух ключей» защищает от ошибки одного человека, но не от атакующего, у которого есть токены обоих. Одобрение имеет смысл, если оно требует фактора, которого нет в токене: аппаратного ключа, отдельного устройства.

3. Временное расширение прав — самое опасное окно

Атака случилась на следующий день после того, как права на публикацию раздали шире. Временные права стоит выдавать с автоматическим сроком истечения и отзывать сразу после использования.

4. Токен владельца организации обходит всё

Защита веток сработала, но токен владельца её просто отключил. Аккаунты владельцев не должны работать с CLI-токенами повседневно, а изменения защиты веток и релизных workflow должны вызывать алерт.

5. Логи с телами ответов — хранилище секретов

Если сервис пишет в лог полные запросы и ответы, туда попадает всё, что в них оказалось, включая чужие токены. Для LLM-шлюза это особенно опасно: через него идут промпты, в которые пользователи вставляют что угодно.

6. Внутренние сервисы не должны смотреть в интернет

Внутренний деплой, открытый в интернет, по версии BerriAI стал точкой входа. «Внутренний» должно значить «доступен только из внутренней сети».

7. Плавающие теги усиливают ущерб

Два с половиной часа под latest лежал заражённый образ. Все, кто деплоил по тегу и перезапускался в это окно, получили его автоматически. Деплой по дайджесту и задержка обновлений на сутки-двое отсекли бы этот сценарий.

Почему шлюз — особенно ценная цель

LLM-шлюз стоит на пути каждого запроса к модели и хранит ключи ко всем вендорам сразу: OpenAI, Anthropic, Gemini, Bedrock и другим. Скомпрометированный шлюз раскрывает их все, а через логи ещё и содержимое промптов. Поэтому к любому шлюзу стоит задать несколько вопросов:

  1. Что исполняется при старте и откуда оно приходит?

  2. Сколько у него зависимостей и как они закреплены?

  3. Кто может менять его релизный workflow и к чему привязана подпись?

  4. Хранятся ли ключи вендоров зашифрованными и где лежит ключ шифрования?

  5. Пишет ли он в логи тела запросов и ответов?

  6. Куда он может ходить по сети?

Открытые вопросы

На момент обновления отчёта BerriAI оставались без ответа:

  • как атакующий попал во внутренний деплой;

  • как именно были получены три токена разработчиков и токен владельца;

  • использовался ли неожиданный аккаунт для чего-то, кроме доступа к логам;

  • как называется файл-маркер, по которому можно понять, что отправка состоялась.

Обновим статью, когда BerriAI опубликует продолжение.

Чек-лист

  1. Деплойте по дайджесту, а не по тегу. latest в продакшене не нужен.

  2. Не ставьте релиз в день выхода.

  3. Ставьте из lock-файла с хэшами (pip install --require-hashes).

  4. Ограничьте исходящий трафик шлюза адресами вендоров.

  5. Держите ключи вендоров зашифрованными, а ключ шифрования отдельно от базы и бэкапов.

  6. Проверяйте подпись с закреплённой идентичностью и помните, что она гарантирует только происхождение.

  7. Не пишите в логи тела запросов и ответов, а если пишете — считайте логи секретом.

  8. Распишите ротацию заранее: что менять при компрометации шлюза и сколько это займёт.

  9. Если вы сами выпускаете open source: временные права с автоистечением, аппаратные ключи для одобрения релизов, алерты на изменения защиты веток и релизных workflow.

Источники

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.