- El paquete malicioso de npm Shai-Hulud 2.0 infectó la máquina de un desarrollador y robó el acceso a la organización de GitHub de Trigger.dev
- La infección comenzó cuando el desarrollador ejecutó
pnpm install y se activó el script preinstall del paquete malicioso, usando la herramienta TruffleHog para robar credenciales
- Durante 17 horas, el atacante clonó 669 repositorios y luego, durante 10 minutos, intentó hacer force-push a 199 ramas y cerrar 42 PR
- Los paquetes y los sistemas de producción no fueron comprometidos, y el ataque fue detectado en 4 minutos, tras lo cual se bloqueó el acceso a la cuenta
- Después del incidente, se reforzó la seguridad con medidas como desactivar scripts de npm, actualizar a pnpm 10, publicación de npm basada en OIDC y protección de ramas aplicada en todos los repositorios
Resumen del ataque
- El 25 de noviembre de 2025, mientras se depuraba internamente en Slack, se detectó una anomalía: varios repositorios recibieron commits de “init” a nombre de Linus Torvalds
- La investigación confirmó que el gusano de cadena de suministro Shai-Hulud 2.0 infectó la máquina de un desarrollador y robó credenciales de GitHub
- Se informó que este gusano infectó más de 500 paquetes de npm y afectó a más de 25,000 repositorios
- Los paquetes oficiales de npm de Trigger.dev (
@trigger.dev/*, CLI) no fueron infectados
Línea de tiempo del ataque
- 24 de noviembre, 04:11 UTC: comenzó la distribución del paquete malicioso
- 20:27 UTC: se infectó la máquina de un desarrollador en Alemania
- 22:36 UTC: primer acceso del atacante e inicio de la clonación masiva de repositorios
- 15:27~15:37 UTC (25 de noviembre): se ejecutó el ataque destructivo durante 10 minutos
- 15:32 UTC: se detectó la anomalía y se bloqueó el acceso en 4 minutos
- 22:35 UTC: se completó la restauración de todas las ramas
Proceso de infección
- Cuando el desarrollador ejecutó
pnpm install, se activó el script preinstall del paquete malicioso, que descargó y ejecutó TruffleHog
- TruffleHog escaneó y exfiltró tokens de GitHub, credenciales de AWS, tokens de npm y variables de entorno
- En la máquina infectada se encontraron el directorio
.trufflehog-cache y archivos relacionados
- El paquete que causó la infección fue eliminado y ya no puede rastrearse
Actividad del atacante
- Tras la infección, mantuvo actividad de reconocimiento durante 17 horas
- Clonó 669 repositorios usando infraestructura ubicada en Estados Unidos e India
- Mantuvo el acceso con un token de GitHub mientras monitoreaba la actividad del desarrollador
- Creó un repositorio llamado “Sha1-Hulud: The Second Coming”, que se cree fue usado para almacenar credenciales
- Después, realizó acciones destructivas durante 10 minutos
- Intentó hacer force-push a 199 ramas en 16 repositorios
- Cerró 42 PR; algunas acciones fueron bloqueadas por la protección de ramas
- Todos los commits aparecían con el formato “Linus Torvalds <email> / init”
Detección y respuesta
- La anomalía se detectó en tiempo real mediante alertas de Slack
- En 4 minutos se bloqueó el acceso a GitHub de la cuenta infectada, y luego se revocó también el acceso a todos los servicios, incluidos AWS, Vercel y Cloudflare
- El análisis de logs de AWS CloudTrail mostró solo llamadas de API de solo lectura, sin acceso a datos de producción
- AWS también detectó por su cuenta actividad sospechosa relacionada con Shai-Hulud y envió una alerta
Daño y recuperación
- 669 repositorios clonados, 199 ramas con intentos de force-push y 42 PR cerrados
- La recuperación fue difícil por la falta de reflog del lado del servidor en GitHub, pero usando la Event API y el reflog local se restauró todo en 7 horas
- Los paquetes de npm y la infraestructura de producción no fueron comprometidos
Exposición de una clave de GitHub App
- Durante la investigación se encontró una clave privada de GitHub App en la papelera de la laptop del desarrollador
- Esa clave tenía permisos de lectura/escritura sobre repositorios de clientes y fue rotada de inmediato
- Como la base de datos (donde se almacenan los installation IDs) no fue comprometida, no hay evidencia de acceso a repositorios de clientes, aunque no puede descartarse por completo
- Se solicitó al soporte de GitHub acceso a logs adicionales y se envió un aviso por correo electrónico a los clientes
Análisis técnico de Shai-Hulud
- Al ejecutarse
setup_bun.js, instala el runtime de Bun y ejecuta bun_environment.js en segundo plano
- Usa TruffleHog para recolectar credenciales dentro del directorio
$HOME
- Los datos recolectados (
contents.json, cloud.json, truffleSecrets.json, etc.) se suben a repositorios aleatorios de GitHub en formato de triple codificación base64
- Si existe un token de npm, modifica y vuelve a publicar paquetes de la cuenta infectada para propagar el gusano
- Si no encuentra credenciales, intenta borrar el directorio home
- Archivos indicadores de infección:
setup_bun.js, bun_environment.js, .trufflehog-cache/, etc.
Medidas de refuerzo de seguridad
- Desactivación total de scripts de npm (
ignore-scripts=true)
- Actualización a pnpm 10: scripts desactivados por defecto y configuración de
minimumReleaseAge (3 días) para retrasar la instalación de paquetes nuevos
- Adopción de npm Trusted Publishers basado en OIDC para eliminar tokens de larga duración
- Protección de ramas aplicada a todos los repositorios
- Adopción de Granted en AWS SSO, con cifrado de tokens de sesión
- En GitHub Actions, ahora se requiere aprobación para ejecutar workflows de contribuidores externos
Lecciones para otros equipos
- La propia ejecución arbitraria de código al instalar paquetes npm es una superficie de ataque
- Es necesario configurar
ignore-scripts=true y mantener una lista blanca solo de los paquetes necesarios
- pnpm
minimumReleaseAge ayuda a retrasar la instalación de paquetes nuevos
- La protección de ramas y los despliegues basados en OIDC son medidas de seguridad esenciales
- No se deben almacenar credenciales de larga duración en máquinas locales; solo se debe permitir el despliegue mediante CI
- El ruido de las alertas de Slack fue la clave para la detección
Aspecto humano
- El desarrollador infectado no tuvo la culpa; el incidente ocurrió simplemente por ejecutar
npm install
- Durante el ataque se encontraron rastros de que esa cuenta marcó automáticamente con “star” cientos de repositorios aleatorios
- El incidente no revela un error individual, sino una vulnerabilidad estructural del ecosistema
Métricas resumidas
- Desde la infección inicial hasta el primer ataque: unas 2 horas
- Tiempo durante el cual el atacante mantuvo acceso: 17 horas
- Duración de la actividad destructiva: 10 minutos
- 5 minutos hasta la detección, 4 minutos hasta el bloqueo
- 7 horas hasta completar toda la recuperación
- Repositorios clonados: 669 / Ramas afectadas: 199 / PR cerrados: 42
Recursos de referencia
- Socket.dev: Shai-Hulud Strikes Again V2
- Informes de análisis de PostHog, Wiz, Endor Labs y HelixGuard
- Documentación de npm Trusted Publishers, pnpm
onlyBuiltDependencies, minimumReleaseAge y Granted
3 comentarios
Se suponía que
pnpm, por su estructura, debía permitirpost-installde forma individual por defecto, pero al final parece que incluso los desarrolladores terminan autorizándolo inconscientemente.Entiendo que
npmestá configurado para ejecutarse de forma predeterminada, así que cambiaron apnpmy desactivaron ese comportamiento por defecto para reforzar la seguridad.Comentarios de Hacker News
Ejecutar
npm installno es negligenciaEl problema es un ecosistema que permite ejecutar código arbitrario durante la instalación de paquetes
Pero la falla de seguridad de fondo es usar un gestor de paquetes donde un tercero puede meter código en mi producto sin ninguna verificación
Al final dependemos indefinidamente de la buena fe y la capacidad de los gestores de paquetes y de quienes los operan
Además, parece que el OP da a entender que guardó credenciales en texto plano en el sistema de archivos
A nivel de lenguaje se puede crear una estructura que limite que el código lea entradas, consuma recursos y solo genere salidas correctas en tipo
No resuelve por completo el problema de la cadena de suministro, pero reduce muchísimo la superficie expuesta
Es como decir: “no está mal que una persona use estas herramientas, pero si todos las usan, entonces el ecosistema es el problema”
Ya se ha demostrado muchas veces que muchas herramientas de desarrollo son inseguras
Si de verdad te importa, deberías demostrarlo con acciones
Cuando usaba VS Code, era molesto tener que instalar plugins hechos por quién sabe quién solo para agregar una función pequeña
Al final da la impresión de que necesariamente terminas ejecutando código en el que tienes que confiar
netrcpara autenticación por HTTPSi usas
gitsobre HTTP, casi siempre existe esta ruta de exposición de credenciales en texto planoHace un año, el maintainer de pnpm propuso “bloquear por defecto los scripts post-install”
Desde la perspectiva del usuario sería incómodo, pero creía que a largo plazo sería un cambio que todos agradecerían
PR relacionada: pnpm/pnpm#8897
Al final este es otro caso en el que la conveniencia le ganó a la seguridad
Dijeron que “la base de datos no fue comprometida”, pero si el atacante tuvo acceso a AWS y a secretos, yo diría que ya fue comprometida
Si existió la posibilidad de acceso, debería considerarse una intrusión
Una vez que el malware se ejecuta, rastrear el origen es casi imposible
pnpm installtambién se completa normalmente, así que es difícil de detectarSi hubieran tenido un EDR como Sentinel One o CrowdStrike, probablemente habría habido más pistas para investigar
Me llamó la atención la parte de “clonaron un total de 669 repos”
Me pregunto si es normal que una empresa con menos de 100 empleados tenga más de 600 repositorios
En la cantidad de repos influye mucho más la antigüedad y el ciclo de vida de los proyectos que el tamaño del equipo
pnpm ya dejó de ejecutar automáticamente scripts de ciclo de vida como
preinstall, así que parece que estaban usando una versión viejaVer PR relacionada
postinstallpnpm bloquea los scripts de las dependencias, pero los scripts a nivel de proyecto siguen ejecutándose
Se agradece que compartieran un análisis post-mortem de forma transparente
Casos así son importantes para toda la industria
Me pregunto si el tráfico del ataque podía distinguirse del tráfico normal de desarrollo
Nosotros también queremos reforzar el filtrado de egress en el entorno de desarrollo, pero
npm installse rompe seguido, así que es complicadoHe estado pensando en la seguridad de
giten una laptop personalPor ahora hago
pushcon claves SSH guardadas localmenteTambién tengo privilegios de administrador, así que es riesgoso. Me pregunto cómo hacerlo más seguro
push/pullcon GitHub OAuth o inicio de sesión desde CLIAsí puedes separar la clave de firma de la clave de acceso, y además manejar por separado la cuenta de administrador
Documentación relacionada: 1Password SSH Agent, Git Commit Signing, GitHub OAuth, GitHub CLI Login
La ventaja es que la clave no puede filtrarse hacia afuera, pero si un malware corre en mi máquina, sigue siendo un riesgo
Ejemplo en Linux, ejemplo en macOS
Con TPM o Yubikey puedes hacerlo un poco más difícil, pero una defensa completa es imposible
Lo seguro es hacer las tareas administrativas en una máquina separada y dedicada
gpg-agentAl hacer
pushocommit, se desbloquea ingresando el PINpushdirecto a la ramamainy haces obligatorio el MFA, al atacante le costará mucho más acceder de inmediato a la rama de despliegueLos commits con el nombre Torvalds eran una marca distintiva común después de la infección
También se menciona en el análisis oficial de Microsoft
Este gusano era muy ruidoso, y algunos atacantes usaron credenciales expuestas para hacer públicos repos privados o modificar readmes como forma de autopromoción
Me pregunto si este tipo de detección habría sido posible si el atacante hubiera exfiltrado información en silencio sin hacer nada destructivo