Специалисты исследовательской группы JFrog Security выявили кластер вредоносных npm-пакетов, маскирующихся под инструментарий Rollup polyfill. Два основных пакета, rollup-packages-polyfill-core и rollup-runtime-polyfill-core, копируют название, содержимое README, метаданные репозитория и структуру легитимного проекта rollup-plugin-polyfill-node. Этот проект широко используется в экосистеме сборки JavaScript - около 295 тысяч загрузок в неделю и более 1,2 миллиона за последний месяц. Пакеты-двойники располагаются в том же пространстве имён rollup, polyfill, core, node, что при быстрой проверке зависимостей выглядит правдоподобно.
Описание
В ходе расследования было зафиксировано шесть пакетов: два упомянутых, а также swift-parse-stream, quirky-token, react-icon-svgs и rollup-plugin-polyfill-connect. На момент написания материала rollup-plugin-polyfill-connect и react-icon-svgs уже получили уязвимые версии на npm, остальные четыре оставались активными. Первые два пакета являются точками входа. rollup-packages-polyfill-core устанавливает и загружает swift-parse-stream, а rollup-runtime-polyfill-core - quirky-token. Пакеты второго этапа представляют собой почти идентичные SVG-утилиты, которые получают JSON-объект с сервиса JSONKeeper и выполняют содержимое поля model через eval.
Такая же схема была замечена у react-icon-svgs, который устанавливал rollup-plugin-polyfill-connect в качестве второго этапа. Слоистая структура, имена-близнецы, легитимные на вид метаданные, скрытое выполнение при установке, проверки окружения и полезная нагрузка для кражи учётных данных и удалённого доступа напоминают предыдущие кампании, связываемые с северокорейской группировкой Lazarus. Ключевое отличие - способ развёртывания полезной нагрузки, который детально проанализирован в отчёте JFrog.
Оба пакета копируют большую часть легитимного проекта: текст README описывает "современный Node.js polyfill для вашей Rollup-сборки", репозиторий и домашняя страница указывают на https://github[.]com/FredKSchott/rollup-plugin-polyfill-node, точка входа содержит легитимный код Rollup-плагина до внедрения вредоносной логики. В образцах вредоносным оказался только CommonJS-файл dist/index.js, ESM-файлы не содержат дополнительного кода.
Цепочка заражения выглядит следующим образом. Два пакета различаются только вторым этапом, после чего оба пути сходятся на нагрузке из JSONKeeper. Вредоносная логика добавлена к безобидному коду Rollup-плагина. В rollup-packages-polyfill-core процедура установки скрыта за именем ValidateSvgModule, которое декодирует команду npm install swift-parse-stream --no-save --silent --no-audit --no-fund и запускает её фоново. Затем пакет декодирует имя модуля swift-parse-stream, подключает его и вызывает функцию getPlugin(). То же самое делает rollup-runtime-polyfill-core с пакетом quirky-token. Заражение происходит в момент импорта пакета в процессе сборки.
Пакеты swift-parse-stream и quirky-token позиционируются как утилиты для очистки SVG. Большая часть их кода - безвредные операции с SVG: хеширование, удаление тегов script, минификация и сохранение файлов. Однако в конце index.js содержится запрос к JSONKeeper по адресу hxxps://www.jsonkeeper.com/b/3P9BF с заголовком bearrtoken: "logo". Функция getPlugin() выполняет этот запрос, парсит ответ и, если поле model является строкой, передаёт его в eval.
Нагрузка из JSONKeeper сначала проверяет окружение. Она завершает работу при обнаружении множества облачных и контейнерных сред: CODESPACE_NAME, CODESANDBOX_HOST, VERCEL, AWS_EXECUTION_ENV, AWS_REGION, AWS_LAMBDA_FUNCTION_NAME, AWS_ACCESS_KEY_ID, GOOGLE_CLOUD_PROJECT, AZURE_FUNCTIONS_ENVIRONMENT, DOCKER, RENDER, GAE_ENV, WEBSITE_SITE_NAME, DYNO, SOCKET_DEV. Также проверяется, содержит ли строка версии операционной системы слово aws. Это позволяет избежать песочниц, облачных сред разработки, серверных runtime и аналитической инфраструктуры.
После проверок нагрузка устанавливает зависимости: npm install axios socket.io-client --no-warnings --no-progress --loglevel silent. Затем отправляет запрос на hxxp://216.126.236[.]244/api/service/98cb54c0b4ac259d30c9c1ca1ae87c68. Ответ содержит IV и шифротекст в base64. Ключ AES получается через crypto.scryptSync с тем же идентификатором. Расшифрованный текст записывается во временную директорию как pack и выполняется через node pack.
Во время анализа удалённый сервер был активен, исследователи смогли получить расшифрованный JavaScript-файл размером около 114 КБ. Этот файл является загрузчиком для нескольких скриптов. В изолированном запуске он пытался записать и запустить скрипты scdata и ldata во временную папку. Также были обнаружены встроенные команды для сбора файлов и мониторинга буфера обмена.
Скрипт scdata устанавливает зависимости socket.io-client, ssh2, node-pty, а также screenshot-desktop, clipboardy и другие. Он использует Socket.IO для связи с сервером 216.126.236.244:4801 и обеспечивает: профилирование хоста (ОС, версия, имя, пользователь), проверку виртуальной машины, выполнение команд, интерактивные терминалы через node-pty, SSH-сессии, на Windows - создание скриншотов, управление мышью и клавиатурой, чтение и вставку буфера обмена. Оператор получает полный контроль над заражённой машиной.
Скрипт ldata нацелен на данные браузеров и криптокошельков. Он загружает файлы на 216.126.236[.]244:4809/upload. Проверяет профили Chrome, Edge, Brave, Opera, LT Browser, ищет Login Data, Web Data, Local Extension Settings. На macOS дополнительно копирует связку ключей login.keychain-db. Среди идентификаторов расширений - известные кошельки, включая MetaMask. Сборщик дублирует хранилища расширений во временную папку перед отправкой.
Отдельная встроенная команда выполняет широкий сбор файлов из домашних директорий, исключая шумные каталоги. Особый интерес представляют файлы .git/config, редакторская история Code, Windsurf, Cursor, а также множество типов файлов: .env, .pem, .key, .secret, файлы, содержащие private key, secret phrase, metamask, bitcoin, solana, и офисные документы. Сканируются также директории .aws, .azure, .ssh, .gnupg, .config, .foundry, .vscode, .cursor, .windsurf, .gemini, .claude, .bash_history, .zsh_history. Это делает нагрузку особенно опасной для рабочих станций разработчиков и сборочных машин, где хранятся API-ключи, SSH-ключи, материалы кошельков и облачные учётные данные.
Ещё одна встроенная нагрузка мониторит буфер обмена с помощью команд pbpaste на macOS и PowerShell на Windows. При изменении содержимого новое значение отправляется на сервер. Это обычный способ перехвата скопированных паролей, токенов, seed-фраз и одноразовых секретов.
Риски значительны, так как компрометация происходит на уровне инструментов сборки. Rollup-плагины часто загружаются из конфигурационных файлов на компьютерах разработчиков и в CI-заданиях. Эти окружения имеют доступ к исходному коду, токенам npm, Git-учётным данным, облачным ключам, SSH-ключам, данным браузеров и секретам проектов. Нагрузка не просто загрузчик - она даёт атакующему как возможности сбора, так и полного контроля.
Рекомендации для пользователей, установивших вредоносные пакеты: удалить их из проектов и lock-файлов, проверить дерево зависимостей на транзитивные включения. На машинах разработчиков и CI-агентах следует искать файлы pack, scdata, ldata во временных директориях, а также строки команд, содержащие node pack, node scdata, node ldata или inline node -e, соединяющиеся с 216.126.236[.]244. Необходимо заблокировать исходящий трафик на этот IP и URL JSONKeeper. Машины стоит считать потенциально скомпрометированными, ротацию учётных данных проводить после удаления активных процессов и закрепления. Также следует проверить браузеры на несанкционированный доступ к хранилищам расширений и конфигурационные директории разработчика.
Эффективность кампании обусловлена тем, что каждый слой выглядит обыденно сам по себе. Пакет входа похож на инфраструктуру Rollup polyfill, второй этап - на SVG-утилиту, ответ JSONKeeper - на структурированные данные. Полная цепочка вскрывает удалённый доступ, кражу браузерных данных и кошельков, сбор файлов и мониторинг буфера обмена. Зависимости-двойники заслуживают тщательной проверки, даже если имя не является очевидной опечаткой. Скопированный README, доверенный адрес репозитория и функциональный код могут скрывать серьёзную компрометацию.
Индикаторы компрометации
URL
- http://216.126.236.244/api/service/98cb54c0b4ac259d30c9c1ca1ae87c68
- http://216.126.236.244/api/service/makelog
- http://216.126.236.244/api/service/process/
- http://216.126.236.244:4801
- http://216.126.236.244:4806/upload
- http://216.126.236.244:4809/cldbs
- http://216.126.236.244:4809/upload
- https://www.jsonkeeper.com/b/3P9BF
Package
- quirky-token
- react-icon-svgs
- rollup-packages-polyfill-core
- rollup-plugin-polyfill-connect
- rollup-runtime-polyfill-core
- swift-parse-stream