Пакет strutil-core описывает себя как набор простых помощников для работы со строками. Устанавливать его достаточно, чтобы на машине заработал червь. Аккаунт jamnajiea выложил чистую версию 1.0.0 первого ноября 2026 года, а менее чем через два часа опубликовал 1.0.1 с полезной нагрузкой внутри. Для запуска не нужно ни подключать библиотеку, ни вызывать её методы.
Описание
Внутри 1.0.1 лежит новая сборка червя testmetest, представителя семейства Miasma. Это семейство выросло из открытого инструментария Shai-Hulud, который выложили в публичный доступ. При этом сам пакет самостоятельный: чужой гем никто не перехватывал, а зависевших от него проектов найти не удалось. Установочный скрипт совпадает с результатом работы модуля, которым червь заражает пакеты в RubyGems. Библиотечная часть осталась от чистой версии: в исходнике даже номер версии не поменялся, хотя гем вышел как 1.0.1.
При установке срабатывает штатный механизм нативных расширений RubyGems. Вместо сборки скрипт расширения скачивает среду выполнения Bun и запускает лежащий рядом JavaScript-файл. По данным исследователей компании Socket, нагрузка вычитывает учётные данные облачных провайдеров, реестров пакетов, Git-хостингов, менеджеров секретов и ассистентов для программирования. Прогон в песочнице зафиксировал обращения к 51 хранилищу секретов. Собранное уходит на сервер paleasbest[.]top, а если тот не отвечает - в публичный репозиторий на GitHub или GitLab. Установка при этом завершается без ошибок: вместо сборки скрипт подставляет пустой файл сборки.
После этого червь пытается размножиться. Внутри есть процедуры перепубликации в другие реестры пакетов, обход списков известных SSH-хостов и поддельный рабочий процесс GitHub Actions, который выгружает секреты как артефакт сканирования. Отдельно стоит Python-бэкдор: раз в час он ищет в публичных коммитах GitHub метку и забирает оттуда ссылку с подписью, которую может поставить только владелец закрытого ключа. На машинах разработчиков нагрузка правит конфигурации AI-инструментов и добавляет задачу редактора, срабатывающую при открытии папки. В CI-раннерах она находит процесс сборки и читает его память: на размещённых у GitHub раннерах для этого хватает прав по умолчанию. Успешных перепубликаций из этого гема не подтвердили.
Ловушка ждёт на этапе отзыва доступа. Если похищенный токен GitHub начинает отвечать отказом, фоновый монитор запускает удаление домашнего каталога пользователя. Проверка идёт каждые 45 секунд, сам монитор живёт двое суток. Ставится он не на каждой заражённой машине, а только там, где червь нашёл токен GitHub. Отсюда неочевидный порядок: сначала снимают монитор, потом отзывают токен. Иначе ответная мера сама запускает уничтожение файлов.
Публичное окно оказалось коротким. RubyGems удалила обе версии примерно через пять часов после выкладки вредоносной. Автоматический сканер заметил JavaScript-файл через 70 секунд после публикации, то есть быстрее, чем пакет успели снять. Сколько систем поставили 1.0.1, неизвестно: счётчики загрузок не равны числу жертв, а основная часть скачиваний пришлась на чистую версию. Подтверждённых жертв у этой сборки нет. Рядом обнаружился родственный пакет на PyPI - pystr-codec с тем же сервером сбора; последняя из трёх его версий появилась меньше чем за три часа до чистой 1.0.0. Сборки у пакетов разные, инфраструктура общая. Удаление гема заражение не отменяет: нагрузка живёт вне каталога пакета.
Показательно, что для заражения хватило одного установочного скрипта в пакете, который никто не собирался подключать. Установка и запуск приложения - больше не два разных события.