Вредоносный пакет в npm выдавали за обычную библиотеку: почти два миллиона загрузок в неделю и 109 Ethereum

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

Вредоносный пакет indexed-btree скачивали из npm почти два миллиона раз в неделю. Снаружи он выглядел как заурядная библиотека для быстрой работы с индексами и повторял назначение известного пакета с похожим названием. Внутри прятался загрузчик, собиравший сведения о заражённых машинах. К моменту публикации этих данных злоумышленники получили 109 единиц Ethereum, что составляет около 231 тысячи евро. Кампания продолжается, поэтому список пострадавших может вырасти. Риск затрагивает не только отдельных разработчиков. Вместе с ними под удар попадают компании, чьи сервисы собираются из заражённых зависимостей.

Описание

От привычных атак на цепочку поставок этот случай отличается механикой запуска. Обычно вредоносный код привязан к сценариям установки: сценарий выполняется до или после установки пакета и заражает машину ещё до первого запуска программы. Здесь сценариев установки нет вовсе. Проверка зависимостей показывает чистый установочный файл, и защитные средства не находят ничего подозрительного. Заражение стартует позже, в тот момент, когда приложение впервые обращается к функциям библиотеки. Такой поворот событий предсказывали заранее, и он объясняет, почему формально безупречный пакет оставался незамеченным несколько недель.

Как показало исследование, ограничения на автоматический запуск сценариев жизненного цикла не устранили угрозу, а лишь отложили её. Логика злоумышленника переехала со стадии установки на стадию использования. Вредоносная вставка спрятана в ключевой функции библиотеки, той самой, которую вызывают постоянно, при каждом обращении к структуре данных. Как только исполнение доходит до неё, запускается отдельный процесс с намеренно запутанным первым этапом вредоносной программы. Аналитикам сложно заметить такую вставку, потому что она выглядит частью рабочей логики, а не отдельным файлом. Стандартные средства поиска вредоносных фрагментов на неё не реагируют.

Первый этап собирает информацию о жертве. В поле зрения попадают разрядность системы, имя узла, загрузка процессора, объём памяти и время работы без перезагрузки. Собранные данные уходят не на классический сервер, а в заранее созданный канал в Slack и чат в Telegram. Такой способ передачи упрощает злоумышленникам отбор добычи по регионам и типам машин. Судя по набору признаков, интерес представляют как офисные компьютеры разработчиков, так и серверы непрерывной сборки, где обычно хранятся ключи доступа к репозиториям.

Главная особенность кампании - управление через смарт-контракт, программу, работающую в блокчейне. Вместо домена или IP-адреса вредоносная программа опрашивает записи в тестовой сети и получает оттуда команды. Заблокировать домен или отозвать адрес в этом случае не получится. Если прежний указатель перестаёт работать, контракт укажет на новый. Дополнительно загрузчик договаривается с управляющим контрактом о секретном ключе: он создаёт собственную пару ключей, забирает открытую часть от злоумышленника и выводит общий секрет. Этим секретом расшифровываются два зашифрованных фрагмента, которые после соединения образуют вторую ступень вредоносной программы. Пока специалисты не увидели, что именно она делает, поэтому ущерб от неё оценивают осторожно.

Правдоподобность пакета подкреплена поддельным репозиторием на GitHub. Для атак на цепочку поставок это редкость: обычно злоумышленники не утруждают себя созданием публичной истории разработки. Здесь же есть аккаунт, десятки правдоподобных коммитов, то есть записей об изменениях, и сгенерированная нейросетью фотография владельца. В самом репозитории вредоносного кода нет. Он появляется только в опубликованной версии пакета. Разработчик, который заглянет в исходники на GitHub, увидит опрятную библиотеку с документацией и не заподозрит подвох. На момент публикации этих данных репозиторий продолжал работать.

После отработки вредоносная программа способна замести следы: удалить собственные файлы и вернуть изменённую функцию библиотеки в исходный вид. Обнаружить заражение постфактум становится труднее. Число загрузок почти в два миллиона в неделю частично объясняется устройством экосистемы. Библиотека могла попасть в сборки как зависимость, подтянутая другими пакетами, и разработчики даже не подозревали о её присутствии в проекте.

Для компаний последствия зависят от того, какие данные обрабатывались на заражённых машинах. Утечка сведений о конфигурации инфраструктуры сама по себе не критична, но она облегчает подготовку следующего удара. Кроме того, вторая ступень может нести более серьёзные функции, включая доступ к внутренним ресурсам. Защита требует не доверять отсутствию сценариев установки как признаку чистоты пакета. Полезно фиксировать версии зависимостей, проверять новые библиотеки перед включением в сборку, следить за исходящими соединениями и ограничивать права процессов, которые запускают сторонний код.

Схема показывает, куда смещается фокус атак на открытые репозитории. Разработчики получают всё более изощрённые подделки: с историей, документацией и аккуратной упаковкой. Один пакет с миллионами загрузок способен разом доставить вредоносный код в тысячи проектов, и остановить такую доставку сложнее, чем заблокировать домен управления. Защитные меры вроде запрета сценариев установки дают эффект только вместе с проверкой поведения библиотек во время работы приложения.

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

Package

  • ordered-kv-index
  • btree-leaderboard
  • priority-slot-queue
  • btree-range-store
  • btree-core
  • btree-time-index
  • btree-lru-cache
  • neighbor-key-map
  • sliding-score-window
  • mutex-forge

Ethereum Contract address

  • 0xE390863Dac96a7118C71227C2b099B50cF602D31

RPC endpoints

  • https://eth-sepolia.g.alchemy.com/v2/D2-TbkB2m05WXSnSDOCDI
  • https://sepolia.infura.io/v3/dc7257d09fab42eca2c354c32fec1938

Telegram exfil channel

  • Bot token: 8961878831:AAG4WTbRUcbXI5UCaN4VXK8k57ghqqkg_qI
  • Chat ID: -1003952553968

Slack exfil channel

  • Bot token: xoxb-11307403103236-11289767127959-U58yt3zLurAvVoZOf0OBtxCW
  • Channel ID: C0B8XPGCKQS

Cryptographic keys

  • Hardcoded X25519 public key (SPKI/DER, hex): bad013df6eec5d686f4cc8551e0a5c87a0135164bdd1dafb1c75141d1b526702

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