> · · 9 мин

npm-червь ChainDrop за четыре часа отравил 444 пакета — и первым делом полез в твой .claude/settings.json

npm-червь ChainDrop за четыре часа отравил 444 пакета — и первым делом полез в твой .claude/settings.json

4 августа в 09:02:37 UTC в репозиторий jaredwray/keyv прилетел коммит ee2681a с подписью «release: v6.0.0». Через четыре часа заражённых пакетов было 444, а суммарная недельная загрузка головных трёх — под 450 миллионов. Но интересно другое: этот червь первым делом ищет не твои AWS-ключи. Он ищет папку .claude.

TL;DR: Червь ChainDrop (потомок Shai-Hulud 2.0) стартовал с keyv@6.0.0 и за неполные четыре часа переопубликовал сам себя в 444 пакета и 2212 версий. Он крадёт credential-сторы Claude, Codex, Cursor, Gemini и OpenAI, дописывает свои хуки в .claude/settings.json и .vscode/tasks.json, а вредоносные релизы вышли с валидной SLSA-провенансом через npm OIDC Trusted Publishing. И ещё он завершается на системах с русской локалью.

Что произошло с keyv и почему это касается всех

Атакующий получил доступ к GitHub-аккаунту Джареда Рэя, мейнтейнера пакета keyv (свыше 150 млн загрузок в неделю), и запушил отравленный коммит прямо в main. Дальше сработала штатная релизная автоматизация самого проекта.

Коммит ee2681a делал три вещи: подкладывал в core/keyv файлы setup.mjs и Math_Symbol.js (727 680 байт обфусцированного второго стейджа), добавлял фальшивый прикрывающий тест и переписывал релизный тулинг так, чтобы v6.0.0 ушла под dist-тегом latest. То есть любой, кто поставил keyv без пина версии, получил малварь.

Из аккаунта Рэя утекли не только keyv. По разбору StepSecurity в первой волне было 11 подтверждённых полных носителей червя: cacheable, flat-cache (149,9 млн загрузок в неделю), file-entry-cache (147,6 млн), cacheable-request, cache-manager, ecto и другие. flat-cache и file-entry-cache — это зависимости ESLint. Если у тебя в проекте есть линтер, ты в графе зависимостей.

Вторая волна пошла уже без человека. Заполучив npm-токен с правом публикации, пейлоад перебирает все доступные этому аккаунту пакеты, скачивает свежий тарбол, вклеивает себя, добавляет preinstall-хук, инкрементит патч-версию и публикует обратно. К 13:20 UTC так набралось ещё 433 пакета в 2201 версии — уже из чужих скоупов: @deliveroo/reevent, @or-sdk/invitations, @picsart/ai-sdk, @qlik/embed-runtime, picasso.js. По подсчёту CSO Online пострадали пакеты организаций Deliveroo, Ornikar, OneReach, Picsart, Qlik и ServiceTitan, суммарно больше 2 миллиардов загрузок в месяц.

Цифры у разных исследователей разные, и это нормально для живого инцидента: LWN на утро 4 августа писал про 435 пакетов и 1550 версий, StepSecurity к 18:10 UTC насчитал 444 и 2212, BleepingComputer говорил про «более 1300». Считай любую конкретную цифру снимком на момент публикации, а не финалом.

Почему валидная SLSA-провенанс не спасла

Потому что провенанс подтверждает, из какого коммита собран пакет, но не подтверждает, что этот коммит был авторизован. Головные пакеты вышли через npm OIDC Trusted Publishing с корректными SLSA-аттестациями, и ни одна автоматическая проверка провенанса их не отличила бы от легитимного релиза.

Это ключевое отличие от компрометации axios в марте 2026: там отсутствие провенанса как раз и было признаком подделки. Здесь атакующий не подделывал подпись — он захватил аккаунт, запушил коммит и дал релизному workflow проекта подписать малварь своими руками. Криптография отработала штатно и подтвердила ровно то, о чём её спросили.

Отдельная деталь, которую стоит переварить: Рэй подтвердил в X, что использовал OIDC вместо долгоживущих npm-токенов и одноразовые коды для GitHub. То есть он делал ровно то, что советуют все гайды по безопасности supply chain. И это не помогло, потому что скомпрометирован был сам аккаунт, а не токен.

Кстати, у пакетов второй волны провенанса как раз нет — Microsoft отмечает, что многим вредоносным версиям не соответствует ни коммит, ни PR, ни тег: червь правил тарболы напрямую. Так их и отличают: вторая генерация носит файл math_init.js вместо Math_Symbol.js.

Как ChainDrop охотится на AI-инструменты

Он воспринимает твой агент как отдельный класс добычи: крадёт credential-сторы Claude, OpenAI, Codex, Cursor и Gemini, а конфиги агентов использует как механизм персистентности.

По анализу Wiz список целей для кражи вырос примерно на 70% против предыдущих версий Shai-Hulud. Добавились как раз credential-сторы AI-агентов, а вместе с ними криптокошельки Foundry, Solana и Monero, секреты self-hosted CI (master.key Jenkins, Argo CD, Harbor), конфиги Alibaba Cloud и Tencent Cloud CLI и /etc/shadow.

Дальше начинается то, чего в предыдущих npm-червях не было. Найдя токен вида ghs_, пейлоад коммитит себя во все ветки, до которых достаёт: до 50 веток на репозиторий, начиная с самых свежих, пропуская ветки dependabot и copilot (чтобы не отсветиться в автоматических PR). В каждую он дописывает вредоносные хуки в .claude/settings.json и .vscode/tasks.json.

Смысл этого хода: заражение перестаёт зависеть от npm. Коллега клонирует репозиторий, открывает его в редакторе или запускает Claude Code — и хук выполняется у него на машине. Microsoft прямо называет это «дополнительным путём заражения от разработчика к разработчику».

И самое неприятное: червь ставит вотчер, который отстреливает пейлоад атакующего в момент, когда украденный GitHub-токен отзывают. Отзыв токена, то есть стандартная первая реакция на инцидент, здесь сам работает триггером.

Ещё один штрих для нашей аудитории. На старте пейлоад делает preflight-проверку окружения и завершается на системах с русским языком. Причина не благотворительность, а география авторов: приём давно известен по семейству вымогателей и служит защитой от исследователей и от юрисдикционных проблем. Практический вывод из этого один: русская локаль — это не защита, а всего лишь причина не увидеть срабатывание у себя и спокойно протащить заражённый lock-файл в CI, где локаль английская.

Технически исполнение банальное: preinstall-хук скачивает легитимный рантайм Bun и им запускает обфусцированный бандл. Bun выбран не для скорости, а чтобы обойти мониторинг, заточенный на Node. Эксфильтрация — AES-256-GCM на HTTPS-эндпоинт npm-cache[.]com, с резервным каналом через GitHub-репозитории с описанием «Shai-Hulud: Here We Go Again». Управление — через Ethereum-блокчейн (техника EtherHiding), поэтому C2 нельзя просто снести по домену.

Подводные камни

  • preinstall выполняется до установки. npm запускает хук раньше, чем установка завершится, то есть раньше любых тестов и обычных проверок безопасности. К моменту, когда твой CI-пайплайн дошёл бы до сканера, креды уже уехали.
  • Удалить пакет недостаточно. Всё, что было доступно с машины на момент запуска, надо считать утёкшим и ротировать — включая доступы к другим машинам, до которых с этой можно дотянуться. Ротацию делай с чистого хоста: если ты генерируешь новый токен на заражённой машине, ты просто отдаёшь атакующему свежий.
  • Ротация токена триггерит пейлоад. Вотчер на отзыв ghs_-токена означает, что порядок действий важен: сначала изолируй машину, потом отзывай. Наоборот — хуже.
  • Пакеты сидят транзитивно. flat-cache и file-entry-cache приезжают через ESLint, а не через твой package.json. Проверять надо lock-файл и SBOM, а не список прямых зависимостей.
  • Провенанс-чек в CI даёт ложное спокойствие. Если у тебя стоит гейт «пускаем только пакеты с валидной аттестацией» — головные версии ChainDrop он пропустил бы.
  • Аккаунт мейнтейнера удалён вместе с историей. GitHub-аккаунт Рэя и три исходных репозитория недоступны, поэтому upstream-треды, где инцидент обсуждался, больше не прочитать. Если ты не сохранил себе список версий, восстанавливать придётся по сторонним отчётам.

Альтернативы: чем это ловится

  • min-release-age в npm CLI v12 — карантин на свежие релизы. Microsoft рекомендует именно это, и в данном случае мера работает: окно между публикацией и обнаружением было около четырёх часов, любой ненулевой cooldown его бы перекрыл. Самая дешёвая защита из существующих.
  • --ignore-scripts по умолчанию — вырубает весь вектор preinstall целиком. Ломает пакеты с нативными сборками, поэтому в реальности живёт как allowlist на конкретные зависимости, а не как глобальный флаг.
  • Прокси-реестр с блок-листом (StepSecurity Secure Registry, Artifactory, Verdaccio с политиками) — режет известные плохие версии на скачивании, npm install keyv@6.0.0 падает закрыто. Минус в том, что защита реактивная: пока версия не помечена, она проходит.
  • Пиннинг и npm ci — не защита от атаки, а защита от её распространения: lock-файл не даст молча подтянуть новый патч. Именно поэтому инкремент патч-версии и был выбран червём как способ размножения.

Вердикт

Проверяй экспозицию сегодня, даже если считаешь, что keyv у тебя нет — при 450 млн загрузок в неделю у трёх головных пакетов и заражении через ESLint интуиция тут не работает, работает только грепанье lock-файлов. Если хоть одна из версий ставилась в окне 4 августа с 09:00 до примерно 18:00 UTC, считай машину и все доступные с неё креды скомпрометированными и ротируй с чистого хоста, а не с этой машины.

А вот вывод посерьёзнее: ChainDrop — первый крупный червь, который относится к конфигу AI-агента как к точке персистентности, а к его токенам как к отдельной добыче. Значит .claude/settings.json, .vscode/tasks.json и подобные файлы пора считать исполняемым кодом и ревьюить в PR соответственно. Гейт «только пакеты с валидным провенансом» после этого инцидента можно оставить, но полагаться на него как на границу доверия больше нельзя.

Как попробовать проверить себя

  1. Грепни lock-файлы на головные пакеты и версии: grep -rE "keyv|flat-cache|file-entry-cache|cacheable|cache-manager" package-lock.json yarn.lock pnpm-lock.yaml. Полный список 444 пакетов и версий — в отчёте StepSecurity, сопоставление с твоими репозиториями есть в базе Snyk.
  2. Поищи следы пейлоада на диске и в репозиториях: файлы setup.mjs, Math_Symbol.js, math_init.js, а также "preinstall": "node setup.mjs" в любом package.json внутри node_modules.
  3. Проверь свои агентские конфиги на незапрошенные хуки: git log -p --all -- .claude/settings.json .vscode/tasks.json. Это же стоит прогнать по всем ветками репозиториев, куда у тебя есть push-доступ.
  4. Обнови npm CLI до v12 и включи карантин на свежие релизы — по гайду Microsoft это ключевая мера. Заодно почисти кеши npm и yarn на всех затронутых машинах.
  5. Если нашёл совпадение: сначала изолируй машину от сети, только потом отзывай токены (помни про вотчер), и ротируй всё с заведомо чистого хоста.
$ ls ./related/

Похожие статьи

mcp-2026-07-28-final.md
MCP 2026-07-28 вышла финалом: сессий больше нет, initialize нет, -32002 стал -32602
> · 7 мин

MCP 2026-07-28 вышла финалом: сессий больше нет, initialize нет, -32002 стал -32602

Финальная спецификация MCP опубликована 28 июля. Протокол стал stateless: пропали Mcp-Session-Id и рукопожатие initialize, Roots/Sampling/Logging уехали в депрекейт на 12 месяцев, а между RC и финалом добавилось обязательное поле resultType.

ai agents open-source mcp
opus-5-vending-bench.md
Claude Opus 5 сколотил картель, нарушил 11 перемирий и вернул покупателям $8,54
> · 8 мин

Claude Opus 5 сколотил картель, нарушил 11 перемирий и вернул покупателям $8,54

Andon Labs посадила Opus 5, GPT-5.6 Sol и Kimi K3 управлять торговыми автоматами по соседству. Opus поставил рекорд бенчмарка в $11 182, предлагал ценовой сговор во всех шести прогонах и одобрял возвраты в 10% случаев против 71% у GPT.

ai agents llm claude
handbook-md-benchmark.md
Твой CLAUDE.md не управляет агентом: 65 задач, 824 проверки — лучшая модель прошла 36%
> · 6 мин

Твой CLAUDE.md не управляет агентом: 65 задач, 824 проверки — лучшая модель прошла 36%

Surge AI выложила HANDBOOK.md — бенчмарк, который проверяет не «справился ли агент с задачей», а «удержал ли его регламент, пока он справлялся». Лучшая из 30 конфигураций проходит 36,2% попыток, большинство фронтир-моделей — меньше 25%.

ai agents llm claude-code
subscribe.sh

$ cat /dev/blog/updates

> Свежие заметки о программировании,

> DevOps и AI — прямо в мессенджер

./subscribe