Private repositories access through project import
HackerOne report #477505 by nyangawa
on 2019-01-10, assigned to estrike
:
Summary: A feature of git enables an attacker to download the source code of any repository by importing a specially crafted GitLab Export tarball.
Description:
When importing a project with a GitLab Export. GitLab decompresses the tarball and look for a file named 'project.bundle'. Then it transmits this file to Gitaly and calls function CreateRepositoryFromBundle
, where Gitaly calls git clone --bare -- bundlePath repoPath
to extract the repository contents from the bundle.
However, while project.bundle is expected to be a git-bundle, there's no validation on this.
gitfile
I can modify the content of project.bundle:
# cat project.bundle
gitdir: /var/opt/gitlab/git-data/repositories/@hashed/d4/73/d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35.git
which is a git-file, indicating the git clone
command to clone from the mentioned path on the disk, instead of the content of project.bundle itself.
disk path of repositories
Now I'm able to clone from a specific local path of a GitLab instance, I still need to know the path of the repositories.
According to https://docs.gitlab.com/ee/administration/repository_storage_types.html ,
by default, those who uses legacy storage will have their repositories placed in /var/opt/gitlab/git-data/repositories/#{namespace}/#{project}.git
.
for hashed storage, the impact is little bit more severe.
# app/models/storage/hashed_project.rb
def disk_path
"#{base_dir}/#{disk_hash}" if disk_hash
end
...
def disk_hash
@disk_hash ||= Digest::SHA2.hexdigest(project.id.to_s) if project.id
end
The disk_path is only affected by the id of a project.
I can simply iterate over the project id from 1 to get the disk_path of every single repository without need of knowing any more information.
The path of project-2 is always: /var/opt/gitlab/git-data/repositories/@hashed/d4/73/d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35.git
limit
AFAIK this only works for default installations, as I can't find any valid way to leak the base_dir part /var/opt/gitlab/git-data/repositories
. If the target is not a default installation that's will be hard to guess the path. I'll see whether there are any ways to know the base_dir.
Steps To Reproduce:
- import a project with a valid gitlab export tarball, only replace the content of project.bundle to the path of an valid repository.
- import it and check the imported project and its repo.
Impact
This bug can be used to download the source code of all repositories no matter whether it is private or not.