Вредоносный npm-пакет nodemon-sudo маскируется под популярный инструмент и выполняет загрузку второй стадии через IPFS

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

Исследователи из SafeDep зафиксировали новую кампанию по распространению вредоносного программного обеспечения через реестр npm. Злоумышленники используют заражённую копию популярного пакета nodemon под именем nodemon-sudo версии 3.1.16, которая сама по себе не содержит вредоносного кода, но внедряет в зависимости подозрительный пакет tslint-conf (v7.2.1). Этот пакет представляет собой переупакованную версию логгера pino и несёт бэкдор, срабатывающий только во время выполнения приложения. Аналитики классифицировали оба пакета как вредоносные с высокой степенью уверенности, а в реестре Open Source Vulnerabilities (OSV) для tslint-conf зарегистрирована запись MAL-2026-7022. Публикация состоялась 7 июля 2026 года от имени учётной записи conodeeth, и на момент написания статьи пакеты оставались доступными.

Описание

nodemon-sudo представляет собой точную копию оригинального nodemon с номером версии 3.1.16, что на одну ревизию выше актуальной на тот момент версии (3.1.14). Внешне пакет выглядит легитимно: он сохраняет авторство Remy Sharp, стандартный путь к исполняемому файлу bin/nodemon.js и все внутренние библиотеки. Единственное изменение касается списка зависимостей. Вместе с обычными зависимостями (chokidar, debug, semver и другими) в него добавлены chai - тестовый фреймворк, не имеющий отношения к рантайму, и tslint-conf. При этом собственный код nodemon-sudo никогда не импортирует tslint-conf, то есть он существует исключительно для того, чтобы в каталоге node_modules оказался пакет с вредоносной нагрузкой.

Представленный анализ пакета tslint-conf показал, что он является копией логгера pino, в которую добавлены три файла, отсутствующие в оригинале: index.js (точка входа), lib/caller.js и lib/const.js. Именно в них содержится механизм атаки. Триггер активации устроен так, что простое подключение модуля (require('tslint-conf')) не вызывает никаких действий. Вредоносная функция срабатывает только при вызове экспортированного middleware, то есть когда разработчик явно использует пакет в качестве логгера, например, app.use(require('tslint-conf')()). При вызове функция spawn запускает новый скрытый процесс Node.js с параметрами detached: true и stdio: 'ignore', который выполняет lib/caller.js. Родительский процесс завершается, а дочерний продолжает работу в фоне, не оставляя сообщений в консоли.

В lib/caller.js реализован загрузчик второй стадии. Он обращается к шлюзу Pinata IPFS (peach-eligible-penguin-917.mypinata.cloud) с заголовком x-secret-key: _ и получает JSON-документ. Из этого документа извлекается поле cookie, содержимое которого передаётся в конструктор Function. Таким образом, код, полученный из IPFS, выполняется с доступом к реальному объекту require в текущем окружении. Это полноценное удалённое выполнение кода (RCE). Конструктор Function, а не eval, используется для обхода типичных сигнатур детекторов. Загрузчик повторяет попытку до пяти раз, а во время выполнения перехватывает и восстанавливает console.log, чтобы не создавать лишнего шума.

Внутри lib/caller.js присутствует локальный объект process с base64-закодированными строками, которые расшифровываются в мёртвый дроп - адрес jsonkeeper.com/b/XRGF3 и точное значение заголовка x-secret-key. Этот код не выполняется, но документирует настройку злоумышленника. Отдельный файл lib/const.js хранит ссылку на второй дроп (jsonkeeper.com/b/4NAKK), который уже не используется в новых пакетах, но связывает текущую кампанию с предыдущей. По данным SafeDep, идентичная бэкдор-схема была ранее реализована в пакетах nodemon-node и ts-await, которые были отозваны и заменены на заглушки 0.0.1-security. Оператор перевыпустил атаку спустя несколько часов в виде nodemon-sudo и tslint-conf.

Среди индикаторов компрометации (IoC) числятся: учётная запись npm conodeeth (coxhazelqja151@outlook.com), поддельная строка автора Alexus111 с адресом hello@jsonspack.com, ссылка на баги jsonspack.com/issues, хэши SHA-256 артефактов и контрактов IPFS. В манифесте вредоносного пакета присутствуют зависимости sqlite3 и request, что указывает на потенциальную кражу куков браузера и файлов баз данных - именно такие действия предполагает ещё не загруженная вторая стадия. При этом установочный скрипт в пакетах отсутствует: ни preinstall, ни postinstall не объявлены. Это означает, что инструменты, сканирующие только на этапе установки (npm install с флагом --ignore-scripts), не обнаружат атаки. Также не будет срабатывания при импорте модуля, так как триггер - только явный вызов экспортируемой функции.

Эксперты отмечают, что схожий набор техник - маскировка под известный npm-пакет, использование jsonkeeper.com для мёртвых дропов, выполнение кода через конструктор Function - фиксировался в июле 2026 года в публикациях JFrog и The Hacker News, где эти атаки связывались с группировками из Северной Кореи. Точную принадлежность установить не удалось: возможно, это те же операторы или подражатели. В любом случае, руткитная вторая стадия даёт полный доступ к файловой системе и окружению заражённой машины.

Для обнаружения такой угрозы наиболее эффективен анализ графа зависимостей. Обычная проверка имени пакета и его содержимого на верхнем уровне не выявляет атаку, потому что nodemon-sudo не содержит вредоносного кода. Однако появление в списке зависимостей процесса-монитора логгера, который этот процесс никогда не импортирует, а также наличие у логгера собственных зависимостей sqlite3, request и axios при минимальном объёме собственного кода - явный маркер. Специалисты рекомендуют обращать внимание на несоответствие функциональности и зависимостей, а также на поведение в рантайме. Если nodemon-sudo или tslint-conf были установлены на рабочую станцию или сервер, следует считать систему скомпрометированной, удалить оба пакета, ротировать все учётные данные, досягаемые с этого хоста, и проверить исходящие запросы на peach-eligible-penguin-917.mypinata[.]cloud и jsonkeeper[.]com.

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

Package

  • nodemon-sudo v3.1.16
  • tslint-conf v7.2.1

Domain

  • jsonkeeper.com
  • peach-eligible-penguin-917.mypinata.cloud

URL

  • https://jsonkeeper.com/b/4NAKK
  • https://jsonkeeper.com/b/XRGF3
  • https://peach-eligible-penguin-917.mypinata.cloud/ipfs/

SHA256

  • 2956b023858d706a5e241cd28b845088e5f414c5f70bd5d8cb73cb427d081065
  • 541c35bd90653c9d7f93cdadb7f52c281d7a1375ffc9c0e118cf1d9df977dea7
  • aa9b9847e4ffd894a19ec9712848651882b0195de5f4953eef9b47791adddabf
  • d10853dde92fdc48d8cb5505d89e0030fd35e1416a16a820fe5ec4aceef01c4f
  • d4b7add83e5c716b5e52082f713d71ec9d7bfd93e358d500d77c2393302dd004

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