Исследователи из 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