This is an announcement for the upcoming UFO CTF 2013 that starts on July 19th, 2013 at 18:00 (UTC+4) and lasts 48 hours. The CTF will be in a Jeopardy format. All members of our Sec.SE CTF Team mark your calendars!

Warning! Our planet is in great danger, 19.07.13 18.00 UTC + 4

The Earth will be subjected to invasion of alien races. The people have been fighting between each other for a long time, but now is time to unite our forces. The main weapon of the militant aliens is their mind, any human weapon has no power against it. The people have no choice, we will have to play by the aliens' rules. The only thing we can try to defend our home planet is to counterattack them with united power of human mind. Take your part in the stand against the alien civilization!

Currently, 125 teams from all over the world have registered to participate. More information in the CTF Time website.

Security.SE is fielding a team. If you want to join, refer to the CTF team announcement. We communicate through the Sec.SE CTF team chat room (locked during competitions, request access in The DMZ chat room, if you want in during competitions). The team credentials will be posted in the CTF chat room once it's locked for the duration of the competition.

Go Team!

Update: CTF competition has ended! Our CTF team members have managed to secure a shared 29th place with 525 points out of 74 participating teams that have successfully completed at least one challenge (~ 360 teams in total). Our team has managed to complete 4 challenges. Write-ups are expected to be posted as answers to this announcement in due time. Keep refreshing this page! :P

  • 6
    GOV TEAM? Ok, but which government? Jul 8, 2013 at 15:52
  • I should be around for this.
    – user2213
    Jul 12, 2013 at 11:26
  • 3
    I'm still recovering from SIGINT. Or should I say, my relationship with my gf is still recovering. I'll be around :P
    – lynks
    Jul 15, 2013 at 18:00
  • 1
    Well done guys! Jul 22, 2013 at 7:30

4 Answers 4


Broken Brokoli [forensics, 100]

brokenarchive.zip contains a file flag.rar, which is encrypted with a password that we have to find. The name brokenarchive hints that we should poke that zip file more closely. It's also clear that something is amiss since the zip file is significantly larger than the rar file it contains. Maybe there's something appended to the archive? If we create a zip file containing only flag.rar, it's 3568 bytes long.

$ tail -c +3446 brokenarchive.zip | file -
/dev/stdin: Zip archive data, at least v2.0 to extract

Yes, we're on the right track. The zip utility tells us that the file is broken, but gzip works.

$ tail -c +3446 brokenarchive.zip | zcat | file -
/dev/stdin: PC bitmap, Windows 3.x format, 700 x 100 x 24

The bmp file contains some strange symbols that spell out the password to the flag in the Wingdings font. The password is #Ed&3m@cd0c!. The rar file contains flag.bmp which spells out the flag in Wingdings (helloearthlings!!!!!!!!!).


Web100: "A1 Galaxy"
status: partially completed

This was the first challenge in the Web series, and completion would secure us 100 points. The challenge involved finding ways to test for possible passwords in multi-level authorisation pages. With the big help from @p____h, I've managed to pass first two authorisations, and extract username, level1_password and level2_password:

                        Web100 - authorisation level 3

The first authorisation page asked for a username and password only, and the input was not sanitized in any way whatsoever. This meant we could try with the simplest of SQL injections: " or 1=1 or " on any of the fields (or both) and got out the username admin1 when presented with the 2nd level authorisation form:

                        Hello, admin1

But the authorisation process was still able to determine that we cheated, so we'll need to actually extract the password, to be able to continue to level 3 authorisation. The robots.txt file published at the web root gave a few hints:

User-agent: *
Disallow: /forgot_username_and_password.txt 

And the /forgot_username_and_password.txt location let us know the length of the password:

username: ******
password: **********************

So the password length is 22 characters long. If we had to brute-force for all the possible values in the character space of 52 possible [a..Z] values (educated guess, but it could have been more than that), that would be 5222 values, and clearly too many to test on. But, since the input on level 1 wasn't sanitized at all, this wasn't too difficult and meant we could test for existence of a single character at a certain password position using substr SQLite function (we've determined what database engine is running the backend based on commands it was accepting). So we could simply iterate through all 22 positions and test for these (52/2)+1 or 27 values, as it could be further optimized to match lowercase values only, and later determine with a single injection, if the character tested was upper or lowercase. This would look something like this: " or (1=1 and lower(substr(password,1,1)) == "c") or ".

If the login form would let us pass (and report an error it was hacked), that means we've determined the character for the n-th position, leaving us only at most 27*22 values to try on (or 594 tests, which is obviously a lot better than 5222).

The first password was successfully extracted and turned out to be: MyPasswordIsNotTheFlag.

The second password (level 2 authorisation) was somewhat easier. While the input was sanitized, it was rather poorly so, and let us determine the string matching in its internal SQL query was based on the LIKE operator. This was again determined by @p____h. I've run a script to iterate through all possible values in the 62-letter address space ([0..9,a..Z]), using SQL injections ending the sought after password with % (matches any value that starts with previous characters when LIKE operator is used), for example: 0a%.

The second password turned out to be really short: 0x42

But, we were only left with roughly 15 minutes to extract level 3 password, and no idea how to test against possible values yet. I've determined it must be a digits only password, and that it's most likely 12 digits long. This was due to the level 3 auth producing different error ("level 3 password mismatch", instead of "following errors occurred: wrong level 3 password 1, wrong level 3 password 2") when entering 0 or a non-integer value for these two input fields for password 3 (indicating the input matching defaults to 0 where type conversion fails), and when entering an integer value longer than 12 digits. In fact, I was able to narrow this down even further by establishing the value must be smaller than 444444444444 (blind test), but is bigger than 100000000000. This would still leave us with too many possible values to try and the time run out.

So the login info successfully extracted was:

username: admin1
level1_password: MyPasswordIsNotTheFlag
level2_password: 0x42

Still, for having roughly 2 times half an hour of spare time (Global economic crisis weekends FTW! - shuffle letters to FTW to get my intended meaning), and @p____h in the meantime working on other challenges, I think we did fairly well. We'll try better next time. ;)


aliensgif [stegano, 150]

We have a GIF that contains three 250x250 images, one mostly red, one mostly green, one mostly blue. Careful examination reveals that the first line of each gif isn't perfectly uniform but has #0000fe bytes interlaced with the #0000ff bytes (mutatis mutandis for the green and blue image). We xor the images together, tempted by the fact that at the beginning the \xff's and \xfe's mix with \x00's of the other image.

convert _aliensgif.gif a.pnm
xor a-[012].pnm >xor.pnm

Instead of xoring, we could extract the bits separately in the next step, then take one bit from each image in rotation. I find xoring simpler.

Now we extract bits from the result, interpreting the byte value as \xff as a 1 bit and the byte value \xfe as a 0 bit.

perl -we '
        scalar <> for 1..3;
        $line = <>;
        while ($line =~ s/^(........)//) {
            $_=$1; y/\xff\xfe/10/;
            $buf .= chr(oct("0b$_"))
        $buf =~ s/\0+$//; print $buf
    ' <xor.pnm >xor.enc

The resulting file contains The flag is soon_we_will_destroy_them followed by some garbage.


I started cryptoMAX but only got part of the way.

cryptoMAX [crypto, joy, 100]

We get an encoded image and an encoding script.

The encoding script iterates over the pixels. It reads each pixel as an RGB triple, masks some bits, and adds the masked R, G and B values together and multiples the sum by a constant. The resulting range is 0..255. The masking and sum cannot be reversed. Also the image is transposed (horizontal swapped with vertical, which is a rotated mirror image).

The encoded image is a raw bitmap. It's 90000 bytes long, so we can guess that the image is 300×300. Let's convert it to a common image format.

{ echo P5; echo 300 300; echo 255; cat img.encode; } | pnmtopng >img.png

The resulting image is clearly a QR code with some impurities. I didn't have a tool handy that recognized the code even after converting the grayscale image to black and white (too many impurities?). Obsqr running on my smartphone didn't break a sweat and told me 9rypBzr_GB_7sBP6S. However, this isn't the flag.

This is as far as I went. After the contest, I got the solution from HamZa: it's rot13 with a twist, that is: digits also need to be rotated (but the underscore remains as is).

echo 9rypBzr_GB_7sBP6S | tr 0-9A-Za-z N-WN-ZA-Mn-za-m

This is still not the flag — the correct answer is welcome_TO_UfoCTF, but I don't get where the lowercasing of the os comes from.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .