ChainDrop: как взлом Keyv превратил npm в конвейер заражённых пакетов

от автора

ChainDrop: как взлом Keyv превратил npm в конвейер заражённых пакетов

За несколько часов вредоносный код попал как минимум в 452 npm‑пакета и 2265 опубликованных артефактов. Червь крал ключи от GitHub, облаков и платёжных сервисов, а затем использовал чужие учётные записи, чтобы выпускать новые заражённые версии.

4 августа 2026 года злоумышленник получил возможность действовать от имени сопровождающего Keyv — одной из самых загружаемых библиотек экосистемы JavaScript. В официальный репозиторий попал вредоносный код, после чего версия keyv@6.0.0 была опубликована в npm через настоящий механизм GitHub Actions и получила корректную аттестацию происхождения.

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

По состоянию на 5 августа живой трекер Socket учитывал 452 уникальных пакета и 2265 заражённых артефактов. Это крупнейшая публично задокументированная волна заражений npm в 2026 году по числу затронутых пакетов. Называть её «крупнейшей хакерской атакой года» вообще некорректно: такой вывод потребовал бы сопоставления с атаками на телекоммуникационные сети, государственные системы и крупные корпорации, для которого открытых данных нет.

Живой трекер Socket: 2265 заражённых артефактов и 452 уникальных пакета

Живой трекер Socket: 2265 заражённых артефактов и 452 уникальных пакета

Живой трекер кампании: 2265 заражённых артефактов и 452 уникальных пакета. Источник: Socket, снимок сделан 5 августа 2026 года.

Как работала схема

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

В случае ChainDrop цепочка выглядела так:

  1. Злоумышленник получил права, позволявшие вносить изменения в GitHub‑репозиторий Keyv и запускать официальный процесс выпуска.

  2. В package.json появился сценарий preinstall: node setup.mjs. Такой сценарий выполняется до установки пакета в средах, где разрешены lifecycle‑скрипты зависимостей.

  3. Файл setup.mjs загружал с GitHub легальную среду выполнения Bun версии 1.3.13.

  4. Через Bun запускался второй, значительно более крупный модуль Math_Symbol.js размером около 728 КБ.

  5. Модуль искал на машине ключи, токены, конфигурационные файлы и учётные данные.

  6. Найденные права публикации использовались для добавления червя в другие npm‑пакеты, доступные скомпрометированному разработчику.

  7. Собранные данные отправлялись через GitHub и управляющую инфраструктуру, адрес которой вредоносная программа получала из смарт‑контракта Ethereum.

Это не классическая атака на посетителей сайта. Основной целью стали рабочие станции разработчиков и серверы непрерывной интеграции — CI/CD, где часто хранятся права на репозитории, облака и выпуск программных пакетов.

В package.json Keyv прописан вредоносный preinstall, рядом лежат setup.mjs и Math_Symbol.js

В package.json Keyv прописан вредоносный preinstall, рядом лежат setup.mjs и Math_Symbol.js

В официальной ветке Keyv строка preinstall запускает setup.mjs; в дереве файлов видны setup.mjs и Math_Symbol.js. Источник: GitHub, снимок сделан 5 августа 2026 года.

Четыре часа, за которые атака вышла за пределы Keyv

Реконструкция StepSecurity показывает, что первый вредоносный коммит появился 4 августа в 09:02:37 UTC. Он был сделан в основной ветке Keyv под сообщением о выпуске версии 6.0.0 и добавлял setup.mjs и Math_Symbol.js.

Коммит ee2681a с сообщением release v6.0.0 в официальном репозитории Keyv

Коммит ee2681a с сообщением release v6.0.0 в официальном репозитории Keyv

Коммит ee2681a, с которого началась первая волна. GitHub показывает сообщение release: v6.0.0, 27 изменённых файлов и только пять успешных проверок из десяти. Источник: GitHub, снимок сделан 5 августа 2026 года.

Через две минуты в репозитории появились настройки для Claude Code и Visual Studio Code, обеспечивавшие повторный запуск вредоносного компонента. В 09:35 keyv@6.0.0 был опубликован в npm. Спустя три минуты исследователи уже фиксировали вторую волну заражений в чужих пространствах имён.

Начало временной шкалы атаки в техническом отчёте StepSecurity

Начало временной шкалы атаки в техническом отчёте StepSecurity

Начало временной шкалы: StepSecurity фиксирует коммит ee2681a в 09:02:37 UTC и маскирующее изменение d8c850c в 09:04:30. Источник: StepSecurity, снимок сделан 5 августа 2026 года.

Время 4 августа, UTC

Событие

09:02

В основную ветку Keyv внесён вредоносный коммит ee2681a

09:04

Добавлены механизмы запуска через Claude Code и VS Code

09:23

Удалён маскирующий тест из истории разработки

09:35

В npm опубликован keyv@6.0.0 с действительной provenance‑аттестацией

09:38

Начинается распространение по пакетам за пределами Keyv

09:39–09:52

Вредоносные файлы добавляются в рабочие пакеты пространства @keyv

10:06–10:14

Публикуются заражённые версии семейства Cacheable

10:17

Появляется первое публичное предупреждение

10:25–10:28

Компрометирован и выпущен пакет ecto

с 10:39

npm начинает удаление и откат заражённых версий

до 13:20

Вторая волна продолжает автоматически публиковать новые версии

Первые 11 пакетов не просто получили копию одного файла: они несли полный механизм червя и стали исходными точками распространения.

Пакет

Заражённая версия

keyv

6.0.0

flat-cache

6.1.24

file-entry-cache

11.1.6

cacheable-request

13.0.20

@cacheable/utils

2.5.1

cacheable

2.5.1

@cacheable/memory

2.2.1

cache-manager

7.2.10

@cacheable/node-cache

3.1.2

ecto

5.0.1

@cacheable/net

2.1.1

Во второй волне названия файлов могли меняться: вместо Math_Symbol.js встречался math_init.js. Поэтому проверка только одного имени файла не охватывает всю атаку.

За какими секретами охотился ChainDrop

Исследователи Aikido, Wiz и StepSecurity описывают вредоносный модуль как универсальный сборщик секретов. Он проверял переменные окружения, домашние каталоги, конфигурации инструментов разработки и локальные файлы.

В список целей входили:

  • токены GitHub и npm;

  • ключи AWS, Google Cloud и Microsoft Azure;

  • конфигурации Kubernetes и HashiCorp Vault;

  • SSH‑ключи и настройки Docker;

  • доступы к базам данных;

  • ключи Stripe и токены Slack;

  • данные криптовалютных кошельков;

  • конфигурации и секреты AI‑инструментов разработчика.

Aikido насчитала около 200 шаблонов поиска. Файлы размером более 5 МБ пропускались, а чтение выполнялось параллельно — до 64 операций одновременно. Это позволяло быстро просмотреть машину, не тратя время на крупные файлы.

Отдельный компонент gh-token-monitor закреплялся в системе и следил за состоянием GitHub‑токена. Настройки .claude/settings.json и .vscode/tasks.json создавали дополнительные точки запуска. Простого удаления npm‑папки после заражения поэтому недостаточно: вредоносный код мог уже выйти за пределы каталога проекта.

GitHub, Ethereum и домен npm‑cache.com

ChainDrop использовал несколько каналов связи. Один из них — автоматически создаваемые GitHub‑репозитории с описанием Shai-Hulud: Here We Go Again, куда помещались собранные данные или служебная информация.

SafeDep обнаружила 546 таких репозиториев. Это не 546 подтверждённых жертв: одна заражённая система могла создать несколько репозиториев, а часть активности могла принадлежать исследовательским стендам.

Второй канал был устроен сложнее. Вредоносная программа обращалась к Ethereum‑контракту 0xE1f2395ee43e45A1556EC6438a88c31B83493103 и считывала из него актуальный адрес управляющего сервера. Исследователи наблюдали домен npm-cache[.]com и соединение с маршрутом /router.

Такой приём называют EtherHiding: блокчейн используется как труднее удаляемая адресная книга для командной инфраструктуры. Указанный контракт не является кошельком, на который доказанно переводились украденные деньги. В открытых источниках нет подтверждённой суммы похищенной криптовалюты, и приписывать контракту финансовый оборот атаки нельзя.

Описания последовательности каналов у исследователей различаются. Aikido называет npm-cache[.]com резервом на случай неудачной загрузки в GitHub, тогда как StepSecurity описывает его как действующий двусторонний C2-канал, способный возвращать код для исполнения. Сам факт использования и GitHub, и домена, полученного через Ethereum, подтверждается несколькими независимыми командами.

Место для инфографики: GitHub maintainer → заражённый npm‑пакет → preinstall → Bun → кража секретов → заражение других пакетов; отдельными стрелками показать GitHub dead‑drop и Ethereum → npm-cache[.]com.

Почему «проверенная сборка» оказалась заражённой

Версия keyv@6.0.0 была опубликована не украденным npm‑токеном напрямую. Злоумышленник использовал настоящий GitHub Actions workflow и доверенную публикацию через OIDC. Поэтому npm получил корректную SLSA/provenance‑аттестацию.

Это не сбой криптографической проверки. Аттестация правильно подтвердила, из какого репозитория и каким процессом собран пакет. Но сам исходный код в репозитории к тому моменту уже был изменён злоумышленником.

Документация GitHub прямо предупреждает: аттестация связывает артефакт с исходным кодом и процессом сборки, но не гарантирует безопасность содержимого. Иными словами, «свидетельство о рождении» было подлинным — заражённым оказался сам новорождённый пакет.

Этот эпизод показывает пределы provenance. Она эффективно выявляет подмену между репозиторием и реестром, но не спасает, когда атакующий уже получил право менять официальный источник и запускать официальный выпуск.

Сколько пакетов действительно пострадало

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

Отчёт StepSecurity: 444 пакета и 2212 вредоносных версий

Отчёт StepSecurity: 444 пакета и 2212 вредоносных версий

Оценка StepSecurity — 444 пакета и 2212 вредоносных версий. Она ниже живого трекера Socket из‑за различий во времени и методике подсчёта. Снимок сделан 5 августа 2026 года.

Источник

Уникальные пакеты

Заражённые версии или артефакты

Socket, живой трекер

452

2265

StepSecurity

444

2212

SafeDep

444

2234

Aikido, обновление 5 августа

444

1381

Разница возникла из‑за времени подсчёта и методики. Одни компании считали только версии, ещё доступные в npm, другие добавляли уже удалённые публикации из собственного потока событий. SafeDep, например, нашла 1684 версии в живом реестре и восстановила ещё 550 уже удалённых записей.

Поэтому наиболее точная формулировка на 5 августа звучит так: не менее 452 уникальных пакетов и 2265 заражённых артефактов пакет@версия. Это не 2265 заражённых компаний, не 2265 компьютеров и не 2265 отдельных библиотек.

То же относится к статистике загрузок. По данным StepSecurity, один Keyv загружался более 153 млн раз в неделю, а flat-cache и file-entry-cache — примерно по 150 млн. Но загрузки включают CI, кэши, ботов, повторные установки и безопасные версии. Они показывают потенциальный охват экосистемы, а не число запусков вредоносного кода.

Что известно о реальных запусках

Распространение заражённых версий подтверждено реестром npm. Реальное выполнение на конкретных машинах установить труднее: для этого нужны журналы сборок, сетевые события и результаты расследования внутри каждой организации.

StepSecurity проверила около 44 тыс. публичных запусков GitHub Actions за восьмичасовое окно и нашла 15 последовательностей обращений к npm-cache[.]com. Пять относились к лаборатории самих исследователей. Остальные десять были запусками в публичном CI репозитория Backstage.

Исследователи увидели характерную цепочку Bun → Ethereum RPC → npm-cache[.]com, то есть червь действительно исполнялся за пределами тестовой среды. При этом в тех заданиях не было долговременных секретов репозитория — только краткоживущий GitHub‑токен. Доказательств кражи постоянных учётных данных Backstage команда не обнаружила.

SafeDep связала вредоносные публикации с 12 не связанными между собой организациями. Появление заражённого пакета в пространстве имён Deliveroo, OneReach, ServiceTitan, Picsart или Qlik доказывает компрометацию цепочки публикации соответствующего пакета, но само по себе не доказывает взлом корпоративной сети или утечку клиентских данных. Для такого вывода нужны результаты внутренних расследований компаний.

npm очищен, GitHub — не полностью

К вечеру 4 августа первоначальные 11 заражённых версий были удалены или заменены безопасными публикациями. Страница Keyv в npm 5 августа показывала безопасную ветку 5.6.0.

npm показывает Keyv версии 5.6.0

npm показывает Keyv версии 5.6.0

После удаления заражённой версии карточка npm показывает Keyv 5.6.0, 1703 зависимых проекта и 85 опубликованных версий. Источник: npm, снимок сделан 5 августа 2026 года.

Но официальный репозиторий выглядел иначе. На момент проверки 5 августа история основной ветки по‑прежнему содержала вредоносные коммиты, файл package.json указывал версию 6.0.1, сценарий preinstall и оба вредоносных файла, а релиз v6.0.1 оставался помечен как Latest.

GitHub-релиз Keyv v6.0.1 с setup.mjs и Math_Symbol.js

GitHub‑релиз Keyv v6.0.1 с setup.mjs и Math_Symbol.js

GitHub помечает v6.0.1 как Latest, а описание релиза прямо говорит о повторной публикации пакетов с setup.mjs и Math_Symbol.js. Источник: GitHub, снимок сделан 5 августа 2026 года.

Это создаёт вторичный риск. Безопасная версия в npm не делает безопасным клонирование текущей основной ветки или использование GitHub‑релиза. До официальной очистки и проверки репозитория разработчикам следует фиксировать известную безопасную версию и не собирать Keyv из main.

Что изменилось в npm 12

Фраза «достаточно выполнить npm install, чтобы заразиться» требует уточнения. Она верна для npm 11 и более старых клиентов, а также для окружений, где сценарии зависимостей разрешены настройками или другим пакетным менеджером.

В npm 12 lifecycle‑скрипты сторонних зависимостей по умолчанию заблокированы. Пользователь должен явно разрешить их через allowScripts или процедуру одобрения. Поэтому стандартная конфигурация npm 12 останавливала preinstall ChainDrop до запуска.

Это не отменяет угрозу. Во многих проектах остаются старые версии npm, другие менеджеры пакетов и настройки совместимости, разрешающие install‑скрипты. Кроме того, заражённый исходный код можно было запустить и за пределами обычной установки.

Что делать разработчикам и компаниям

Если один из заражённых пакетов устанавливался или собирался 4 августа, проверять нужно не только node_modules, но и все секреты, доступные процессу установки.

Приоритетные действия:

  1. Сопоставить lock‑файлы и журналы CI с полным списком версий в трекерах Socket, StepSecurity и SafeDep.

  2. Найти хэши вредоносных компонентов: 54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668 для первого setup.mjs, fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb для второй волны и 9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc для Math_Symbol.js/math_init.js.

  3. Проверить обращения к Ethereum RPC, npm-cache[.]com и создание репозиториев с описанием Shai-Hulud: Here We Go Again.

  4. При подтверждённом запуске отозвать и перевыпустить GitHub/npm‑токены, облачные ключи, SSH‑ключи, доступы к Vault, Kubernetes, базам, Stripe и Slack.

  5. Изучить журналы GitHub, npm и облаков на неизвестные коммиты, релизы, публикации и входы.

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

  7. Перейти на npm 12 либо отключить сценарии зависимостей и разрешать их только для проверенного списка пакетов.

Руководство GitHub по реагированию рекомендует сначала сохранить доказательства и определить затронутые учётные данные, а затем проводить отзыв ключей и очистку. Для корпоративной среды эти действия должны выполняться как полноценный инцидент, а не как обычное обновление зависимости.

Кто стоял за атакой

Код и оформление связывают ChainDrop с семейством Shai‑Hulud. Однако имя оператора не установлено. Надпись в GitHub‑репозиториях и повторное использование известного инструментария не доказывают, что за атакой стоит та же группа, которая применяла его раньше.

Не установлен и первоначальный способ доступа к GitHub сопровождающего Keyv. Публичные данные подтверждают несанкционированные действия от его имени и использование штатного release workflow. Был ли украден токен, браузерная сессия или доступ к самой учётной записи, пока неизвестно.

На момент подготовки материала редакция не нашла публичного технического отчёта от npm, GitHub или сопровождающего Keyv с разбором точки первоначального проникновения. Очистка реестра npm видна по удалению версий, но это не заменяет отчёт об инциденте.

Главный вывод

ChainDrop оказался опасен не только количеством публикаций. Атака показала, как доверенная учётная запись, штатный GitHub Actions и настоящая provenance‑аттестация могут вместе выпустить заведомо заражённый пакет, если официальный исходный код уже захвачен.

Наиболее надёжная текущая оценка — 452 уникальных пакета и 2265 вредоносных артефактов за несколько часов. Подтверждены автоматическое распространение, функции сбора и вывода широкого набора секретов, реальные исполнения в публичном CI и связь с управляющей инфраструктурой через Ethereum. Массовое заражение конечных машин и конкретный финансовый ущерб пока не установлены.

Для экосистемы это означает простую вещь: подпись сборки подтверждает маршрут кода, но не его безопасность. Защита требует одновременно ограничивать install‑скрипты, минимизировать права CI, контролировать публикации и отслеживать изменения в самом исходном репозитории.

ссылка на оригинал статьи https://habr.com/ru/articles/1067402/