Can you capture the flag?

Preface

HEY! Stop cheating! Go back to the CTF and try to solve it on your own!

SPOILER ALERT: The required resources to solve this CTF are now offline.

During the last year or so I created a CTF out of boredom. I posted a QR code / string to various friends, work colleagues, and on-line forums where I expected people to be able to solve it.

This is the QR code which decodes to:

#QBdg%HGILH&R9vch)NBTAt5.X3oWb\BxMbn5,GR0wTQ*0xuHBc=

Sadly nobody has been able to solve it so far, which I find a bit strange. No way I’m that awesome that I have created the best way to obfuscate data on the Internet? Probably the right person to solve it has not yet come along, or maybe I wasn’t effective enough at spreading the CTF to enough people capable of solving it.

Anyway, here is the solution.

Step 1

By looking at the string the solver should recognize the striking similarity it has with a base64-encoded string, indicated by the “=” character at the end of the string. However there seem to be a bunch of other characters that do not belong in a base64 string.

A closer look will reveal that, starting with the first character, every 6th character after that doesn’t belong in a base64 string. So there is a pattern. Let’s remove every 6th character and see that we have left:

Base 64 string: QBdgHGILHR9vchNBTAt5X3oWbBxMbn5GR0wTQ0xuHBc=
Characters that don’t belong: #%&).\,*

Now we have a valid base64 string! Let’s decode it and see what we get:

@`borALy_zlLn~FGLCLn

Yeah, it doesn’t make much sense. The solver is now supposed to recognize that the base64 string decodes to binary data. Why would that be the case? Let’s go back to the extra chars that don’t make sense, why did we have those chars there? It is highly possible that the binary data is actually encrypted data and the extra chars are the key. What is the simplest cipher for binary data encryption? The XOR cipher. Alright, let’s write a small script to test this hypothesis:

<?php
$string = "QBdgHGILHR9vchNBTAt5X3oWbBxMbn5GR0wTQ0xuHBc=";
$key = "#%&).\,*";

// Base64 decode
$dec = base64_decode($string);

// XOR decode $dec with $key
for ($i = 0; $i < strlen($dec); $i++) {
  $dec{$i} = $dec{$i} ^ $key{$i % strlen($key)};
}

// Echo the result
echo $dec;

Wow it worked, we get something that makes sense:

c2F5LW15LW5hbWUuY3J5b2Rldi5jb20=

Now let’s base64 decode this and see what we get:

say-my-name.cryodev.com

Step 1 completed.

Step 2

Step 1 provided us with a hostname. Let’s open it up in a browser and see what we get:

Those Greeks have concealed my writing, keep looking…

Interesting, not much information here. But wait, “say-my-name” must be a reference to something. Name -> Domain Name System -> DNS. Alright, let’s see if there are any TXT DNS records for this subdomain:

$ host -t TXT say-my-name.cryodev.com
say-my-name.cryodev.com descriptive text "/bobby-tables"

Step 2 completed.

Step 3

Step 2 revealed a TXT DNS record. It returns "/bobby-tables". So let’s append that to the hostname previously found and visit http://say-my-name.cryodev.com/bobby-tables:

An XKCD comic appears… named exploits_of_a_mom.jpg. Is there an SQL injection vulnerability on the site? Maybe, but currently we cannot find any GET/POST variables on the site to exploit. Let’s keep looking… The solver should recognize that the image ends with .jpg. XKCD comics are always .png! Oh wait, one more thing. Do you remember this quote from earlier?

Those Greeks have concealed my writing, keep looking…

The solver is now supposed to think of Steganography. A quick Wikipedia search reveals:

The word steganography comes from New Latin steganographia, which combines the Greek words steganós (στεγανός), meaning “covered or concealed“, and -graphia (γραφή) meaning “writing“.

Got it now? There must be a message steganographically hidden within the .jpg image. A quick Google search reveals that the most common steganography tool in Linux is steghide. Let’s see if that can decode anything:

$ steghide --extract -sf exploits_of_a_mom.jpg 
Enter passphrase: # try without a passphrase
wrote extracted data to "message.txt".
$ cat message.txt 
?mom

Step 3 completed.

Step 4

Step 3 revealed what we desperately needed all along, a GET variable! Let’s visit http://say-my-name.cryodev.com/bobby-tables/?mom and see what we get:

Come on, I’m spoon-feeding you now!

Alright alright, let’s assign some value to ?mom e.g. http://say-my-name.cryodev.com/bobby-tables/?mom=1:

Name: Robert

Interesting, a reference to Robert “Bobby Tables” from the comic. Let’s seek for an SQL injection at ?mom, e.g. using something like http://say-my-name.cryodev.com/bobby-tables/?mom=1' OR 'x'='x:

Name: Robert
Name: https://www.youtube.com/watch?v=6Ejga4kJUts
Name: https://www.youtube.com/watch?v=jxjeqCd6Zm0
Name: https://upload.wikimedia.org/wikipedia/en/0/00/Machine_Head_album_cover.jpg

Step 4 completed.

Step 5

In step 4 we injected a GET variable and retrieved a bunch of database entries in the form of various links. The first link points to the Cranberries song “Zombie” with the unforgettable chorus “In your head, in your head“. RIP Dolores :'( The second and third links both refer to a band named “Machine Head“.

Did you get it yet? Yes we have to check the HTTP header! A quick inspection in your favorite browser’s inspection tool reveals the following entries that don’t belong there:

Step 5 completed.

Step 6

Step 5 revealed a telnet server and a telnet key. Let’s make a telnet connection and see what we get:

$ telnet bofh.cryodev.com 666
Trying 83.212.84.234...
Connected to bofh.cryodev.com.
Escape character is '^]'.
OQZaPmlBB1wKIGILaSooGFZRfkk7HnY0CgctVxFIUUcHJmUZBHw8B24rcgsbP2YIKXQnJX0oOlsiaDgWIXMPMXo1eEInIHYBUSoKBHggCXk4fwQJDHNBRjsyKkIBeHoVF1lXEX0wYh0jMGUQAnYZKlBcLk4xdwJLCwcyLGofdE00P2UfaSVPA30kelsoCmEJDHMQQBEDXFM3DGVCBHNfBH0/RAcYW1cEKmQrKlE5UQUxeBYMDGMbIHo1NUQPWmEfVDVCD20NAVsoN1sJMnMURBMiOk0CHAoIHHMgEVEvCRkgMG0WKAEncA==
Connection closed by foreign host.

Very interesting, yet another base64 string. Can we decode it directly?

9Z>iA\
 bi*(VQ~I;v4
-WHQG&e|<n+r?f)t'%}(:["h8!s1z5xB' vQ*
x 	y8	sAF;2*BxzYW}0b#0ev*P\.N1wK2,jtM4?ei%O}$z[(
a	s@\S7eBs_}?D[W*d+*Q9Q1xc z55DZaT5Bm
[(7[	2sD":M
s Q/	 0m('p

It doesn’t seem so, we get binary data again. Oh wait, we have the X-Telnet-Key from earlier! Let’s run the XOR cipher once more with the new key and the new base64 string and see what we get:

<?php
$string = "OQZaPmlBB1wKIGILaSooGFZRfkk7HnY0CgctVxFIUUcHJmUZBHw8B24rcgsbP2YIKXQnJX0oOlsiaDgWIXMPMXo1eEInIHYBUSoKBHggCXk4fwQJDHNBRjsyKkIBeHoVF1lXEX0wYh0jMGUQAnYZKlBcLk4xdwJLCwcyLGofdE00P2UfaSVPA30kelsoCmEJDHMQQBEDXFM3DGVCBHNfBH0/RAcYW1cEKmQrKlE5UQUxeBYMDGMbIHo1NUQPWmEfVDVCD20NAVsoN1sJMnMURBMiOk0CHAoIHHMgEVEvCRkgMG0WKAEncA==";
$key = "h4cK3rM4nh4x0rz";

// Base64 decode
$dec = base64_decode($string);

// XOR decode $dec with $key
for ($i = 0; $i < strlen($dec); $i++) {
  $dec{$i} = $dec{$i} ^ $key{$i % strlen($key)};
}

echo base64_decode($dec);

And the result is:

Congratulations! You solved the riddle! There is no prize, I’m too poor for that, I was just bored and made this. Let me know if you would like to brag: <my email here>

Step 6 completed.

Conclusion

So, that’s the CTF. Do you think it was too complicated? I think the first step was the hardest, once you recognize the XOR cipher and the fact that it can be reused in step 6 the CTF is not that complicated.

Anyway, I will leave the infrastructure for this CTF up and running for a few more weeks if anyone wants to verify my solution, but then I will take it down.

Thanks for reading!

PS: Don’t try to hack my server using the intentionally created SQL injection, it will not work ;)

Leave a Comment