First off let me just say a big thank you to the MWR guys who put this CTF together, usually I don’t partake in CTFs because the skillset required is usually out of my grasp (IANAP).
To have developed this CTF in a manner that allows people who do not work with crypto/hackz0r wizardry to still have a chance of solving the problems is awesome! I didn’t solve all of the problems, but I did spend far too much of my free time and apologise to the many bars I had to let down during that time. After this writeup I shall resume my social responsibilities ;)
Each of the various problems took my many hours of frustrating, wallpunching, facepalming and omgnoobing to complete, however I will just go over the solutions to each of them without the hours of frustration — the tl;dr of each one if you will.
Challenges:
Challenge 1 ( GUASS RIFLE ) — A book cipher requiring you to parse various words from lines in books
Challenge 2 ( RADIATION POISONING ) — An LSB Stego QR code that needed to be decoded and then parsed
Challenge 3 — Not completed
Challenge 4 ( FACSIMILE ) — A audio fax that needed to be decoded
Challenge 5 ( GREEN SKIN ) — A literal jigsaw puzzle representing 4 sides of a puzzle piece with 3 characters
Challenge 6 ( WHIRLPOOL ) — A multiple times rotated image that needed to be ‘unrotated’
Challenge 7 ( SCORCHED EARTH ) — An Office document with a weak password
Challenge 8 ( SMOG AND SMOKE ) — A Modified playfair cipher that needed to be recronstructed based on solar systems
Challenge 9 — Not completed
Challenge 10 — Not completed
If you have the writeup to challenges 3/9/10 please let me know so I can link to them!
GitHub
All the challenges/instruction text and solutions are available on the following github: https://github.com/AndrewMohawk/HackFu2016
Challenge 1
[Instruction Text] [GitHub Page]
For challenge one we were giving a number of books in plain text format that we needed to work with as well as a readme that contained the following text:
85,8 124,11 1984,8 3,5 901,1 3,13 8546,12 5,2 3,4 85,10 3437,7 |
The formula above (after a few quick attempts) described {line,word}. From this I could parse each of the books and do the following:
1. Does the book contain enough lines (it needs at least 3437 lines)
2. Populate a string/array with each word for each co-ordinate pair found
3. If we have found words for each co-ordinate pair then display the sentence.
Writing a quick script to do this you get the following:
# php solution.php Sense and Sensibility -- sentence: providingwastheirthewholeandsoonitforthosethe The Count of Monte Cristo -- sentence: wemustembracethepainandburnitforourjourney
The first sentence ‘providingwastheirthewholeandsoonitforthosethe‘ seems to be cut off, but the second one makes more sense. Simply decoding the AES-CBC-256 file with that will give you the output.
openssl aes-256-cbc -d -base64 -in solution.txt.enc -out solution.dec.txt \ -pass pass:wemustembracethepainandburnitforourjourney |
Challenge 2
[Instruction Text] [GitHub Page]
We were provided an image and asked to derive a key.
Using Stegsolv.jar we could see that there was a QR code hidden within the 0 layer of each individual colour (R,G,B):
To solve this I wrote a simple python script to parse each RGB value and identify if the least significant bit (LSB) was either a 0 or a 1 and then set the RGB value of a new image to either 255 or 0 depending on the LSB.
import Image def LSB(b): b = str(bin(b))[-1:] if(b == "1"): return 0 else: return 255 image = Image.open("image") out = image.copy() out = image.convert('RGB') out.putdata( [(LSB(r), LSB(g), LSB(b)) for r, g, b in out.getdata()] ) out.save("LSB.bmp") |
Initially I had the values reversed, but the QR code would not decode, however after looking at the specification it appeared that I needed to reverse the colours to create the white/negative space needed to properly decode it:
The QR code decoded to the text ‘theleastsignificantisthemostsignificant’
Challenge 4
[Instruction Text] [GitHub Page]
For Challenge four we were given an audio file as well as a zip file (exif.zip) with a password. Audio is purely digital and the zip file is password protected.
Looking at the exif for the audio file we get the following:
# exiftool audio1.wav ExifTool Version Number : 10.13 File Name : audio1.wav Directory : . File Size : 375 kB File Modification Date/Time : 2016:03:03 08:17:22+01:00 File Access Date/Time : 2016:05:15 11:45:34+02:00 File Inode Change Date/Time : 2016:05:15 11:41:57+02:00 File Permissions : rw-r--r-- File Type : WAV File Type Extension : wav MIME Type : audio/x-wav Encoding : Microsoft PCM Num Channels : 2 Sample Rate : 8000 Avg Bytes Per Sec : 32000 Bits Per Sample : 16 Title : Fldigi Artist : Rudolf Light Writer Comment : feld Duration : 11.99 s |
Having a look at the Title actually gives us an application ‘fldigi’ that can be used to encode and decode various digital audio formats. The Artist field is ‘Rudolf Light Writer’ and using this and a few minutes on google you will also see that this text describes a type of Audio Fax called hellschreiber or feld hell.
Playing this audio file into a decoder you receive a small segment of text in the form of a fax that contains the words “bringlifetothewasteland”. The easiest way I found to do this was to run fldigi on a one machine listening to incoming audio via the microphone and then play back the audio file from another machine
After you have decoded that you can use the password (‘bringlifetothewasteland’) to decrypt the zip file.
exiftool exif.zip ExifTool Version Number : 10.13 File Name : exif.zip Directory : . File Size : 505 kB File Modification Date/Time : 2016:03:03 09:26:46+02:00 File Access Date/Time : 2016:05:20 10:58:30+02:00 File Inode Change Date/Time : 2016:05:15 11:41:57+02:00 File Permissions : rw-r--r-- File Type : ZIP File Type Extension : zip MIME Type : application/zip Zip Required Version : 819 Zip Bit Flag : 0x0001 Zip Compression : Unknown (99) Zip Modify Date : 2016:03:03 10:26:02 Zip CRC : 0x00000000 Zip Compressed Size : 516693 Zip Uncompressed Size : 705838 Zip File Name : audio2.wav |
To unzip this zip file you can simply use 7z with the following:
7z -pbringlifetothewasteland x exif.zip |
This zip file contains a single file [audio2.wav].
Looking at the exif for the file audio2.wav you will notice the title field is populated with “backmasking” and the Artist field is populated with “speed”. These two give you the hints that tell you how to “decrypt” the audio.
When you first listen to the audio you can clearly hear that it is reversed, simply going to effect->reverse within Audacity will have the audio the correct way round. Next you will hear that it is painfully slow so you will need to move the speed slider on the right up to about 1.8x/2x to hear the audio:
“You have solved the fourth challenge, proceed to eden to face the fifth challenge”
You can then use the entire string as the password:
openssl aes-256-cbc -d -base64 -in solution.txt.enc -out solution.dec.txt -pass pass:youhavesolvedthefourthchallengeproceedtoedentofacethefifthchallenge |
Challenge 5
[Instruction Text] [GitHub Page]
Challenge 5 was by far the most difficult challenge for me and the one that I spent the most time on. I tried so many different possible ideas to solve this, and had got to the stage where I was asking strangers how they saw the puzzle in the hopes it shone some new light on it. However there was a clue within the instructions, it mentioned the word ‘Puzzle’ in the final sentence as well “Challenge: Retrieve a phrase by assembling the string puzzle.”
Looking at what was given to you, you had the following ‘pieces’:
OIOOb IIOIe IIIId IOIO9 IOOI8 IIIO8 OOII8 IOOO6 OIOI9 OOOOa OIIIe OOIOb _IOI0 OI_If _OOO8 I_II9 _IOO1 _III8 IOI_f OO_Of _OIO0 _IIO4 IO_Oe OIO_d O_OO4 IO_Ic IO_I0 OO_I5 __IOc II__3 O__I2 _IO_1 |
Each piece was made up of 3 characters (O, I and _) as well as a hex character at the end. There was a single duplicate row in the form of IO_Ic and IO_I0, but these were interchangeable later.
First if you split the pieces on _’s you would see there are 4 pieces with two underscores, 16 pieces with one underscore and 12 pieces that have neither. After many hours of frustratingly staring at it I realised this was a co-ordinate system for a puzzle.
Pieces were describing physical jigsaw puzzle pieces that are square with 4 sides being either a Male piece, Female piece or Side/border piece.
To visualise it, here is an actual possibility for the puzzle:
Looking at the possibilities of how the pieces could fit together we can see that the formats for each piece could be one of the following for (T)op, (L)eft, (B)ottom, (R)ight and could be in either an 8×4 or 4×8 configuration :
8x4: #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### T,R,B,L T,L,B,R B,R,T,L B,L,T,R 4x8: #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### #### L,T,R,B L,B,R,T R,T,L,B R,B,L,T
The reason it can only be these is that there are 6 pieces that contain an underscore in the first col and 6 that contain an underscore in the third col. These will need to be on opposite sides to each other (ie, the top and bottom or left and right). This is of course presuming this puzzle will be rectangular (because otherwise – cryingemoticonhere).
Once I have the pieces I needed to figure out how they fit together as there were a number of possibilities, using the co-ordinate system above I used the following “formula”:
1. Iterate through each of the schemas above
2. Place the corner pieces
3. Calculate all the permutations for possible borders (top row, bottom row, left row, right row).
4. Validate each of these based on the rules for jigsaw puzzles – ie for pieces to fit together one side of the piece needs to be ‘male’ and the other needs to be ‘female’ of the piece it is attaching to. This will greatly reduce the number of possibilities that will work for the borders of our puzzle.
5. Build the top row and the sides for each 8×4 (in this case) and send it to a recursive function that can look at the remaining pieces and justify where they should be. Because we don’t have all the information available we still have bits of it. For example even though we cant know the piece below we can still calculate possible pieces that will fit into each spot from the piece above. We usually have 2 sides of each piece. Once we have calculated that piece we can branch off to calculate the next piece and recurse through the function.
6. Validate and prune every second row we now have from 5.
7. Do the same as (5) but now building the ‘top’ row as row 3
8. Validate and prune every third row
Once this was done we had all the possibilities for rows 2 and 3 as well as all the permutations for the top and bottom borders and sides. From here I simple iterated through every combination of the puzzle and validated each combination. Once I had these combo’s I saved the output hex to a ‘dict’ file, something like:
# wc dict 30160 30160 995280 dict # head dict 1801084cfe9dbae4d8b968893ffec052 1801084cf8e8a8e4d9db69b93ffec052 1801084cf8d68eb4d8bea9993ffec052 1801084cf8d8bae4d9896eb93ffec052 1801084cfd6ea8e4db8989b93effc052 1801084cfd689be4db89ea893effc052 1801084cf8d8a894d6ebeb993effc052 1801084cf8d8b9b4d68eae993effc052 1801084cfd688eb4dbe8a9993fcfe052 1801084cfd9ebae4db9868893fcfe052 |
This gave mme about 30K odd results to try, so I simply bruteforced them with a bash script:
#!/bin/bash echo "################################" k=1 total=`wc dict | cut -d " " -f 4` echo "total is: $total" lastPercent=0 while read -r line;do chomped_line=${line%$'\r'} openssl enc -aes-256-cbc -base64 -d -in solution.txt.enc -out out/$chomped_line -pass pass:$chomped_line 2> /dev/null if [ $? -eq 0 ]; then yes=no #wut. else rm out/$chomped_line fi percent=$((100*$k/$total)) #echo $percent if [ $percent != $lastPercent ]; then echo "($percent) $k of $total" lastPercent=$percent fi ((k++)) done < dict echo "Total number of lines in file: $k" |
From here I simply ran the script to get the output as follows:
./dictbash.sh ################################ total is: 30160 (1) 302 of 30160 (2) 604 of 30160 (3) 905 of 30160 (4) 1207 of 30160 ....snip... (94) 28351 of 30160 (95) 28652 of 30160 (96) 28954 of 30160 (97) 29256 of 30160 (98) 29557 of 30160 (99) 29859 of 30160 (100) 30160 of 30160 Total number of lines in file: 30161 |
This then create a lot of files within the out/ directory and it was merely the task to find the right one:
# file out/* | grep -i unicode out/1148008cfeb9dae4d68898b93e05cff2: UTF-8 Unicode English text, with very long lines # head out/1148008cfeb9dae4d68898b93e05cff2 “CORRECT. YOU MAY ENTER THE GARDEN OF EDEN.” And with that, the giant metal door begins to slowly creak open. You stand before it, wide-eyed and relieved that you didn’t waste your last attempt on “4pp134”. |
Challenge 6
[Instruction Text] [GitHub Page]
Challenge 6 was a relatively straight forward one, we were given the following image and asked to decode:
If you zoom into the image you can see that there is a pattern to it where every piece is rotated, then there is a ‘zoom’ and then it is rotated again:
From here it was simply creating a python script with the help of the PIL library to solve this and was as follows:
#!/usr/bin/python from PIL import Image import _imaging import sys img = Image.open("image.jpg") for i in range (0,100): x1 = 790 - (i * 10) x2 = 810 + (i * 10) y1 = 790 - (i * 10) y2 = 810 + (i * 10) inlay = img.crop((x1,y1,x2,y2)).rotate(90) img.paste(inlay, (x1,y1,x2,y2)) img.save("imagedecoded.jpg") |
I used a photo editing application to get the initial co-ordinates of the center square and ran it to produce the following which included the decoded message :)
Challenge 7
[Instruction Text] [GitHub Page]
This challenge was fairly straight forward, and also not that straight forward. So first we got an office document “OPENME.doc”, its password protected and we need to get in. To crack the password was relatively simple and the easiest way to get in.
First we needed the hash of the password (this we get with office2john.py)
python office2john.py OPENME.doc OPENME.doc:$oldoffice$1*62aac83b6f5ce35078ed4c9bf6a0946f*4c8858d41869a01cf3a5af53a8f50005*0f099570e1e4a850d6bc0a3195b80b7c:::1252 1 1 220 786432 1252 mwri Normal mwri 8 1440 13101308220 13101313620 1 32 189 Microsoft Office Word 1::OPENME.doc |
Next we can use the fantastic cudaHashCat to attack the password with the default rockyou password list:
cudaHashcat64.exe -a 0 -m 9700 --username OPENME.doc:$oldoffice$1*62aac83b6f5ce35078ed4c9bf6a0946f*4c8858d41869a01cf3a5af53a8f50005*0f099570e1e4a850d6bc0a3195b80b7c "rockyou.txt" cudaHashcat v2.01 starting... Device #1: GeForce GTX 960, 2048MB, 1304Mhz, 8MCU Device #1: WARNING! Kernel exec timeout is not disabled, it might cause you errors of code 702 You can disable it with a regpatch, see here: http://hashcat.net/wiki/doku.php?id=timeout_patch Hashes: 1 hashes; 1 unique digests, 1 unique salts Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates Rules: 1 Applicable Optimizers: * Zero-Byte * Precompute-Init * Not-Iterated * Single-Hash * Single-Salt Watchdog: Temperature abort trigger set to 90c Watchdog: Temperature retain trigger set to 80c Device #1: Kernel ./kernels/4318/m09700_a0.sm_52.64.cubin Cache-hit dictionary stats rockyou.txt: 139921507 bytes, 14343297 words, 14343297 keyspace $oldoffice$1*62aac83b6f5ce35078ed4c9bf6a0946f*4c8858d41869a01cf3a5af53a8f50005*0f099570e1e4a850d6bc0a3195b80b7c:salinas Session.Name...: cudaHashcat Status.........: Cracked Input.Mode.....: File (rockyou.txt) Hash.Target....: $oldoffice$1*62aac83b6f5ce35078ed4c9bf6a0... Hash.Type......: MS Office <= 2003 MD5 + RC4, oldoffice$0, oldoffice$1 Time.Started...: 0 secs Speed.GPU.#1...: 1822.7 kH/s Recovered......: 1/1 (100.00%) Digests, 1/1 (100.00%) Salts Progress.......: 12288/14343297 (0.09%) Rejected.......: 0/12288 (0.00%) Restore.Point..: 0/14343297 (0.00%) HWMon.GPU.#1...: 0% Util, 45c Temp, 0rpm Fan Started: Sat Jun 04 21:17:43 2016 Stopped: Sat Jun 04 21:17:45 2016 |
Opening the file with this password will give you the key.
However you can also view the macros within the document and you find this interest snippet:
# ./oledump.py OPENME.doc 1: 121 '\x01CompObj' 2: 4096 '\x05DocumentSummaryInformation' 3: 4096 '\x05SummaryInformation' 4: 6580 '1Table' 5: 491 'Macros/PROJECT' 6: 71 'Macros/PROJECTwm' 7: M 1319 'Macros/VBA/NewMacros' 8: M 2036 'Macros/VBA/ThisDocument' 9: 2549 'Macros/VBA/_VBA_PROJECT' 10: 1423 'Macros/VBA/__SRP_0' 11: 110 'Macros/VBA/__SRP_1' 12: 384 'Macros/VBA/__SRP_2' 13: 103 'Macros/VBA/__SRP_3' 14: 84 'Macros/VBA/__SRP_4' 15: 103 'Macros/VBA/__SRP_5' 16: 574 'Macros/VBA/dir' 17: 4096 'WordDocument' #./oledump.py -s 7 -v OPENME.doc Attribute VB_Name = "NewMacros" Sub Hackfu() ' ' Hackfu Macro 'Sec 'ond Fl 'ag is: 'youcannotspellscorchedearthwithoutdeath' ' End Sub |
I am still not sure where that came into play — perhaps later?
Challenge 8
[Instruction Text] [GitHub Page]
The final challenge I did was Challenge 8, it came with 12 image files and a very strange story about sextants and the night sky. After a lot of learning about old navigation methods and then trying tons of different things with the images (exif, stego, etc) I ended up with a listing of the various places and GPS co-ordinates for them as well as the date which they were taken:
DC046_2015:12:01.jpg / Tokyo Tower / 35.6585805,139.7454329 DC097_2015:03:01.jpg / abuja National Mosque nigeria / 9.0603227, 7.4891962 DC097_2015:06:01.jpg / texas state capitol / 30.2746652, -97.7403505 DC099_2015:07:01.jpg / cliffs of moher ireland / 52.9715368, -9.4308825 DC100_2015:09:01.jpg / branden burg gate ( Berlin ) / 52.5162746, 13.377704 DC105_2015:02:01.jpg / Djinguereber Mosque / 16.7715422, -3.0101439 DC105_2015:08:01.jpg / Old Trafford Manchester, England / 53.4630589, -2.2913401 DC108_2015:05:01.jpg / palacio de bellas artes mexico ( Mexico city) / 19.4352, -99.1412 DC109_2015:01:01.jpg / Hassan II Mosque casablanca / 33.6073889, -7.6323399 DC112_2015:04:01.jpg / Christ the redeemer, Rio de Janeiro, Brazil / -22.951916, -43.2104872 DC115_2015:11:01.jpg / Great wall of China / 40.4319077, 116.5703749 DC117_2015:10:01.jpg / suomenlinna finland / 60.1454, 24.9881401 |
From here I started plotting them in different orders on maps to try and see if there was any pattern I could find:
Looking at it, I was convinced it was a reference to the big dipper, but simply couldnt get it to match exactly.
Next I started looking at the playfair cipher, it’s a relatively simple cipher and being lazy as I am, I managed to grab some code from here and here to get something I wanted to work. They had modified the cipher slightly to have some repetition as per the instructions but the modification wasn’t too difficult to make.
Now I had the cipher down and the output I just needed to find the keyword used to try and break it. Still convinced the map was trying to show me a constellation, I tried another method, bruteforce. I looked at various pages include this to build up a constellation list of 106 different constellations.
Additionally looking at the output it looked like we were getting a base64 encoded string out so we needed to decode that as well, so I added that to the script as well. From here I changed the playfair cipher to read these in and try them as the key and return the output, and got the following:
# python solution.py ANTLIA : dGhcY1JhdGVhkYTmZSKmaz0bazgygqzZtsFxsbWMwb254Nb9VzaL5mZXLfb9H3b9HsaT==x ------------------------------- APUS : dGikdlIidGrYbsSYfHCcfn2vfnhxinymvqO6tvjVwc254Pv4CzqQ7laWJnv4Rxv4JrZP==x ------------------------------- AQUARIUS : dGhckxJhdGvZbjSklRLZmz2vmzfzguzlpZ5xtkWKws254Qv59poP8kcAGuv5Pyv5GiqR==x ....snip... CARINA : dGhld3JhdGhvZnRoZWNvc21vc2hhc2ZhbGxlbnVwb255b3VyaW5mZXJpb3J3b3JsZA==x thewrathofthecosmoshasfallenuponyourinferiorworld |
The key ‘CARINA’ unlocked the cipher and decoded the B64 string to ‘thewrathofthecosmoshasfallenuponyourinferiorworld’.
The Carina constellation did look somewhat like the original Google map, but you definitely would have needed some intricate knowledge of the solar system to see it!
Cheers!
I had a ton of fun solving these problems and look forward to next years hackfu!
[…] http://andrewmohawk.com/2016/06/05/hackfu-2016-writeup/ […]
A follow up on challenge 8. If you sort out the images in chronological order and look up the ASCII letters corresponding to the name of the images you will get the word Maiplacidus. Google search that and you have your key.
No Way! Thats awesome!
-Cheers