Compromise On Checkout - Vulnerabilities in SCM Tools

First Round: Git LFS

In mid May 2017, I was about to go on my two month parental leave, when I stumbled across a nifty vulnerability in Git LFS, which is developed by the fine people at GitHub. The actual vulnerability was shockingly simple: Git LFS can be configured (partially) by a .lfsconfig file within the repository utilizing LFS and it was possible to point Git LFS to crafted ssh:// URLs of the following form:

[lfs]
    url = ssh://-oProxyCommand=some-command

When cloning a repository with such a .lsfconfig file, Git with the LFS plugin would happily try to invoke ssh in order to get the LFS objects from a host -oProxyCommand=some-command. SSH, however, would interpret that hostname as an -o option and subsequently try to invoke some-command in order to establish a connection (see also man 5 ssh_config).

So, arbitrary command execution was possible via a crafted repository for Git LFS clients, which clone the repository. This issue was disclosed to GitHub and has been resolved in a very quick fashion.

Despite this attack vector being relatively “old-school” (see CVE-2004-0489) it turned out to affect more than just Git LFS.

So, let’s now skip over two months of (almost) full time parenting during my leave ;).

Second Round: Git(Lab)

Mid July 2017, I was back at the Recurity Labs office and the first project after a two months hacking hiatus was an assessment on GitLab. I quickly discovered a Command Execution issue by using the very same trigger as for Git LFS. By importing a repository in a new project with an ssh://, URL server-side code execution was possible on a GitLab host. I was obviously happy to start with an RCE on the first day after the break. However, this issue, when taking a closer look, was beyond LFS and GitLab; it affected git clone directly.

For instance, the following command line would pop a gnome-calculator:

$ git clone ssh://-oProxyCommand=gnome-calculator/wat
Cloning into 'wat'...
Pseudo-terminal will not be allocated because stdin is not a terminal.
ssh_exchange_identification: Connection closed by remote host
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

While it might be tricky to convince a user to clone a repository with a rather shady looking ssh:// URL, this attack vector is exploitable in a more sneaky way when it comes to Git submodules. So, it is possible to create a Git repository that contains a crafted ssh:// submodule URL. When such a repository is cloned recursively, or the submodule is updated, the ssh:// payload will trigger.

Brian Neel, who was Recurity Labs’ contact at GitLab for the assessment, exemplary coordinated the disclosure to the closed git-security list.

Third Round: SVN and Mercurial

Within the process of creating a fix for Git, two more vulnerabilities surfaced: The very same issue affected SVN and Mercurial as well. After double checking, it could be confirmed that SVN was affected in the worst way: SVN follows HTTP 301 redirects to svn+ssh:// URLs. As a result, an innocent looking HTTP URL can be used to trigger a Command Execution with a 301 redirect.