Северной Кореи — а США не могли этого сделать 70 лет
Недавно команда Mandiant из Google связала компрометацию npm-пакета axios с группировкой UNC1069, которая ранее была замечена в киберкриминальных действиях, связанных с кражей криптовалюты и атаками на DeFi-платформы. Вредоносный код был похож на WAVESHAPER — C++ бэкдор, который Mandiant ранее приписывал этой же группе
Северная Корея использовала один из самых популярных HTTP-клиентов для JavaScript в своих атаках. Он имеет 100 миллионов загрузок в неделю. Полезная нагрузка: кроссплатформенный RAT (Remote Access Trojan, троян удалённого доступа), который собирает учётные данные, SSH-ключи и облачные токены с каждой машины разработчика, запускающего npm install
Соединённые Штаты на протяжении семи десятилетий потратили триллионы долларов на сдерживание Северной Кореи. Ядерные переговоры, введение санкций, размещение авианосцев, дипломатические усилия и резолюции ООН не сумели помешать КНДР стать одной из наиболее активных киберугроз в мире
Сэндвич sloppy joe останавливает их за 3 секунды
- Хронология атаки на npm-пакет axios: что произошло 30 марта
- Как sloppy-joe блокирует каждый уровень этой атаки
- Сигнал 1: Ограничение по возрасту версии
- Сигнал 2: Обнаружение нового пакета
- Сигнал 3: Опасный install-скрипт — как npm install запускает RAT
- Сигнал 4: Отсутствие репозитория с исходным кодом
- Сигнал 5: Сходство имени
- Пять сигналов. Один сэндвич. Ноль долларов
- Что sloppy-joe не может обнаружить — и почему это важно знать
- Как подключить sloppy-joe к своему проекту
- Типичные ошибки при защите цепочки поставок
- Частые вопросы о защите цепочки поставок npm
Хронология атаки на npm-пакет axios: что произошло 30 марта
30 марта злоумышленник скомпрометировал npm-аккаунт ведущего мейнтейнера axios (jasonsaayman), используя похищенный токен доступа. Он изменил email аккаунта на адрес Proton Mail и опубликовал две вредоносные версии:
- axios@1.14.1 — опубликована 31 марта, 00:21 UTC
- axios@0.30.4 — опубликована 31 марта, 01:00 UTC
Обе версии внедряли новую зависимость: plain-crypto-js@4.2.1. Этот пакет никогда не импортировался нигде в исходном коде axios. Его единственная цель — запустить postinstall-хук (скрипт, выполняемый после установки пакета), который разворачивал платформо-специфичные RAT:
- macOS: бинарный файл по пути
/Library/Caches/com.apple.act.mond, запускаемый через AppleScript - Windows: PowerShell RAT с персистентностью через реестр и инъекцией бинарного файла в память
- Linux: Python RAT-скрипт через nohup
Злоумышленник подготовил plain-crypto-js за 18 часов до атаки и за 39 минут атаковал обе релизные ветки. Это свидетельствует о серьёзной подготовке и детальном планировании, рассчитанном на минимизацию времени реакции сообщества
Как sloppy-joe блокирует каждый уровень этой атаки
Sloppy-joe — это инструмент для обеспечения безопасности цепочки поставок с открытым исходным кодом. Он проверяет ваш package-lock.json, оценивая каждую зависимость по нескольким независимым критериям перед выполнением npm install, не загружая никакие пакеты и не выполняя код
Когда я впервые запустил sloppy-joe на тестовом проекте, меня удивило именно это: инструмент работает как статический анализатор метаданных реестра, а не как антивирус. Никакой сигнатурной базы, никакого облачного агента
Сигнал 1: Ограничение по возрасту версии
ERROR axios [metadata/version-age] Version '1.14.1' of 'axios' was published 0 hours ago
(minimum: 72 hours). New versions need time for the community and security scanners to
review them. Fix: Wait until the version is at least 72 hours old, or pin to an older version.
Скомпрометированные версии были доступны 2–3 часа до того, как npm их отозвал. 72-часовой барьер означает, что они никогда не будут установлены. Точка. Этот механизм не требует никаких знаний об атаке — он работает исключительно на принципе, что новые версии должны пройти проверку сообществом, прежде чем попасть в продакшн
Одна эта проверка останавливает атаку
Но sloppy-joe не ограничивается одним сигналом. При глубоком транзитивном сканировании с флагом --deep пакет plain-crypto-js подвергается пяти независимым проверкам
Сигнал 2: Обнаружение нового пакета
ERROR plain-crypto-js [metadata/new-package] 'plain-crypto-js' was first published 0 days ago.
New packages are higher risk — verify this is a legitimate, maintained project before depending
on it. Fix: Verify 'plain-crypto-js' at its registry page and source repository.
plain-crypto-js был создан за день до атаки. Совершенно новые пакеты в качестве транзитивных зависимостей пакетов со 100 миллионами загрузок изначально подозрительны — это структурная аномалия, которую sloppy-joe фиксирует без каких-либо внешних фидов
Сигнал 3: Опасный install-скрипт — как npm install запускает RAT
ERROR plain-crypto-js [metadata/install-script-risk] 'plain-crypto-js' has install scripts AND
was published 0 days ago and with 0 downloads. Install scripts on new, low-download packages
are the #1 malware delivery vector. Fix: Do not install this package. Verify it is legitimate
before proceeding.
Install-скрипты + новый пакет + ноль загрузок. Это точный отпечаток атаки на цепочку поставок. Сигнал риска install-скрипта в sloppy-joe объединяет несколько слабых сигналов в высоконадёжное обнаружение. Каждая реальная атака на цепочку поставок npm за последние 5 лет соответствовала этому паттерну
Сигнал 4: Отсутствие репозитория с исходным кодом
WARNING plain-crypto-js [metadata/no-repository] 'plain-crypto-js' has no source repository URL
and is a new package (< 30 days old). Legitimate packages almost always link to their source
code. Fix: Verify 'plain-crypto-js' at its registry page.
Легитимные пакеты ссылаются на свой репозиторий GitHub/GitLab. Вредоносные пакеты, созданные как транспортные средства для доставки пейлоада, этим не утруждаются — и это молчание само по себе является сигналом
Сигнал 5: Сходство имени
WARNING plain-crypto-js [similarity/mutation-match] 'plain-crypto-js' is suspiciously similar
to existing package 'crypto-js' Fix: Verify this is the package you intend to use.
plain-crypto-js — явная попытка выглядеть как crypto-js, настоящий популярный криптографический пакет с сотнями миллионов загрузок. Генераторы мутаций sloppy-joe это улавливают: инструмент проверяет не только точные совпадения, но и типичные паттерны typosquatting (подмены имён пакетов)
Пять сигналов. Один сэндвич. Ноль долларов
Вот что примечательно: ни одно из этих обнаружений не требует фидов разведки угроз (threat intelligence feeds), баз данных сигнатур вредоносного ПО или поведенческого анализа на основе ИИ. Все они — вариации одного примитива: перекрёстная проверка того, что код заявляет об использовании, с тем, что реально существует в реестре
- Версия слишком новая? Пометить.
- Транзитивная зависимость совершенно новая? Пометить.
- У нового пакета есть install-скрипты? Заблокировать.
- Нет репозитория с исходным кодом? Пометить.
- Имя похоже на популярный пакет? Пометить.
Каждый сигнал по отдельности носит информационный характер. Все пять, срабатывающие одновременно на одном пакете, — это уже достоверность. Именно такая комбинация делает ложноположительные срабатывания редкостью, а реальные атаки — практически неизбежно видимыми
Стоимость этой защиты:
# Добавьте в любой CI-пайплайн
npx sloppy-joe check
UNC1069 потратил 18 часов на подготовку пейлоадов, предварительную сборку RAT для трёх платформ и компрометацию аккаунта мейнтейнера. sloppy-joe обнаруживает это за время, необходимое для чтения package-lock.json
Что sloppy-joe не может обнаружить — и почему это важно знать
Честность важна: sloppy-joe не обнаружил бы саму кражу учётных данных. Злоумышленник захватил аккаунт настоящего мейнтейнера — поле npm _npmUser по-прежнему показывает jasonsaayman. Изменения издателя для обнаружения нет
Аттестация происхождения npm (публикация на основе OIDC через GitHub Actions) — это настоящая защита от кражи токенов: вредоносные версии были опубликованы вручную, в обход CI-пайплайна axios. Это отдельный слой защиты, который sloppy-joe не заменяет
sloppy-joe обнаруживает пейлоад, а не компрометацию. Но именно пейлоад причиняет вам вред — и именно здесь инструмент закрывает брешь, которую не закрывает ни один другой бесплатный механизм в стандартном npm-воркфлоу
Как подключить sloppy-joe к своему проекту
# Установка (единый статический бинарный файл, без зависимостей от среды выполнения)
cargo install sloppy-joe
# Или скачайте проверяемый бинарный архив из GitHub Releases
# https://github.com/brennhill/sloppy-joe/releases
# Проверка текущего проекта — автоматически определяет экосистему по файлам манифеста
sloppy-joe check
Для GitHub Actions достаточно одной строки в воркфлоу:
- uses: brennhill/sloppy-joe-action@v1
sloppy-joe — инструмент безопасности цепочки поставок с открытым исходным кодом, который перехватывает галлюцинированные, typosquatted и скомпрометированные зависимости до того, как они попадут в продакшн. Он запускается до вашего пакетного менеджера, не требует выполнения кода и блокирует атаки типа axios, LiteLLM, event-stream и ua-parser-js
Атака на цепочку поставок LiteLLM (март 2026) скомпрометировала пакет с 97 миллионами ежемесячных загрузок. Злоумышленники похитили учётные данные для публикации, выпустили вредоносные версии, которые собирали SSH-ключи, облачные учётные данные и секреты K8s (Kubernetes)
72-часовой барьер по возрасту версии в sloppy-joe по умолчанию заблокировал бы обе отравленные версии — они были обнаружены в течение нескольких часов, задолго до того, как барьер открылся бы. Если вы запускаете sloppy-joe check в CI, эта атака проваливается
Генераторы кода на основе ИИ галлюцинируют имена пакетов примерно в 20% случаев. Злоумышленники регистрируют эти имена и ждут. sloppy-joe перехватывает их в CI до того, как запустится npm install или pip install
Типичные ошибки при защите цепочки поставок
Я вижу одни и те же паттерны у команд, которые считают, что уже защищены, но на деле оставляют открытые окна. На мой взгляд, самая опасная из них — слепое доверие к npm audit как единственному инструменту
Проверяют только прямые зависимости. Атака axios прошла через транзитивную зависимость — plain-crypto-js никогда не появлялся в package.json проекта напрямую. Без флага --deep или аналогичного механизма транзитивный слой остаётся слепой зоной
Полагаются только на npm audit. npm audit работает с базой известных CVE (Common Vulnerabilities and Exposures, база общеизвестных уязвимостей). Новый вредоносный пакет, созданный 18 часов назад, в этой базе не числится — и audit пройдёт чисто
Не фиксируют версии транзитивных зависимостей. Если package-lock.json не закоммичен или регулярно пересоздаётся без проверки, окно для подмены зависимости остаётся открытым при каждом npm install
Запускают проверки только локально, не в CI. Локальная среда разработчика — не единственное место, где происходит установка зависимостей. Сборочный агент, Docker-образ, деплой-пайплайн — каждый из них является точкой входа
Добавление sloppy-joe check как первого шага в CI закрывает все четыре сценария одновременно, не требуя изменений в остальной инфраструктуре
Частые вопросы о защите цепочки поставок npm
Почему 72 часа — именно такой порог для блокировки новых версий?
Это эмпирически выбранное окно, за которое сообщество и автоматические сканеры успевают проверить новый релиз. Большинство атак на цепочку поставок рассчитаны на первые часы после публикации — именно тогда пакет ещё не попал в базы угроз, но уже активно скачивается через CI-пайплайны. 72 часа перекрывают это окно с запасом
Работает ли sloppy-joe только с npm, или поддерживает другие экосистемы?
Инструмент автоматически определяет экосистему по файлам манифеста в проекте. Упоминание pip install в документации подтверждает поддержку Python-экосистемы. Точный список поддерживаемых менеджеров пакетов доступен в репозитории проекта на GitHub
Может ли sloppy-joe создавать ложноположительные срабатывания на легитимные новые пакеты?
Да, теоретически — если вы намеренно добавляете совершенно новый пакет без репозитория и с install-скриптами. На практике легитимные пакеты почти всегда имеют хотя бы ссылку на репозиторий и несколько загрузок. Комбинация пяти сигналов одновременно на одном пакете встречается у реальных атак, а не у добросовестных новых библиотек
Заменяет ли sloppy-joe npm audit или Dependabot?
Нет, и это не цель инструмента. npm audit и Dependabot работают с базами известных уязвимостей — они закрывают угрозы, которые уже задокументированы. sloppy-joe закрывает угрозы нулевого дня в цепочке поставок: новые вредоносные пакеты, typosquatting и slopsquatting (подмена имён, галлюцинированных ИИ-генераторами кода). Оба слоя нужны одновременно
Нужно ли менять структуру проекта или CI-пайплайна для интеграции?
Нет. Достаточно добавить один шаг перед установкой зависимостей: npx sloppy-joe check или - uses: brennhill/sloppy-joe-action@v1 в GitHub Actions. Инструмент читает существующий package-lock.json и не требует изменений в остальной конфигурации



