During Black Hat USA 2020 we here at Cyborg Security released into the ether of the internet a cyber security hacking puzzle, similar to a CTF challenge. We had a couple hundred participants and those that completed various stages of the puzzle won prizes! We gave away 100 Cyborg Security stickers, 10 Cyborg Security t-shirts, and 1 lucky grand winner won an Apple iPad with a Cyborg Security engraving. Thank you very much for all of those who participated in our first hacking puzzle. The solutions to all stages of the puzzle are detailed below. We look forward to doing similar events in the future.
Scanning the QR Code began the puzzle, which contains the starting link:
http://18.104.22.168 showed a mysterious webpage:
After sending a DM to @cyb0rgsecur1ty with
i_w4nt_t0_b3_4_cyb0rg a password was given back
cyb0rg_c4ts_4r3_b3st_c4ts. This password is needed for part 3.
By taking the advice to “FOLLOW THE WHITE KITTY”, the image above (
metacatismeta.png) should be analyzed.
Using the password from part 1, it can be used to extract the contents of the 7-zip archive found in part 2.
$ wget http://22.214.171.124/d0wn_th3_r4bb1t_h0l3.7z ...$ 7z x d0wn_th3_r4bb1t_h0l3.7z -pcyb0rg_c4ts_4r3_b3st_c4ts ...$ ls d0wn_th3_r4bb1t_h0l3/ 4lph4b3t_bo1z.pcap FREESTUFFS.txt new_xxd_who_dis
The first 100 people to get this far won a free Cyborg Security sticker:
$ cat d0wn_th3_r4bb1t_h0l3/FREESTUFFS.txt Congrats on making it this far!!! The first 100 people to DM us at @cyb0rgsecur1ty with "pls_g1v3_th1s_h4x0r_fr33_st00fz" will receive a free Cyborg Security sticker! Please include a complete mailing address in your DM.There's more files in this archive, keep going to win more free stuff.
The extracted archive from part 3 contains a PCAP file,
4lph4b3t_bo1z.pcap. This is a packet capture analysis challenge. The PCAP file can be opened with a tool like Wireshark to view the packets. It contains 26 HTTP GET requests (one for each letter of the alphabet), all for the same file name:
These files can be easily extracted from the PCAP by doing: File -> Export Objects -> HTTP -> Save All. While these files all have the same name, one of these files is different and can be determined by analyzing file size or hash. This unique file will then need to be analyzed further.
my_lips_are_sealed file extracted from the PCAP in part 4 is an Linux ELF:
Upon running the program, an unfriendly cyborg is presented:
This is a reverse engineering challenge. Opening the binary in IDA (or similar), the disassembly of the main function looks like so:
Notice a few things in this disassembly:
fgetswhich takes in some user input
what_is_my_purposethe program either returns a good or bad output
Based on this information, the next logical step is to view the disassembly of the
what_is_my_purpose function. Immediately observable in the disassembly is a large number of
mov instructions creating an array of data:
The logic after this data can be more easily understood via decompilation (this is from Ghidra):
param_1 is the user input from
local_a8 is the aforementioned array of data. The length of the user input is decremented as it iterates over the user input. During the iteration of the user input, string characters are compared with the data array by integer value. Because the loop starts from the length of the user input and is decrementing, the string is being compared in reverse.
Therefore, this array of data can be reversed and converted to characters to reveal the password (Python example):
>>> array = [51, 114, 48, 109, 121, 110, 52, 95, 114, 51, 116, 116, 117, 98, 95, 51, 104, 116, 95, 115, 115, 52, 112, 95, 48, 116, 95, 116, 110, 52, 119, 95, 116, 110, 48, 100, 95, 49] >>> ''.join([chr(i) for i in array[::-1]]) '1_d0nt_w4nt_t0_p4ss_th3_butt3r_4nym0r3'
This password seems to finally appease the cyborg:
The other file from the
d0wn_th3_r4bb1t_h0l3.7z archive in part 3 is the output from an xxd hex dump:
$ head -n 5 new_xxd_who_dis 00000000: 504b 0304 0a00 0000 0000 9a53 f850 0000 PK.........S.P.. 00000010: 0000 0000 0000 0000 0000 0800 1c00 7465 ..............te 00000020: 7374 696e 672f 5554 0900 0333 fe1a 5f34 sting/UT...3.._4 00000030: fe1a 5f75 780b 0001 04e8 0300 0004 e803 .._ux........... 00000040: 0000 504b 0304 0a00 0900 0000 9d53 f850 ..PK.........S.P
The ASCII file header
PK indicated that this file is a zip archive, which comes from the initials of Phil Katz who created the zip file format. This xxd hex dump can be reversed into the original file by a variety of means, the easiest is to use the
-r switch option from xxd:
$ xxd -r new_xxd_who_dis > out.zip
The zip archive is password-protected, and can be extracted using the password found in part 5:
$ unzip -P 1_d0nt_w4nt_t0_p4ss_th3_butt3r_4nym0r3 out.zip Archive: out.zip extracting: out/jeff.png
The extracted zip archive from part 6 contains an image,
In analyzing the image,
binwalk will reveal a zip file contained within the image:
$ binwalk jeff.png DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 PNG image, 459 x 430, 8-bit/color RGB, non-interlaced 13444 0x3484 Zip archive data, at least v1.0 to extract, name: brut3_m3_pl5_d1g1tz_0nly/ 13507 0x34C3 Zip archive data, encrypted at least v1.0 to extract, compressed size: 17, uncompressed size: 5, name: brut3_m3_pl5_d1g1tz_0nly/brut3_m3_pl5_d1g1tz_0nly.txt 13767 0x35C7 End of Zip archive, footer length: 22
The zip file can be extracted using
binwalk --extract jeff.png. The zip file is password-protected, and can be extracted using the password from the original image (
$ ls _jeff2.png.extracted/*.zip 3484.zip$ unzip -P t4k3_m3_t0_y0ur_l34d3r 3484.zip Archive: 3484.zip extracting: 3484/MOREFREESTUFFS.txt extracting: 3484/brut3_m3_pl5_d1g1tz_0nly.zip
The first 10 people to get this far won a free Cyborg Security shirt:
$ cat MOREFREESTUFFS.txt Congrats on making it this far!!!The first 10 people to DM us at @cyb0rgsecur1ty with "1m_c0ld_c4n_h45_cyb0rg_cl0thz_pl0x" will receive a free Cyborg Security shirt! Please include a complete mailing address in your DM.
The zip file
brut3_m3_pl5_d1g1tz_0nly.zip extracted from part 7 is password-protected with the only clue being the file name. The file is begging to be brute-forced using only digits. This can be done with John the Ripper (or similar) password cracker:
$ zip2john brut3_m3_pl5_d1g1tz_0nly.zip > hash.txt$ john --incremental=digits hash.txt Using default input encoding: UTF-8 Loaded 1 password hash (PKZIP [32/64]) Will run 12 OpenMP threads Press 'q' or Ctrl-C to abort, almost any other key for status 72339853041 (brut3_m3_pl5_d1g1tz_0nly.zip/f1nd_th3_cyb0rg_tr34sur3) 1g 0:02:39:04 DONE (2020-07-30 02:21) 0.000104g/s 22967Kp/s 22967Kc/s 22967KC/s 72339894832..72339834956 Use the "--show" option to display all of the cracked passwords reliably Session completed
The password for the zip file once cracked is discovered to be,
72339853041. Using John with default options, in incremental mode, on a common Intel CPU, cracking this password took about two and a half hours. It would be significantly faster using a GPU, a better CPU, and/or better cracking options.
$ unzip -P 72339853041 brut3_m3_pl5_d1g1tz_0nly.zip Archive: brut3_m3_pl5_d1g1tz_0nly.zip extracting: brut3_m3_pl5_d1g1tz_0nly/f1nd_th3_cyb0rg_tr34sur3 extracting: brut3_m3_pl5_d1g1tz_0nly/the_singularity_is_the_end.7z
By extracting the archive in part 8, the file
f1nd_th3_cyb0rg_tr34sur3 is given which is comprised of a many 8-bit binary strings.
$ more f1nd_th3_cyb0rg_tr34sur3 1000111 1000010 1011001 1101011 1010001 1000111 1100011 1111010 111 1100 1001010 1000101 1101001 1101111 1100000 1001101 1000101 111011 110010 1010000 1000010 1100100 111110 1111101 1000111 110010 10001 01 101101 1011110 1000100 1010010 1001000 1111001 1111100 1111000 1 000010 1000110 1100111 1010000 1110111 1010101 1001000 110110 10101 10 1001110 101101 1000111 1000010 1011001 1101011 1010001 1000111 1 ...
Using Python, the file can be opened and the binary strings placed in a list:
$ python >>> with open('f1nd_th3_cyb0rg_tr34sur3') as f: ... data = f.read().split() ... >>> data ['1000111', '1000010', '1011001', ... ]
These binary strings can then be converted to integers, and look like they might be ASCII characters:
>>> int_list = [int(i, 2) for i in data] >>> int_list [71, 66, 89, 107, 81, 71, 99, 122, 124, 74, ... ]
Converted to characters, it’s revealed to be a base85 encoded blob (this can be determined by a variety of means):
>>> encoded_blob = ''.join([chr(i) for i in int_list]) >>> encoded_blob 'GBYkQGcz|JEio`ME;2PBd>}G2E-^DR...' >>> import base64 >>> decoded_blob = base64.b85decode(encoded_blob).decode() >>> decoded_blob '23.1337 -102.25 | 23.1337 -108.25 | ...' >>> decoded_blob.split(' | ') ['23.1337 -102.25', '23.1337 -108.25', ... ] >>> len(decoded_blob.split(' | ')) 174
This decoded blob contains a list of 174 pairs of numbers. These happen to be longitudes and latitudes. Plotting these coordinates on a map will reveal the password for the last part:
After extracting the final 7-zip archive using the password found in part 9, a Windows EXE file is given:
$ 7z x the_singularity_is_the_end.7z -pIHATETHEHOOMANZ ...$ file the_singularity_is_the_end.exe the_singularity_is_the_end.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows
When this file is executed on Windows, some text is displayed giving a hint about GameBoy games…
This is a reverse engineering challenge. Using a static analysis tool for disassembly and/or decompilation (such as IDA or Ghidra) will quickly reveal that that this binary was written in Golang. By observing the functions in IDA we can see this Go binary is using fmt, base64, and a lz4 compression library from GitHub.
After this there are also ~90 functions labeled “decrypt”:
By viewing the disassembly from the main function entry point, we can see that after the hints are printed out to the screen all of these decrypt functions are called:
These decrypt functions are extremely similar in logic. By looking at the disassembly for one of these decrypt functions we can see a base64 encoded blob (
main_blob) is base64 decoded and then lz4 decompressed:
This plaintext, base64 encoded blob can be extracted using strings or IDA with ease:
After this blob is decoded some further logic is applied to the bytes. It might be easier to understand what is happening using a decompiler, this is from Ghidra and is shortened to show the relevant parts:
Notice here that two operations are occurring on the bytes of the decoded blob. First the bytes are XOR’d by the number corresponding to the decrypt function (10 in this case) and after this any byte matching that same number is rewritten to 0. This function doesn’t write anything to a file or return any value, these decrypt functions aren’t actually producing any results. It might be interesting to get the results of these decrypt functions and see if they produce anything useful.
Using this information we can write a quick Golang program that uses the same lz4 library to reproduce the operations of the decrypt functions. This program will need to: take the encoded blob -> base64 decode -> lz4 decompress -> XOR the bytes by a number -> replace any bytes with that number with a null byte. The results of each of these reversed “decrypt” functions (10 thru 99) will then be dumped to a file for further analysis.
This program will output ~90 files, and by viewing the file type of each of these output files we can see that one file stands out. The decrypt69 function produces a GameBoy ROM!
By changing the file extension to
decrypt69.gb and loading it up in a GameBoy emulator we can play the ROM. This is using VisualBoyAdvance for example:
Thanks everyone who participated by playing our Black Hat USA 2020 hacking puzzle. We look forward to doing similar challenges in the future and giving away more goodies to worthy hackers! So long for now.