4 июня 2026 года в реестр пакетов npm была загружена серия вредоносных публикаций, объединённых единой инфраструктурой и целевым механизмом поражения. За 40 минут (с 07:27 до 08:05 UTC) один злоумышленник создал не менее двенадцати учётных записей и опубликовал под ними пакеты, маскирующиеся под вспомогательные утилиты для разработчиков. Каждый пакет содержит обфусцированный пэйлоад размером от 260 до 282 КБ, который выполняется в момент установки через скрипт postinstall. Пэйлоад относится к семейству ранее осуждённого похитителя учётных данных браузера Chromium, известного под именем @apexcraft/nano-key. Атака нацелена на разработчиков, использующих npm в рабочих процессах и CI-средах, а также на любые системы, устанавливающие пакеты из списка скомпрометированных.
Описание
Все пакеты были опубликованы от имени учётных записей, зарегистрированных на одноразовом почтовом домене wshu.net. Адрес каждой записи следовал шаблону: <имя_области>-<шесть_случайных_символов>@wshu.net. Например, для пакета @zymkit/jwtbytes использовался адрес zynkit-sk393b@wshu.net, для @petitcode/eb-retry - petitcode-eq1efk@wshu.net. В файле package.json каждого пакета в поле author указывался адрес в домене @pm.me и ссылка на репозиторий GitHub, соответствующую названию области. Такая маскировка делает каждый пакет внешне не связанным с другими - поиск по одному автору не приводит к обнаружению остальных.
Согласно анализу, все области были созданы в узком временном окне 4 июня 2026 года, а публикации вредоносных версий произведены практически одновременно. Это указывает на автоматизированный процесс развёртывания. Злоумышленник использовал единый инструмент обфускации и единый обёрточный код для пэйлоада, меняя только имя файла-загрузчика. Среди обнаруженных имён: seed.cjs, bootstrap.cjs, prelude.cjs, warmup.js, primer.cjs, preflight.js, tzinit.cjs, tickinit.cjs, encoder.js. Размер файла-пэйлоада всегда значительно превышает размер легитимного кода пакета: в @zymkit/jwtbytes настоящий код занимает около 13 КБ, тогда как dist/prelude.cjs - 282 КБ.
Механизм выполнения основан на скрипте postinstall в package.json. Установка пакета запускает команду, которая передаёт файл пэйлоада интерпретатору Node.js. Пэйлоад проверяет, является ли он главным модулем, и при совпадении вызывает функцию выполнения. Кроме того, два пакета - @petitcode/eb-retry и @briskforge/envcheck - содержат второй триггер: при вызове экспортируемой функции (retry или require модуля) пэйлоад также выполняется. Это позволяет атаке срабатывать даже в случае, если postinstall был отключён флагом --ignore-scripts.
Пэйлоад представляет собой артефакт, созданный с помощью библиотеки javascript-obfuscator. Внутренняя логика включает цикл с перестановкой массива строк, а также декодер на основе Base64 и RC4. Каждый пакет использует уникальную "посевную" строку, поэтому хеши файлов различаются. Однако в двух случаях - @lazyutil/dater@0.9.4 и @glitchpad/throttler@2.2.3 - обнаружен абсолютно одинаковый скомпилированный пэйлоад (SHA256 68b4fe54...), что доказывает принадлежность к одному автору. Для чтения паролей из Chrome-профилей применяется AES-256-GCM, соответствующий схемам app-bound key версий 10 и 11 Chromium. Данные exfiltr по протоколу HTTPS с подменой User-Agent Mozilla/5.0.
Важной особенностью кампании стала так называемая "чистка" версий. После публикации вредоносных версий (например, 0.5.3, 0.10.4 и т.д.) злоумышленник выпускал следующую версию с пустым блоком scripts и без пэйлоада. Например, у @frostnode/waitfor версии 0.10.3-0.10.5 содержали postinstall, а 0.10.6 - уже пустой. Команда npm view показывает последнюю чистую версию, не указывая на наличие вредоносных. Пакет @nullzero/urlcat был полностью удалён автором, что делает анализ его содержимого невозможным. Таким образом, проверка только последней версии не даёт гарантии безопасности.
Общее число скомпрометированных пакетов не окончательно. Поскольку регистрация областей и публикации были плотно упакованы во времени, исследователи продолжают выявлять новые области, использующие ту же инфраструктуру. На данный момент в список подтверждённых индикаторов включены 31 версия, распределённые по 12 областям. Две из них - @zynkit/probe и @frostnode/probe - содержат только зарезервированные области без пэйлоада, что свидетельствует о планомерном захвате пространства имён.
Последствия атаки значительны. Любая установка вредоносной версии на машину разработчика или в CI-окружение приводит к немедленному сбору и отправке паролей, куки и токенов сессий из всех установленных браузеров на базе Chromium (Chrome, Edge, Brave и др.). Это ставит под угрозу корпоративные системы, в которых разработчики используют одни и те же браузерные профили для доступа к внутренним ресурсам, облачным консолям и Git-репозиториям.
Злоумышленник не использовал сложные техники обхода антивирусов: пакеты легко детектируются по размеру аномально большого загрузочного файла. Однако из-за того, что большинство разработчиков и систем автоматической сборки доверяют реестру npm и не проверяют содержимое postinstall, кампания может оставаться незамеченной до момента, когда пароль уже скомпрометирован. Единственный надёжный метод защиты - полный запрет установки любых версий перечисленных пакетов и их областей, а также немедленная смена всех паролей и инвалидация сессий, если подозрительный пакет был установлен на какой-либо хост.
Индикаторы компрометации
Package
- @apexcraft/nano-key 1.3.2
- @apexcraft/nano-key 1.3.3
- @apexcraft/nano-key 1.3.4
- @apexcraft/nano-key 1.3.5
- @apexcraft/nano-key 1.3.6
- @apexcraft/nano-key 1.3.7
- @apexcraft/nano-key 1.3.8
- @briskforge/envcheck 0.5.2
- @briskforge/envcheck 0.5.3
- @briskforge/envcheck 0.5.4
- @bytemend/mfebus 1.4.2
- @chunklab/hexparse 1.0.7
- @chunklab/hexparse 1.1.4
- @chunklab/hexparse 1.1.5
- @chunklab/hexparse 1.1.6
- @frostnode/probe 0.0.1
- @frostnode/waitfor 0.10.3
- @frostnode/waitfor 0.10.4
- @frostnode/waitfor 0.10.5
- @frostnode/waitfor 0.9.0
- @glitchpad/throttler 2.2.3
- @lazyutil/dater 0.8.1
- @lazyutil/dater 0.9.2
- @lazyutil/dater 0.9.3
- @lazyutil/dater 0.9.4
- @nullzero/urlcat 1.4.2
- @petitcode/eb-retry 1.3.5
- @thymelab/logfx 2.15.5
- @tinyfox/shapecheck 0.8.7
- @zynkit/jwtbytes 0.5.3
- @zynkit/probe 0.0.1
- apexcraft-8uiljr@wshu.net
- briskforge-5psyxc@wshu.net
- bytemend-dwzfhq@wshu.net
- chunklab-bpriqx@wshu.net
- frostnode-gk8pbf@wshu.net
- glitchpad-revqgz@wshu.net
- lazyutil-78muyg@wshu.net
- nullzero-rlnozk@wshu.net
- petitcode-eq1efk@wshu.net
- thymelab-v0et8w@wshu.net
- tinyfox-yjwiqz@wshu.net
- zynkit-sk393b@wshu.net
SHA256
- 0d27ca72b6f02faf4db95effb18347a7e2fa2def2034707bf9e56fa217879a3b
- 24c8f9b8ac17c2f88cc01d44543963206472112510962b68cf5f74d598b3b065
- 26ddfae644673e0ad65b63caaaf67c0f7dc6c2b2b4127bb5271f8d03fb62091a
- 2de602e6422a991346aaf0b74ed6bd525215f5177b9f7f267ccb4d82e919273d
- 32d02f806d58a6670f7cc9b93f1d85b22e0e0f535e1f90a62d86918033896f54
- 4e927f22ad04f4ac9b487ae11412fc2a55210188789ac29f3a47ad77931907a5
- 618dfffb6829356c131fded9f4c6528b73b4f9d7ff1fc1d3b457599a12584e29
- 68b4fe54a4c05cd0115535ebd4aa8d3cccb03ea5a685f440314814ba1b89e875
- a9a28f2e9f7e0348092682940b3bf63d47a63afc18eb8bfe628e5ddab0d73b47
- d06ee17d30ebb333ab2e5b6e8a1324fcf95edaaae17b6793ec0f3647338efda1