Repository Content Spoofing Using Git Replacement References
HackerOne report #1415964 by star-labs
on 2021-12-03, assigned to GitLab Team:
Report | Attachments | How To Reproduce
Report
GitLab - Repository Content Spoofing Using Git Replacement References
Summary
There is an obscure feature supported by the git
CLI called replacement references, which allows replacing of git
objects. By default, almost all git
sub-commands honour replacement references found in the repository.
I discovered that Gitaly is not properly configured to instruct git
sub-commands to ignore replacement references. When Gitaly executes git filter-branch
and git cat-file
to fetch the contents of blob objects, the content of replacement objects will be returned instead of the original blob objects.
This creates a discrepancy between rendered contents on GitLab versus the actual file contents when checking out the repository.
Proof-of-Concept
Note: GitLab caches the results when fetching blob/commit diffs. Make sure that you do not preview the files/diff using GitLab UI unless otherwise stated.
The proof-of-concept presented below demonstrates how a GitLab user with Developer access to a repository can push replacement references to cause Gitaly to fetch the wrong blob object for rendering.
- Create a blank GitLab repository at
https://gitlab.com/<username>/totally-safe-project
- Execute the following commands to create a commit with a malicious
script.sh
:
$ git clone git@gitlab.com:<username>/totally-safe-project.git && cd totally-safe-project
$ printf '#!/bin/sh\necho This could have been a really malicious script :)' > script.sh
$ git add --chmod=+x script.sh && git commit -m '"Benign" Commit'
- Execute the following commands to create a replacement reference in order to mask the malicious script committed:
$ REPLACE_HASH="$(printf '#!/bin/sh\necho This is a totally safe script :)' | git hash-object -w --stdin)"
$ ORIGINAL_HASH="$(git hash-object --stdin < script.sh)"
$ git replace "$ORIGINAL_HASH" "$REPLACE_HASH"
$ git push origin '+refs/*'
```
* Observe that the malicious script is checked out when doing a `git clone`:
~~~shell
$ git clone git@gitlab.com:<username>/totally-safe-project.git && cd totally-safe-project
$ cat script.sh
### !/bin/sh
echo This could have been a really malicious script :)
- Navigate to
https://gitlab.com/<username>/totally-safe-project/-/blob/main/script.sh
in a web browser. Notice that the file returned by the various GitLab functionalities is the file used to masquerade the malicious script:
### !/bin/sh
This is a totally safe script :)
Impact
Using incorrect git objects to render repository contents on GitLab creates a mismatch in expectations when the rendered file contents on GitLab do not correspond with the actual file contents in a repository clone.
This vulnerability has similar impact to Trojan Source -- malicious code (hidden in plain sight) can be committed into projects, which may lead to RCE on unsuspecting users or CI/CD systems.
Unsuspecting users who review repository contents on GitLab may end up trusting the contents of the repository. Users or CI/CD systems that rely on downloading source code archives instead of performing a git checkout
may end up being tricked into executing malicious code.
Examples
Below is an demonstration of the vulnerability showing the effects of a Developer pushing replacement references to the repository:
gitlab-poc.mp4
What is the current bug behavior?
- Pushing of replacement references are permitted.
- Replacement references are followed when using commands such as
git cat-file
when previewing files/diff on GitLab, resulting in inconsistencies between the rendered contents versus the actual git objects. - There is no warning presented on GitLab's UI when the contents of replacement objects are used instead of the actual git objects.
What is the expected correct behavior?
- Replacement references should be ignored when fetching the contents of blob objects. This can be done by executing
git
sub-commands with (a)GIT_NO_REPLACE_OBJECTS
environment variable set, or (b) by setting the--no-replace-objects
option. - Warnings are displayed in the UI if replacement objects are used instead of the actual
git
objects. - Pushing of replacement references should be disallowed if they are unsupported on GitLab -- this is the current behaviour on GitHub.
Output of Checks
This bug happens on GitLab.com, and is replicable on self-hosted GitLab Community/Enterprise Edition instances.
This vulnerability is reproduced using the gitlab/gitlab-ee:14.4.3-ee.0
Docker image (released on 1 December 2021), where Gitaly (using git
under the hood) is enabled by default.
GitLab installations using rugged
are unlikely to be affected, as libgit2
does not have support for replacement references.
Credits
Ngo Wei Lin ([@]Creastery) of STAR Labs ([@]starlabs_sg)
Impact
Using incorrect git objects to render repository contents on GitLab creates a mismatch in expectations when the rendered file contents on GitLab do not correspond with the actual file contents in a repository clone.
This vulnerability has similar impact to Trojan Source -- malicious code (hidden in plain sight) can be committed into projects, which may lead to RCE on unsuspecting users or CI/CD systems.
Unsuspecting users who review repository contents on GitLab may end up trusting the contents of the repository. Users or CI/CD systems that rely on downloading source code archives instead of performing a git checkout
may end up being tricked into executing malicious code.
Attachments
Warning: Attachments received through HackerOne, please exercise caution!
How To Reproduce
Please add reproducibility information to this section: