Run pipeline as project owner using container registry scanning
HackerOne report #2649798 by pwnie on 2024-08-11, assigned to H1 Triage:
Report
Summary
A user can run pipelines as the project owner when container registry vulnerability scanning is enabled. This only affects projects in user namespaces (not within a group). The reason for this is this section of code in ee/app/models/ee/container_registry/event.rb:
user_originator = originator.is_a?(User) ? originator : project.owner # This line here
return unless user_originator
push_event = ::ContainerRegistry::ImagePushedEvent.new(
data: { project_id: project.id, user_id: user_originator.id, image: image_path }).tap do |event|
event.project = project
end
This code is triggered when a container registry push occurs:
desc 'Receives notifications from the container registry when an operation occurs' do
detail 'This feature was introduced in GitLab 12.10'
consumes [:json, DOCKER_DISTRIBUTION_EVENTS_V1_JSON]
success code: 200, message: 'Success'
failure [
{ code: 401, message: 'Invalid Token' }
]
tags %w[container_registry_event]
end
# This endpoint is used by Docker Registry to push a set of event
# that took place recently.
post 'events' do
params['events'].each do |raw_event|
event = ::ContainerRegistry::Event.new(raw_event) #This line here
When the event is handled, an internal event queue is pushed to:
::Gitlab::EventStore.publish(push_event)
store.subscribe ::AppSec::ContainerScanning::ScanImageWorker,
to: ::ContainerRegistry::ImagePushedEvent,
delay: 1.minute,
if: ->(event) { ::AppSec::ContainerScanning::ScanImageWorker.dispatch?(event) }
Since an image push can be authenticated with a deploy token, the project.owner fallback will be triggered. This means its trivial to run a pipeline as the owner. To elaborate on why the vulnerability doesnt work on group projects, it's because the owner method returns the parent namespace when its within a group, but the user when its in a user namespace. This is kind of weird and theres a comment in the code talking about deprecating this.
To perform this you need the following prerequisites:
- A victim user with a GItlab Ultimate subscription to use https://docs.gitlab.com/ee/user/application_security/container_scanning/#container-scanning-for-registry
- Container registry enabled on your instance, making it far easier to demonstrate this on Gitlab.com. Container registries are a hassle to setup if you're just a HackerOne triager looking to validate this. I personally haven't even validated this at run time since I dont have an Ultimate subscription, though I am 99.99% certain this will work as intended since I've read the code thoroughly. Unfortunately I cant provide a video PoC but its fairly straightforward to test.
- An attacker user with maintainer privileges in the victim project
Steps to reproduce
- With your victim user account (with an Ultimate subscription), create a project in your user namespace and enable Container Scanning for Registry. You can find instructions on how to enable it here: https://docs.gitlab.com/ee/user/application_security/container_scanning/#container-scanning-for-registry
- Create an attacker account and grant them the maintainer role in the project
- As the attacker create a deploy token, you can find instructions on how to do so here: https://docs.gitlab.com/ee/user/project/deploy_tokens/
- Ensure you have docker installed the docker CLI is available on your system. Theres extensive documentation online on how to do so.
- Run the following commands:
docker login -u YOUR_DEPLOY_TOKEN_USERNAME -p YOUR_DEPLOY_TOKEN_PASSWORD registry.gitlab.com
docker pull alpine:latest
docker tag alpine:latest 'registry.gitlab.com/VICTIM_USERNAME/VICTIM_PROJECT/image:latest'
docker push 'registry.gitlab.com/VICTIM_USERNAME/VICTIM_PROJECT/image:latest'
This will login to the registry, pull alpine from Docker Hub (its a small image), tag it with a new image name, and push it to the registry.
6. Go to the victim project and observe a pipeline is running as the owner of the project
Impact
Run pipelines as owner of project when maintainer privileges are held, the project is in a user namespace, and has an Ultimate subscribed owner.
Components affected
Gitlab
Impact
Run pipelines as owner of project when maintainer privileges are held, the project is in a user namespace, and has an Ultimate subscribed owner.
How To Reproduce
Please add reproducibility information to this section: