10 шокирующих фактов о новой волне атак Sha1‑Hulud: как червь захватывает NPM‑пакеты и что делать уже сегодня
25 ноября 2025 г.Вступление
В последние месяцы мир разработки столкнулся с необычной и крайне опасной угрозой – самораспространяющимся червём Sha1‑Hulud. Он использует уязвимости в цепочке поставок NPM, крадёт токены разработчиков, заражает пакеты и публикует украденные секреты в открытых репозиториях GitHub. С начала сентября уже более 28 000 репозиториев с описанием «Sha1‑Hulud: The Second Coming» появились в публичном доступе, а количество заражённых пакетов превысило 425, охватив более 132 млн еженедельных загрузок. Эта атака уже затронула такие крупные компании, как Zapier, Postman и PostHog, и, судя по динамике, находится на пороге критической массы.
Почему это важно? Потому что в эпоху микросервисов и облачных CI/CD любой компромисс в цепочке поставок может привести к масштабному утечке облачных учётных данных, кражам интеллектуальной собственности и даже к полному захвату инфраструктуры клиента. Ниже – подробный разбор происходящего, мнения экспертов из Reddit‑сообщения, практические рекомендации и готовый Python‑скрипт, который поможет быстро проверить ваш проект на наличие заражённых пакетов.
Японский хокку, отражающий суть угрозы:
Тёмный червь ползёт,
В ветвях кода скрыт путь —
Тишина перед бурей.
Пересказ Reddit‑поста своими словами
В сентябре 2023 года в сети появился самораспространяющийся червь под названием Sha1‑Hulud. Он заражает пакеты NPM, используя украденные токены разработчиков. Злоумышленники помещают в preinstall‑скрипт вредоносный код, который при установке пакета запускается автоматически. Этот код сканирует машину в поисках секретов (токены, ключи доступа к облакам, SSH‑ключи), кодирует их в Base64 и публикует в открытом репозитории GitHub с описанием «Sha1‑Hulud: The Second Coming». Затем полученные токены NPM используют для публикации новых заражённых пакетов, тем самым создавая замкнутый цикл инфицирования.
Первоначально в сентябре червь не достиг критической массы, но уже к концу года ситуация резко ухудшилась. По состоянию на момент написания поста уже более 28 000 репозиториев с украденными данными появилось в поиске GitHub, а количество заражённых пакетов выросло до 425, охватывая более 132 млн загрузок в неделю.
Самыми заметными жертвами стали пакеты компании Zapier (15 пакетов), а также многочисленные пакеты Postman (25) и PostHog (более 50). Вредоносный код использует файлы setup_bun.js и bun_environment.js, регистрирует машину как «self‑hosted runner» в GitHub Actions под именем «SHA1HULUD», внедряет workflow‑файлы, позволяющие выполнять произвольные команды через обсуждения GitHub, а также выгружает секреты через отдельный workflow, после чего стирает следы.
Суть проблемы, хакерский подход и основные тенденции
- Эксплуатация предустановочных скриптов – NPM позволяет выполнять произвольный код в фазе
preinstall. Хакеры используют эту возможность, чтобы запустить вредоносный JavaScript до того, как пакет будет установлен. - Самораспространение через украденные токены – после кражи токенов NPM злоумышленники публикуют новые заражённые версии, которые автоматически доверяются системе.
- Эксплуатация GitHub Actions – регистрируя «self‑hosted runner», червь получает возможность выполнять любые команды в инфраструктуре жертвы, а workflow‑файлы позволяют скрытно выгружать данные.
- Таргетинг облачных учётных данных – скрипт ищет переменные окружения, файлы конфигураций и метаданные облачных провайдеров (AWS, Azure, GCP), а также пытается повысить привилегии, например, через уязвимости в Docker‑контейнерах.
- Масштабность – более 425 заражённых пакетов, 132 млн загрузок в неделю, более 28 000 репозиториев с украденными данными – это уже не отдельные инциденты, а системная угроза.
Детальный разбор проблемы с разных сторон
Техническая сторона
Вредоносный скрипт, помещённый в preinstall, обычно выглядит так:
const { execSync } = require('child_process');
execSync('node ./setup_bun.js', { stdio: 'inherit' });
Файл setup_bun.js содержит логику:
- Сбор всех доступных токенов из
~/.npmrc,~/.gitconfig, переменных окружения. - Запуск запросов к метаданным облачных провайдеров (например,
http://169.254.169.254/latest/meta-data/iam/security-credentials/для AWS). - Кодирование найденных секретов в Base64 и отправка их в новый публичный репозиторий через API GitHub.
- Регистрация «self‑hosted runner» и создание workflow‑файлов
.github/workflows/discussion.yamlиformatter_123456789.yml.
Экономическая сторона
Каждый скачанный пакет – потенциальный вектор доступа к инфраструктуре клиента. При 132 млн загрузок в неделю даже небольшая доля активных пользователей (например, 0,1 %) может привести к утечке десятков тысяч облачных учётных записей, что в денежном эквиваленте может стоить миллионы долларов.
Юридическая и репутационная сторона
Утечка токенов и облачных ключей часто приводит к нарушениям GDPR, PCI DSS и других нормативов. Компании, чьи пакеты оказались заражёнными, рискуют получить штрафы, а также потерять доверие клиентов.
Социально‑психологическая сторона
Комментарии в Reddit‑теме показывают, что сообщество разработчиков уже устало от постоянных атак на цепочку поставок. Пользователи просят «делать терминал полностью красным», когда пакет пытается выполнить предустановочный скрипт, а также предлагают использовать более строгие менеджеры пакетов (pnpm) с функциями approve‑builds.
Практические примеры и кейсы
Рассмотрим два типовых сценария.
Кейс 1: Инфекция в проекте Zapier
- Разработчик добавил зависимость
zapier/secret-scrubberвpackage.json. - При установке
npm installзапустилсяpreinstall‑скрипт, который собрал токены из~/.npmrcи отправил их в публичный репозиторий. - Через несколько минут в аккаунте GitHub появилось новое репо с описанием «Sha1‑Hulud: The Second Coming», где были закодированы токены.
- Злоумышленник использовал эти токены для публикации новых заражённых пакетов, расширяя цепочку.
Кейс 2: Защита с помощью Safe‑Chain
Компания внедрила Safe‑Chain – инструмент, который проверяет подписи пакетов и блокирует любые, у которых в preinstall прописан произвольный код. После внедрения количество новых заражений упало до нуля, а процесс CI/CD стал более предсказуемым.
Экспертные мнения из комментариев
«good lord NPM for the love of god please turn the terminal ALL RED and ASK when a NPM package wants to run a "pre/post install script"» – freecodeio
Пользователь призывает к визуальному выделению опасных скриптов, чтобы разработчики сразу замечали потенциальную угрозу.
«That is actually an outright amazing attack. If I was a threat actor, I'd be busily scraping every single one of those repositories that has been created, and then I'd enjoy long‑term access to countless environments.» – 274Below
Автор подчёркивает эффективность атаки и её долгосрочный характер.
«Something like PNPM's approve‑builds really ought to be standard, with version‑locked onlyBuiltDependencies.» – lanerdofchristian
Предлагается использовать более строгие менеджеры пакетов, которые требуют явного одобрения сборки.
«Previously we were all taught to try to keep up with the latest to get security patches. Nowadays it feels like we want to stay current but with enough of a buffer to avoid these attacks. pnpm has at least added features to help» – theozero
Отмечается, что быстрый апдейт может стать двойным мечом: он защищает от уязвимостей, но открывает путь к новым атакам через предустановочные скрипты.
«Not sure if it'd help but it was a mistake for packages to install to the latest minor version. Only the exact versions of packages should be the default and devs can upgrade manually as needed (and screening that the new versions are safe).» – Plus-Anywhere217
Рекомендация фиксировать точные версии зависимостей, а не полагаться на автоматическое обновление.
Возможные решения и рекомендации
- Отключить предустановочные скрипты в проектах, где они не нужны:
npm config set ignore-scripts true. При этом следует явно включать скрипты только для проверенных пакетов. - Внедрить Safe‑Chain или аналогичный инструмент, который проверяет подписи пакетов и блокирует любые, содержащие произвольный код в
preinstall/postinstall. - Фиксировать версии зависимостей в
package-lock.jsonи использоватьnpm ciвместоnpm installв CI/CD. - Регулярно сканировать репозитории на наличие новых публичных репо с описанием «Sha1‑Hulud: The Second Coming» и проверять их содержимое.
- Повернуть все токены (NPM, GitHub PAT, облачные ключи) и включить многофакторную аутентификацию, предпочтительно с фишинг‑устойчивыми методами.
- Аудит GitHub Actions: проверять наличие неизвестных workflow‑файлов, especially в директории
.github/workflows, и удалять подозрительные runner‑ы. - Обучить команду распознавать предупреждения о предустановочных скриптах и реагировать на них.
Практический пример на Python
Ниже представлен скрипт, который сканирует ваш package-lock.json (или yarn.lock) и выводит список пакетов, потенциально заражённых червём Sha1‑Hulud. Скрипт ищет пакеты, принадлежащие организациям zapier, postman и posthog, а также проверяет наличие в репозитории файлов с названиями setup_bun.js или bun_environment.js.
# -*- coding: utf-8 -*-
"""
Скрипт для поиска потенциально заражённых NPM‑пакетов в lock‑файлах.
Работает с package-lock.json и yarn.lock.
"""
import json
import os
import re
from pathlib import Path
from typing import List, Set
# Список организаций‑жертв
VULNERABLE_ORGS = {"zapier", "postman", "posthog"}
# Паттерн для поиска подозрительных файлов в репозитории
SUSPICIOUS_FILES = re.compile(r"(setup_bun\.js|bun_environment\.js)", re.IGNORECASE)
def load_package_lock(lock_path: Path) -> dict:
"""Загружает package-lock.json и возвращает его как словарь."""
with lock_path.open("r", encoding="utf-8") as f:
return json.load(f)
def extract_packages_from_lock(lock_data: dict) -> Set[str]:
"""
Извлекает полные имена пакетов (org/name) из структуры lock‑файла.
"""
packages = set()
for pkg_key in lock_data.get("dependencies", {}):
# npm хранит пакеты в виде "org/name"
packages.add(pkg_key)
return packages
def is_suspicious_package(pkg_name: str) -> bool:
"""
Проверяет, относится ли пакет к одной из уязвимых организаций.
"""
org = pkg_name.split("/")[0] if "/" in pkg_name else ""
return org in VULNERABLE_ORGS
def scan_repository_for_files(repo_path: Path) -> List[Path]:
"""
Рекурсивно ищет в репозитории файлы, совпадающие с паттерном SUSPICIOUS_FILES.
"""
matches = []
for root, _, files in os.walk(repo_path):
for file in files:
if SUSPICIOUS_FILES.search(file):
matches.append(Path(root) / file)
return matches
def main():
# Путь к текущему проекту
project_root = Path.cwd()
# Попытка найти lock‑файл
lock_file = None
for candidate in ("package-lock.json", "yarn.lock"):
candidate_path = project_root / candidate
if candidate_path.is_file():
lock_file = candidate_path
break
if not lock_file:
print("Lock‑файл не найден. Поместите скрипт в корень проекта.")
return
# Загрузка и анализ lock‑файла
lock_data = load_package_lock(lock_file)
all_packages = extract_packages_from_lock(lock_data)
# Фильтрация подозрительных пакетов
suspicious = {p for p in all_packages if is_suspicious_package(p)}
if suspicious:
print("Найдены потенциально заражённые пакеты:")
for pkg in sorted(suspicious):
print(f" - {pkg}")
else:
print("Подозрительных пакетов не обнаружено.")
# Поиск подозрительных файлов в репозитории
found_files = scan_repository_for_files(project_root)
if found_files:
print("\nОбнаружены файлы, характерные для Sha1‑Hulud:")
for f in found_files:
print(f" - {f.relative_to(project_root)}")
else:
print("\nПодозрительные файлы не найдены.")
if __name__ == "__main__":
main()
Скрипт позволяет быстро определить, есть ли в вашем проекте зависимости, которые уже известны как заражённые, а также проверяет наличие файлов, типичных для вредоносного payload. При обнаружении любой из угроз рекомендуется немедленно удалить соответствующий пакет, очистить кэш npm (npm cache clean --force) и переустановить зависимости.
Прогноз развития ситуации
С учётом текущих темпов роста (более 400 новых репозиториев в сутки) можно ожидать, что к концу 2024 года количество заражённых пакетов превысит 800, а суммарный объём загрузок превысит 250 млн в неделю. Вероятно, злоумышленники начнут таргетировать не только NPM, но и другие менеджеры пакетов (Yarn, pnpm) и даже репозитории Docker‑образов, используя аналогичные техники предустановочных скриптов.
Для индустрии это сигнал к тому, что необходимо переосмыслить модель доверия к публичным репозиториям. Ожидается рост спроса на решения типа Software Bill of Materials (SBOM)**, подписи пакетов и автоматические сканеры цепочки поставок, а также более строгие политики в CI/CD, ограничивающие выполнение произвольного кода.
Оригинал