Vuelve el gusano de NPM y es mucho peor (Hackeo a TanStack)

BBetter Stack
Computing/SoftwareManagementInternet Technology

Transcript

00:00:00Shai Hulud ha vuelto para una cuarta secuela.
00:00:02Esta vez se dirige a paquetes como TanStack,
00:00:04literalmente horas después de que publicara este vídeo sobre Next.js,
00:00:07lo cual fue una sincronización brillante por mi parte.
00:00:08Este es en realidad un ataque masivo a la cadena de suministro de NPM
00:00:11que afecta a algo más que a TanStack.
00:00:13También afectó a paquetes como UiPath, Mistral,
00:00:15y otros 160 paquetes,
00:00:17incluso incluyendo paquetes PyPy como Guardrails.ai.
00:00:20Lo que hace que este sea aún más divertido
00:00:22es que incluía un interruptor de hombre muerto,
00:00:24así que si detectaba que rotabas tus claves robadas,
00:00:26borraba toda tu PC,
00:00:28y también tenía algo de política global incorporada.
00:00:30Así que vamos a sumergirnos.
00:00:36Para esta secuela, El Gusano tiene el mismo objetivo,
00:00:39robar credenciales de máquinas de desarrolladores y runners de CI/CD,
00:00:42luego usar esas credenciales para llegar a más paquetes.
00:00:44Para TanStack, eso significó publicar 84 versiones maliciosas
00:00:47en 42 paquetes de TanStack en solo unos minutos.
00:00:51Ahora, voy a explicar cómo lograron
00:00:52infectar TanStack en primer lugar,
00:00:54pero primero veamos qué hace el malware en sí
00:00:56si instalaras uno de estos paquetes afectados.
00:00:58Dentro de los paquetes maliciosos,
00:00:59encontrarías un nuevo archivo llamado routerinit.js,
00:01:02así como una dependencia opcional inyectada,
00:01:04que conduce a lo que parece
00:01:05un enlace legítimo de GitHub del enrutador TanStack,
00:01:08pero en realidad es un commit huérfano en el fork del atacante.
00:01:10Esta es solo la forma en que GitHub maneja los enlaces de fork,
00:01:13así que la URL en realidad puede parecer
00:01:14que pertenece al proyecto original,
00:01:16aunque el commit sea en realidad de un fork.
00:01:18En ese fork, hay un script de ciclo de vida,
00:01:20prepare, que ejecuta bun run task runner JS,
00:01:22y tiene exit uno al final.
00:01:24Esa es solo una forma inteligente de hacer que la dependencia opcional falle
00:01:27después de que el payload ya se haya ejecutado,
00:01:28así que la instalación continúa como de costumbre,
00:01:30y deja menos rastros obvios en tus registros de instalación.
00:01:33Además, es posible que hayas notado que esto no está ejecutando
00:01:35ese archivo routerinit.js que dije que fue inyectado
00:01:37en los paquetes al principio,
00:01:38pero por ahora, solo piensa en estos dos archivos
00:01:40como si desempeñaran exactamente el mismo papel con nombres diferentes.
00:01:42La versión corta es: cuando instalas esto,
00:01:44va a ejecutar este script.
00:01:46Lo primero que intenta hacer ese script
00:01:47es desacoplarse del flujo de instalación obvio,
00:01:50así que comprobará si ya se está ejecutando
00:01:51en segundo plano, y si no es así,
00:01:53crea una copia desprendida de sí mismo
00:01:54y sale del script principal limpiamente.
00:01:57De esta forma, tus registros de instalación de npm
00:01:58no muestran nada de la salida del script
00:02:00porque el malware ahora se ha separado
00:02:01de ese proceso y se está ejecutando en segundo plano.
00:02:04Después de esto, hace algo realmente inteligente.
00:02:06Escribe copias de sí mismo
00:02:07en tu directorio de ganchos de Claude Code,
00:02:08luego configura tus ajustes de Claude
00:02:10para ejecutar este gancho cada vez que uses Claude Code
00:02:12en ese proyecto.
00:02:13De esta manera, puede sobrevivir después de la instalación original
00:02:16y seguir disparándose cada vez que
00:02:17abras Claude Code en ese proyecto.
00:02:20De hecho, hace lo mismo con el ejecutor de tareas de VS Code,
00:02:22duplicándose ahí también,
00:02:23así que si usas las funciones de ejecución automática de espacios de trabajo de VS Code,
00:02:26tienes exactamente el mismo problema.
00:02:28Incluso configura un servicio a nivel de SO
00:02:29llamado Monitor de Tokens de GitHub,
00:02:31pero volveremos a eso
00:02:32porque ese es absolutamente diabólico.
00:02:34También es bastante diabólico
00:02:35que aún no te hayas suscrito.
00:02:37Lo siguiente que hace el payload
00:02:38es ponerse a trabajar robando tus credenciales,
00:02:40e intenta de todo.
00:02:41En GitHub Actions, busca credenciales
00:02:43y secretos en el entorno del runner.
00:02:45Más específicamente, analizando la memoria
00:02:47del proceso del worker del runner
00:02:48de GitHub Actions para obtener tus secretos de flujo de trabajo
00:02:50que incluyen secretos enmascarados,
00:02:52e incluso inserta un flujo de trabajo de GitHub falso que parece CodeQL
00:02:55que serializa los secretos de tu repositorio
00:02:57y los exfiltra más tarde.
00:02:58También busca secretos de AWS,
00:03:00primero yendo tras tus variables de entorno
00:03:02y archivos de configuración locales,
00:03:03pero luego también va tras los servicios de metadatos de AWS
00:03:06como IMDS v2 y los metadatos de tareas de ECS.
00:03:09Para Kubernetes, roba tokens de cuentas de servicio
00:03:11y certificados, lo que le permite acceso a la API dentro del clúster
00:03:14a cualquier privilegio de control de acceso basado en roles
00:03:17que tuviera la cuenta de servicio de ese pod,
00:03:19que en clústeres mal configurados
00:03:21puede ser extremadamente amplio,
00:03:22a veces efectivamente administrador.
00:03:24Y para empeorar aún más eso,
00:03:25también va tras HashiCorp Vault,
00:03:27reuniendo todas tus variables de entorno y tokens
00:03:29relacionados con Vault,
00:03:30luego usa cualquier acceso a Kubernetes que tenga
00:03:32para recuperar todos tus secretos gestionados por Vault.
00:03:34Y todo eso es solo lo que hace
00:03:35a tus despliegues de CI.
00:03:37Si está en tu estación de trabajo,
00:03:38va tras todas tus claves SSH,
00:03:39tus credenciales de NPM,
00:03:41tus credenciales de Git,
00:03:42el historial del shell,
00:03:43credenciales de proveedores de la nube,
00:03:44claves criptográficas,
00:03:45Signal,
00:03:45Slack,
00:03:45y archivos de Discord.
00:03:46Y además de todo eso,
00:03:47extrae el historial de sesiones de Claude Code.
00:03:49Así que si alguna vez le has dado credenciales a Claude
00:03:51o has dejado que lea archivos que contienen credenciales,
00:03:53tiene acceso a esos también.
00:03:55Así que sí, como dije,
00:03:56estaban detrás de absolutamente todo
00:03:57lo que podían tener en sus manos,
00:03:58y luego exfiltrarían estos datos
00:04:00a través de la red de mensajería Session.
00:04:02Y como respaldo,
00:04:02también dejaron caer estos datos robados
00:04:04en repositorios de GitHub.
00:04:05Y siguiendo la temática de todos sus ataques,
00:04:07estas ramas tienen nombres de referencias de Dune.
00:04:09Así que tiene tus credenciales.
00:04:11No puede ponerse peor, ¿verdad?
00:04:12Bueno, sí.
00:04:13Sí, puede.
00:04:14Además de todo eso,
00:04:15si recuerdas ese servicio
00:04:16que dije que configura en tu máquina,
00:04:18bueno, ese monitorea tus tokens de GitHub
00:04:19y sigue re-exfiltrándolos.
00:04:21Pero también cada minuto,
00:04:22verifica si el token sigue siendo válido.
00:04:24Y si no lo es,
00:04:25ejecuta RMRF en tu directorio de usuario,
00:04:27borrándolo todo.
00:04:28También intenta crear un token de NPM
00:04:30con tus credenciales,
00:04:31con la descripción,
00:04:32si revocas este token,
00:04:33borraremos la computadora del propietario,
00:04:35dando a entender que hace lo mismo
00:04:36también para los tokens de NPM.
00:04:38Así que si revocas estos tokens
00:04:39antes de aislar tu máquina
00:04:40y eliminar ese proceso en segundo plano,
00:04:42el payload puede autodestruir tu PC,
00:04:44lo cual es simplemente absolutamente diabólico.
00:04:46Y como nota al margen aquí,
00:04:47la variante de Python de este ataque
00:04:48hace más o menos lo mismo,
00:04:49pero también incluye una verificación
00:04:51para ver si el idioma de tu máquina es ruso.
00:04:53Si lo es,
00:04:53simplemente se detiene.
00:04:54Y si tu máquina parece ser
00:04:55de Israel o Irán,
00:04:56genera un número aleatorio
00:04:58entre 1 y 6.
00:04:59Y si ese número es 2,
00:05:00ejecuta un comando de borrado destructivo
00:05:01e intenta reproducir a todo volumen
00:05:03un MP3.
00:05:04Tristemente,
00:05:05no pude averiguar
00:05:05cuál es ese MP3.
00:05:07De todos modos,
00:05:07ahora que ha hecho todo eso,
00:05:08lo peor aún está por llegar
00:05:09porque eso fue solo la etapa 1.
00:05:11La etapa 2 es la autopropagación
00:05:13y esa es la parte más peligrosa
00:05:15de este ataque.
00:05:16Primero,
00:05:16buscará en tu máquina
00:05:17algún token de NPM válido
00:05:19donde pueda publicar
00:05:19sin autenticación de dos factores.
00:05:21Y si encuentra uno,
00:05:22escaneará todos los paquetes
00:05:24a los que esa cuenta tenga acceso,
00:05:26luego usará esas credenciales
00:05:26para añadirse a esos paquetes
00:05:28y publicar nuevas versiones infectadas.
00:05:30Eso es obviamente bastante malo,
00:05:32pero probablemente tampoco deberías
00:05:33tener tokens publicados
00:05:33dando vueltas
00:05:34que pueden saltarse
00:05:35la autenticación de dos factores.
00:05:36Así que la versión mucho más aterradora de esto
00:05:38es lo que sucede
00:05:39cuando se ejecuta dentro de tu CI/CD.
00:05:41Porque en CI,
00:05:42el atacante no necesita
00:05:43un token de NPM de larga duración
00:05:44porque las buenas configuraciones
00:05:45a menudo dependen de OIDC,
00:05:47que se supone que es más seguro.
00:05:48Esencialmente,
00:05:49en lugar de almacenar
00:05:50un token de NPM como un secreto,
00:05:51GitHub Actions le prueba a NPM:
00:05:53oye,
00:05:53soy este repositorio
00:05:54ejecutando este flujo de trabajo
00:05:55en esta rama,
00:05:56y NPM entonces le da
00:05:57un token de publicación de corta duración.
00:05:59El problema con esto, sin embargo,
00:06:00es que si el script obtiene acceso
00:06:01a un entorno de confianza de GitHub Actions,
00:06:03puede ponerse en el mismo lugar
00:06:04que un editor legítimo.
00:06:06Así que el malware puede usar
00:06:07el entorno relacionado con OIDC
00:06:08que GitHub expone al trabajo
00:06:10para solicitar un token JWT OIDC
00:06:12desde el punto final de tokens de GitHub,
00:06:14luego intercambia ese token JWT
00:06:16con NPM
00:06:17por un token de publicación de corta duración
00:06:18a través del sistema de publicación
00:06:19de confianza de NPM,
00:06:20y ahora puede publicar
00:06:22sin tener que robar nunca
00:06:22un token permanente de NPM
00:06:24y parecer completamente legítimo.
00:06:26En este caso,
00:06:26el malware incluye una copia
00:06:27de ese archivo router init.js
00:06:29en la tabla del paquete,
00:06:30luego agrega la dependencia
00:06:31opcional maliciosa,
00:06:32luego lo publica todo
00:06:33como la última etiqueta
00:06:34para ese paquete,
00:06:35así que cuando alguien
00:06:35o alguna tubería CI/CD
00:06:37instala esos paquetes,
00:06:38el ciclo comienza de nuevo,
00:06:40esparciéndose lo más lejos
00:06:40que pueda llegar.
00:06:42Así que todo eso
00:06:42es bastante loco, ¿verdad?
00:06:43Pero ahora enfoquémonos
00:06:44en el paciente cero,
00:06:46TanStack.
00:06:46¿Cómo se infectaron
00:06:47en primer lugar?
00:06:48Bueno,
00:06:49según su propio post-mortem,
00:06:50el atacante abusó
00:06:51de esa tubería de GitHub Actions.
00:06:53Comenzaron el día
00:06:53antes de que los paquetes maliciosos
00:06:54fueran realmente publicados,
00:06:56cuando crearon una bifurcación
00:06:57de TanStack router,
00:06:58pero en realidad lo renombraron
00:06:59como configuración
00:06:59para intentar hacerlo más difícil
00:07:01de encontrar si estabas buscando
00:07:02a través de los nombres de bifurcación obvios.
00:07:04Luego agregaron
00:07:04una confirmación maliciosa
00:07:05a esta bifurcación,
00:07:06que falsificaron como autor
00:07:07a Claude,
00:07:07y tenía un mensaje de confirmación
00:07:08que estaba precedido
00:07:09por skip CI,
00:07:10para que no ejecutara inmediatamente
00:07:11el CI en un evento de envío.
00:07:13Al día siguiente,
00:07:13luego abrieron una PR
00:07:14contra TanStack router
00:07:15llamado Work in Progress
00:07:16Simplify History Build.
00:07:18Y aquí es donde
00:07:18ocurre el ataque real.
00:07:20El resumen es que
00:07:21TanStack tenía un flujo de trabajo
00:07:22de GitHub Actions de tamaño de paquete
00:07:23que usaba pull request target,
00:07:25y eso es notable
00:07:26porque pull request target
00:07:27en realidad se ejecuta
00:07:28en el contexto de seguridad
00:07:29del repositorio base,
00:07:30no en la bifurcación.
00:07:31Eso significa que tiene acceso
00:07:32al ámbito de caché del repositorio base
00:07:33y a su token de GitHub.
00:07:35Así que este flujo de trabajo
00:07:35revisó la PR,
00:07:36instaló sus dependencias,
00:07:38y ejecutó una compilación de referencia.
00:07:39El problema, sin embargo,
00:07:40es que esa bifurcación contenía
00:07:40código malicioso.
00:07:41En este caso,
00:07:42era un script de configuración V
00:07:43que envenenó el
00:07:44almacén de paquetes PMPM
00:07:45bajo la clave de caché exacta
00:07:47que la acción de lanzamiento
00:07:48usaría más tarde.
00:07:49De hecho, pre-calcularon
00:07:50esto desde el archivo
00:07:51de bloqueo público de PMPM
00:07:52usando la misma fórmula exacta
00:07:54que también usa el flujo de trabajo.
00:07:56Una vez que esa caché envenenada
00:07:57se guardó,
00:07:57de hecho, restablecieron
00:07:58esa rama
00:07:59para que coincida con la rama
00:07:59principal actual,
00:08:00así que la PR visible
00:08:01parecía un archivo cero
00:08:02sin operaciones,
00:08:03y luego cerraron esa PR
00:08:04y eliminaron
00:08:05la rama maliciosa.
00:08:06Así que desde fuera,
00:08:07parece como si
00:08:07absolutamente nada
00:08:08hubiera pasado,
00:08:09pero han envenenado
00:08:10la caché de esa acción de GitHub.
00:08:11Esto significa que
00:08:12ocho horas más tarde,
00:08:13cuando un mantenedor normal
00:08:14fusionó una PR no relacionada
00:08:15a la principal,
00:08:16se activó el flujo de trabajo
00:08:17de lanzamiento de TanStack,
00:08:18que restauró la
00:08:19caché envenenada de PMPM,
00:08:20y ahora el código controlado por el atacante
00:08:22se estaba ejecutando dentro
00:08:23de esa acción de lanzamiento.
00:08:24Entonces usó la misma lógica
00:08:25con OIDC
00:08:26para obtener un token de publicación de NPM,
00:08:28y logró publicar
00:08:2984 versiones de sí mismo
00:08:30a través de 42 paquetes Tanstack,
00:08:32y ni siquiera necesitó
00:08:33llegar al paso
00:08:34de publicar paquete
00:08:35de la acción.
00:08:36Curiosamente,
00:08:36la acción realmente falló
00:08:37porque algunas pruebas fallaron,
00:08:39así que nunca llegó a ese paso,
00:08:40pero el código malicioso se ejecutó
00:08:41y publicó todas ellas
00:08:43de todos modos.
00:08:43Así que el atacante logró
00:08:44encadenar tres límites de confianza.
00:08:46Primero,
00:08:47el código PR de la bifurcación
00:08:47pudo envenenar
00:08:48la caché del repositorio base,
00:08:49luego esa caché del repositorio base
00:08:51se restauró dentro
00:08:52del flujo de trabajo de lanzamiento real,
00:08:53luego el flujo de trabajo de lanzamiento real
00:08:54tiene permisos OIDC,
00:08:56que se convierten en
00:08:57acceso de publicación de NPM,
00:08:58así que pueden publicar
00:08:59lo que parece ser
00:08:59paquetes completamente legítimos.
00:09:01Y eso es lo que creo
00:09:02que se está volviendo realmente aterrador
00:09:03sobre los ataques a la cadena de suministro.
00:09:05Se están alejando
00:09:05de robar
00:09:06el token de un mantenedor
00:09:07a abusar
00:09:08del sistema CI/CD completo en sí,
00:09:10y eso significa
00:09:11que todas nuestras señales de confianza
00:09:12están empezando a trabajar
00:09:13para el atacante.
00:09:14Este era un paquete firmado
00:09:15con procedencia válida
00:09:16publicado por un flujo de trabajo real.
00:09:18Así que ahí lo tienen,
00:09:19eso es ShaiHalud4,
00:09:20y si quieres comprobar
00:09:21si has sido comprometido
00:09:21por alguno de estos paquetes,
00:09:23dejaré enlaces
00:09:23a publicaciones de blog a continuación,
00:09:25que cubrirán
00:09:25cómo puedes averiguarlo
00:09:26y qué puedes hacer
00:09:27si has instalado
00:09:28uno de estos.
00:09:29Déjame saber en los comentarios
00:09:30qué piensas
00:09:30acerca de todo esto
00:09:31y el ecosistema NPM,
00:09:33mientras estás allí abajo,
00:09:33suscríbete,
00:09:34y como siempre,
00:09:34nos vemos en la próxima.

Key Takeaway

Los ataques modernos a la cadena de suministro como Shai Hulud 4 logran publicaciones maliciosas legítimas al abusar de la confianza en los entornos CI/CD y envenenar cachés de compilación en lugar de simplemente robar credenciales estáticas.

Highlights

  • Un ataque a la cadena de suministro de NPM infectó 160 paquetes, incluyendo componentes críticos como TanStack, UiPath, Mistral y Guardrails.ai.

  • El malware emplea una técnica de persistencia que inyecta ganchos en Claude Code y VS Code para reactivarse automáticamente en cada sesión.

  • El atacante utiliza GitHub Actions con permisos de pull-request-target para envenenar la caché de PMPM, logrando ejecutar código malicioso en el contexto del repositorio base.

  • La carga útil exfiltra secretos de GitHub, AWS, Kubernetes, HashiCorp Vault y diversas aplicaciones de mensajería como Signal, Slack y Discord.

  • El malware incluye un interruptor de hombre muerto que elimina el directorio de usuario si detecta la revocación de tokens robados.

  • La variante de Python del ataque detiene su ejecución si detecta una configuración regional en ruso para evitar la detección en ciertas regiones.

Timeline

Alcance y naturaleza del ataque

  • El ataque compromete la cadena de suministro de NPM afectando a más de 160 paquetes diversos.
  • Los atacantes inyectan archivos maliciosos como routerinit.js mediante dependencias opcionales que parecen legítimas.

El ataque aprovecha commits huérfanos en forks de GitHub para inyectar scripts maliciosos. La dependencia opcional utiliza un script de ciclo de vida 'prepare' que fuerza un error tras ejecutar el payload, permitiendo que la instalación continúe sin levantar sospechas en los registros.

Mecanismos de persistencia y exfiltración

  • El malware se desprende del proceso de instalación original para operar silenciosamente en segundo plano.
  • Se configuran servicios persistentes a nivel de SO y ganchos en herramientas como Claude Code y VS Code.
  • El atacante exfiltra credenciales de nube, claves SSH e historiales de sesiones utilizando la red de mensajería Session.

Una vez ejecutado, el script busca y exfiltra secretos de entornos de ejecución como GitHub Actions, AWS IMDSv2, Kubernetes y HashiCorp Vault. El malware incluye un sistema de monitoreo de tokens que puede ejecutar comandos destructivos de borrado si detecta actividad de revocación de credenciales.

Autopropagación y abuso de CI/CD

  • El malware busca tokens de NPM para publicar versiones infectadas automáticamente sin requerir 2FA.
  • Los entornos CI/CD que utilizan OIDC son vulnerables al intercambio de tokens JWT para publicaciones legítimas maliciosas.

En entornos de integración continua, el atacante utiliza el token OIDC expuesto por GitHub para obtener un token de publicación de NPM válido. Esto permite al atacante publicar paquetes que aparecen como firmados y legítimos, propagando el malware a través de las actualizaciones estándar de los repositorios.

Infección inicial de TanStack

  • El atacante envenenó la caché del repositorio base de TanStack a través de una pull request maliciosa.
  • La ejecución del flujo de trabajo de lanzamiento restauró la caché envenenada, permitiendo al atacante publicar 84 versiones maliciosas.

La vulnerabilidad se centró en el uso de 'pull-request-target' en las acciones de GitHub, que otorga privilegios del repositorio base. Al pre-calcular la clave de caché de PMPM, el atacante logró que el flujo de trabajo de lanzamiento ejecutara su código malicioso automáticamente sin intervención adicional del mantenedor.

Community Posts

No posts yet. Be the first to write about this video!

Write about this video