Tuesday, August 18, 2009

Crypto challenges - WOWHacker CTF

Last week team CLGT took part in the WOWHacker CTF. I was in charged of crypto challenges, so I decide to write something about challenge 1 and challenge 10.

1. Challenge 1

Challenge 1 is...crazy hahaha. Only one or two teams could solve it until the author (hello hinehong :-D) gave out a list of 7 hints. I have designed some web-related crypto challenges (which you will see soon ^^) so I think the difficulty of challenge 1 relies on how fast people can guess the meaning of the cookie. It would be easier for the teams if the author sets the cookie as cookie = cipher + "|" + key. BTW, here's my solution.

When you access the link above, you'll see a bunch of javascripts. After decoding those javascripts (which I leave as exercise for readers), you'll see a form whose target is http://221.143.48.96:8080/you_are_the_man_but_try_again.jsp. This form accepts a parameter named "hong" which is either true or false. If you set hong=true, the server sends back a cookie like below:

id =pKCdQgyJb4dziUESVUv+5qBIoGwQgL2WB@ae506e

This looks like a base64 encoded string, but it's not. In fact you need to modify it a little bit before you can base64 decode it. This is, as I said previously, why this challenge is hard.

One trick I learn from this challenge is to guess the boundary between key and cipher text, one should try to truncate one character a time and base64 decode the string until he gets an output whose length in bytes is a multiple of 8 or 16, which are common block cipher's block length.

The cookie can be either "cipher + key" or "key + cipher", so one should try the above process in both cases. If you can't find any such output, then you know your theory is wrong, i.e. the cookie is in some other form. Fortunately, my theory is right in this case.

It turns out that the first 32 bytes of the cookie is the cipher text, and the rest 8 bytes is the key. It's a 8-bytes key, so this cookie should be encrypted by DES which is a popular 8-bytes key block cipher. I wrote a small python script to decrypt the it:

>>>from Crypto.Cipher import DES
>>>cookie = 'pKCdQgyJb4dziUESVUv+5qBIoGwQgL2WB@ae506e'
>>> key = cookie[32:]
>>> data = base64.b64decode(cookie[0:32])
>>> des = DES.new(key, DES.MODE_CBC)
>>> des.decrypt(data)
'wowhacke\xd6\xe0\xbc*e\xe7\n\xc7\x1a\xf92w6H\xfd\xe5'

Hmm. I remembered I tried to submit 'wowhacke' to the scoring server, but, of course, it's not the correct answer. Then I wasted the next hour to test various stupid theories to understand what the last 16 bytes of the output are.

It was not until I nearly gave up on this challenge, I realized the obvious: this is mode ECB stupid!!! Why on earth I always thought it's CBC? I changed the mode, and the result is:

>>> des = DES.new(key)
>>> des.decrypt(data)
'wowhacker@!hine@ipsec\x03\x03\x03'

Remember those '\x03\x03\x03'. You'll see them again in my crypto challenges ;-).

2. Challenge 10

Challenge 10 is cool. In summary, the author sets a RSA private key as a property of a Java object, then he gives out the serialization stream of that object, and asks teams to recover the private key to decrypt a ciphertext.

So the first thing we must do is to understand how Java does serialization. I was never a fan of Java, so this is something completely new to me. But that's why I really enjoy playing capture the flag games. It forces me to learn new thing fast in a very short time.

I spent nearly 1 hour reading the spec, mostly on the object serialization stream protocol. Then I spent one and a half hour starring at my hex editor screen and acting as a binary parser which was, I don't know why I feel that, really fun (later on Tora of SexyPwndas fame showed me a much less painful way to recover the object. Thanks Tora!)

I recovered the RSA private key eventually. How to use it to decrypt the ciphertext in key.txt? While de-serializing the object, you would see that there's a field named tripleDesKey containing a 24-bit string which you can get by base64-decoding the last 32 bytes of the serialized object.

At first I thought I should use the RSA key to decrypt this 24-bit string to get the real tripleDesKey, and uses that key in turn to decrypt key.txt. This hybrid approach is the standard way to do encryption using public key cryptosystem. But you shouldn't expect anything standard in CTF, rite?

It turns out all that tripleDes key and ciphertext are just there to distract me. I have to admit that I don't like challenges giving false trails. You can either give good trails or no trail at all. Giving false trail is a sin :-P.

Anyway, if you look at key.txt, you'll see that its content is a 128-bytes string. 128-bytes = 1024-bit = the size of the modulus in the recovered RSA private key. So this string should be the ciphertext encrypted directly using the RSA private key. Indeed it is!

>>> from M2Crypto.RSA import *
>>> rsa = load_key('my_rsa_key')
>>> data = open('key.bin').read()
>>> rsa.private_decrypt(data, 3)
'\x00\x02#padding#x00isec@#$wowhacker!!'

There's a small minor issue that I intentionally left out. Can you find out what it is and resolve it yourself?

I hope you enjoy reading this. Happy hacking!

3 comments:

Chris said...

Thank for your writing.
I am very happy for your enjoy hacking contest.
See you September in Korea.

Tora said...

Nice job!
I'll be waiting for the level12 writeup xD

thieu said...

anh ơi hack dùm e ít tiền trong nhà hàng vui vẻ nhé!!!!!!!http://me.zing.vn/jusethieu/home