Trickery Index

Trend Micro CTF 2017 Quals - Forensic 300 writeup

Posted at — Jun 28, 2017

15092A12

Ajaarvi 17th

Just finished sketching the remains. The boat, left here to her final rest gods know how long before, radiates silent dignity even in this tormented state, and the quiet rustling of leaves on branches sprouting from the bridge soothes my ears like a Ryathi litany.

My scavenging attempt resulted in recovery of some digital storage hardware, partially intact, along with enough working parts for the board analyst’s magic to figure out and restore their systems’ function by the end of the day. It appears the inhabitants of this forgotten place used to spend their leisure time in making elaborate brain-teasers - or maybe some sacred ritual demanded it from them, rushing to chase the ‘Flags’ said to be hidden within? I cannot help but wonder what purpose those held.

In the databanks of the dead vessel, once boasting a satellite uplink, I found a treasure trove of the challenges, which will no doubt help me battle loneliness on this long mission to the unknown. Though I’m no artisan, and by no means a mystic, they seem to be good puzzles, and I like puzzles; from now on, I’m going to keep notes of my progress with these.

Taking the Azalea back to the field camp tonight, should finish packing. Vesmarri logging off.

Rusty boat


15092A13

Ajaarvi 17th

Finally managed to get a Flag from my mystery archive! Describing the course of action for posterity.

The description part of the riddle said the challenger must extract the Flag from some ‘Cuckoo’ logs provided; as part of the game, it had been encrypted by some villain, but enough information was supposed to remain unharmed to restore it. I wasn’t entirely sure what the bird had to do with this, but dived in nevertheless.

And so, upon unravelling the initial compression, I got the encrypted files in the files directory - the Flag itself in the JPEG picture format (seems popular) along with two others called Thumbs.db and desktop.ini - no memory dumps, nothing in buffer and shots… A private cryptographic key of the common encrypting scheme, called RSA by the natives, resided in the reboot.json file - that seemed interesting, but the files didn’t in fact look like they could be just encrypted with RSA; what about all the ‘TMCTF’ strings jammed in them here and there?

Thus, I went on. Some basic information could be gathered from analysis.log and cuckoo.log; a couple of complicated-looking .bson files in logs I decided to avoid, not eager to request more help from the analyst’s data on the 17th right away. But what lay hidden in reports made my heart skip a beat!

In there, a whole slew of ‘Windows’ API calls took cover among other things. By now somewhat familiar with their kind, I searched for flag.jpg with renewed vigour:

{
    "category": "file", 
    "status": 1, 
    "stacktrace": [], 
    "api": "NtCreateFile", 
    "return_value": 0, 
    "arguments": {
        "create_disposition": 1, 
        "file_handle": "0x000000c8", 
        "filepath": "C:\\Users\\cbaqWin7\\Pictures\\flag.jpg", 
        "desired_access": "0x80100080", 
        "file_attributes": 128, 
        "filepath_r": "\\??\\C:\\Users\\cbaqWin7\\pictures\\flag.jpg", 
        "create_options": 96, 
        "status_info": 1, 
        "share_access": 3
    }, 
    "time": 1496717890.440668, 
    "tid": 2840, 
    "flags": {
        "create_disposition": "FILE_OPEN", 
        "desired_access": "FILE_READ_ATTRIBUTES|SYNCHRONIZE", 
        "create_options": "FILE_NON_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT", 
        "file_attributes": "FILE_ATTRIBUTE_NORMAL", 
        "status_info": "FILE_OPENED", 
        "share_access": "FILE_SHARE_READ|FILE_SHARE_WRITE"
    }
}, 

Here it was, opening up! And then a bit later it was getting read in a dyad of consecutive API calls, to later be encrypted and written to the corresponding .TMCTF file; but in an especially emboldening turn of events, I saw what seemed to represent the whole data buffer passed to the NtReadFile call dumped right into my impatient hands:

{
    "category": "file", 
    "status": 1, 
    "stacktrace": [], 
    "api": "NtReadFile", 
    "return_value": 0, 
    "arguments": {
        "file_handle": "0x000000c8", 
        "buffer": "\u00ff\u00d8\u00ff\u00e0\u0000\u0010JFIF
        ...
       "length": 20480,
       "offset": 0
    },
    "time": 1496717890.440668,
    "tid": 2840,
    "flags": {}
},
{
    "category": "file",
    "status": 1,
    "stacktrace": [],
    "api": "NtReadFile",
    "return_value": 0,
    "arguments": {
        "file_handle": "0x000000c8",
        "buffer": "\u0080\n(\u00a2\u0080\n
        ...
        "length": 4096,
        "offset": 0
    },
    "time": 1496717890.440668,
    "tid": 2840,
    "flags": {}
},

All I needed was a tool to siphon this out of the file. In a stroke of quaintness, I decided to use the alien analytical language called ‘Python’ for this; the natives undoubtedly loved their fauna, though in this case the brand seems to stem from some sort of an entertainment show sharing the name. This resulted in the following automation:

import json
import pprint

with open("Analysis/reports/report.json") as f:
    data = f.read()

processes = json.loads(data)["behavior"]["processes"]
for p in processes:
    if "ransomware" in p["process_path"]:
        calls = p["calls"]
        break

files = {}
count = {}

for c in calls:
    if "NtReadFile" in c["api"]:
        cur_fh = c["arguments"]["file_handle"] 
        if not cur_fh in files :
            files[cur_fh] = ""
        files[cur_fh] += c["arguments"]["buffer"]
    if "NtClose" in c["api"]:
        cur_fh = c["arguments"]["handle"]

        if not cur_fh in files:
            continue

        if not cur_fh in count:
            count[cur_fh] = 0
        count[cur_fh] += 1

        files["%s_%d" % (cur_fh, count[cur_fh])] = files[cur_fh]
        files[cur_fh] = ""


for fh in files.keys():
    filename = fh
    if files[fh] == "":
        continue
    if files[fh][:2] == "PK":
        filename = fh + ".zip"
    with open(filename, "wb") as f:
        f.write(files[fh].encode('latin-1'))

The purpose of this piece was to crawl through the file looking for successive NtReadFile calls ending with an NtClose, dropping the corresponding buffers in the storage as it went, with the files getting renamed to their handles. Of course, I could pick out the actual filenames with some more cleverness, but chose not to, for the target was clear: the encrypted Flag alone enjoyed the handle of 0x000000c8.

Upon performing the automation, I was left with the following outcome:

lvesmarri@azalea:~/ajaarvi17/archive/trendmicro2017quals/for300$ python solve.py 
lvesmarri@azalea:~/ajaarvi17/archive/trendmicro2017quals/for300$ ls
0x00000098_1.zip   0x000000a8_21.zip  0x000000a8_31.zip  0x000000a8_43.zip  0x000000b4_14.zip  0x000000c0_1.zip
0x00000098_2.zip   0x000000a8_22.zip  0x000000a8_32.zip  0x000000a8_44.zip  0x000000b4_15.zip  0x000000c0_2.zip
0x000000a0_1.zip   0x000000a8_23.zip  0x000000a8_33.zip  0x000000a8_4.zip   0x000000b4_1.zip   0x000000c0_4
0x000000a8_10.zip  0x000000a8_24.zip  0x000000a8_35.zip  0x000000a8_5.zip   0x000000b4_3.zip   0x000000c0_6
0x000000a8_11.zip  0x000000a8_25.zip  0x000000a8_37.zip  0x000000a8_6.zip   0x000000b4_4.zip   0x000000c8_1
0x000000a8_12.zip  0x000000a8_26.zip  0x000000a8_38.zip  0x000000a8_7.zip   0x000000b4_5.zip   Analysis
0x000000a8_14.zip  0x000000a8_27.zip  0x000000a8_39.zip  0x000000a8_8.zip   0x000000b4_7.zip   Analysis.zip
0x000000a8_16.zip  0x000000a8_28.zip  0x000000a8_3.zip   0x000000a8_9.zip   0x000000b4_8.zip   solve.py
0x000000a8_18.zip  0x000000a8_29.zip  0x000000a8_40.zip  0x000000b4_10.zip  0x000000b4_9.zip
0x000000a8_19.zip  0x000000a8_2.zip   0x000000a8_41.zip  0x000000b4_11.zip  0x000000b8_1.zip
0x000000a8_1.zip   0x000000a8_30.zip  0x000000a8_42.zip  0x000000b4_13.zip  0x000000bc_1.zip
lvesmarri@azalea:~/ajaarvi17/archive/trendmicro2017quals/for300$ file 0x000000c0*
0x000000c0_1.zip: Zip archive data, at least v2.0 to extract
0x000000c0_2.zip: Zip archive data, at least v2.0 to extract
0x000000c0_4:     Little-endian UTF-16 Unicode text, with CRLF, CR line terminators
0x000000c0_6:     Composite Document File V2 Document, Cannot read short stream
0x000000c8_1:     JPEG image data, JFIF standard 1.01, resolution (DPI), density 96x96, segment length 16, baseline, precision 8, 768x614, frames 3

I knew by then the numerous ‘ZIP’-compressed files were not immediately relevant, and the Flag appeared to be stored neatly now in the file named 0x000000c8_1, so of course I leaped at it without hesitation; but alas! This was all I could see:

Corrupted Flag

I became sullen: could the archive itself be irreversibly corrupted by age and elements? Would the mystery remain unsolved forever now? But an extra ration from the Azalea holds worked to improve my outlook, and I cheered up, sure now that the unfortunate hiatus must have been part of the challenge.

For, indeed, one of the other encrypted files was named Thumbs.db - that tugged on my memory; I brought up the analyst’s banks on the known systems of the 17th, and found out that this kind of files was supposed to be designed to hold the downsized copies of the images present in the same directory! That must have been what I needed, and, noticing that unencrypted Thumbs.db should have been renamed to 0x000000c0_6 by the automation, I followed with another indigenous command, binwalk, to carve out the precious freight:

lvesmarri@azalea:~/ajaarvi17/archive/trendmicro2017quals/for300$ binwalk -D jpeg:jpg 0x000000c0_6
lvesmarri@azalea:~/ajaarvi17/archive/trendmicro2017quals/for300$ ls _0x000000c0_6.extracted/
818.jpg

Success! But what if this one is damaged too?..

Thumbnail Flag

So it was, but this time not presenting a great concern; for any person with eyes as good as mine could easily derive the Flag now: TMCTF112242317!

Thus ended my first tour into the depths of the riddle archive (well, not quite; reading some more descriptions I came to believe the Flag should in truth carry some braces around the number part to be entirely valid, which would make it look like TMCTF{112242317}). Needless to say, I am fairly satisfied with my performance!

Perhaps another extra ration wouldn’t be unreasonable for tonight’s dinner? Should also check the berries I’ve seen growing around the camp looking rather tempting. Vesmarri logging off.