Представьте: вы запускаете pip install для библиотеки, которую используете каждый день. Она прошла миллионы загрузок, стоит в зависимостях десятков ваших проектов. Но вот именно эта версия — уже не та библиотека. Кто-то подменил пакет, и при следующем import ваши SSH-ключи, токены AWS и пароли от баз данных утекают на сервер злоумышленников.
Именно это произошло на неделе 24–28 марта 2026 года. Три популярных пакета на PyPI — LiteLLM, telnyx и Trivy — оказались скомпрометированы одной и той же группировкой. Разбираемся, как это работает, чем грозит и как защититься.
Supply chain атака (атака на цепочку поставок) — это тип кибератаки, при котором злоумышленник внедряет вредоносный код не напрямую в вашу систему, а через доверенную зависимость: библиотеку, инструмент или сервис, который вы уже используете. Вместо того чтобы взламывать ваш сервер, атакующий компрометирует то, что вы сами устанавливаете. Это особенно опасно, потому что такие пакеты проходят через все ваши проверки — они же «легитимные».
Ключевые выводы
— За одну неделю скомпрометированы три крупных пакета: litellm (версии 1.82.7 и 1.82.8), telnyx (версии 4.87.1 и 4.87.2) и Trivy (сканер уязвимостей от Aqua Security).
— Все атаки связаны с группировкой TeamPCP: совпадают RSA-ключ шифрования, схема эксфильтрации и имя архиваtpcp.tar.gz.
— Вредоносный код крал SSH-ключи, токены облачных провайдеров (AWS, GCP, Azure), секреты Kubernetes, файлы криптокошельков и переменные окружения.
— LiteLLM — самый агрессивный вариант: латеральное перемещение по кластерам K8s и установка персистентного бэкдора через systemd.
— telnyx использовал стеганографию: вредоносный бинарник был спрятан внутри WAV-файла.
— Вектор первоначальной атаки — компрометация Trivy, через который были украдены PyPI-токены из CI/CD пайплайнов.
Хронология атак
Атаки развивались каскадно — каждая следующая была следствием предыдущей.
- Март 2026, начало месяца — Trivy. Группировка TeamPCP скомпрометировала apt-репозиторий сканера уязвимостей Trivy от Aqua Security. Отравленный бинарник Trivy, установленный в CI/CD пайплайнах, начал красть секреты раннеров — в том числе токены для публикации на PyPI.
- 24 марта 2026, 10:52 UTC — LiteLLM. На PyPI появляется версия litellm 1.82.8 с вредоносным
.pth-файлом. Пакет крадёт учётные данные, перемещается по кластерам Kubernetes и устанавливает бэкдор. Чуть ранее вышла версия 1.82.7 с менее агрессивным вариантом — без.pth-файла, но с таким же пейлоадом в коде прокси-сервера. - 27 марта 2026 — telnyx. Публикуются версии telnyx 4.87.1 и 4.87.2. Вредоносный код внедрён в
_client.py. Используется новая техника доставки — стеганография в WAV-файлах. Версия 4.87.1 содержит баг (опечатка в регистреSetup()вместоsetup()), версия 4.87.2 исправляет его.
Ни одна из вредоносных версий не имела соответствующего тега или релиза в GitHub-репозитории. Пакеты были загружены напрямую на PyPI с помощью украденных токенов, минуя стандартный CI/CD-процесс.
LiteLLM — подробный разбор
LiteLLM — популярная Python-библиотека от BerriAI, которая предоставляет единый интерфейс к более чем 100 провайдерам LLM: OpenAI, Anthropic, Google, Azure и другим. Она широко используется в AI-стартапах, MCP-плагинах и продакшн-системах. Именно поэтому компрометация этого пакета особенно опасна — он часто оказывается в окружениях с доступом к критическим секретам.
Как работал вредоносный код
Атака использовала малоизвестную особенность Python — файлы .pth (path configuration files). Модуль site обрабатывает их при каждом запуске интерпретатора: строки, начинающиеся с import, передаются в exec(). Это значит, что вредоносный код выполнялся при любом запуске Python в окружении, где был установлен litellm — не только при импорте библиотеки.
Файл litellm_init.pth (34 628 байт) содержал одну строку:
Эта строка запускала фоновый процесс через Popen, который декодировал и выполнял трёхуровневый вредоносный пейлоад.
Три стадии атаки
Стадия 1 — оркестратор (25 844 байта). Содержал захардкоженный RSA-4096 публичный ключ и ещё один base64-закодированный блоб с логикой сбора данных. Управлял шифрованием и отправкой украденных данных.
Стадия 2 — коллектор (17 281 байт). Собирал:
- SSH-ключи всех типов (RSA, Ed25519, ECDSA, DSA) со всех пользователей системы
- Переменные окружения и
.env-файлы - Учётные данные AWS, GCP, Azure — включая активную эксплуатацию через AWS SigV4 для дампа Secrets Manager и SSM Parameter Store
- Конфигурации Kubernetes и секреты из всех namespace
- Файлы криптокошельков: Bitcoin, Ethereum, Solana, Litecoin, Dogecoin, Zcash и другие
- Историю shell,
.gitconfig, пароли баз данных
Стадия 3 — персистентность (1 125 байт). Устанавливал бэкдор, замаскированный под «System Telemetry Service»:
Домен checkmarx.zone имитировал известную компанию Checkmarx, работающую в сфере безопасности приложений.
Латеральное перемещение по Kubernetes
Самая агрессивная часть атаки — распространение по кластерам Kubernetes. Если на скомпрометированной машине был доступен токен сервисного аккаунта K8s, вредонос создавал привилегированный pod на каждом узле кластера:
Поды именовались node-setup-* в пространстве имён kube-system, чтобы выглядеть легитимно. Каждый под монтировал корневую файловую систему хоста, получал доступ к PID и сети хоста и через chroot устанавливал персистентный бэкдор непосредственно на узел.
Шифрование и эксфильтрация
Украденные данные шифровались по гибридной схеме: случайный 32-байтный ключ AES-256-CBC для данных, затем сам ключ шифровался RSA-4096 (OAEP). Архив отправлялся POST-запросом на домен models.litellm.cloud — он был зарегистрирован 23 марта 2026 года, за день до публикации вредоносных пакетов, и не является частью легитимной инфраструктуры LiteLLM.
Побочный эффект — из-за того, что .pth-файл срабатывал при каждом запуске интерпретатора, дочерний процесс снова запускал тот же .pth, создавая экспоненциальную fork-бомбу. Именно так компрометацию и обнаружили: один из инженеров FutureSearch заметил, что машина упала после установки litellm как транзитивной зависимости MCP-плагина в Cursor.
telnyx — стеганография в WAV-файлах
telnyx — официальный Python SDK для телекоммуникационной платформы Telnyx, используемый для программной телефонии, SMS и работы с сетью. Пакет загружается более миллиона раз в месяц (~30 000 в день), что делает его компрометацию особенно масштабной.
Вектор атаки
Как и в случае с LiteLLM, GitHub-репозиторий telnyx не был скомпрометирован. Все недавние коммиты принадлежат stainless-app[bot] — боту платформы Stainless, которая генерирует SDK. Ни версия 4.87.1, ни 4.87.2 не имеют соответствующих тегов или релизов в GitHub.
Ключевое доказательство — отпечаток инструмента загрузки. Легитимный CI-пайплайн использует rye publish, а метаданные PyPI для вредоносных версий показывают twine/6.2.0 CPython/3.14.3. Злоумышленник загрузил пакеты вручную, используя украденный API-токен.
Репозиторий не использовал PyPI Trusted Publishers (OIDC), которые привязывают загрузку к конкретному репозиторию и workflow. Без этой защиты украденный токен позволяет загрузить любую версию с любой машины.
Стеганография в WAV-файлах
Главное нововведение этой атаки — доставка пейлоада через аудиофайлы. При импорте модуля telnyx вредоносный код скачивал WAV-файл с C2-сервера и извлекал из аудиоданных скрытый исполняемый файл:
Этот подход позволяет обойти сетевые инструменты инспекции трафика: WAV-файл выглядит как обычный аудиоконтент, а сам пейлоад закодирован в base64 и XOR-шифрован внутри аудиоданных.
Два вектора атаки
Вредоносный код содержал два независимых пути — для Windows и для Linux/macOS.
Windows — функция setup() скачивала WAV-файл hangup.wav, извлекала из него бинарник и сохраняла как msbuild.exe в папку автозагрузки Windows (директория Startup в %APPDATA%). Имя файла имитировало легитимный Microsoft Build Engine. Для предотвращения повторных загрузок использовался скрытый lock-файл с 12-часовым таймаутом.
Linux/macOS — функция FetchAudio() скачивала ringtone.wav, декодировала из base64-переменной _p скрипт сбора учётных данных и запускала его в фоновом процессе. Скрипт собирал те же категории данных, что и в атаке на LiteLLM, шифровал их AES-256-CBC + RSA-4096 и отправлял через HTTP POST.
Любопытная деталь: версия 4.87.1 содержала баг — вызов Setup() с заглавной буквой при определении def setup() со строчной. NameError прерывал выполнение модуля, и ни один из векторов атаки не работал. Версия 4.87.2 исправляла опечатку.
Trivy — когда сканер безопасности сам становится угрозой
Trivy — популярный open source сканер уязвимостей от Aqua Security, который проверяет контейнеры, файловые системы и Git-репозитории. Он широко используется в CI/CD пайплайнах для автоматического поиска уязвимостей перед деплоем.
Именно Trivy стал точкой входа для всей цепочки атак. TeamPCP скомпрометировала apt-репозиторий Trivy и подменила бинарник. Когда CI-раннеры устанавливали Trivy без фиксации версии, они получали отравленную сборку:
Отравленный Trivy с полными привилегиями CI-раннера собирал секреты окружения, включая PYPI_PUBLISH_PASSWORD. Эти украденные токены затем использовались для публикации вредоносных версий LiteLLM и telnyx напрямую на PyPI.
Мейнтейнер LiteLLM подтвердил связь с Trivy на Hacker News. Ирония ситуации в том, что инструмент, призванный защищать от уязвимостей, сам стал вектором атаки.
Кто за этим стоит — группа TeamPCP
Все три атаки атрибутированы одной группировке — TeamPCP. Атрибуция основана на трёх совпадающих индикаторах:
- Идентичный RSA-4096 публичный ключ. Ключ шифрования в пейлоаде telnyx побайтово совпадает с ключом из litellm 1.82.8. Расшифровать данные может только владелец соответствующего приватного ключа.
- Имя архива
tpcp.tar.gzи HTTP-заголовокX-Filename: tpcp.tar.gz. Используются в обеих атаках при эксфильтрации. «TPCP» — сокращение от TeamPCP. - Идентичная схема шифрования. Одинаковая последовательность команд openssl: генерация случайного ключа, шифрование AES-256-CBC, шифрование ключа через RSA OAEP — одни и те же вызовы в обоих пейлоадах.
При этом атакующие активно развивают инструментарий: LiteLLM использовал скомпрометированный домен (models.litellm.cloud) для C2, telnyx — голый IP-адрес (83.142.209.203). Стеганография в WAV-файлах — новая техника, не встречавшаяся в атаке на LiteLLM.
Как защититься
Если вы используете любой из скомпрометированных пакетов — действуйте немедленно. Вот практический чеклист:
- Проверьте версии зависимостей. Запустите
pip show litellmиpip show telnyxво всех ваших окружениях, включая CI/CD раннеры и Docker-образы. - Удалите скомпрометированные версии и очистите кеш. Удалите пакеты и выполните
pip cache purgeилиrm -rf ~/.cache/uv, чтобы закэшированные wheel-файлы не установились повторно. - Проверьте признаки персистентности. Ищите
~/.config/sysmon/sysmon.pyи~/.config/systemd/user/sysmon.service. В Kubernetes — подыnode-setup-*вkube-system. На Windows — файлmsbuild.exeв папке Startup. - Ротируйте все секреты. SSH-ключи, токены облачных провайдеров, ключи API из
.env, пароли БД, токены PyPI — всё, что присутствовало в скомпрометированном окружении. - Фиксируйте версии зависимостей. Используйте lock-файлы:
pip freeze,uv lock,poetry.lock. - Включите PyPI Trusted Publishers. Привяжите публикацию пакетов к конкретному GitHub-репозиторию и workflow через OIDC. Украденные токены станут бесполезными.
- Проверяйте хеши пакетов. Используйте флаг
--require-hashesпри установке через pip или аналогичные механизмы в uv и poetry. - Настройте мониторинг зависимостей. Инструменты вроде SafeDep vet, pip-audit или Socket помогут обнаружить вредоносные пакеты до попадания в продакшн.
Пример фиксации хешей с pip:
Пример фиксации с uv:
FAQ
Как понять, что мой проект затронут?
Проверьте установленные версии: pip show litellm и pip show telnyx. Если у вас litellm 1.82.7 или 1.82.8, либо telnyx 4.87.1 или 4.87.2 — вы затронуты. Также проверьте кеш пакетного менеджера: для uv поищите файл litellm_init.pth в ~/.cache/uv. Наличие этого файла — признак компрометации.
Достаточно ли просто обновить пакет?
Нет. Если вредоносная версия была установлена, одного обновления недостаточно. Вредонос мог уже установить персистентный бэкдор (~/.config/sysmon/sysmon.py), украсть секреты или создать привилегированные поды в Kubernetes. Необходимо провести полный аудит: проверить систему на признаки персистентности, ротировать все секреты и очистить кеш пакетного менеджера.
Работает ли вредоносный код в Docker-контейнерах?
Да. .pth-файл из litellm срабатывает при любом запуске Python в контейнере, где установлен пакет. Более того — если в контейнере доступен Kubernetes service account token (что является стандартной конфигурацией), вредонос может выйти за пределы контейнера и скомпрометировать весь кластер.
Как TeamPCP получила токены PyPI?
Через скомпрометированный Trivy. CI/CD пайплайны LiteLLM и, вероятно, telnyx устанавливали Trivy из apt-репозитория без фиксации версии. Когда TeamPCP подменила бинарник в репозитории, отравленный Trivy с привилегиями CI-раннера извлёк секреты окружения — в том числе токены для публикации на PyPI. Репозиторий telnyx не использовал Trivy, поэтому точный вектор получения его токена пока не установлен, но паттерн атаки идентичен.
Можно ли было обнаружить атаку до установки?
Да, при наличии правильных инструментов. Отсутствие соответствующего GitHub-тега для опубликованной версии, изменение инструмента загрузки (twine вместо rye publish), появление .pth-файла в wheel — всё это детектируемые аномалии. Инструменты типа SafeDep vet умеют проверять пакеты на подобные сигналы. Также помогает проверка хешей через --require-hashes.
Выводы
Волна атак TeamPCP демонстрирует новый уровень supply chain атак: это не тайпсквоттинг и не подмена малоизвестных пакетов. Атакующие компрометируют инфраструктуру публикации настоящих, широко используемых библиотек — и делают это через цепочку доверия. Trivy ломает CI/CD, CI/CD отдаёт PyPI-токены, PyPI-токены используются для публикации троянизированных пакетов.
«Безопасность цепочки поставок — это не отдельная задача, а свойство всего процесса разработки. Если ваш сканер уязвимостей сам становится вектором атаки, проблема не в конкретном инструменте — проблема в том, что мы устанавливаем инструменты безопасности с тем же уровнем доверия, что и обычные зависимости.»
Три ключевых урока из этой истории:
- Фиксируйте версии всего — не только библиотек, но и инструментов в CI/CD.
apt-get install trivyбез версии — это приглашение для атакующего. - Используйте Trusted Publishers — PyPI OIDC привязывает публикацию к конкретному репозиторию. Украденный токен без workflow бесполезен.
- Мониторьте аномалии — отсутствие GitHub-тега для PyPI-версии, смена upload tool, появление
.pth-файлов — всё это сигналы компрометации.
Источники: SafeDep — анализ litellm, SafeDep — анализ telnyx, FutureSearch — обнаружение litellm, GitHub Advisory litellm #24512.