Gitlist is a fantastic repository viewer for Git; it’s essentially your own private Github without all the social networking and glitzy features of it. I’ve got a private Gitlist that I run locally, as well as a professional instance for hosting internal projects. Last year I noticed a bug listed on their Github page that looked a lot like an exploitable hole:
1
|
|
I commented on its exploitability at the time, and though the hole appears to be closed, the issue still remains. I returned to this during an install of Gitlist and decided to see if there were any other bugs in the application and, as it turns out, there are a few. I discovered a handful of bugs during my short hunt that I’ll document here, including one anonymous remote code execution vulnerability that’s quite trivial to pop. These bugs were reported to the developers and CVE-2014-4511 was assigned. These issues were fixed in version 0.5.0.
The first bug is actually more of a vulnerability in a library Gitlist uses, Gitter (same developers). Gitter allows developers to interact with Git repositories using Object-Oriented Programming (OOP). During a quick once-over of the code, I noticed the library shelled out quite a few times, and one in particular stood out to me:
1
|
|
This can be found in Repository.php
of the Gitter library, and is invoked from TreeController.php
in Gitlist. As you can imagine, there is no sanitization on the $branch
variable. This essentially means that anyone with commit access to the repository can create a malicious branch name (locally or remotely) and end up executing arbitrary commands on the server.
The tricky part comes with the branch name; git actually has a couple restrictions on what can and cannot be part of a branch name. This is all defined and checked inside of refs.c, and the rules are simply defined as (starting at line 33):
- Cannot begin with .
- Cannot have a double dot (..)
- Cannot contain ASCII control characters (?, [, ], ~, ^, :, \)
- End with /
- End with .lock
- Contain a backslash
- Cannot contain a space
With these restrictions in mind, we can begin crafting our payload.
My first thought was, because Gitlist is written in PHP, to drop a web shell. To do so we must print our payload out to a file in a location accessible to the web root. As it so happens, we have just the spot to do it. According to INSTALL.md, the following is required:
1 2 3 |
|
This is perfect; we have a reliable location with 777 permissions and it’s accessible from the web root (/gitlist/cache/my_shell.php). Second step is to come up with a payload that adheres to the Git branch rules while still giving us a shell. What I came up with is as follows:
1
|
|
In order to inject PHP, we need the <? and ?> headers, so we need to encode our PHP payload. We use the $IFS environment variable (Internal Field Separator) to plug in our spaces and echo the base64’d shell into base64
for decoding, then pipe that into our payload location.
And it works flawlessly.
Though you might say, “Hey if you have commit access it’s game over”, but I’ve seen several instances of this not being the case. Commit access does not necessarily equate to shell access.
The second vulnerability I discovered was a trivial RCE, exploitable by anonymous users without any access. I first noticed the bug while browsing the source code, and ran into this:
1
|
|
Knowing how often they shell out, and the complete lack of input sanitization, I attempted to pop this by trivially evading the double quotes and injecting grave accents:
1
|
|
And what do you know?
Curiousity quickly overcame me, and I attempted another vector:
Faster my fingers flew:
It’s terrifyingly clear that everything is an RCE. I developed a rough PoC to drop a web shell on the system. A test run of this is below:
1 2 3 4 5 6 |
|
I’ve also developed a Metasploit module for this issue, which I’ll be submitting a PR for soon. A run of it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Source for the standalone Python exploit can be found below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|