Как один npm-пакет для защиты кода сам стал источником заражения?

11 июля 2026 года в реестре npm появилась версия 8.14.0 пакета jscrambler — того самого, который разработчики используют для обфускации и защиты JavaScript-кода. Ирония в том, что именно этот релиз оказался троянским конём. Компания Socket засекла подозрительную активность спустя всего 6 минут после публикации — редкий случай, когда реакция на угрозу опередила её распространение на сколько-нибудь заметное число установок. Предыдущая версия, 8.13.0, была чистой.
Внутри пакета обнаружились файлы, которых не было ни в 8.13.0, ни в публичном репозитории jscrambler на GitHub. Первый — dist/setup.js, небольшой загрузчик. Второй — dist/intro.js, файл с расширением.js, но по факту вовсе не JavaScript, а контейнер весом около 7,8 мегабайт, упаковывающий три gzip-сжатых нативных бинарника: под Windows, macOS и Linux. Preinstall-хук запускал setup.js, тот определял операционную систему хоста, извлекал нужный бинарник, записывал его под случайным именем во временную папку системы, помечал исполняемым и запускал в скрытом режиме, без вывода на экран.
StepSecurity и SafeDep независимо друг от друга проверили репозиторий jscrambler на GitHub и не нашли там ни коммита, ни тега, ни pull request'а, соответствующих версии 8.14.0. Последним тегом так и остался 8.13.0. Пакет был опубликован в npm напрямую, минуя обычный процесс релиза, при этом с легитимного аккаунта мейнтейнера. На момент первых сообщений причина оставалась неясной — то ли скомпрометировали сам npm-аккаунт, то ли добрались до пайплайна сборки.
Полезная нагрузка написана на Rust и работает как классический инфостилер: сканирует машину разработчика на предмет секретов и отправляет находки через TLS на сервер злоумышленников. Список целей широкий — учётные данные AWS, Azure, Google Cloud, включая метаданные, которые используют CI-раннеры; сид-фразы кошельков М⃰Mask, Phantom, Exodus; хранилище паролей Bitwarden; сохранённые пароли и куки браузеров; токены сессий Discord, Slack, Telegram, Steam. Отдельно стоит новая для подобных зловредов цель — конфигурации ИИ-инструментов для написания кода: Claude Desktop, Cursor, Windsurf, VS Code, Zed, вместе с API-ключами и учётными данными серверов Model Context Protocol.
На Linux бинарник подключает библиотеку ядра BPF и способен загружать eBPF-программу прямо в память ядра — это уже не просто доступ к файлам пользователя, а закрепление на уровне ядра. Оба исследователя, StepSecurity и SafeDep, зафиксировали это поведение, хотя точное назначение eBPF-кода пока анализируется. На Windows и macOS в код встроены проверки на отладчик. Для устойчивости на Windows создаётся скрытая задача планировщика с перезапуском каждую минуту, на macOS — LaunchAgent, перезагружающий процесс при каждом входе в систему. Адреса управляющих серверов зашиты в бинарник и не всплывали при статическом анализе, но StepSecurity в рантайме зафиксировала обращения к двум жёстко прописанным IP и к инфраструктуре Tor — первые опубликованные сетевые индикаторы этой кампании.
У jscrambler около 15 800 скачиваний в неделю — цифра скромная на фоне крупных npm-инцидентов, затрагивающих миллиарды загрузок. Но для стилера, нацеленного на машины сборки, охват не так важен, как ценность добычи: ключи от облака, токены деплоя, доступ к исходному коду. Для контекста стоит вспомнить сентябрьского червя Shai-Hulud, распространявшегося через install-хуки и токены по сотням пакетов, компрометацию пакетов chalk и debug через фишинг мейнтайнера с перенаправлением криптоплатежей, и мартовский случай с захваченным аккаунтом, через который троян попал в Axios — HTTP-библиотеку с более чем 83 миллионами скачиваний в неделю.
Здесь есть и элемент неудачного тайминга для атакующих. За три дня до инцидента, 8 июля, вышел npm 12, который по умолчанию отключает скрипты установки зависимостей. На нём preinstall-хук требует явного подтверждения — но более старые клиенты npm по-прежнему запускают такие скрипты автоматически, так что защита работает не для всех.
Версия 8.15.0 вышла с того же аккаунта мейнтейнера и заняла верхнюю строчку в списке версий npm, не показывая ни одного признака малвари — ни install-скрипта, ни встроенного бинарника. Проблема в том, что 8.14.0 при этом не была удалена из реестра и осталась доступной для установки, так что закреплённые в lock-файлах или прописанные явными командами сборки продолжали получать заражённую версию. Плагины jscrambler для webpack, gulp, Metro и grunt инцидент не затронул — они остались на чистых июньских релизах без install-хуков, компрометация коснулась только основного CLI-пакета.
Рекомендации на тот момент сводились к следующему: перейти на 8.15.0 либо зафиксироваться на 8.13.0, полностью вычистив упоминания 8.14.0 из lock-файлов и кэшей менеджеров пакетов. Проверить логи установки и записи CI на предмет любого запуска dist/setup.js начиная с 11 июля — фиксированного имени бинарника для поиска нет из-за случайной генерации, поэтому нужно сопоставлять время установки с запуском дочерних процессов Node и исполнением файлов во временных директориях. На Windows стоило проверить планировщик задач на скрытые записи, на macOS — просмотреть ~/Library/LaunchAgents на предмет незнакомых plist-файлов. Если 8.14.0 всё же запускалась, все доступные с этой машины секреты следовало считать украденными, а не просто потенциально раскрытыми: ротация облачных ключей, токенов npm и GitHub, ключей ИИ-инструментов и MCP, отзыв сессий Discord, Slack, браузера и Bitwarden, вывод криптовалюты с любых кошельков, установленных на заражённом хосте, и блокировка обоих указанных IP управляющих серверов.
Индикаторы компрометации включают хеш SHA-256 файла dist/setup.js: a742de963f14a92d24ebcbc7b44ac867e23a20d31d1b0094a13a4f83287f4e6 (хеши intro.js и распакованных полезных нагрузок в исходных материалах приведены не полностью). Сетевые индикаторы — IP-адреса 37.27.122[.]124 и 57.128.246[.]79, а также обращения к инфраструктуре Tor через check.torproject[.]org и archive.torproject[.]org. На заражённой машине можно искать скрытый файл со случайным именем во временной папке в формате.{random} или.{random}.exe для Windows, скрытую задачу планировщика на Windows и LaunchAgent на macOS.
Позже Jscrambler подтвердил: атакующий использовал скомпрометированные учётные данные для публикации в npm. Socket связал с тем же злоумышленником уже пять вредоносных версий, опубликованных примерно за три часа — 8.14.0, 8.16.0, 8.17.0, 8.18.0 и 8.20.0, все с одной и той же кроссплатформенной полезной нагрузкой. В официальном advisory компании из них перечислены только четыре — версия 8.18.0 в списке отсутствует, так что до окончательного прояснения все пять стоит считать подозрительными. Версия 8.15.0, вышедшая между первыми двумя заражёнными релизами, не фигурирует ни в одном из списков и признаётся чистой. При этом метод доставки эволюционировал: в 8.14.0, 8.16.0 и 8.17.0 малварь запускалась через preinstall-хук, а в 8.18.0 и 8.20.0 дроппер переехал прямо в основной код и CLI пакета — срабатывает при импорте или запуске, и команда npm install --ignore-scripts в этих версиях уже не спасает. Advisory компании описывает только вариант с preinstall-хуком и не упоминает этот новый вектор.
Jscrambler заявила, что вторжение ограничилось только пакетом jscrambler в составе продукта Code Integrity, остальные продукты не пострадали. Компания отозвала и обновила учётные данные для публикации и другие секреты, ужесточила пайплайн публикации и пометила вредоносные релизы как deprecated — хотя депрекация сама по себе не мешает установить пакет по точному номеру версии. Расследование продолжается. И Jscrambler, и Socket рекомендуют обновиться до версии 8.22.0, а если одна из заражённых версий уже была установлена — обновиться и провести аудит рабочей станции или CI-раннера, который её получил, с ротацией всех перечисленных ранее учётных данных. Jscrambler также сообщила, что npm сейчас показывает ноль скачиваний вредоносных версий, но сама компания предупреждает: эта статистика запаздывает на часы и всё ещё проверяется, так что нулём пока нельзя доказать отсутствие эксплуатации.


Новое на сайте

20275Может ли обычное письмо взломать вашу почту в Zimbra? 20274Зачем сразу несколько разведок взломали портал полиции Белуджистана? 20273Кошельки, которые «родились слабыми»: как уязвимость Ill Bloom стоила криптовладельцам... 20272Как мошенники используют фальшивую регистрацию passkey, чтобы захватить чужой Microsoft... 20271Как безобидный установщик 7-Zip превращает компьютер в чужой прокси-сервер? 20270Термометр, а не трофей: зачем всем вдруг понадобились базы уязвимостей 20269Почему кнопка «разрешить» в AI-редакторах кода может обмануть даже опытного разработчика? 20268Как китайская группировка Silver Fox превратила инструмент против цензуры в оружие для... 20266Почему физик из Лондона получил один из самых престижных призов в науке за измерение... 20265Сколько времени нужно хакеру, чтобы взломать вашу сеть — и успеете ли вы это заметить? 20264Как ИИ-агент, который должен ловить вирусы, сам стал вирусом 20263Переговорщик по выкупам работал на тех самых хакеров, от которых должен был защищать...
Ссылка