JavacDoor, malware de Maven que se ejecuta en tiempo de compilación.

JavacDoor: Un artefacto de Maven que se ejecutaba durante la compilación, no durante la instalación.

TL; DR

Un artefacto de Maven Central publicado como io.github.davidtimur:c2-lab Se lanzaron nueve versiones, ocho de las cuales ejecutaban una carga útil de acceso remoto durante la compilación de cualquier proyecto derivado que colocara el archivo JAR en su ruta de procesamiento de anotaciones. Ningún código de aplicación tenía que importarlo. Ninguna línea de código fuente tenía que hacer referencia a él. No había ningún script de instalación, ni un equivalente posterior a la instalación, ni ningún tipo de gancho de ciclo de vida para que las herramientas pudieran inspeccionarlo.

El vector de ejecución es un único archivo de 46 bytes dentro del jar: un registro de proveedor de servicios Java que nombra una clase que implementa javax.annotation.processing.Processor. El compilador de Java descubre automáticamente dichos registros. Una vez encontrados, javac La clase se instancia y se ejecuta como parte normal del proceso de compilación. Esto significa que el entorno de ejecución de la carga útil es la máquina de compilación, y en este momento la máquina de compilación está haciendo lo que se supone que debe hacer.

A lo largo de las nueve versiones, el canal de comando y control se reconstruyó tres veces: una URL de devolución de llamada proporcionada por el operador, luego una shell inversa sobre un túnel TCP ngrok, y luego un canal de sondeo HTTP cuyas rutas cambiaron dos veces más. La versión final instaló un administrador de confianza TLS sin operación como predeterminado de la JVM, aceptar cualquier certificado durante la duración de la compilación.

El artefacto llevaba las palabras "C2 Lab Payload" en su propio POM y una licencia MIT. Estuvo activo en Maven Central durante todo el período que lo observamos y Desde entonces ha sido retirado, junto con todo su io.github.davidtimur grupo.

Anatomía: el compilador como motor de ejecución

El marco de procesamiento de anotaciones de Java existe para que las bibliotecas puedan generar código en tiempo de compilación; este es el mecanismo detrás de Lombok, Dagger y una larga lista de herramientas ORM y de serialización. Un procesador se anuncia con un archivo de texto plano dentro del JAR:

META-INF/services/javax.annotation.processing.Processor

El contenido de ese archivo en cada versión afectada, textual y completo:

io.github.davidtimur.c2lab.C2Procesador

El archivo es idéntico byte a byte en las versiones 1.0.1 a 1.0.8, md5 7d2a08a5c8869a47eea9fa62487dfbe4. La versión 1.0.0 no lo incluye.

Cuando javac Al ejecutarse, escanea la ruta del procesador de anotaciones en busca de estas entradas de servicio y carga lo que encuentra. No es necesario que el proyecto que se está compilando mencione el procesador, anote nada ni configure nada. Su presencia en la ruta es suficiente. Esta es la propiedad que distingue a JavacDoor de los patrones de cadena de suministro en los que se basan la mayoría de las herramientas:

Patrón de Costura Desencadenar Visible como
gancho de instalación de npm npm install scripts.postinstall en el manifiesto
Carga útil de importación de Python primera importación del módulo Declaración a nivel de módulo en el código fuente
Puerta Javac javac en cualquier proyecto posterior un nombre de archivo de registro de servicio

Un hook de instalación es una declaración en un manifiesto, y un manifiesto es lo primero que lee cualquier persona. Una carga útil en tiempo de importación, al menos, se encuentra en código fuente legible. Un registro de servicio no es ninguna de las dos cosas: es un nombre de archivo más una línea que nombra una clase, y el comportamiento reside en el código de bytes compilado en un directorio aparte.

La propia clase de carga útil realiza un reconocimiento del host mediante la ejecución de comandos. Los grupos de constantes de las clases compiladas contienen / Bin / sh, whoami, uname -a, pwd en la ruta Unix y lista de tareas en la ruta de Windows, junto con flujo de errores de redirección para fusionar la salida de error del proceso hijo en el flujo capturado. Dos plantillas JSON transportan los resultados fuera del host: una baliza de registro:

{"host":"%s","os":"%s","user":"%s","dir":"%s"}

poblado desde el hostname comando y el os.name, user.name, y directorio de usuario propiedades del sistema y una función de devolución de llamada de resultado:

{"version":"%s","host":"%s","time":"%s","output":"%s"}  

Los indicadores de progreso que aparecen en la salida del propio compilador son inusualmente sinceros: [C2] Ejecución en tiempo de compilación completada, [C2] devolución de llamada enviada → HTTP , [C2] shell conectado a .

Nueve lanzamientos, tres generaciones C2

Las versiones no son nueve copias de una misma carga útil. Son un registro de iteraciones, y leerlas en orden muestra que el canal se reconstruyó mientras que el vector de entrega permaneció fijo.

tortugitas Se ejecuta automáticamente al compilar. Channel
1.0.0 no (sin archivo de servicio) URL de devolución de llamada proporcionada únicamente por el operador
1.0.1 Sí (vector introducido) URL de devolución de llamada proporcionada por el operador
1.0.2 Shell inversa, túnel TCP ngrok
1.0.3 Canal HTTP, /registrar /cmd /salida
1.0.4 Canal HTTP, /registrar /cmd /salida
1.0.5 Canal HTTP, /registrar /sondeo /salida
1.0.6 Canal HTTP, /registrar /sondeo /salida
1.0.7 Canal HTTP, /registrar /cmd
1.0.8 Canal HTTP + validación TLS deshabilitada

Vale la pena destacar tres detalles de esa tabla, ya que cada uno de ellos modifica la forma en que debe interpretarse el conjunto de artefactos.

El vector llega a 1.0.1, no a 1.0.2. La versión 1.0.0 mantiene la misma lógica de reconocimiento y devolución de llamada, pero no tiene archivo de servicio ni importaciones de procesamiento de anotaciones; se ejecuta solo si algo la llama. A partir de la versión 1.0.1, el archivo de servicio está presente y las clases compiladas importan javax.annotation.processing.SupportedSourceVersionCualquier análisis que compare las dos versiones con entrada de pago —1.0.0 y 1.0.2— concluye correctamente que algo cambió, pero no identifica dónde.

La shell inversa existe en una única versión. La versión 1.0.2 contiene 0.tcp.ngrok[.]io, la línea de registro [C2] shell conectado a 0.tcp.ngrok[.]io:19823, un banner interactivo capa c2y un terminador de marco __FIN__Las versiones 1.0.3 en adelante no contienen ninguna de esas opciones y, en su lugar, acceden a un host HTTPS. Una solicitud de eliminación que mencionara únicamente las versiones con incidencias habría citado un punto final TCP inactivo, dejando sin mencionar el canal HTTP activo, presente en seis versiones posteriores.

La versión final elimina la verificación de transporte. La versión 1.0.8 agrega una clase que implementa javax.net.ssl.X509TrustManager cuyos métodos de comprobación de certificados no hacen nada, un verificador de nombre de host siempre verdadero registrado a través de establecerVerificadorDeNombreDeHostPredeterminado, y un Confía en todos Rutina que instala ambos como valores predeterminados de la JVM. El efecto es que, durante el resto de la compilación, la JVM acepta cualquier certificado de cualquier host, no solo para el tráfico de la propia carga útil, sino también para cualquier otra acción que la compilación realice posteriormente a través de TLS.

En las seis versiones que utilizan el canal HTTP, este está gestionado por un único host: tableful-fervor-crazed.ngrok-free[.]devCada solicitud lleva el encabezado ngrok-skip-browser-warning, que suprime la página intersticial que los túneles ngrok gratuitos sirven a los navegadores. Los conjuntos de rutas cambian entre versiones. /afuera desaparece en 1.0.7 y / cmd alterna con /encuesta — pero el anfitrión nunca cambia.

La autoexclusión dice

Cada archivo POM a partir de la versión 1.0.1 establece un argumento del compilador para la compilación del propio artefacto:

-proc:ninguno

Esa bandera desactiva el procesamiento de anotaciones. Su efecto aquí es previocise: cuando el proyecto que contiene el procesador se compila, el procesador no se ejecuta.

Esta bandera tiene usos completamente comunes. Un proyecto que distribuye un procesador de anotaciones a menudo necesita evitar aplicar dicho procesador a sí mismo durante el arranque, y la documentación de la herramienta de compilación recomienda precisamente esto. Considerada aisladamente, no demuestra nada.

Sin embargo, en conjunto con la función del procesador, esto describe una asimetría específica: el código se ejecuta en las máquinas de todos los que compilan el artefacto, y no en la máquina que lo compila. El indicador aparece en la misma versión que introduce el archivo de servicio (1.0.1) y en todas las versiones posteriores. La correlación entre «la versión donde comienza la ejecución en tiempo de compilación» y «la versión donde se desactiva localmente la ejecución en tiempo de compilación» es la señal analítica más útil del conjunto de artefactos, y es visible en un archivo POM de texto plano sin necesidad de descompilar nada.

Observamos el efecto y ahí nos detenemos. Nada en los artefactos indica por qué se activó la bandera.

Hay otro dato que merece mención, principalmente para descartarlo. El POM nombra el proyecto "C2 Lab Payload", lo describe como un "artefacto de carga útil de laboratorio C2" y lo licencia bajo la MIT. Este tipo de autoetiquetado a veces se ofrece como evidencia de que un paquete es un ejercicio de investigación.cisEn lugar de una amenaza activa, y en ocasiones esa interpretación es correcta, un canario declarado sin infraestructura accesible es un objeto distinto. Aquí no aplica. Un implante funcional publicado en un repositorio público, accesible para cualquier usuario, con una infraestructura de salida que se reconstruyó tres veces en nueve versiones, es una capacidad activa independientemente de cómo lo denominen sus metadatos. El nombre en el POM no cambia nada sobre lo que sucede en una máquina que lo compila.

Indicadores para máquinas de construcción

Si un host de compilación compiló con este artefacto, la evidencia se encuentra en los registros de compilación y la telemetría de red, en lugar de en un implante persistente en el disco: la carga útil se ejecuta dentro del proceso del compilador y finaliza con él.

En el archivo JAR o en la caché del repositorio local

  • META-INF/services/javax.annotation.processing.Processor nombrando io.github.davidtimur.c2lab.C2Processor
  • Archivo de servicio md5 7d2a08a5c8869a47eea9fa62487dfbe4
  • Clases bajo io/github/davidtimur/c2lab/: C2Processor, C2Task, Tasky en 1.0.8 la clase interna Task$1

En la salida de compilación

  • [C2] compile-time execution complete
  • [C2] callback sent → HTTP
  • [C2] callback failed:
  • [C2] shell connected to
  • Banner interactivo c2-shell; terminador de marco __END__

Telemetría en proceso

  • javac como padre de /bin/sh -c (Unix) o un intérprete de comandos de Windows
  • Órdenes para niños whoami, uname -a, pwd (Unix) o tasklist (Windows) censurado por un paso de compilación

En la telemetría de red

  • TCP saliente a 0.tcp.ngrok[.]io:19823 (versión 1.0.2)
  • HTTPS a tableful-fervor-crazed.ngrok-free[.]dev, caminos /register, /cmd, /poll, /out (versiones 1.0.3 a 1.0.8)
  • Encabezado de la solicitud ngrok-skip-browser-warning: true
  • Cuerpos de solicitud coincidentes {"host":...,"os":...,"user":...,"dir":...} or {"version":...,"host":...,"time":...,"output":...}

En configuración

  • Variables de entorno CALLBACK, CALLBACK_URL; propiedad del sistema callback.url

metadatos del editor

  • Grupo procesos io.github.davidtimurDirección del editor davudboi999@gmail[.]com; clave de firma E520C345EF94423D

Una compilación que ejecute la versión 1.0.8 requiere una verificación adicional. Dado que dicha versión instala un administrador de confianza permisivo como configuración predeterminada para toda la JVM, cualquier conexión TLS realizada posteriormente en la misma JVM (resolución de dependencias, carga de artefactos, paso de implementación) se realizó sin validación de certificado. No se debe asumir que el tráfico proveniente de esa ventana está autenticado.

Por qué los artefactos compilados necesitan un escaneo diferente

JavacDoor es un caso de prueba útil porque refuta dos suposiciones comunes a la vez, y ninguno de los fallos es específico de las herramientas de ningún proveedor en particular.

La primera premisa es que el código peligroso se anuncia en un manifiesto. Gran parte de las herramientas de la cadena de suministro se organizan en torno al ciclo de vida. hooks, porque para npm y PyPI ahí es donde suele estar la acción. JavacDoor no tiene gancho. Su activador es un archivo de registro de servicio cuyo nombre es una interfaz Java y cuyo contenido es un nombre de clase. Para capturar eso estáticamente tienes que tratar META-INF/services/javax.annotation.processing.Processor como punto de entrada de ejecución por derecho propio, a la par con un postinstalación script — y luego seguir la clase nombrada hasta el código de bytes. Los ecosistemas tienen sus propios mecanismos de autodescubrimiento de esta forma, y ​​cada uno es un punto de entrada, independientemente de que las herramientas lo enumeren como tal.

La segunda suposición es que las cadenas de texto residen en los archivos fuente. Para un jar, los puntos finales, los comandos de shell, las plantillas JSON y los marcadores de registro están todos en los grupos constantes de .clase archivos. Las herramientas que buscan texto no encuentran nada, no porque las cadenas estén ofuscadas, sino porque están en un contenedor binario estructurado que un escaneo de texto no analiza. Todos los indicadores de red en esta publicación surgieron del análisis de un grupo constante. Uno de ellos es un archivo completamente formado. https:// La URL está a la vista dentro de un archivo de clase; un escaneo de texto del contenido legible del JAR no la detectaría. Aquí no hay codificación que sortear, solo un formato de contenedor que leer.

Ambas brechas tienen la misma forma: el formato de un artefacto se trató como un conjunto de archivos en lugar de una estructura con semántica definida. La solución es sencilla: analizar el contenedor, enumerar los puntos de entrada de autodescubrimiento del ecosistema y seguirlos hasta el código compilado. Para los vectores de tiempo de compilación en particular, una tercera comprobación es sencilla y sorprendentemente útil: comparar lo que un artefacto hace a los consumidores con aquello de lo que se exime. Un artefacto que registra un procesador de tiempo de compilación y, al mismo tiempo, desactiva el procesamiento de tiempo de compilación para su propia compilación, ya ha revelado información sobre sí mismo en dos líneas de POM en texto plano.

Para los equipos que utilizan artefactos Maven en la actualidad, a continuación se presentan tres medidas prácticas:

  • Considere la ruta del procesador de anotaciones como un límite de ejecución. Las dependencias que llegan allí ejecutan código en su compilación. Donde una compilación no necesita procesamiento de anotaciones, -proc:ninguno es tan útil a nivel defensivo como aparentemente lo fue localmente aquí; donde lo hace, fije el conjunto de procesadores explícitamente en lugar de heredarlo de la ruta de clases de compilación.
  • Registrar los subprocesos del compilador. Un paso de compilación que genera una consola es anómalo en la mayoría de los proyectos y se puede detectar fácilmente mediante una alerta.
  • No considere los metadatos como testimonio. Los términos “Laboratorio”, “prueba”, “carga útil” y “PoC” en el nombre o la descripción de un paquete no constituyen límites de alcance. Lo que sí lo son es la accesibilidad y el comportamiento.

El artefacto y todo su grupo fueron retirados de Maven Central, tras nuestro informe, devuelve ahora un error 404 tanto la ruta del artefacto como la del grupo, y el índice de Central no encuentra coordenadas coincidentes. Esto cierra el artefacto, pero no el vector, una función documentada del compilador de Java disponible para cualquiera que publique un archivo JAR.

Referencias

Esta publicación no cita fuentes externas. Todos los hallazgos son análisis estáticos directos de los nueve archivos JAR publicados, recuperados de Maven Central antes de su eliminación. No se ejecutó ningún código de los artefactos en ningún momento.

sca-tools-software-herramientas-de-analisis-de-composicion
Priorice, solucione y proteja sus riesgos de software
Obtén tu cuenta gratuita.
Sin tarjeta de crédito.

Asegure el desarrollo y entrega de software

con la suite de productos Xygeni