DEFCON 24 EFF T-Shirt Puzzle

After successfully winning the DC24 badge challenge, we ventured out of the 1o57 room. Only to find more crypto & puzzles to solve!
Every year, for the past few years, the EFF has embedded a puzzle in their defcon tshirt.
- https://www.eff.org/deeplinks/2013/08/effs-encryption-t-shirt-puzzle-solved
- https://www.eff.org/deeplinks/2014/08/effs-defcon-22-t-shirt-puzzle-explained (those puzzle solvers look familiar)
- https://www.eff.org/deeplinks/2015/08/effs-def-con-23-t-shirt-puzzle-crypto-noir
This trend continued this year…
SPOLIERS AHEAD (and gophers) - but first, donate to the EFF, seriously: https://supporters.eff.org/donate/button
The T-Shirt

What this looks like under a UV light:

The Puzzle
Ok, lets get started. The EFF defcon tshirt featured an awesome robot on the back. Looking at the text under a UV light, the following was highlighted:
- base64 text
- an arrow showing how to read the base64 text
- the word “LIBERTY”
The base64 text:
U2FsdGVkX1/zCTZ6hI6lPrjBG1/HYrMVQLDprPHiVQg=Turning that into a file:
$ cat base64.txt | base64 --decode | hexdump -C | more
00000000 53 61 6c 74 65 64 5f 5f f3 09 36 7a 84 8e a5 3e |Salted__..6z...>|
00000010 b8 c1 1b 5f c7 62 b3 15 40 b0 e9 ac f1 e2 55 08 |..._.b..@.....U.|
00000020
The word “Salted__” immediately stands out. This was likely created using the “openssl enc” command (for more information: http://justsolve.archiveteam.org/wiki/OpenSSL_salted_format)
The file is 32 bytes, the first 16 are the salt, this leaves 16 bytes to attempt to analyze. Passing this through bletchley-analyze does not show anything exciting:
$ cat base64.txt | base64 --decode | dd bs=1 skip=16 2>/dev/null | bletchley-analyze
================================================================================
Beginning analysis after decoding by chain:
Unique Lengths: 16
Maximum Possible Block Size: 16
Matching Common Block Sizes: 8,16
Possible Encodings:
Best Encoding: None
First 1 Values:
0000: b8c11b5fc762b315 40b0e9acf1e25508 | b'\xb8\xc1\x1b_\xc7b\xb3\x15@\xb0\xe9\xac\xf1\xe2U\x08'
Tool link: https://github.com/ecbftw/bletchley
At this point, we know we have encrypted data, we think our password is “LIBERTY”, but we do not know what algorithm was used to encrypt the data (we can guess it’s a block cipher with a block length of 8 or 16 bytes).
Trying a few different symmetric ciphers, we arrive at DES!
$ cat base64.txt | openssl enc -des -a -d -pass pass:LIBERTY
15920320062
A reasonable assumption is that we have a phone number (1-592-032-0062). That assumption was incredibly incorrect (<3 EFF) - this is actually an IP address:
Visiting that site gives us a “Welcome to nginx!” default page. Using nmap shows something far more interesting:
$ nmap 159.203.200.62
Starting Nmap 6.40 ( http://nmap.org ) at 2016-08-13 02:03 EDT
Nmap scan report for 159.203.200.62
Host is up (0.0018s latency).
Not shown: 997 closed ports
PORT STATE SERVICE
22/tcp open ssh
70/tcp open gopher
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 21.46 seconds

After learning more about gopher, we discovered this server was running PyGopherd, meaning that no gopher client was required:

A “Mystery” page appears! Downloading that, we get a binary blob. Our OS immediately identifies this as a PDF, opening the PDF shows the following image (the tshirt):

Suspecting that we were not finished yet with this mysterious binary blob, we looked at the end of it:
$ hexdump -C mystery.pdf | tail -n 6
0009a5d0 73 43 69 50 55 54 05 00 03 8f a0 8a 57 75 78 0b |sCiPUT......Wux.|
0009a5e0 00 01 04 e8 03 00 00 04 e8 03 00 00 50 4b 05 06 |............PK..|
0009a5f0 00 00 00 00 01 00 01 00 4a 00 00 00 57 da 02 00 |........J...W...|
0009a600 00 00 73 00 63 00 72 00 69 00 70 00 74 00 6b 00 |..s.c.r.i.p.t.k.|
0009a610 69 00 74 00 74 00 79 00 00 00 00 0a |i.t.t.y.....|
0009a61c
That’s no PDF! There is something attachted to the end of this binary blob, lets dissect it!
Binwalk shows a zip file in the middle of the PDF:
$ binwalk mystery.pdf
DECIMAL HEX DESCRIPTION
-------------------------------------------------------------------------------------------------------
445259 0x6CB4B Zip archive data, at least v2.0 to extract, compressed size: 186905, uncompressed size: 205218, name: "sCiP"
632322 0x9A602 End of Zip archive
Pulling that out and unzipping it shows the following image (filename “sCiP”):

Looking closer at the binary blob, the PDF ends at the start of the ZIP file and then zip file runs almost to the end of the blob. There is something left at the end however:
$ dd if=mystery.pdf bs=1 skip=632322 2>/dev/null | hexdump -C
00000000 73 00 63 00 72 00 69 00 70 00 74 00 6b 00 69 00 |s.c.r.i.p.t.k.i.|
00000010 74 00 74 00 79 00 00 00 00 0a |t.t.y.....|
0000001aWe got stuck here for a while, we were convinced something was hidden in the PDF, once again, we were incorrect. There was something hidden in the cat! Running steghide on the image in the zip file:
$ steghide extract -sf sCiP
Enter passphrase: -- no password entered --
wrote extracted data to "flag.py".
$ cat flag.py
flag = ['0x27', '0xb', '0x17', '0x49', '0x16', '0x18', '0xa', '0xe', '0x54', '0x1d', '0xa', '0x49', '0x43', '0x37', '0x7', '0x13', '0x6', '0x12', '0x19', '0x0', '0x54', '0x18', '0x1f', '0xf', '0x52', '0x1d', '0x18', '0x11', '0x4b', '0x1d', '0x1c', '0x1d', '0x17', '0x14', '0x10', '0x53']
print "".join(map(lambda b: chr(int(b, 16)), flag))
Running flag.py prints gibberish. Searching the internet for similar python code, one of our team members came across https://hazinski.net/post/eff-ctf/ and noticed that what we were doing was very similar to the “Stego-saurus-rex (400)” challenge.
Throwing together some quick python code to XOR the flag with “scriptkitty” (embedded at the end of the mystery blob) gave us our solution:
$ cat flag_solution.py
flag = ['0x27', '0xb', '0x17', '0x49', '0x16', '0x18', '0xa', '0xe', '0x54', '0x1d', '0xa', '0x49', '0x43', '0x37', '0x7', '0x13', '0x6', '0x12', '0x19', '0x0', '0x54', '0x18', '0x1f', '0xf', '0x52', '0x1d', '0x18', '0x11', '0x4b', '0x1d', '0x1c', '0x1d', '0x17', '0x14', '0x10', '0x53']
f = "".join(map(lambda b: chr(int(b, 16)), flag))
sc = 'scriptkittyscriptkittyscriptkittyscriptkittyscriptkitty'
def sxor(s1,s2):
# thanks to http://stackoverflow.com/questions/2612720/how-to-do-bitwise-exclusive-or-of-two-strings-in-python/2612730#2612730
return ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(s1,s2))
print sxor(f,sc)
Running flag_solution.py gives our solution.
The flag is: Encrypt all the things!@EFF @xor encrypt all the things! #defcon #!mC - solved by @TheCouncilOf9
— Sam Erb (@erbbysam)August 7, 2016
The EFF was generous enough to give us a sweatshirt & a few tshirts for being the first to solve this puzzle.
Congratulations to @TheCouncilOf9 for solving or #defcon 24 shirt puzzle! pic.twitter.com/niIokgtuUt
— EFF (@EFF)August 7, 2016
Thank you EFF!
Donate
Have you donated yet? https://supporters.eff.org/donate/button
The Council
- ziot (@bbuerhaus)
- 0rigen (@_0rigen)
- erbbysam (@erbbysam)
- mstc (@M57C)
- Wumpus
- junkmail (@jumknail3)
- w1pe0u7
- ben
- if_
- qa_ninja
- Wumpus
- Thor (@potatosec)
- Punk (@punk_AB)
Would you like to know more?
Want to challenge yourself against crypto?
Want to create a crypto challenge?
We created a website to host crypto challenges designed by us and the community.
Check out the challenges on Potato Planet Crypto
Want more to read? Check out our write-ups from the previous badge challenges:



