Blog / CVE explicados

CVE-2026-97229: el matiz que oculta el RCE «no autenticado» de GitLab

El CVSS 10.0 de GitLab y la lista KEV de CISA llaman a esto no autenticado. Las únicas dos personas que lograron explotarlo necesitaron antes una cuenta de GitLab. Aquí explico por qué eso importa.

Estudios de doctorado en Seguridad de la Información completadosDefensa universitaria superada · Defensa final de la tesis pendiente

Cero credenciales sobre el papel. Una cuenta en la práctica.

Cero. Esa es la cantidad de credenciales que exige el propio vector CVSS de GitLab para CVE-2026-97229: AV:N/AC:L/PR:N/UI:N, un 10.0 perfecto, ejecución arbitraria de código sin necesitar ningún privilegio. Una. Esa es la cantidad de cuentas que necesitaron las únicas dos personas que realmente lograron que esto funcionara, de forma independiente, antes de poder enviar la solicitud que hacía algo. Ambas cifras son ciertas. Describen dos pasos distintos del mismo ataque.

CVE-2026-97229 es un desbordamiento de enteros crítico en el compilador de expresiones regulares de CI/CD de GitLab, corregido el 12 de octubre de 2026 en las ramas 19.2, 19.3 y 19.4.[1] El propio aviso de GitLab describe exactamente lo que implica esa puntuación: una expresión regular manipulada en una configuración de CI/CD, enviada por un usuario no autenticado, se convierte en ejecución arbitraria de código en el servidor, bajo ciertas condiciones.[1] CISA la añadió a su catálogo de vulnerabilidades explotadas conocidas menos de tres semanas después, confirmando que se está usando contra objetivos reales.[2] Nada de eso es incorrecto. Simplemente omite el único detalle que de verdad le dice cuán expuesto está: para llegar al paso no autenticado, primero se necesita una cuenta.

Lo que GitLab publicó en realidad, y cuándo

GitLab fechó la versión con la corrección el 12 de octubre de 2026, publicando 19.2.8, 19.3.5 y 19.4.2 para las tres ramas afectadas: 19.2 antes de 19.2.8, 19.3 antes de 19.3.5, 19.4 antes de 19.4.2.[1] GitLab.com ya ejecutaba una compilación corregida antes de que se publicara el aviso, y los clientes de GitLab Dedicated no tuvieron que hacer absolutamente nada.[1] El investigador que lo reportó usó el alias joaxcar en HackerOne, y reportó por separado un fallo de redirección abierta de gravedad media, CVE-2026-7767, corregido en la misma versión.[1]

Un detalle cambió después de los hechos, y conviene saberlo antes de comprobar el número de su propia versión. Las notas de la versión de GitLab señalaban inicialmente la 19.3.4 para la rama 19.3; al día siguiente, la cobertura informó de que esa compilación en realidad incluía una corrección no relacionada para un fallo de la página de merge request, y que 19.3.5 es la versión realmente corregida para esta CVE, algo que el aviso actual ya refleja.[8] A partir de ahí, las cosas se movieron rápido: Rapid7 reportó una reproducción independiente y funcional aproximadamente un día después de la divulgación, le siguió una prueba de concepto pública y firmada unos cuatro días más tarde, y el listado de explotación confirmada de CISA llegó unos diecisiete días después del parche original.[1][2][4][7] Esa no es la historia principal de este artículo. Aun así merece la pena detenerse en ella: un CVSS 10.0 en un producto como GitLab se arma rápidamente en cuanto el propio parche le dice a los atacantes exactamente dónde mirar.

El motor de expresiones regulares que ejecuta su comando en lugar de comparar su cadena

Yo mismo no he ejecutado esto. Todo lo que sigue proviene de la prueba de concepto que Jonah Burgess (GitHub: jburgess-r7) publicó unos cuatro días después del parche, calificada explícitamente como «una versión inicial de PoC», de la reproducción reportada por separado por Rapid7, y del propio aviso de GitLab, todo consultado el 2 de noviembre de 2026.[1][3][4] El disparador vive detrás del endpoint CI-lint de GitLab, el mismo que GitLab utiliza para validar un archivo .gitlab-ci.yml antes de que se ejecute cualquier pipeline. Si se le entrega una expresión regular manipulada en la configuración de CI/CD, junto con una rama del repositorio cuya ruta usa un truco de nomenclatura UTF-8 particular, el conteo interno de longitud del compilador de expresiones regulares se desborda.[3]

Ese desbordamiento no solo hace caer al worker, aunque a menudo también lo hace. Redirige hacia dónde va la ejecución del motor de expresiones regulares, directo a una función que ejecuta un comando arbitrario.[3] El comando se ejecuta como la cuenta de servicio de bajo privilegio «git» de GitLab, no como administrador ni como root en el host.[3] Ese es un techo real para el alcance del daño, y conviene decirlo con claridad: esto es ejecución de código del lado del servidor, no una vía instantánea al control de administrador completo. Desde un shell de «git», un atacante ya tiene bastante que merece la pena tomar, empezando por el contenido de los repositorios y cualquier secreto de CI que esa cuenta pueda leer.

// The CI-lint trigger, reconstructed from Burgess's public PoC. Paraphrased
// shape of the chain -- not the working payload itself.

1. Attacker-controlled branch is pushed to a public project, with a
   specially-named (UTF-8) path and a crafted regex in .gitlab-ci.yml.
2. An unauthenticated request hits GitLab's CI-lint endpoint, which
   validates that regex before any pipeline actually runs.
3. The regex compiler's internal length accounting overflows -- an
   integer wraps where it shouldn't.
4. Execution inside the regex engine is redirected into a function that
   runs an OS command, as the low-privilege "git" service account.
5. On a vulnerable build, the worker process frequently crashes right
   after (a bare HTTP 502 is a side effect, not proof by itself).

El paso que ocurre antes del paso no autenticado

He aquí el detalle que se saltan todos los titulares sobre un CVSS 10. El propio README de Burgess expone las condiciones previas sin rodeos: una cuenta ordinaria capaz de subir una rama a un proyecto público en la instancia objetivo; un objetivo que ejecute exactamente la compilación oficial gitlab/gitlab-ce:19.4.1-ce.0 (cualquier otra necesita desplazamientos binarios reconstruidos); alrededor de 9 GiB de memoria libre; y alcance de red hasta un listener si se quiere la salida del comando.[3] La solicitud CI-lint que realmente dispara el desbordamiento no necesita ningún token, cookie ni cabecera de autorización, esa parte sí es realmente no autenticada.[3][4] Simplemente no es el primer paso.

Según la cobertura de Help Net Security, Rapid7 reprodujo de forma independiente una ejecución de código funcional contra una imagen oficial sin parchear de GitLab CE 19.4.1 aproximadamente un día después de la divulgación (la misma entrada falló contra la imagen parcheada 19.4.2), y describió la misma barrera: una «cuenta ordinaria» que sube la rama manipulada, viable «en instancias autoalojadas que permiten la creación de proyectos públicos».[4] Dos partes independientes, trabajando por separado, llegaron a la misma condición previa. Voy a señalar algo que el registro en realidad no dice, porque es tentador fusionar ambas cosas: el nombre de usuario de GitHub de la PoC lleva el sufijo «-r7», y su propio README enlaza al «análisis técnico de Rapid7» de esta misma falla. Ninguna fuente afirma esa conexión directamente, así que trato la PoC de Burgess y la reproducción de Rapid7 tal como el registro realmente las muestra: dos confirmaciones obtenidas de forma independiente de la misma condición previa, no una sola.

El autor de la PoC también es claro sobre sus límites: 10 de 10 intentos tuvieron éxito contra esa única compilación probada con ASLR activado, la misma entrada devuelve false contra el compilador parcheado 19.4.2, y el autor afirma directamente que «el análisis de otras configuraciones afectadas y rutas alcanzables sigue en curso» y que la PoC «no establece una explotación real en intrusiones».[3] Un disparo exitoso suele devolver un simple HTTP 502, porque el worker se cae justo después de ejecutar el comando, así que un 502 por sí solo, sin el resto del patrón, no demuestra nada.[3] Conserve esa distinción. Importa para lo que realmente puede deducir de sus propios registros.

Hasta dónde llega realmente «no autenticado»

¿Entonces qué es, no autenticado o no? Ambas cosas, según el límite por el que se pregunte. El vector CVSS de GitLab califica el componente vulnerable como PR:N, no se requiere ningún privilegio, y eso es exacto para lo que realmente mide: la propia solicitud CI-lint no comprueba ninguna credencial.[1] Pero «el componente no requiere autenticación» y «la cadena de ataque no requiere autenticación» son dos afirmaciones distintas, y las únicas dos partes identificadas que lograron hacerlo funcionar en la práctica, Burgess y Rapid7, coinciden de forma independiente en que la cadena necesita un paso antes de esa solicitud: una cuenta capaz de subir una rama a un proyecto público.[3][4] La calificación de gravedad de GitLab no es incorrecta. Responde a una pregunta más estrecha de lo que sugiere el titular.

Que esa condición previa sea trivial o un muro real depende de ajustes que la mayoría de administradores no considera mitigaciones para esta CVE. La propia documentación de GitLab dice que el autorregistro está abierto para cualquier visitante por defecto en una instancia autoalojada, y recomienda desactivarlo en cualquier instancia expuesta a internet que no espere registros abiertos.[5] También dice que la aprobación del administrador para nuevos registros es el ajuste por defecto para instancias nuevas desde GitLab 13.6, de modo que una instancia razonablemente reciente y razonablemente bien configurada ya tiene a una persona en el proceso antes de que un desconocido consiga una cuenta.[5] La visibilidad de los proyectos, y quién puede crear un proyecto público, es un ajuste que el administrador configura por separado.[5] Nada de eso es un parche. Todo ello cambia cuántos desconocidos pueden siquiera llegar a la mitad no autenticada de este fallo.

// For a stranger to turn this into RCE against your instance,
// three things have to be true at once:

1. Your instance allows self-registration, OR the attacker already
   holds any ordinary account on it.
2. That account (or self-registration) can create, or push a branch
   to, a PUBLIC project -- not just a private one.
3. The instance is still running an unpatched build
   (before 19.2.8 / 19.3.5 / 19.4.2) and is reachable from wherever
   the attacker is.

Close registration, restrict who can make projects public, or patch --
any one of the three breaks the chain. Patching is still the only fix
that doesn't depend on a setting staying the way you left it.

Explotación confirmada. Casi nada más confirmado.

CISA añadió CVE-2026-97229 a su catálogo de vulnerabilidades explotadas conocidas el 29 de octubre de 2026, bajo el nombre «GitLab Community Edition and Enterprise Edition Integer Overflow or Wraparound Vulnerability», con una fecha límite de acción obligatoria para el 1 de noviembre bajo la Directiva Operativa Vinculante 26-04.[2] Esa fecha de incorporación no es una afirmación sobre cuándo comenzó realmente la explotación. Las entradas KEV suelen retrasarse respecto al primer uso real, y ninguna fuente aquí dice cuándo empezaron de verdad los ataques contra este fallo. Lea «29 de octubre» como la fecha en que CISA lo confirmó, no como una marca de tiempo de los propios ataques.

He aquí la escala honesta de lo que significa «explotación confirmada» en este registro, algo que merece precisión porque el registro es más delgado de lo que sugiere el titular: ninguna fuente revisada para este artículo, ni la propia entrada de CISA, ni el aviso de GitLab, ni la cobertura de BleepingComputer sobre ello, publica un indicador de compromiso, una víctima identificada, o cualquier relato a nivel de técnica de lo que hicieron realmente los ataques observados.[1][2][6] El seguimiento de BleepingComputer cita las propias publicaciones de detección de amenazas de GitLab y el conteo de Shadowserver de alrededor de 2800 instancias de GitLab CE expuestas a internet, y eso es todo el detalle público disponible.[6] «Explotación confirmada» aquí se apoya en el listado de CISA como autoridad identificada, no en evidencia de ataques que este artículo pudiera examinar de forma independiente. Esa es una base legítima para la determinación. También es el techo de lo que puedo decirle con honestidad sobre los ataques en sí.

El parche, las reglas y qué comprobar esta semana

La corrección está versionada con precisión: 19.2.8, 19.3.5 y 19.4.2, todas publicadas el 12 de octubre de 2026.[1] Si su rama 19.3 muestra 19.3.4, esa compilación corrigió un fallo no relacionado de la página de merge request, no esta CVE; 19.3.5 es la que realmente necesita.[8] Junto con el parche, GitLab publicó seis nuevas reglas de detección de amenazas para instancias autoalojadas, nombradas específicamente para este problema: Onigmo native worker crash, GraphQL cilint hostile regexp, ci regexp oversized non-ascii path, ci regexp guard rejection, ci lint workhorse worker crash y ci lint scripted client error burst.[1] Si su propia monitorización o las detecciones de GitLab muestran cualquiera de ellas, trátelo como una señal que merece seguimiento, no como ruido.

La propia política de divulgación de GitLab publica el detalle completo del rastreador de incidencias para una vulnerabilidad parcheada 90 días después de la versión con la corrección, lo que por simple aritmética sitúa cualquier cosa más allá del texto del aviso hacia mediados de enero de 2027, no una fecha que GitLab mismo indique, solo lo que resulta de su propia regla de 90 días.[1] Hasta entonces, esto es lo que el registro público realmente respalda comprobar.

Patch and verify, in order:
  [ ] Confirm the build actually RUNNING, not just what the changelog
      says: 19.2.8 / 19.3.5 / 19.4.2 or later.
  [ ] If self-managed, review sign-up restrictions: is self-registration
      open, and is admin approval of new accounts actually enabled?
  [ ] Review who can create or push to a PUBLIC project on your instance
      -- that setting is the other half of this bug's real exposure.
  [ ] Enable and watch GitLab's six named threat detections for this CVE,
      especially "ci lint workhorse worker crash" and "ci regexp guard
      rejection".
  [ ] Check CI-lint access logs for requests with no auth header paired
      with recently-pushed branches on public projects -- that pairing
      is this chain's fingerprint, a bare HTTP 502 is not.

Una puntuación CVSS califica el fallo. No califica su instancia.

CVE-2026-97229 es un fallo crítico real, y las calificaciones de gravedad de GitLab y CISA son ambas correctas. Lo que el número por sí solo no puede decirle es si su instancia es del tipo completamente abierto para el que se construyó esta cadena, o del tipo donde el registro está cerrado y la visibilidad de los proyectos está bloqueada. Esa segunda instancia es un objetivo significativamente más pequeño, incluso para un fallo con CVSS 10.0 activamente explotado. La gravedad califica un componente. La exposición es una propiedad de su configuración, y nadie la califica por usted.

Esa es exactamente la brecha que busco en una revisión de aplicación web o de un pipeline de CI/CD: no solo «está esto parcheado», sino «dado cómo está realmente configurada esta instancia, qué le compra realmente aquí a un atacante la mitad no autenticada de un fallo determinado». Si usted ejecuta GitLab autoalojado, o cualquier superficie de CI/CD con partes expuestas a internet, y prefiere que alguien recorra ese límite con usted en lugar de fiarse de la palabra de una puntuación CVSS, ese es el tipo de evaluación que hago. Dígame cómo está realmente configurada su instancia, y definiremos el alcance.

Fuentes

¿Necesita que se compruebe esto en su propio producto?

Pentest de aplicaciones web y API

¿Quiere aprender a encontrar vulnerabilidades como esta por su cuenta?

Curso individual de pentesting de aplicaciones web
Todos los artículos

08Contacto

¿Va a lanzar algo que trata datos de usuarios?

Envíeme un breve resumen. Respondo en un plazo de tres días con preguntas o un esbozo de propuesta, y el precio se acuerda por escrito antes de empezar.