FlawOpen

flawopen.com/Incidents/shai-hulud-npm-worm-september-2025

● npm ecosystem · CWE-506 / CWE-829 · September 2025 · Критический
Исследования · FlawOpen

Shai-Hulud: червь npm сентября 2025 года и доверие при установке

В сентябре 2025 года кампания Shai-Hulud использовала взломанные учётные записи сопровождающих npm и вредоносные обработчики postinstall, чтобы похищать учётные данные и публиковать новые заражённые пакеты.[1]

💡 Простыми словами (ELI5)

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

Ключевые понятия и термины

Lifecycle hook
В сентябрьской кампании npm postinstall запускал вредоносный код при установке. Это функция пакета, а не ошибка памяти npm с номером CVE.
Publishing credential
Токен npm предоставляет права, заданные для учётной записи или пакета. Украденное право публикации помогало червю распространяться; срок, область доступа и MFA задаются политикой.
Trusted publishing / OIDC
OIDC даёт разрешённому процессу CI краткосрочное право публикации без хранимого токена npm. Взломанный разрешённый процесс всё равно опасен.

Связи со стандартами

  • Ссылка CWE для слабого места, описанного в этом руководстве: CWE-506 CWE-829

Хронология инцидента

2025-09-14

GitHub сообщил, что получил уведомление о Shai-Hulud.[1]

2025-09-22

GitHub заявил об удалении более 500 заражённых пакетов и блокировке загрузок по индикаторам вредоносного кода.[1]

Пошаговый сценарий атаки

Шаг 1

Подменённый выпуск

С учётной записи сопровождающего опубликован изменённый пакет. Фишинг был предположением о первом проникновении, а не доказанным объяснением для каждой учётной записи.[1], [2]

Шаг 2

Запуск при установке

Сентябрьский postinstall выполнялся с правами разработчика или CI. Ноябрьский preinstall нельзя приписывать этой волне.[1], [3], [2]

Шаг 3

Утечка данных

Код искал в настройках и окружении учётные данные npm, GitHub и облачных сервисов; под угрозой были не только токены публикации.[1], [2]

Шаг 4

Размножение

Украденные права публикации позволяли изменять и выпускать другие пакеты, сопровождаемые жертвой.[1], [2]

Исходный код: Уязвимый vs Защищённый вариант

Безопасное сравнение планов установки на Python: ни одна функция не запускает npm, не читает секреты и не обращается к реестру. Пример основан на документации npm v10. Усиленный план требует проверенного файла зависимостей, отключает установочные скрипты и не наследует окружение. Настоящему исполнителю нужны известная программа npm и изолированные домашний каталог и настройки без секретов и чувствительных подключений. --ignore-scripts не делает безопасными последующие импорты или явно вызванные скрипты; закреплённые версии не подтверждают безвредность кода.

УЯЗВИМАЯ РЕАЛИЗАЦИЯ
def install_plan(reviewed_lockfile, inherited_env):
    # Возвращает только план: пакеты не устанавливаются, команды не выполняются.
    return {"argv": ["npm", "ci"],
            "env": dict(inherited_env)}
БЕЗОПАСНЫЙ ИСПРАВЛЕННЫЙ ВАРИАНТ
def install_plan(reviewed_lockfile, inherited_env):
    # Возвращает только план: пакеты не устанавливаются, команды не выполняются.
    if reviewed_lockfile is not True:
        raise ValueError("Review the lockfile before installation")
    return {"argv": ["npm", "ci", "--ignore-scripts"],
            "env": {}}

Чек-лист по защите системы для инженеров

Источники