4 августа 2026 года в реестре пакетов npm (менеджера зависимостей для языка JavaScript) произошла атака на цепочку поставок. Злоумышленники получили контроль над процессом публикации пакета keyv и связанных с ним библиотек. В результате в репозиторий попали обновления, содержащие скрытый вредоносный код. Он запускается до начала работы приложения и может похищать учётные данные разработчиков.
Описание
Компания Snyk провела независимое расследование и насчитала 11 вредоносных релизов. Среди них - keyv версии 6.0.0, несколько пакетов семейства cacheable и библиотека ecto. Восемь из них на момент проверки сохраняли статус актуальных, поэтому они могли устанавливаться автоматически. Остальные три npm удалил, но их копии могли сохраниться в частных зеркалах и локальных хранилищах команд.
Исследователи Snyk не устанавливали заражённые пакеты на свои машины. Вместо этого они скачали архивы и сравнили их с чистыми предыдущими версиями. Такой подход позволил выявить точный набор изменений и подтвердить, что во всех 11 релизах находятся одни и те же вредоносные компоненты. Дополнительно аналитики перечислили все пакеты, связанные с аккаунтом сопровождающего, и проверили каждый релиз, вышедший 4 августа. Это позволило обнаружить не только очевидные подозреваемые пакеты, но и менее известные библиотеки, которые могли остаться незамеченными при точечном анализе.
Как выяснили аналитики Snyk, вредоносный код запускается через сценарий предустановки. Система управления пакетами npm выполняет такой сценарий каждый раз при установке зависимости. Для заражения достаточно, чтобы уязвимая версия пакета попала в список зависимостей проекта, даже если её функции нигде не вызываются.
Механизм атаки двухступенчатый. Сначала работает небольшой загрузчик, который определяет операционную систему и подготавливает среду. Затем он скачивает и запускает основную полезную нагрузку. Этот крупный скрипт собирает с заражённого компьютера секреты: токены GitHub и npm, ключи облачных сервисов, закрытые ключи, пароли к базам данных. В зону риска попадают не только локальные машины разработчиков, но и сборочные серверы, где хранятся секреты для автоматического развёртывания.
Особую опасность представляет механизм поддержания доступа. После похищения токена вредоносная программа устанавливает в систему компонент, который следит за этим токеном. Если владелец отзывает токен, монитор может немедленно уведомить злоумышленника. Поэтому специалисты по безопасности рекомендуют сначала найти и отключить такой механизм, а только потом проводить ротацию ключей.
Помимо заражённых пакетов, атакующие добавили вредоносные файлы в репозиторий keyv на GitHub. Эти файлы относятся к конфигурации сред разработки и инструментов автоматизации. Они запускаются при открытии проекта в редакторе кода или при старте специального агента. Следовательно, инфицирование возможно даже без установки пакета: достаточно клонировать репозиторий.
Анализ показал, что вредоносные файлы в опубликованных пакетах и в репозитории идентичны. Это говорит о том, что атакующие использовали один и тот же инструментарий для разных каналов распространения. При этом основная часть кода пакета осталась неизменной, поэтому функциональность библиотек сохранилась. Такая маскировка позволила вредоносным обновлениям долго оставаться незамеченными.
Важно подчеркнуть, что атака не использует уязвимостей в коде самих пакетов. Причину нужно искать в компрометации учётной записи сопровождающего или инфраструктуры публикации. Поэтому пострадавшего мейнтейнера нужно считать жертвой инцидента, а не злоумышленником.
Компания Snyk выпустила официальную рекомендацию, в которой классифицировала инцидент как критический. Причина - вредоносный код получает доступ к рабочей станции разработчика или сборочному серверу с их правами. На момент написания исследования стандартный идентификатор уязвимости CVE (общепринятый номер в базе известных уязвимостей) пока отсутствовал, что типично для активных инцидентов.
Стоит также учитывать, что атака была активной. Вредоносные версии публиковались в течение нескольких часов, а после первых сообщений некоторые из них удалили. В то же время часть пакетов оставалась доступной для установки, что подтверждает реальное распространение угрозы. Отдельного внимания требуют частные реестры пакетов и корпоративные прокси. npm удаляет вредоносные версии из публичного реестра, но локальные копии могут остаться во внутренних хранилищах. Такие копии представляют угрозу, даже если публичные версии уже недоступны.
Предполагаемый масштаб ущерба серьёзен. Пакеты keyv, flat-cache и file-entry-cache входят в сотни тысяч проектов по всему миру, в том числе через популярные инструменты статического анализа кода. Однако значительная часть загрузок приходится на прошлые версии. Вредоносные релизы находились в открытом доступе всего несколько часов, поэтому количество реально заражённых систем, скорее всего, намного меньше общего числа загрузок.
Командам разработчиков следует немедленно проверить зависимости своих проектов. Особое внимание нужно уделить транзитивным зависимостям: многие библиотеки попадают в проект не напрямую, а через вспомогательные инструменты. Если на машине стоит вредоносная версия, её нужно изолировать от сети. Далее необходимо сохранить логи и другие следы для расследования. Затем с чистого компьютера стоит сменить все пароли и токены, а также просмотреть журналы облачных провайдеров и систем контроля версий на предмет подозрительных действий. Проверка включает поиск подозрительных версий в файлах зависимостей и в установленных пакетах. Если подозрения подтвердились, стоит убедиться, что в репозиториях не созданы новые ветки, не изменены настройки автоматизации, не появились неожиданные артефакты сборки.
В качестве превентивных мер стоит ограничить автоматическое выполнение сценариев установки и закрепить проверенные версии в файле блокировки зависимостей. Регулярный аудит зависимостей и мониторинг публикаций помогают вовремя заметить подозрительные изменения. Snyk опубликовала предупреждение об инциденте и продолжает обновлять информацию по мере развития событий.
Индикаторы компрометации
SHA256
- 54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668
- 9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc
- d584f9b6af48b7ed1f93713944f033783bf149e1c25e1643eb8c0e9df5dc7782