DEFCON 24 EFF T-Shirt Puzzle



image




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.

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


image


What this looks like under a UV light:

image






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:

http://159.203.200.62/


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

image


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

http://159.203.200.62:70/


image


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):


image


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”):


image



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.....|
0000001a

We 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!

The EFF was generous enough to give us a sweatshirt & a few tshirts for being the first to solve this puzzle.


Thank you EFF!






Donate



Bloggers' Rights at EFF


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:

image

image