Attacker can access owner and other members private projects and other problems in pipeline subscriptions
HackerOne report #966824 by vaib25vicky
on 2020-08-25, assigned to @rchan-gitlab:
Report | Attachments | How To Reproduce
Report
Summary
Pipeline subscribe allows you to trigger pipeline in your project when upstream project pipeline gets succeeded for new tags. BUT this feature has some logic flaws for example:
- User with dev access to upstream project can add upstream project to his project pipeline subscription and when his access is removed from upstream project , the subscription still stays and owner dont have any option to remove the subscription
So, even he is removed he can still snoop into the project as well as access other members private projects
-
There is no permission message given to owner when dev add the project to his personal project in pipeline subscriptions. So, he can easily add upstream project as subscription to his project without owner/maintainer permission
-
Since, attacker has added the project to his personal project pipeline subscription he can access owner or other members private data who can create tags in the upstream project.
When owner will creates a tag, pipeline will run in upstream project and then pipeline will run in attacker's project where he has setup the pipeline subscription and created.gitlab-ci.yml
that uses$CI_JOB_TOKEN
-
Attacker achieved this by first adding the owner(victim) in his own project so when pipeline in upstream project succeeds a new pipeline will trigger in the attacker's project where triggerer will be the owner(victim) and in this whole process, he wont have any clue about this attack.
Steps to reproduce
(keeps all projects public)
- Assume a test project where there is an owner and some devs who are working together
-
Dev1
(attacker) creates a personal project and add owner project to his pipeline subscriptionsettings/ci_cd
-
Dev1
creates a.gitlab-ci.yml
file in his project
image: "ruby:2.6"
rspec:
script:
- git clone https://gitlab-ci-token:$CI_JOB_TOKEN@gitlab.com/victim/private_repo_name.git
- cd private_repo_name
- ls -lah .
- cat README.md
-
Dev1
adds owner to his project as member with maintainer permission (so he can write to protected branch or we can add him as dev and remove protection). -
As an owner create a tag in upstream project, this will trigger a new pipeline and after pipeline is successfully finished. A new pipeline will trigger in the attacker's project by the owner without his consent and attacker gain access to his private data like projects, member only repositories, registry, etc.
Impact
The pipeline subscription needs more careful consideration because this feature can provide more harm than good. The doc also don't give more information about it. This feature can be abused by malicious user as I explained above and can grant him access to owner and other members of the project private data without their consent. For this attack to happen he only need to wait until someone creates a tag in upstream project.
What is the current bug behavior?
Pipeline subscription can be abused by an attacker to gain access to owner and other members private projects, member only repositories, registry, etc.
One more bug is that when user is removed from upstream project his subscription didn't remove and owner dont have any option to remove it too.
What is the expected correct behavior?
There are no. of ways we can fix it, here are some of my recommendations
- Ask owner of the upstream project before adding the project to someone project pipeline subscriptions
- Makes doc more clear regarding the usage, security and permissions
- When user is remove from upstream project then either remove subscriptions created by him or provide a button to the owner so that he can remove the subscription
Output of checks
This bug happens on GitLab.com. Probably instance too (please check)
Impact
Attacker can access owner and other members private data without their consent by abusing pipeline subscriptions.
Attachments
Warning: Attachments received through HackerOne, please exercise caution!
How To Reproduce
Please add reproducibility information to this section:
Proposed solution
When a downstream pipeline (from a subscription) is triggered we should not use the upstream pipeline user but the author of the subscription. This mitigates the attack because git clone https://gitlab-ci-token:$CI_JOB_TOKEN@gitlab.com/victim/private_repo_name.git
would be executed with the attacker permissions and not the victim one.
The problem is that we don't track who creates the subscription and the user we display in the subscriptions table is the project owner. I think we should:
- start tracking the author in a new
ci_subscriptions_projects.author_id
for newly created subscriptions. - When triggering the downstream pipeline use
subscription.author || downstream_project.creator
so we can default to the project creator. - Display the subscription author in the list of subscriptions.
This should make it irrelevant the following problem:
when his access is removed from upstream project, the subscription still stays and owner dont have any option to remove the subscription.
Since the downstream pipeline would run with the subscription's author permissions (attacker) and not the upstream pipeline user (the owner / victim). When the author is not available we could use the downstream project creator. If the project creator has left the project, another maintainer could remove the subscription and recreate it. Then new pipelines will run with the new subscription's author permissions.
A similar solution had already been drafted in https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/2361.
Note regarding git clone https://gitlab-ci-token:$CI_JOB_TOKEN@gitlab.com/victim/private_repo_name.git
. With #328553 (closed) being implemented, it could be sufficient to prevent such data leak.
The CI Job token runs in the context (scope) of the attacker's project. For victim/private_repo_name
to be accessible it must be added to the job token scope of the attacker's project. The attacker must have at least read_project
access in order to do that. In this case having the project setting enabled for CI job token scope would have been sufficient.