VolgaCTF 2015 Quals write ups


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 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.

Code execution

Now that have the flag name, just cat it: eval c'at *>&4' (but pipe that to head to avoid seeing the other files.

Cat the flag

database (75 points)

hack the database!

nc 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.

Exploit the database

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.

Exploit running

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 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.

The final countdown

homework (100 points)

My dog has eaten my homework. Please help restore it!

Picture of a cut and mixed QR code

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:

Using some Gimp skills

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.

Clean QR code

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!

Bruteforce: 10 minutes

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 (, but we have forgotten a port number. Can you GET something from it?

ssh:team@ pass:volgactf123

Once logged on the server, you have access to the machine at 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/


Damn, port 54321, I could have tried it! Anyway, let's use SSH as a proxy to go there:

Showing the flag

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:

Part 09

Looks like a }, doesn't it?

Next, we put all the parts together, and plot them:

Part 09

After some tries (see that F after the !?), we found the valid flag: {!F0rG0tMYUV}.


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.

This article and its images are released under the CC BY-SA licence.

Short URL: