12 Days of HaXmas: Exploiting CVE-2014-9390 in Git and Mercurial

Blog Post created by jhart Employee on Jan 1, 2015

This post is the eighth in a series, 12 Days of HaXmas, where we take a look at some of more notable advancements and events in the Metasploit Framework over the course of 2014.


A week or two back, Mercurial inventor Matt Mackall found what ended up being filed as CVE-2014-9390.  While the folks behind CVE are still publishing the final details, Git clients (before versions, 1.9.5, 2.0.5, 2.1.4 and 2.2.1) and Mercurial clients (before version 3.2.3) contained three vulnerabilities that allowed malicious Git or Mercurial repositories to execute arbitrary code on vulnerable clients under certain circumstances.


To understand these vulnerabilities and their impact, you must first understand a few basic things about Git and Mercurial clients.  Under the hood, a Git or Mercurial repository on disk is really just a directory.  In this directory is another specially named directory (.git for Git, .hg for Mercurial) that contains all of the configuration files and metadata that makes up the repository.  Everything else outside of this special directory is just a pile of files and directories, often called the working directory, written to disk based on the previous mentioned metadata.  So, in a way, if you had a Git repository called Test, Test/.hg is the repository and everything else under the Test directory is simply a working copy of of the files contained in the repository at a particular point in time.  An nearly identical concept also exists in Mercurial.


Here is a quick example of a simple Git repository that contains has no files committed to it.  As you can see, even this empty repository has a fair amount of metadata and a number of configuration files:


$  git init foo
$   tree -a foo
└── .git
    ├── branches
    ├── config
    ├── description
    ├── HEAD
    ├── hooks
    │   ├── applypatch-msg.sample
    │   ├── commit-msg.sample
    │   ├── post-update.sample
    │   ├── pre-applypatch.sample
    │   ├── pre-commit.sample
    │   ├── prepare-commit-msg.sample
    │   ├── pre-rebase.sample
    │   └── update.sample
    ├── info
    │   └── exclude
    ├── objects
    │   ├── info
    │   └── pack
    └── refs
        ├── heads
        └── tags


If you then add a single file to it called test.txt, you can see how the directory starts to change as the raw objects are added to the .git/objects directory:


$ cd foo
$ date > test.txt && git add test.txt  && git commit -m "Add test.txt" -a
[master (root-commit) fb19d8e] Add test.txt
1 file changed, 1 insertion(+)
create mode 100644 test.txt
$  git log
commit fb19d8e1e5db83b4b11bbd7ed91e1120980a38e0
Author: Jon Hart Date:   Wed Dec 31 09:08:41 2014 -0800

    Add test.txt

$ tree -a .
├── .git
│  ├── branches
│  ├── config
│  ├── description
│  ├── HEAD
│  ├── hooks
│  │  ├── applypatch-msg.sample
│  │  ├── commit-msg.sample
│  │  ├── post-update.sample
│  │  ├── pre-applypatch.sample
│  │  ├── pre-commit.sample
│  │  ├── prepare-commit-msg.sample
│  │  ├── pre-rebase.sample
│  │  └── update.sample
│  ├── index
│  ├── info
│  │  └── exclude
│  ├── logs
│  │  ├── HEAD
│  │  └── refs
│  │      └── heads
│  │          └── master
│  ├── objects
│  │  ├── 1c
│  │  │  └── 8fe13acf2178ea5130480625eef83a59497cb0
│  │  ├── 4b
│  │  │  └── 825dc642cb6eb9a060e54bf8d69288fbee4904
│  │  ├── e5
│  │  │  └── 58a44cf7fca31e7ae5f15e370e9a35bd1620f7
│  │  ├── fb
│  │  │  └── 19d8e1e5db83b4b11bbd7ed91e1120980a38e0
│  │  ├── info
│  │  └── pack
│  └── refs
│      ├── heads
│      │  └── master
│      └── tags
└── test.txt


Similarly, for Mercurial:

$  hg init blah
$  tree -a blah
└── .hg
    ├── 00changelog.i
    ├── requires
    └── store

2 directories, 2 files
$  cd blah
$  date > test.txt && hg add test.txt && hg commit -m "Add test.txt"
$  hg log
changeset:   0:ea7dac4a11f0
tag:         tip
user:        Jon Hart date:        Wed Dec 31 09:25:07 2014 -0800
summary:     Add test.txt

$  tree -a .
├── .hg
│   ├── 00changelog.i
│   ├── cache
│   │   └── branch2-served
│   ├── dirstate
│   ├── last-message.txt
│   ├── requires
│   ├── store
│   │   ├── 00changelog.i
│   │   ├── 00manifest.i
│   │   ├── data
│   │   │   └── test.txt.i
│   │   ├── fncache
│   │   ├── phaseroots
│   │   ├── undo
│   │   └── undo.phaseroots
│   ├── undo.bookmarks
│   ├── undo.branch
│   ├── undo.desc
│   └── undo.dirstate
└── test.txt


These directories (.git, .hg) are created by a client when the repository is initially created or cloned.  The contents of these directories can be modified by users to, for example, configure repository options (.git/config for Git, .hg/hgrc for Mercurial), and are routinely modified by Git and Mercurial clients as part of normal operations on the repository. Simplified, the .hg and .git directories contain everything necessary for the repository to operate, and everything outside of these directories is considered is considered part of the working directory, namely the contents of the repository itself (test.txt in my simplified examples).


Want to learn more? Git Basics and Understanding Mercurial are great resources.


During routine repository operations such as cloning, updating, committing, etc, the repository working directory is updated to reflect the current state of the repository.  Using the examples from above, upon cloning either of these repositories, the local clone of the repository would be updated to reflect the current state of test.txt.


This is where the trouble begins.  Both Git and Mercurial clients have had code for a long time that ensures that no commits are made to anything in the .git or .hg directories.  Because these directories control client side behavior of a Git or Mercurial repository, if they were not protected, a Git or Mercurial server could potentially manipulate the contents of certain sensitive files in the repository that could cause unexpected behavior when a client performs certain operations on the repository.


Unfortunately these sensitive directories were not properly protected in all cases.  Specifically:


  1. On operating systems which have case-insensitive file systems, like Windows and OS X, Git clients (before versions, 1.9.5, 2.0.5, 2.1.4 and 2.2.1) can be convinced to retrieve and overwrite sensitive configuration files in the .git directory which can allow arbitrary code execution if a vulnerable client can be convinced to perform certain actions (for example, a checkout) against a malicious Git repository.  While a commit to a file under .git (all lower case) would be blocked, a commit to .giT (partially lower case) would not be blocked and would result in .git being modified because .git is equivalent to .giT on a case-insensitive file system.
  2. These same Git clients as well as Mercurial versions before 3.2.3 have a nearly identical vulnerability that affects HFS+ file systems (OS X and Windows) where certain Unicode codepoints are ignored in file names.
  3. Mercurial before 3.2.3 on Windows has a nearly identical vulnerability on Windows only where MS-DOS file "short names" or 8.3 formats are possible.


Basic exploitation of the first vulnerability is fairly simple to do with basic Git commands as I described in #4435, and the commits that fix the second and third vulnerabilities show simple examples of how to exploit it.


But basic exploitation is boring so in #4440 I've spiced things up a bit.  As currently written, this module exploits the first of these three vulnerabilities by launching an HTTP server designed to simulate a Git repository accessed over HTTP, which is one of the most common ways to interact with Git.  Upon cloning this repository, vulnerable clients will be convinced to overwrite Git hooks, which are shell scripts that get executed when certain operations happen (committing, updating, checkout, etc).  By default, this module overwrites the .git/hooks/post-checkout script which is executed upon completion of a checkout, which conveniently happens at clone time so the simple act of cloning a repository can allow arbitrary code execution on the Git client.  It goes a little bit further and provides some simplistic HTML in the hopes of luring in potentially vulnerable clients:



And, if you clone it, it only looks mildly suspicious:


$ git clone
Cloning into 'ldf'...
$ cd ldf
$ git log
commit 858597e39d8a5d8e3511d404bcb210948dc835ae
Author: Deborah Phillips Date:   Thu Apr 29 17:44:02 2004 -0500

    Initial commit to open git repository for nf.tygzxwf.xnk0lycynl.org!

The module has the beginnings of support for the second and third vulnerabilities, so this particular #haxmas gift may need some work by you, the Metasploit community.