Атака на npm и PyPI: 404 вредоносные версии в TanStack, Mistral AI, UiPath за пять часов
404 вредоносные версии в 170+ npm-пакетах и двух PyPI-пакетах за пятичасовое окно. Payload самовоспроизводится, коммитя .claude/settings.json и .vscode/tasks.json в чужие репозитории. Что делать прямо сейчас.
Новости TprogerЕсли у вас в зависимостях есть что-то из @tanstack/*, @mistralai/* или @uipath/* — проверьте lockfile прямо сейчас. В ночь с 11 на 12 мая 2026 года атакующий опубликовал 404 вредоносных версии в 170+ npm-пакетах и двух PyPI-пакетах за пятичасовое окно. Самое неприятное: payload самовоспроизводится, подкладывая .claude/settings.json и .vscode/tasks.json в репозитории жертв.
Это одна из крупнейших координированных атак на реестры пакетов в 2026 году, и первая, которая в одной кампании задела сразу и npm, и PyPI. StepSecurity и Socket трекают её как «mini-shai-hulud». Разбираемся, что произошло, кто пострадал и что делать.
Ключевые выводы
Атака на npm и PyPI 11 мая 2026
Главное за пять минут
- 404 вредоносные версии в 170+ npm-пакетах и 2 PyPI-пакетах, опубликованы в течение пятичасового окна 11 мая.
- Под удар попали: @tanstack (42 пакета роутера для React, Vue, Solid), @mistralai (3 SDK), @uipath (65 пакетов автоматизации), @opensearch-project/opensearch (1,3 млн загрузок в неделю), Guardrails AI.
- Два механизма триггера в npm: preinstall-хук (@mistralai) и optionalDependency с prepare-скриптом (@tanstack). На PyPI — инжекция в __init__.py, срабатывает при импорте, а не при установке.
- Payload крадёт GitHub-токены, npm-токены, AWS IAM-credentials и HashiCorp Vault-credentials. Эксфильтрация — через onion-routed мессенджер Session, поэтому C2-домен заблокировать нельзя.
- Самораспространение: payload использует украденный GitHub-токен, чтобы закоммитить .claude/settings.json и .vscode/tasks.json в feature-ветки чужих репозиториев через GraphQL-мутацию createCommitOnBranch.
- Безопасные версии: mistralai ≤ 2.4.5, guardrails-ai ≤ 0.10.0. По npm — пиньте версии и регенерируйте lockfile.
Что произошло
В ночь с 11 на 12 мая 2026 года автоматическая система детекта SafeDep засекла массовый burst странных публикаций на npm. Атакующий действовал не точечно, как в случае с axios в марте 2026, а целыми scope-ами: внутри одного scope падали сразу все пакеты. К утру стали известны границы кампании — 170 npm-пакетов, 404 вредоносные версии, плюс 2 PyPI-пакета.
Кого затронуло
- TanStack (42 пакета, 84 версии): целый экосистем роутера —
@tanstack/react-router,@tanstack/vue-router,@tanstack/solid-router, их devtools, SSR-плагины и build-инструменты. У@tanstack/react-router— 3 миллиона загрузок в неделю. - Mistral AI (3 пакета, 9 версий): core SDK
@mistralai/mistralaiи его обёртки под Azure и GCP. По три вредоносные версии на каждый. - UiPath (65 пакетов, 65 версий): весь scope
@uipath— SDK агентов, оркестратор, инструменты RPA, пакеты для интеграции. - OpenSearch (
@opensearch-project/opensearch): официальный JavaScript-клиент, 4 затронутые версии — 3.5.3, 3.6.2, 3.7.0, 3.8.0. 1,3 миллиона загрузок в неделю. - Guardrails AI (
guardrails-ai==0.10.1на PyPI): фреймворк validation guardrails для Python. - Mistral AI на PyPI (
mistralai==2.4.6): официальный SDK; легитимного релиза 2.4.6 не существовало, последняя версия — 2.4.5 от 7 мая.
Как работает атака
На npm — два механизма триггера
Для Mistral AI атакующий заменил блок scripts в package.json на preinstall-хук. Этот хук скачивает Bun runtime и запускает payload setup.mjs, который, в свою очередь, запускает основной обфусцированный бинарь router_init.js.
Для TanStack атакующий пошёл хитрее. В легитимный package.json добавлен optionalDependency, указывающий на вредоносный коммит в реальном репозитории tanstack/router на GitHub (коммит #79ac49eedf774dd4b0cfa308722bc463cfe5885c). В этом коммите лежит prepare-скрипт, который снова тянет Bun и запускает тот же payload.
Преимущество атакующего: коммит в публичном репозитории, который ссылается из optionalDependency, не вызывает алертов автоматических сканеров — внешне это легитимная зависимость.
На PyPI — инжекция в __init__.py
PyPI устроен иначе: sandboxed install через pip download и pip wheel не выполняет код пакета. Поэтому атакующий не стал прятать payload в install-хуке. Вместо этого в __init__.py добавили 15 строк, срабатывающих при первом import:
Никакой обфускации: URL, путь и команда выполнения — открытым текстом. Проверка sys.platform запускает dropper только на Linux. macOS- и Windows-сборки несут трояны, но dropper там не сработает. Cloudflare помечает домен git-tanstack.com как phishing-сайт.
Что крадёт payload
Внутри обфусцированного router_init.js — модульный credential stealer с дедикейтными провайдерами под разные источники секретов:
- GitHub PAT, OAuth и App-installation токены: паттерны
ghp_*,gho_*,ghs_*. - GitHub Actions OIDC JWT — формата
ghs_NNN_xxx.yyy.zzz. - npm publish tokens (
npm_*). - AWS IAM credentials через зондирование metadata-эндпоинта 169.254.169.254/latest/meta-data/iam/security-credentials/.
- HashiCorp Vault — пробу на localhost:8200.
Сканер регулярок прогоняется по содержимому переменных окружения, файлам конфигурации и истории shell. Цель — максимизировать lateral movement из CI/CD-окружений и dev-машин в публичное облако.
Самораспространение через Claude Code и VS Code
Самая интересная и пугающая часть payload — self-replication механизм. Имея украденный GitHub-токен, payload использует GraphQL-мутацию createCommitOnBranch, чтобы закоммитить вредоносные файлы в репозитории жертвы:
.claude/settings.jsonи.claude/setup.mjs— конфиг для Claude Code, запускающий payload при открытии проекта..vscode/tasks.jsonи.vscode/setup.mjs— то же самое для VS Code..claude/router_runtime.js— полная копия 2,2-мегабайтного payload, чтобы следующая стадия не зависела от внешнего хоста.
Цели коммитов выбираются хитро: payload запрашивает до 50 веток через GraphQL и отфильтровывает main, master, develop и release (типичный набор). Коммиты идут в feature- и topic-ветки, где меньше шансов нарваться на ревью. В коммит-сообщении добавляется случайный Co-authored-by, чтобы выглядеть «коллективно».
Итог цепочки: любой разработчик, который git pull заражённую ветку и откроет проект в VS Code или Claude Code, запустит payload без явного действия. Затем его токены крадутся — и цикл повторяется.
Эксфильтрация через Session — почему домен не закрыть
Атакующий не использовал классический C2-домен. Вместо этого payload содержит полную реализацию клиента Session — onion-routed мессенджера на сети Oxen. Payload связывается с pre-pinned seed-нодами (seed1.getsession.org, seed2.getsession.org, seed3.getsession.org), получает список snode-ов и отправляет украденные креды через peer-to-peer-сеть.
Для больших данных payload использует filev2.getsession.org/file/ — централизованный файловый сервер Session. Это единственная фиксированная точка инфраструктуры; всё остальное — динамическое peer-to-peer-разрешение через swarm. Защитники не могут отозвать swarm так, как отзывают домен.
Шифрование на ed25519 и x25519, перебор snode-адресов в рантайме — блокировка C2 на уровне DNS или firewall здесь не работает.
Что делать прямо сейчас
Для npm-проектов
Проверьте lockfile на наличие вредоносных версий по затронутым scope-ам:
Если что-то найдено — запиньте пакеты на известные безопасные версии и перегенерируйте lockfile. После этого ротейтьте все секреты, к которым имела доступ ваша dev-машина или CI: GitHub PAT, npm-токены, AWS-ключи, Vault-токены.
Для Python-проектов
Проверьте установленные версии:
Если у вас mistralai==2.4.6 или guardrails-ai==0.10.1 — окружение скомпрометировано. Безопасные версии: mistralai ≤ 2.4.5 и guardrails-ai ≤ 0.10.0.
Дополнительная гигиена
- Проверьте feature- и topic-ветки своих репозиториев на свежие коммиты с подложенными файлами в
.claude/и.vscode/— особенно если у вас под рукой Claude Code или VS Code с активным GitHub-токеном. - Запретите автоматический запуск
preinstall- иpostinstall-скриптов через флаг--ignore-scripts. - Просканируйте репозитории на наличие
.claude/router_runtime.js— это копия payload, размер около 2,2 МБ.
Indicators of Compromise (IoC)
FAQ
Часто задаваемые вопросы
Я установил @tanstack/react-router утром 12 мая — что делать?
Считайте машину потенциально скомпрометированной. Безопасной версии в зоне атаки не было, потому что @tanstack/react-router использует optionalDependency на вредоносный коммит. Ротейтьте все локальные секреты: GitHub PAT, npm-токены, AWS-ключи, SSH-ключи. Проверьте свежие коммиты в свои репозитории — payload мог уже законспирировать их.
А если pip install mistralai==2.4.6 я запустил из CI?
Сам по себе pip install/pip download/pip wheel не запускает payload — он сработает при первом import. Если CI делает только установку и не импортирует пакет — вы, вероятно, чисты. Если в pipeline есть python -c "import mistralai" или запуск приложения, использующего SDK — окружение скомпрометировано. Безопаснее ротейтить секреты CI на всякий случай.
Почему именно npm и PyPI, а не другие реестры?
Атакующий получил доступ к публикационным credentials в этих экосистемах. На npm и PyPI взлом происходит через украденный publishing-токен, который позволяет залить новые версии без code review. RubyGems, crates.io, NuGet эта кампания не затронула. Архитектурно npm уязвимее остальных за счёт повсеместного preinstall/postinstall.
Почему C2 нельзя зарубить?
Эксфильтрация идёт через Session — peer-to-peer onion-routed мессенджер на сети Oxen. Snode-адреса разрешаются динамически из seed-нод. Можно заблокировать domain seed1/2/3.getsession.org, но это не сломает уже работающие swarm-соединения. Кроме того, payload параллельно эксфильтрирует через GitHub repository contents API, что ещё сложнее зарубить — это легитимная инфраструктура.
Это связано с March-атакой axios?
По SafeDep, нет. Та атака целилась в один высокоценный пакет; нынешняя — массированная по сотням пакетов одновременно. Тактика разная, но root cause общий: украденные publishing credentials дают атакующему unrestricted access на публикацию любых версий.
Выводы
Эта атака подсветила два архитектурных риска, которые мы коллективно проигнорировали. Первый — preinstall/postinstall-скрипты в npm по дефолту запускаются от лица пользователя; одно зависимое дерево из 100 пакетов даёт атакующему 100 шансов запуститься на dev-машине. Второй — IDE-конфиги в репозиториях. Удобство «открыл проект и сразу работает» оборачивается атакующим прямо тем же удобством: пушнул .claude/setup.mjs в feature-ветку — и payload запускается у каждого, кто склонирует ветку.
Защита здесь — это --ignore-scripts в npm, изолированные среды для install (контейнеры, devcontainers), review-обязательность для всех веток, отслеживание незапланированных .claude/- и .vscode/-файлов в pull-requests.
Полный технический разбор с дизассемблированным payload, swarm-логикой и appendix-ом со всеми пакетами — в посте SafeDep.