В начале июля 2026 года в реестре PyPI обнаружен вредоносный пакет nhmpy, эксплуатирующий опечатку в названии популярной библиотеки NumPy (n-h-mpy вместо n-u-mpy). Пакет уже удалён из индекса, однако его анализ показал сложную многоступенчатую архитектуру, нацеленную на кражу токенов разработчиков и автоматическое распространение через репозитории GitHub. Инцидент затронул цепочку поставок программного обеспечения и представляет прямую угрозу для сред непрерывной интеграции и развёртывания (CI/CD).
Описание
Пакет nhmpy версии 2.4.7 был опубликован учётной записью elitexp и содержал полную рабочую копию библиотеки NumPy в качестве прикрытия. При установке и импорте nhmpy ведёт себя идентично оригиналу, не вызывая подозрений у разработчика. Вредоносная функциональность реализована всего двумя дополнительными файлами: nhmpy-setup.pth и _index.js объёмом 5,2 мегабайта. Файл .pth выполняется автоматически при запуске любого интерпретатора Python, задолго до того, как пользователь вызовет какой-либо импорт. Такой механизм закрепления делает бесполезным ручной анализ кода на этапе импорта - вредонос срабатывает уже при выполнении pip, pytest или запуске ноутбука Jupyter.
Согласно анализу исследователя, изучившего внутреннее устройство пакета, цепочка запуска начинается с файла .pth, который использует стандартную возможность Python выполнять любую строку, начинающуюся с import. Сценарий проверяет наличие однократной метки %TEMP%/.bun_ran, после чего загружает легитимный рантайм Bun версии 1.3.13 с официального GitHub-релиза компании oven-sh. Bun - это быстрый JavaScript-движок, не требующий установки Node.js. Использование чистой, подписанной сборки Bun позволяет обойти средства защиты, ориентированные на анализ Python-кода, и получить полноценный JS-движок даже в средах, где Node.js отсутствует. После загрузки Bun выполняет файл _index.js. Все операции - загрузка, разархивирование, запуск - осуществляются через встроенные средства Python и утилиты командной строки (curl, unzip), что дополнительно маскирует активность.
Сам _index.js представляет собой четырёхуровневую обфускацию. Первый уровень - простой шифр Цезаря (сдвиг ROT-17), разобрав который исследователь получил около 1,5 мегабайт JavaScript. Второй уровень - шифрование AES-128-GCM с ключами, встроенными непосредственно в файл. Такое шифрование нельзя считать криптографической защитой, однако оно эффективно против сканеров строк. Третий уровень - стандартный обфускатор obfuscator.io с модифицированным алфавитом base64 (буквы в нижнем регистре идут первыми). Четвёртый уровень представляет собой собственный шифр, названный G1: он использует PBKDF2-HMAC-SHA256 для получения мастер-ключа, а затем применяет пермутационную подстановку на основе SHA-256 и алгоритма Фишера - Йетса для каждого отдельного строкового литерала. Такая кастомная сборка останавливает автоматические деобфускаторы, которые справляются с obfuscator.io, но не умеют разбирать нестандартные примитивы.
После расшифровки становится ясна полная функциональность вредоноса. Это стилер учётных данных и самораспространяющийся червь, целящийся в широкий спектр конфиденциальной информации: токены доступа к системам управления версиями (GitHub, NPM, PyPI, RubyGems, JFrog), облачные учётные записи (AWS, Azure, GCP), токены для контейнерных оркестраторов (Kubernetes, Vault), API-ключи для AI-ассистентов (Anthropic), SSH-ключи, историю командной оболочки, данные криптокошельков, мессенджеров и конфигурации VPN. Особое внимание уделено метаданным облачных инстансов (AWS IMDS, Azure IMDS), что позволяет добывать временные токены даже без хранения паролей.
Архитектура эксфильтрации не содержит собственных серверов злоумышленников. Вредонос использует скомпрометированный GitHub-токен жертвы для взаимодействия с API GitHub. Он создаёт репозиторий с описанием Hades - The End for the Damned и записывает туда украденные данные. Для получения дополнительных нагрузок он периодически опрашивает публичный GitHub по ключевому слову TheBeautifulSnadsOfTime. Распространение через репозитории жертвы осуществляется путём добавления в них файла .github/workflows/codeql.yml - вредоносного рабочего процесса GitHub Actions, замаскированного под стандартную проверку безопасности CodeQL. Ветки создаются с типовыми названиями chore/add-codeql-static-analysis, а сообщения коммитов имитируют рутинные обновления (chore: update dependencies, fix: ci). GraphQL-запросы позволяют червю изучить структуру репозиториев жертвы и подобрать подходящие ветки для внедрения.
Строки в расшифрованной полезной нагрузке содержат характерные маркеры, связывающие данный инцидент с волной атак Hades, о которой сообщалось в июне 2026 года. Это копирующий штамм в более широком семействе Shai-Hulud, а не работа оригинальной группировки. Исследователи также обнаружили признаки дополнительных возможностей, не восстановленных в данном образце: механизм прекращения атаки при обнаружении русскоязычной локали, сканер памяти процессов и деструктивную процедуру "уничтожение при отзыве токена". Маркер DontRevokeOrItGoesBoom подтверждает существование такой угрозы, однако сам вредоносный код не был полностью извлечён.
Для обнаружения аномалий рекомендуется отслеживать процессные цепочки, в которых Python запускает Bun, нехарактерные загрузки рантайма Bun с GitHub, а также файлы .pth в site-packages, содержащие строки import os и exec. Дополнительно следует проверять репозитории на наличие новых рабочих процессов codeql.yml на ветках chore/.
Данный инцидент подчёркивает смещение вектора атаки к моменту установки и запуска интерпретатора. Файлы .pth и импорт-хуки позволяют выполнять код до того, как разработчик вызовет хотя бы одну строку из своей программы. Использование легитимных runtime-сред вроде Bun затрудняет сигнатурное обнаружение и переносит акцент на анализ родословной процессов. Отсутствие собственной C2-инфраструктуры, опора на API GitHub и облачные метаданные требует пересмотра подходов к защите: блокировка вредоносных доменов здесь бессмысленна, основными барьерами становятся гигиена токенов, минимальные привилегии в CI, фильтрация исходящего трафика и ограничение областей действия OIDC.
Индикаторы компрометации
SHA256
- 6506d31707a39949f89534bf9705bcf889f1ecae3dbc6f4ff88d67a8be3d01b2
- 999577b1701d051a2ee2174631ee2e127e2d80f3bb0dadaf369a004a8395e050
- c0501df195ae335f6764c214d6dd6cb58e05a188e86313b7a7b10e2cd7fea251