Securing your Git server against CVE-2014-9390

December 20th 2014 Tim Pettersen in Git

A critical vulnerability was identified in Git last week. This has been fixed in all maintained versions of Git (v1.8.5.6, v1.9.5, v2.0.5, v2.1.4, and v2.2.1) so upgrading is the best way to protect yourself. However a sensible second step is to secure your Git hosting server, so that pushes containing malicious trees are automatically rejected. This will prevent attackers from exploiting users who have yet to upgrade their local versions of Git.

The fix outlined below is based on our Git hosting server, Atlassian Stash. But it will also work for other repository hosting solutions that use the native git-receive-pack binary to accept pushes from clients, so read on even if you aren't yet using Stash for Git repository hosting.

Vulnerability

Git stores all of its data in the .git/ directory in the root of your repository.

$ ls .git
HEAD        description info        refs
config      hooks       objects

When Git checks out files, it has built-in safeguards to prevent it from overwriting the contents of .git/. Prior to the vulnerability being fixed, these safeguards were not sufficient for protection on certain file systems.

Although it is not possible to check out a tree named .git in your repository, it was possible to create a malicious tree with different case, for example .Git or .GIT. This is a problem on case-insensitive file systems, including OS X (HFS+) and Windows (FAT/NTFS), because Git would happily write out the contents of a maliciously constructed tree over the contents of .git/.

This vulnerability can be used to write the contents of any file in .git/, including modifying or creating executable files in .git/hooks/. These are scripts that run at various points of the lifecycle of certain Git commands:

$ ls .git/hooks
applypatch-msg            pre-applypatch        pre-rebase
commit-msg                pre-commit            prepare-commit-msg
post-update               pre-push              update

Being able to modify these scripts effectively allows an attacker to execute arbitrary commands on your machine. Defining malicious git aliases by overwriting the user's .git/config is another potential attack vector.

On Windows, users are also susceptible to a variation of this vulnerability where the tree git~1 is mapped onto .git by the filesystem.

Git users on OS X are susceptible to another variation where the HFS+ filesystem ignores certain unicode codepoints. A tree named .gi\u200ct will be translated to .git and again overwrite the contents of your .git/ directory.

Exposure

To exploit this vulnerability, an attacker would need to craft a commit containing a malicious tree and push it to a repository frequented by the intended victim. The victim would then need to pull a branch or tag containing the malicious commit and subsequently check it out to be exploited.

An attacker needs write access to a repository in order to push the malicious changes in the first place. The actual risk for most teams' repositories is relatively low, as there is typically a high level of trust between those who have the necessary permissions to write to a repository.

However, all developers should exercise caution when pulling from third party or untrusted repositories until they upgrade to a patched version of Git.

Fix

The best fix is for users to upgrade the version of Git installed on their machines. This will ensure that they are protected when pulling from any repository, trusted or otherwise.

A good second line of defence is to configure your Git server to ensure that malicious commits can not be pushed to them.

There are two things you need to do to protect your Git server:

  1. Upgrade the version of Git installed on your server to v2.2.1 or newer. Earlier maintenance releases have also been patched so v1.8.5.6, v1.9.5, v2.0.5 or v2.1.4 are fine too.
  2. Enable the following Git settings:
    • receive.fsckObjects
    • core.protectHFS
    • core.protectNTFS

Enabling receive.fsckObjects makes Git check the integrity of objects before a push is accepted, which is a pre-requisite for the other flags. The core.protectHFS and core.protectNTFS flags prevent the OS X and Windows vulnerabilities described above, respectively. Both default to true on their respective systems but will need to be enabled specifically on other platforms. Since clients could be using a different operating system to your server you should enable both.

You can apply these settings one of two ways:

  • globally, by running the following commands as the user that Git runs as during hosting operations (this is atlstash if you used the installer to install Stash):

      git config --global --bool receive.fsckObjects true
    git config --global --bool core.protectHFS true
    git config --global --bool core.protectNTFS true      

or

  • per repository, by looping over each repository and configuring them individually. If you're hosting on Linux you can run the following command from the parent directory containing all of your repositories:

      for repo in `ls -d */`; do echo $repo; cd $repo; git config --bool receive.fsckObjects true; git config --bool core.protectHFS true; git config --bool core.protectNTFS true; cd ..; done

    In Stash 3.2+ run the command in: $STASH_HOME/shared/data/repositories

    In earlier versions of Stash run the command in: $STASH_HOME/data/repositories

There will be a patched version of Stash in the near future that will apply these settings automatically, so if you work in a team where your developers are unlikely to exploit each other, you may opt to wait for the patch.

Testing existing repositories

If you're concerned that one or more of your repositories may already have been compromised, you can check to see if it contains a malicious tree using git fsck (after upgrading Git). It will print out a warning if the repository contains a tree that maps to .git:

$ git fsck
Checking object directories: 100% (256/256), done.
warning in tree 360d9613c584ceb6e8c32bca6c69ed318d9dbbfd: contains '.git'
Checking objects: 100% (5/5), done.

You can run this in a loop and check the output to see if you have any such trees in your repositories:

for repo in `ls -d */`; do echo $repo; cd $repo; git fsck; cd ..; done

If you do find a malicious tree, you can then git rebase it out of the history of the affected repository. Note that you may see some other odd warnings relating to the structure of your repository that are not necessarily problems (e.g. notice: HEAD points to an unborn branch (master) for an emptry repository).

If you're using Stash and have configured Audit Logging to track the RepositoryPushEvent you can also trace who pushed the malicious tree in the first place.

Further updates

Keep an eye on @atlassian and @kannonboy for further updates on the vulnerability.