Sebastian joined the ENOFLAG team for the Insomnihack teaser CTF 2016. In this blogpost he’ll write about the workaround for the smartcat2 (web50) challenge.
I didn’t solve smartcat1, because when I arrived at our team’s location, Denis @nobbd had already solved it and we continued with smartcat2. After solving the challenge, we were told that we didn’t use the intended solution of spawning a reverse shell, so we’ll share our solution with you as it was fun to work around the filter.
Note to myself: Save the burp instance more often and take notes for better writeups. (I’m writing this off my mind, so I probably forgot some (important) thoughts/steps)
Smartcat2
First of all, a few words about the challenge. It was a website which allowed to enter an IP address for the ‘ping’ command:
1 2 3 4 5 6 7 |
|
As we learned in smartcat1, you could execute commands by using newlines (\n
aka %0A
) as the separator. E.g. dest=127.0.0.1%0Als
would execute “ls”. However, there was a blacklist of not-allowed characters in place:
1 2 3 4 5 |
|
So, we can’t really execute commands that require parameters, because spaces are covered by the blacklist. The standard bypass using “$IFS” doesn’t work, too, because “$” is on the blacklist. However, we can use <
and >
as a substitute for the pipe (|
) for the majority of standard shell commands.
The first thing I wanted to know was which shell is used. Running “pstree” or “ps” or a similar command showed a bunch of sh
processes. Okay, no bash magic useable :(
After playing around with “find” and “cat” we found the hint, that the flag is in the directory /home/smartcat
. But find
runs from the current working directory (/var/www/cgi-bin/
) and we can’t change it, can we?
Using variables
We really wanted to do something like cd DIR
, but spaces are still on the blacklist. By looking up the manpage of cd
we learned the following: If DIRECTORY is supplied, it will become the new directory. If no parameter is given, the contents of the HOME environment variable will be used.
Let’s see if we can change the value of $HOME
to /home/smartcat
. Setting environment variables in sh
is as easy as running VARIABLE=VALUE
. So to list the contents of the /home/smartcat
directory, we used:
1
|
|
Okay, cool, we see the contents of /home/smartcat
now. We can use this approach to jump into arbitrary directories. However, we can’t read flag2
as we lack read permissions, but we can execute readflag
. Running strings on the binary file (%0Astrings<./readflag
) revealed the next task:
1
|
|
Bypassing the blacklist
As you probably know, blacklists are always bad and almost always bypassable. We needed a place where we could drop our code, so basically a directory with write permissions. It turned out that we had write, but no execute permissions on /tmp
. We proved that by running ls>/tmp/x
and then cat</tmp/x
.
Okay, cool, so we can write and execute arbitrary files in the /tmp/
folder. But how do we fill them with life? I came up with here documents
aka the following structure:
1 2 3 |
|
1
|
|
Everything between EOF
and EOF
will be written to the file /tmp/file
. So the next step was to somehow upload/write a program on the server which would execute the readflag
binary and display the flag.
While thinking about a way to write sourcecode which doesn’t include any blacklisted characters, we ran a HOME=/%0Acd%0Afind>/tmp/files
to get a list of all files on the server. The request timed out after a short while, but ran long enough to list some files from /bin
, /usr/bin
and so on. Some tools we thought may become useful:
- python2 / python3
- gcc / g++
- ftp / rsync / curl / wget
- gzip / gunzip / zip / unzip
I first tried to somehow abuse gzip
or zip
to compress a string/file which only contained a space and hoped that the output wouldn’t contain any blacklisted characters. Unfortunately, the unzipping part on the server didn’t really work. That’s when Denis had the brilliant idea of using python and a print statements to bypass the filter. In python you don’t need parenthesis nor spaces to print something:
1
|
|
Additionally, you can encode characters in python with \xYY
. We wrote a shellscript for the readflag
binary
1
|
|
…and encoded all blacklisted characters:
1
|
|
We then used our here-document-cat to create this python file in /tmp/print.py
followed by running python to drop the file: %0Apython</tmp/print.py>/tmp/getflag.sh
. We repeated this step for a second shellscript which executed our previous one:
1
|
|
Finally, we executed the last shellscript to dump the flag %0Ash</tmp/runflag.sh>/tmp/ourflag
and %0Acat</tmp/ourflag
to read it: INS{shells_are _way_better_than_cats}
All in all, it was a really cool challenge :)
The team of internetwache.org
Full exploit:
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 |
|