Oct
31

## Hack.lu 2010 CTF #16 (Rattlesnake’s Riddle) writeup

Solve this riddle to impress Captain Rattlesnake!

The .pyc file is a byte-compiled python code, and there is a wonderful tool called uncompyle to deal with it.

$decompyle secret.pyc > secret.py The decompyled file: 1. Takes 3 arguments 2. Checks 2nd to be 1337 3. Calculates ‘token‘ value, which is 11111112671 4. After some obscure checks, calculates binary sha1 of “adsf” . (token + argv[3]) 5. Uses this binary hash as a key to deXOR a crypted cPickle piece of data 6. Unserializes the data, dexors it using argv[1] and prints it out. First, we needed to get the cPickle data. Instead of bruteforcing the sha1 value, we attacked the xor key itself: it is only 20 bytes long. *** Now cutting to hellman who will explain how to do it ! When you want to break a cipher, a good thing to know is a kind of the expected plaintext. We know, it’s a dump made with the cPickle python module. Let’s play with it a bit: >>> import cPickle >>> import redbeard >>> pl = redbeard.Player("hellman") >>> cPickle.dumps(pl) "(ibattleships\nPlayer\np1\n(dp2\nS'rand'\np3\ncrandom\nWichmannHill \np4\n(tRp5\n(I1\n(I16656\nI13508\nI1\ntp6\nNtbsS'name'\np7\nS'hellm an'\np8\nsb." >>> a = [100, "teststring", 1337] >>> cPickle.dumps(a) "(lp1\nI100\naS'teststring'\np2\naI1337\na." >>> b = [a, {"x": 100, "y": 200}] >>> cPickle.dumps(b) "(lp1\n(lp2\nI100\naS'teststring'\np3\naI1337\naa(dp4\nS'y'\nI200\ns S'x'\nI100\n >>> cPickle.dumps(list) 'c__builtin__\nlist\np1\n.' We can find here some rules: 1. if a dumping thing is an object (which is the most probably), the dump starts with ‘(‘ 2. there are lots of p<N>\n, where N is growing from 1 to some number 3. S’ means start of a string 4. \n follows a string closing quote 5. the last byte is ‘.’ I wrote a small php script to simplify guessing. So, let’s begin. Notice: ¬ is used to show newline character ( “\n” ) to make the output looking straight • Last byte is ‘.’ so let’s write: 10 19 . • Also let’s think it’s an object: 0 0 ( Now we have: -----01234567890123456789 0: (******************r 1: p******************¬ 2: a******************z 3: '******************K 4: F******************p 5: 9******************g 6: F******************p 7: 1******************G 8: F******************p 9: 1******************' 10: V******************. • Look at 3 1. Before is ‘z’ so it’s a string’s end: 3 1 \n • Look at 9 18: next byte after is ‘V’, so it’s not a string’s end. Then, ‘S’ must prepend that: 9 18 S -----01234567890123456789 0: (l****************'r 1: p*****************4¬ 2: aS****************rz 3: '¬****************WK 4: F'****************¬p 5: 9¬****************'g 6: F[****************¬p 7: 12****************'G 8: F\****************¬p 9: 15****************S' 10: VZ****************a. • Same trick at 0 17: 0 17 S • Look at the beginning of line #2 p4\naS – should be a string: 2 2 ‘ Currently we have: -----01234567890123456789 0: (lp**************S'r 1: p*j**************p4¬ 2: aS'***************rz 3: '¬p**************'WK 4: F'¬**************'¬p 5: 9¬a**************S'g 6: F[[**************'¬p 7: 12¬**************S'G 8: F\\**************'¬p 9: 15¬**************aS' 10: VZ[**************¬a. • Start of a string at 3 16: 3 16 S • 0 3 – should be (lp1\n: 0 3 1,0 4 \n • 4 5p8 ends: 4 5 \n • 0 6 string beginning: 0 6 ‘ Now we have: -----01234567890123456789 0: (lp1¬S'*********aS'r 1: p*j'¬p3*********¬p4¬ 2: aS'iuxt*********q*rz 3: '¬p6¬aS*********S'WK 4: F'¬p8¬a*********b'¬p 5: 9¬aS'dO*********aS'g 6: F[[@Z'¬*********E'¬p 7: 12¬aS']*********aS'G 8: F\\'¬p1*********T'¬p 9: 15¬aS'Z*********¬aS' 10: VZ[VGPA*********7¬a. Some more easy-guessed steps: • 1 7 \n • 4 8 ‘ Also we can notice an \n before each aS’. It’s useful! • 0 15 \n • 3 14 \n • 2 9 \n • 1 10 ‘ • 3 11 \n • 0 12 \n • 0 13 p Finally, we got it! The final picture: -----01234567890123456789 0: (lp1¬S'Dxzr'¬p2¬aS'r 1: p*j'¬p3¬aS'krhh'¬p4¬ 2: aS'iuxto'¬p5¬aS'q*rz 3: '¬p6¬aS'HO'¬p7¬aS'WK 4: F'¬p8¬aS'VKJR\x0b'¬p 5: 9¬aS'dORDL'¬p10¬aS'g 6: F[[@Z'¬p11¬aS'HJE'¬p 7: 12¬aS']D^^'¬p13¬aS'G 8: F\\'¬p14¬aS'_P\\T'¬p 9: 15¬aS'Z]G\\'¬p16¬aS' 10: VZ[VGPAP\x1b'¬p17¬a. Enter “save” to save the key and the dump: > save Dump and key saved! $ cat dump.cp
(lp1
S'Dxzr'
p2
aS'rp}j'
p3
...
p15
aS'Z]G\\'
p16
aS'VZ[VGPAP\x1b'
p17
a.

*** Now cutting back to vos!

Now as we have the correct cPickle dump, we can bruteforce argv[1] as it has only 256 meaningful values – the chars. This is what different values of argv[1] give us: out.txt.

Some kids piss their name in the snow. Chuck Norris can piss his name into concrete. looks much like an answer ;)

1. ##### sleepya says:

Nice xor. I did bruteforcing. I noticed from the last loop after getting “o”. “o” should be list/tuple of encrypted words.

I set “token” to be “11111111111L” then bruteforcing secret with xrange(1000000). Check if “o” is list/tuple and “len(o) > 0”.

Then, got possible secret3 “556007” in a few minutes.

I also tried to find the arguments to get the answer.
python secret.pyc 23 1337 554433