Вредоносные бета-версии npm-пакетов Joyfill доставляли блокчейн-загрузчик и RAT

Атака на цепочку поставок NPM

28 июля 2026 года в реестр npm были загружены две вредоносные бета-версии пакетов из пространства имён @joyfill. Пакеты @joyfill/layouts@0.1.2-2773.beta.0 и @joyfill/components@4.0.0-rc24-2773-beta.4 содержали встроенный имплант, который запускался не при установке, а при импорте модуля в любом приложении. Это означает, что стандартная команда "npm install --ignore-scripts" не могла предотвратить выполнение вредоносного кода, так как он был внедрён непосредственно в файлы пакетов, а не в сценарии установки.

Описание

Joyfill - это легитимный набор инструментов для встраивания форм, документов и PDF в веб- и мобильные приложения. Оба скомпрометированных пакета имели в сумме около 16 тысяч еженедельных загрузок, и хотя затронуты были только бета-версии, точное число установивших их пользователей неизвестно. Злоумышленники использовали публикацию под существующим аккаунтом и бета-тег, чтобы не перезаписывать стабильные версии. Через несколько часов после публикации вредоносные версии были удалены, однако они остались в кэшах сторонних зеркал реестра.

Цепочка атаки: от npm до удалённого управления

Вредоносный код был встроен в скомпилированные JavaScript-бандлы пакетов. При импорте модуля исполнялся обфусцированный загрузчик, который получал управляющие команды через публичные блокчейн-транзакции. Как показал анализ компании StepSecurity, загрузчик обращался к сети Tron, оттуда извлекал хеш транзакции в BNB Smart Chain, а затем через RPC-запрос получал зашифрованное полезное содержимое. Данные расшифровывались с помощью XOR-ключа и исполнялись в памяти. Такая схема позволяла операторам менять управляющий сервер в любой момент, просто публикуя новую транзакцию, - блокировка традиционных доменов не остановила бы атаку.

Первый этап разрешения давал два независимых потока: один выполнял код непосредственно в текущем процессе, другой создавал скрытый дочерний процесс Node.js с флагами "detached: true", "stdio: "ignore"" и "windowsHide: true". Это позволяло загрузчику продолжать работу даже после завершения основного приложения. На втором этапе загрузчик на основе метки кампании ("A9-0135-3") выбирал один из трёх заранее заданных IP-адресов командного сервера и повторял блокчейн-запрос с другим набором Tron-адресов и ключей.

Финальная нагрузка: удалённый троян и похититель паролей

Третьим этапом загрузчик получал и исполнял 77-килобайтный код удалённого доступа (RAT), написанный на JavaScript и использующий библиотеку Socket.IO для связи с сервером. Восстановленный экспертами код содержал команды для сбора информации о хосте, кражи содержимого буфера обмена (через PowerShell на Windows, pbpaste на macOS и xclip/xsel на Linux), загрузки и выгрузки файлов, выполнения произвольных команд и JavaScript. Троян также мог устанавливать себе зависимости через npm прямо во время работы.

Особую опасность представлял механизм закрепления: RAT вставлял самовоспроизводящийся блок в файлы, которые регулярно используются инструментами разработчика - модуль @vscode/deviceid (применяется в VS Code, Cursor, Antigravity), основной модуль Discord Desktop, файл main.js GitHub Desktop и глобальный клиент npm. Каждая вставка помечалась уникальными комментариями, что позволяло избежать дублирования. Таким образом, заражение одного компьютера могло привести к компрометации всех проектов, с которыми работает разработчик.

Кроме того, отдельный ответ с сервера "/$/boot" загружал второй большой скрипт (около 66 КБ), который в свою очередь доставлял Python-похититель данных. Этот похититель собирал логины и пароли из браузеров (Chrome, Firefox), хранилища менеджеров паролей и криптовалютных кошельков, данные Git и GitHub CLI, а также содержимое системных хранилищ учётных данных (Credential Manager на Windows, Keychain на macOS, Secret Service на Linux). Собранная информация упаковывалась в зашифрованный ZIP-архив и отправлялась на сервер. По оценкам аналитиков, этот похититель является вариантом известного вредоносного ПО OmniStealer.

Связь с другими кампаниями и последствия

Загрузчик и его методы полностью совпадают с семейством PolinRider, а финальный RAT - с семейством DEV#POPPER, которое ранее анализировалось в связи с атакой на форк репозитория ShoeVista. Использование идентичных блокчейн-адресов и ключей шифрования подтверждает, что за обоими инцидентами стоят одни и те же злоумышленники. Отличие лишь в векторе: в случае Joyfill была скомпрометирована учётная запись мейнтейнера, а не внесён вредоносный код через пул-реквест.

Разработчикам следует немедленно удалить из блокировочных файлов и кэша все версии с пометкой 2773-beta. Пакеты @joyfill/layouts@0.1.2-2773.beta.0, @joyfill/components@4.0.0-rc24-2773-beta.4, а также более поздние чистые сборки с тем же бета-номером (например, .beta.5 и .beta.6) должны быть заменены на проверенные стабильные версии, опубликованные до 28 июля. Любое устройство, где эти пакеты были импортированы, следует считать потенциально скомпрометированным, поскольку RAT предоставляет полный удалённый доступ к системе. Необходимо изолировать такие хосты, проверить модификации перечисленных файлов инструментов разработчика, а при подозрении на выполнение Python-похитителя - сменить все пароли, сохранённые в браузерах, и ключи доступа к репозиториям.

Индикаторы компрометации

Package

  • @joyfill/layouts@0.1.2-2773.beta.0
  • @joyfill/components@4.0.0-rc24-2773-beta.4

IPv4

  • 166.88.134.62
  • 198.105.127.210
  • 23.27.13.43
  • 23.27.202.27

IPv4 Port Combination

  • 166.88.134.62:443
  • 166.88.134.62:80
  • 198.105.127.210:443
  • 198.105.127.210:80
  • 23.27.202.27:27017
  • 23.27.202.27:443

Domain

  • api.trongrid.io
  • bsc-dataseed.binance.org
  • bsc-rpc.publicnode.com
  • fullnode.mainnet.aptoslabs.com

SHA256

  • 1352ad22c99983d91e600348b7cbf58235131b1ee34cea9f09623206d5b7dea7
  • 26351aed0397158d3a3b8cc8fd3047d4c015d264c9895f10f20f1521b974ed18
  • 26e679eaf1e9baeb7c55eb48db482301171d4d26e1728544b23734a90dc70e1b
  • 2cfede38fb121a71a2f3607474aa8cd588a99f51b37e5e6f0d8cb789fa275032
  • 36ff00b45e67baa7e3674b0c80f48e88737264c61e5c6b3b091200972de8157c
  • 5f6a92006ca2ea4b464d66fb41af777edce7296939a7c6ee491e2b3cbfe09848
  • 67c6ef602cc850f10d935fee53fa40440df841adf081563bf4fc2631a71249ce
  • 78f0de8682e0e894a5784eb7e95db4da6088f528918ca3107dd1e76f80a561d8
  • 8e8b90dedd456ded0c5748119836e1ca1066112bc569c1b41ca70eb931d1d4dc
  • adc4af90540d33cd1e98f44b51482ae9250fbeb97d6f8d7841c81b618cb2c6e6
  • ae7565109fd01b88d82acf7f73ab20709cbc2c9f26fdea13e429ccc87a55d4fb
  • bcc93dc55bc7daedf4ca57254f0e7a7f1c40e09851eab98fe10cde801982db17
  • c5742ea1875ecd2360022624149994909cd0546e221e4203dffd01f48de45469
  • cb46f12d70824ea24ed1f8bcf45bf3f86680e02a9089aafc03b27f691be57be3
  • f452f9cfa539f4a1fe25187a99a484391290d5dbaa422ba455edf6b04f81b7d1

Комментарии: 0