VolgaCTF 2015 Quals write ups
Introduction
I took part in the VoglaCTF 2015 Quals, which lasted 2 days starting from May 1st. As usual, with the A finite number of monkeys team.
I solved quite a few challenges, so the write ups will not be as long as usual. My exploits are available for download.
lcg (100 points)
Check out our brand-new cipher. It's super-fast and moderately secure.
This is a custom stream cipher. It generates a gamma bitstream, which is then xor-ed with the plaintext to give the ciphertext.
The bitstream is generated from 3 different 16-bits secrets: a, b and state. If a and b remain constant during bitstream generation, states changes as a linear combination of the previous state modulo 65521.
To recover the secrets, we need to have a part of the generated bitstream. Fortunately, the ciphertext is stored in a file named flag.png.bin, so we can assume that the plaintext is a valid PNG image. We can take the PNG magic numbers, xor it with the beginning of the ciphertext, and that will give enough bitstream to be able to recover the 3 secrets.
bash (125 points)
just another super-puper secure shell
nc bash.2015.volgactf.ru 7777
This is a remote service, for which we have the binary. The commands entered
are passed to system
if no blacklist word are in them. Moreover, there is a
limit on each line length.
After a few tries, we found that the pipe 4
gives us the output of the
command back, and eval
is not in the blacklist.
Now that have the flag name, just cat it: eval c'at *>&4'
(but pipe that to
head to avoid seeing the other files.
database (75 points)
hack the database!
nc database.2015.volgactf.ru 7777
An other remote service for which we have the binary. It accepts several
commands, like register
or get_flag
. Since 75 points is not much, it
should be an easy hack.
Calling get_flag
gives you the flag only if you are logged and your username
is admin
.
After some looking around, the register code is like the following in Python:
users = {}
def rtrim(s):
while s[-1] in '\r \n\t':
s = s[:-1]
return s
def register(username, password):
global users
if username in users:
raise ValueError('This user is already exists.')
users[rtrim(username)] = (password, '')
As you can see, rtrim
is called after the override check. As a result, it is
enough to register with admin\t
to be logged as admin
.
YACST (200 points)
Try to solve another captcha
This task is to solve 5 audio captcha in a raw, with a very short timeout.
The audio captcha are WAV files, with 6 spoken digits. There is absolutely no noise: each digit is always the same sequence of bits in the file. All we need to to is to code some program to do the solving for us. With Python and the requests library, it's all easy.
interstellar (200)
Just a small binary from a far-far galaxy
This task is a pure reverse engineering task on a ELF file for 64-bits architectures. You give it an argument, and it tells you if it is the flag.
The beginning starts with debugging tools detection, which can be NOPed easily if you want to. Then your input goes to some perl Math::GMPz operations, before other assembly functions were applied to it.
To make a long story short, here is the binary rewritten in Python:
import sys
if len(sys.argv[1]) != 36:
sys.exit(0)
a0 = 0
for c in sys.argv[1]:
a0 *= 0x133
a0 += ord(c)
a0 = bin(a0).replace('0b', '').replace('L', '')
salt = '011111010010001010000001111010010010111111100100111001110100110...'
a1 = ''
for a, b in zip(a0, salt):
a1 += '1' if a == b else '0'
a2 = hex(int(a1, 2)).replace('0x', '').replace('L', '').decode('hex')
if a2 == 'From a seed a mighty trunk may grow.\n':
print "Success! You've found the right flag!"
If you take all the steps in reverse order, you find the flag:
seed = 'From a seed a mighty trunk may grow.\n'
seed = int(seed.encode('hex'), 16)
blob = 0x7d2281e92fe4e74c2b7686b94e00d18b6a90123198cb57bd8cb28cefb46ca99...
ones = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff...
xored = seed ^ blob ^ ones
out = ''
while xored:
out = chr(xored % 0x133) + out
xored /= 0x133
print out # W@ke_up_@nd_s0lv3_an0ther_ch@113nge!
mathproblem (300 points)
nc mathproblem.2015.volgactf.ru 8888
This is a simple programming task. We have to solve what looks like a simplified version of the countdown problem.
Since there are only 4 digits to start with, and 4 different operations, the
total number of possibilities is (4*3*4)*(3*2*4)*(2*1*4)
which is less than
10.000. A simple solution is to bruteforce them using a usual Breadth-first
search.
homework (100 points)
My dog has eaten my homework. Please help restore it!
This was the most frustrating task ever. You “just” have to reconstruct the QR code so that your reader is happy with it.
An other team member, Joe Gardiner did the first step, printing the picture, cutting it and figuring out how to put them together.
I then try to put them together in Gimp, like so:
Good enough? Not for my QR code reader!
I ended up doing it pixel perfect (or close enough) by hand, in Gimp. Scan it, and it shows the flag.
0/10 would do again.
carry (500 points)
Here's a bunch of files. Someone has done something with these files (apparently, encrypted). To get the flag decrypt everything.
An other custom crypto cipher! This one is looing really similar to a LSFR, except that the tapped bits are fed into an other state machine.
There are two 32-bits secrets: q
and init_state
. Together, they make the
key. If you look closely, the initial state is output as the first bits of the
stream cipher. So we are left to bruteforce q
. Moreover, we know that the
least significant bit of q
does not really matter as far as bruteforcing
goes.
Bruteforcing 31 bits? Easy! Write some C code, run it, profit!
10 minutes for the bruteforce on a sigle core of an old computer.
However, it seems that using the same key as the one from the unit test would also decipher the encrypted flag. I bet some teams scored 500 points for free.
remote web (200 points)
There is a web server somewhere (10.0.1.18), but we have forgotten a port number. Can you GET something from it?
ssh:team@194.190.143.247 pass:volgactf123
Once logged on the server, you have access to the machine at 10.0.1.18.
However, most commands are unavailable. After excuting ash
via /bin/busybox
(thanks to Tom Chothia, an other member of our team), I was able to see what
every other team was doing with ps
, namely running a nmap
on that machine
via /lib64/ld-linux-x86-64.so.2
:
Damn, port 54321, I could have tried it! Anyway, let's use SSH as a proxy to go there:
strange text (350 points)
We found some text with strange symbols - can you get a flag? link
The file contains in ASCII some floating points numbers, like so (line breaks are mine):
[…]
0.09.491787910461426,0.3002592921257019,
0.09.47504711151123,0.30399078130722046,
[…]
It appears that these are a succession of coordinates, with a 2-digits key
(09
in the example above) inserted in the first coordinate.
If we plot these coordinates when the key is 09
, we see this:
Looks like a }
, doesn't it?
Next, we put all the parts together, and plot them:
After some tries (see that F
after the !
?), we found the valid flag:
{!F0rG0tMYUV}
.
Conclusion
That was a fun CTF to play with! Our team, A Finite Number Of Monkeys, scored 3250 points which granted us the 20th rank (over 251 teams having scored at least one flag).
Out of them, I solved challenges for 2150 points, and the one team happening to have scored 2150 points at the end of the challenge was ranked 48. Not bad!
You can download all my exploits for this challenge, and don't forget to ping me if you find some use for them in the future.