FlawOpen

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

● npm ecosystem · CWE-506 / CWE-829 · September 2025 · Crítica
Investigación · FlawOpen

Shai-Hulud: el gusano de npm de septiembre de 2025 y la confianza al instalar

La campaña Shai-Hulud de septiembre de 2025 usó cuentas comprometidas de mantenedores de npm y ganchos postinstall maliciosos para robar credenciales y publicar más paquetes infectados.[1]

💡 Explicación en Lenguaje Sencillo (ELI5)

Imagina una fábrica que recibe una caja de piezas con instrucciones que se ejecutan solas al abrirla. Copian las acreditaciones de envío de la fábrica y las usan para mandar cajas manipuladas a otras. Desactivar esas instrucciones automáticas cierra una entrada, pero las piezas aún necesitan revisión antes de usarse.[1]

Conceptos Clave y Términos

Lifecycle hook
En septiembre, postinstall de npm ejecutaba código malicioso durante la instalación. Era comportamiento incluido en el paquete, no una vulnerabilidad de memoria de npm con CVE.
Publishing credential
Un token de npm otorga los permisos configurados para la cuenta o el paquete. La autoridad de publicación robada facilitó la propagación; duración, alcance y MFA dependen de la política.
Trusted publishing / OIDC
OIDC permite a un flujo de CI autorizado obtener credenciales de publicación breves sin guardar un token npm. Un flujo autorizado comprometido sigue siendo peligroso.

Mapeos de marcos

  • Referencia CWE para la debilidad descrita en esta guía: CWE-506 CWE-829

Cronología del Incidente

2025-09-14

GitHub afirma que recibió el aviso sobre Shai-Hulud.[1]

2025-09-22

GitHub comunicó que retiró más de 500 paquetes comprometidos y bloqueó cargas que coincidían con los indicadores del programa malicioso.[1]

Flujo de Ataque Paso a Paso

Paso 1

Versión comprometida

Una cuenta de mantenedor publicó un paquete modificado. El phishing fue una hipótesis para la entrada inicial, no una explicación demostrada para todas las cuentas.[1], [2]

Paso 2

Ejecución al instalar

El postinstall de septiembre se ejecutaba con el contexto del desarrollador o CI. No debe atribuirse a esa ola el preinstall de noviembre.[1], [3], [2]

Paso 3

Exposición de credenciales

El programa malicioso buscaba en la configuración y el entorno claves de npm, GitHub y la nube: el riesgo superaba los tokens de publicación.[1], [2]

Paso 4

Replicación

Las credenciales de publicación robadas permitieron modificar y publicar otros paquetes mantenidos por la víctima, ampliando la campaña.[1], [2]

Código Fuente: Vulnerable vs. Seguro

Comparación segura de planes de instalación en Python: ninguna función ejecuta npm, lee credenciales ni consulta el registro. El ejemplo sigue la documentación de npm v10. El plan reforzado exige un archivo de dependencias revisado, suprime scripts de instalación y no hereda variables de entorno. Un ejecutor real debe proporcionar un binario npm conocido y un directorio personal y configuración aislados, sin secretos ni montajes sensibles. --ignore-scripts no vuelve seguras las importaciones posteriores ni los scripts invocados explícitamente; fijar versiones no certifica código benigno.

IMPLEMENTACIÓN VULNERABLE
def install_plan(reviewed_lockfile, inherited_env):
    # Solo devuelve un plan: no instala paquetes ni ejecuta comandos.
    return {"argv": ["npm", "ci"],
            "env": dict(inherited_env)}
PARCHE SEGURO Y ROBUSTO
def install_plan(reviewed_lockfile, inherited_env):
    # Solo devuelve un plan: no instala paquetes ni ejecuta comandos.
    if reviewed_lockfile is not True:
        raise ValueError("Review the lockfile before installation")
    return {"argv": ["npm", "ci", "--ignore-scripts"],
            "env": {}}

Lista de Verificación de Seguridad para Ingeniería

Fuentes