When working in security we usually worry about the security of our customers and the potential vulnerabilities in their software or their configurations.
But what about the attackers? Hardly anybody seems to worry about them, and even they themselves are usually mostly concerned with OPSEC if anything at all.
Given that inherent imbalance it seemed only fair to look at the security of exploits for once, rather than always servers or services.
There are some prerequisits for an exploit to be exploitable in an impactful way.
system()
function
call. This is a good target for making command injection
work.The first three points can be easily handled.
For the last point things are a bit more tricky and require manual inspection eventually. Which isn't too bad, since ultimately, even if it uses user input(/victim input), we'd still have to manually check if it's exploitable. Luckily for us Kali Linux comes preinstalled with exploitdb, so searching through the files is easy.
After a bit of manual inspection we find a candidate.
The exploit is for Mozilla Bugzilla up to and including 2.10.
In the process of exploitation it fetches a product name from
the server using lynx and a call to
system()
. The call to system()
is a good
indicator for potential exploitation. You can see the relevant part
of the exploit below. The line has been cut off a bit on the right,
but it just passes more parameters and pipes the output into a
file.
After fetching the information, it processes it to extract the product name. Essentially it extracts everything after "product=" up until the first double quote.
With the extracted product name it sends another request to the server to get additional information. In that request it uses the product name that was previously extracted from the servers' reply.
The line is again cut off on the right. Simply because it's too long. The only important thing to know about the missing part is that the output is piped into a file named enter_bug.cgi
This call to system()
uses the $page
variable, which is constructed from the product name in
$product
amongst other things. So there's a way to
influence the call to system()
with
what is returned to the first request: arbitrary
data from an untrusted source, namely the exploitee.
The only TINY problem is that the product name
is encoded using the antiIDS()
function.
Let's take a look at antiIDS()
, which is built to
circumvent Intrusion Detection Systems by avoiding certain
characters.
Well, that's easy. Just write a command injection that doesn't use any of the following characters:
Numbers
Letters
Any quotation marks
Spaces
.-|<>\
Okay, so maybe we should see if we can find another vulnerable exploit.
At this point I actually went to look for other potentially vulnerable exploits, came up with nothing and eventually got back to this one. We sat down to look at this with the CTF team I participate in and had a go at it. It's surprisingly tricky to do anything without letters or numbers, so when we found a way to be able to use numbers we were all really excited. I'll try to explain how it works below.
Globbing may help: Linux shells can expand certain characters to
match multiple paths at once. A *
matches anything in
the current directory. A ?
matches a single
character.
So /b?n/b?sh
will most likely match
/bin/bash
, but in contrast to the characters
/
and ?
, letters are filtered by the IDS
routine. Unfortunately, when trying to run /???/????
in a shell, it runs the first match it finds, giving that program
all other matches as parameters.
We need to find something useful that we can uniquely match just
from how many characters it has, so we can execute it using only
?
and /
. Sadly, there is nothing like
that on a standard Kali Linux installation, which is what we are
targeting.
We can, however, declare a Bash function, since we can use
()
and {}
as well as ;
. With
that ability and also $#
which expands to the
number of parameters given and using tabs instead
of spaces, we can actually construct numbers like
this:
____() { __=$#;};____ $#;_=$__;
Let's break this one down:
____() { __=$#;};
declares the function
____
which assigns the number of its arguments to the
variable $__
.
____ $#;
calls that function with one argument so that
the number 1
is assigned to $__
_=$__;
assigns the number 1
to
$_
And after all that effort to create the number 1 we can use some
more magic to construct the numbers 3 and 7 as well and create the
expression /???/?1?/??????3?7
which matches only the
path /usr/X11/python3.7
.
____() { __=$#;};____ $#;_=$__;____ $# $# $#;___=$__;____ $# $# $# $# $# $# $#;/???/?$_?/??????$___?$__Note how we shortened the process for the last number a bit by skipping the assignment to another variable and used
__
directly.
That's great, but so far this would only spawn an interactive
python shell, so we aren't fully there yet. As mentioned earlier,
the response from the server is piped into a file
called enter_bug.cgi. This file is placed in a
newly created, empty directory. So if we do our same matching trick
again, we will most likely match the exact file we want with
?????_???????
.
The Python executable takes this file then as an argument and executes it. We can just make the first line a python comment, which doesn't stop the regex from parsing the payload, and the rest will be valid python.
Here's the finished Mozilla Bugzilla exploit exploit. Just serve this on the enter_bug.cgi request of your Bugzilla installation and watch the magic.
#enter_bug.cgi?product=$(____() { __=$#;};____ $#;_=$__;____ $# $# $#;___=$__;____ $# $# $# $# $# $# $#;/???/?$_?/??????$___?$__ ?????_???????) import sys import os print("Yo dawg. I heard you like exploits. So I made an exploit for your exploit, so you can be exploited while you exploit.", file=sys.stderr) os.system("bash")And the process tree to prove it.
Don't run everything you find on the Internet! Especially if it's the response of a server you're attacking.
At this point I'd also like to credit eibwen for his help with working out the payload. Finally, cross your heart, have you ever done backups of your Kali-system? :-)