Solve this riddle to impress Captain Rattlesnake!
download
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 5 – p8 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 ;)
3 comments
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
update .pyc decompiler link please =)
uncompyle is better now. updated link