DevOps 6 min de lectura 1125 palabras

Actualizar no siempre basta: el incidente de Alpine Linux con GitLab y Ruby

EN
Actualizar no siempre basta: el incidente de Alpine Linux con GitLab y Ruby

Alpine Linux ha publicado el informe de un incidente en su instancia de GitLab que tiene una lección que me parece muy útil para cualquiera que mantenga sus propias imágenes de contenedor: actualizaron GitLab a una versión corregida y seguían siendo vulnerables.

El fallo

CVE-2026-97229 es una vulnerabilidad crítica, con la puntuación máxima de 10, en cómo GitLab evalúa las expresiones regulares de la configuración de CI. Según el aviso de GitLab, una expresión regular manipulada en un fichero de CI podía permitir ejecutar código en el servidor. Afecta a GitLab CE/EE 19.2 antes de la 19.2.8, 19.3 antes de la 19.3.5 y 19.4 antes de la 19.4.2.

El detalle importante es dónde está el fallo de verdad: no en el código de GitLab, sino en Onigmo, el motor de expresiones regulares que lleva Ruby. GitLab publicó sus versiones corregidas el 12 de octubre, pero la corrección consiste en parches al Ruby que GitLab incluye en sus propios paquetes e imágenes. El Ruby oficial no lo corrigió hasta el 23 de octubre, con las versiones 3.3.13, 3.4.12 y 4.0.9, que arreglan varios fallos del motor de expresiones regulares relacionados con este y otros CVE de GitLab.

Lo que pasó en Alpine

La cronología, tal como la cuentan, es una sucesión de pequeños tropiezos que cualquiera podría tener:

FechaQué pasó
12 de octubreGitLab publica la corrección
13 de octubreSe publica el CVE. Alpine actualiza, pero por error a la 19.3.4 en lugar de la 19.3.5, que era la que tenía el arreglo
20 de octubreUna cuenta recién registrada empieza a hacer pruebas
21 de octubreAtaque principal: errores 502 y trabajos de CI fallidos
22 de octubreActualizan a GitLab 19.3.6
23 de octubreBloquean la cuenta del atacante. Ruby publica la corrección
24 de octubreVerifican aports, los paquetes y las versiones del 21 contra registros externos
25 y 26 de octubreEl atacante vuelve a intentarlo, sin éxito
26 de octubreDescubren que la instancia sigue siendo vulnerable tras la actualización
27 de octubreCierran GitLab salvo para desarrolladores y servidores de compilación y empiezan a reconstruirlo
28 de octubreReconstrucción completa, contraseñas restablecidas y claves secretas de GitLab sustituidas

El atacante envió configuraciones de CI manipuladas que tumbaron los procesos de GitLab 8 veces. En 4 de ellas llegó a completar parte del exploit y falló en el paso siguiente. Además, descargó la imagen de contenedor de GitLab de Alpine, lo que hace pensar que estaba ajustando el exploit a esa compilación exacta.

Por qué actualizar no sirvió

Aquí está la lección. La imagen de GitLab de Alpine no usa los paquetes oficiales de GitLab, sino el Ruby estándar. Así que al actualizar GitLab a la 19.3.6 se actualizó el código de GitLab, pero no el Ruby con el que se ejecuta, que era donde estaba el fallo. Como dice el propio informe:

Si ejecutas GitLab con tu propio Ruby (desde el código fuente o en tu propia imagen de contenedor) en lugar de los paquetes de GitLab, actualizar GitLab no es suficiente: también necesitas Ruby 3.3.13, 3.4.12, 4.0.9 o posterior.

flowchart LR
    A["Paquetes oficiales de GitLab"] --> B["GitLab 19.3.5+ · Ruby con parche"]
    C["Imagen propia con Ruby estándar"] --> D["GitLab 19.3.6 · Ruby sin parche"]
    B --> E["Protegido"]
    D --> F["Vulnerable hasta Ruby 3.3.13"]

Si usas la imagen alpinelinux/gitlab, hay que volver a descargar alpinelinux/gitlab:19.3.6: la publicada el 27 de octubre ya lleva Ruby 3.3.13, y cualquier 19.3.6 descargada antes, o cualquier etiqueta anterior, es vulnerable.

El problema no se limita a GitLab. Como explica Alpine, el fallo está en el motor de expresiones regulares de Ruby, así que cualquier programa que compile expresiones regulares a partir de entradas no confiables está expuesto. Alpine 3.22, 3.23, 3.24 y edge ya tienen Ruby 3.4.12, y Alpine 3.21 tiene la 3.3.13, así que basta con apk upgrade.

Qué saben y qué no pueden descartar

Lo que más me gusta del informe es lo claro que es separando lo comprobado de lo que no se puede descartar:

  • Comprobado: las versiones y los paquetes de Alpine no se modificaron. Verificaron cada commit de aports del 20 al 22 de octubre contra registros que el atacante no podía alcanzar (el clon de un desarrollador y los 45 servidores de compilación), y todos los paquetes compilados entre el 20 y el 24 de octubre salieron de esos commits. Las claves que firman los paquetes nunca han estado en el servidor de GitLab.
  • Sin indicios de ejecución de código: no encontraron ficheros, claves, hooks ni procesos inesperados.
  • Pero no pueden estar seguros: un exploit con éxito no dejaría ni un fallo ni rastro en los logs web, faltan algunos logs del día del ataque y no registraban las conexiones de salida.

Así que tomaron el camino difícil: tratar la instancia como comprometida y reconstruirla desde cero. Han restablecido todas las contraseñas y sustituido las claves secretas, porque si el ataque hubiera funcionado podrían haberse expuesto correos, hashes de contraseñas, secretos de 2FA, variables de CI/CD, tokens de los runners y repositorios privados. También avisan de que han visto intentos automáticos de inicio de sesión con contraseñas filtradas en otros sitios, así que quien usara la misma contraseña en otro lugar debería cambiarla.

Lo que van a cambiar

  • Ejecutar la instancia reconstruida con un Ruby corregido.
  • Aplicar las actualizaciones de seguridad críticas de GitLab en un día y revisar en cada versión si incluye parches a Ruby u otros componentes que su propia compilación tenga que aplicar.
  • Registrar las conexiones de salida y las de la base de datos, y guardar los logs más de 14 días.
  • Firmar las etiquetas de las versiones, para poder verificarlas sin depender de GitLab.

Lo que me llevo

Este caso me toca de cerca. Hace poco contaba cómo mantengo una imagen Docker de PHP que puede compilar PHP desde el código fuente. Y la lección de Alpine es justo la pregunta que conviene hacerse con cualquier imagen así: cuando sale un parche de seguridad, ¿dónde está realmente? ¿En el código de la aplicación, en el intérprete, en una biblioteca del sistema? ¿Y llega a mi imagen o sólo a los paquetes oficiales del proveedor?

Construir tus propias imágenes da control, pero también te hace responsable de algo que los paquetes oficiales resuelven por ti. Y el caso de Alpine, un proyecto con gente muy capaz, muestra lo fácil que es que se escape: una versión equivocada por error y un parche que no estaba donde se esperaba.

También me quedo con la honestidad del informe. Decir “no hay indicios, pero no podemos descartarlo, así que lo reconstruimos todo” es mucho más difícil que decir “no pasó nada”, y es lo correcto.

Fuente: GitLab security incident: attempted exploitation of CVE-2026-97229, Alpine Linux.