Vulnerabilidad crítica en GitLab permite ejecución remota de código
Publicado el
Vulnerabilidad en GitLab permite ejecución remota de código
Un investigador de ciberseguridad, Yuhang Wu de depthfirst, ha dado a conocer un exploit que permite a usuarios autenticados ejecutar comandos como el usuario git en servidores GitLab autogestionados con la versión 18.11.3, que no han sido parcheados. Este exploit se activa al comprometer dos notebooks Jupyter modificados y solicitar su diferencia (diff). Lo alarmante es que este ataque no requiere derechos de administrador, acceso a un CI runner, interacción del usuario víctima ni acceso a proyectos de otros usuarios.
El exploit es específico para la versión GitLab 18.11.3 en arquitecturas x86-64, aunque los errores subyacentes en Oj, un parser de JSON para Ruby, afectan a un rango más amplio de versiones. Las versiones afectadas incluyen GitLab Community Edition (CE) y Enterprise Edition (EE) desde la 15.2.0 hasta la 18.10.7, así como de la 18.11.0 a la 18.11.4 y la 19.0.0 a la 19.0.1. Las primeras versiones corregidas son la 18.10.8, 18.11.5 y 19.0.2.
Oj es conocido por ser un parser de JSON de alto rendimiento que utiliza un considerable código nativo en C. Las versiones publicadas del gem entre 3.13.0 y 3.17.1 son vulnerables, mientras que la 3.17.3 es la primera que incluye las correcciones. Esta vulnerabilidad afecta a las versiones Free, Premium y Ultimate de GitLab, pero el propio Ruby no se ve afectado.
El éxito del ataque permite la ejecución de comandos como git, cuyo alcance efectivo depende de la aislación del despliegue. Sin embargo, puede incluir acceso a código fuente, secretos de Rails, credenciales de servicio, datos de CI/CD y servicios internos accesibles desde la aplicación. Cabe destacar que GitLab.com recibió parches el 10 de junio, y los clientes dedicados no requieren ninguna acción adicional. Por su parte, los operadores autogestionados deben actualizar a una versión soportada que contenga la corrección.
Los usuarios de Helm y Operator deben verificar la versión de GitLab dentro de la imagen del Webservice, no solo la versión del chart o del operador. depthfirst informó que, hasta el 24 de julio, no había conocimiento de explotación en entornos reales.
Ni la divulgación de depthfirst ni las notas de la versión de GitLab del 10 de junio incluyen identificadores CVE o puntuaciones CVSS para los errores de esta cadena. Tampoco proporcionan una solución temporal; ambos dirigen a los operadores autogestionados a actualizar. depthfirst ha listado nueve CVE para otros errores de Oj encontrados en la misma revisión.
Análisis técnico del exploit
El renderer de notebooks de GitLab pasa archivos .ipynb JSON controlados por el repositorio a Oj::Parser.usual.parse dentro de un worker Puma de larga duración. Esto introduce datos controlados por el atacante en el estado del parser nativo de Oj dentro del proceso de aplicación de GitLab. El análisis técnico de depthfirst revela cómo un error controla un puntero de retorno, mientras que el otro filtra una dirección de heap necesaria para reducir el espacio de búsqueda de la ASLR (Address Space Layout Randomization).
Oj almacena el estado de anidamiento en una pila fija de 1,024 bytes, pero nunca verifica si la profundidad excede este límite. Así, los arrays profundamente anidados pueden sobrescribir bytes en el estado del parser adyacente. El exploit corrompe buf.head, lo que provoca que Oj pase un puntero interior forjado a realloc(). Una asignación posterior de un array en Ruby reclama la misma región de jemalloc de 3,584 bytes y sobrescribe p->start. Oj asigna un objeto de clave de 65,565 bytes, trunca su longitud a 29 en un campo de 16 bits con signo y devuelve 29 bytes que contienen el puntero de asignación de clave en uso.
GitLab lleva ese puntero a la diferencia de notebook renderizada, proporcionando al exploit la fuga de dirección necesaria para afinar la búsqueda de ASLR. En una instalación GitLab 18.11.3 con dos workers, la búsqueda normalmente tardaba entre cinco y diez minutos, proyectándose que en un rango más amplio podría tardar de una a dos horas.
Dos archivos de notebooks ordenados lexicográficamente en una única solicitud de diffs_stream mantienen ambas fases dentro del mismo worker de Puma, reutilizando el parser Oj global del proceso. El primer archivo corrompe el puntero de retorno y genera un error que GitLab captura antes de continuar con la diferencia. El siguiente parse invoca el puntero sobrescrito y accede a system() a través de una secuencia de gadget específica de la versión.
La demostración pública empaqueta la cadena en un laboratorio local de GitLab 18.11.3 x86-64 y hace que el worker Puma se conecte de vuelta como git. depthfirst informó sobre los errores de Oj el 21 de mayo, y el mantenedor fusionó las correcciones el 27 de mayo. La versión Oj 3.17.3 fue lanzada el 4 de junio. Los investigadores reportaron la cadena de GitLab el 5 de junio; depthfirst confirmó que GitLab lo reconoció el 8 de junio. GitLab lanzó las versiones corregidas el 10 de junio y resolvió el informe el 17 de julio, según depthfirst. Un análisis reveló que GitLab incluyó el ajuste de Oj 3.17.3 bajo correcciones de errores y no en la tabla de correcciones de seguridad, omitiendo la descripción de la cadena RCE de diferencia de notebooks.