Forensics Walkthrough (DefCon CTF 2007 Qualifiers)
This category is always lots of fun. It required a good deal of attention to detail, creative thinking, and a wide knowledge base.
100: EXIF for the win!
Last year's DefCon quals included a tarball of images where the key was in the exif metadata of a photo of Giovanni Vigna from UCSB (shellphish). I immediately ran 'exifprobe' on the JPG provided looking for keys. From some past experience, 'exif' and a few other commandline exif metadata tools don't pull out all the information while 'exifprobe' always seems to return more than you ever wanted!
While digging through the 'exifprobe' output, I noticed the following:
<GPS IFD> (in IFD 0) 5 entries starting at file offset 0x2b4=692
  <000000=    0> VersionID                   [1 =BYTE          4]  = 2,2,0,0
  <0x0001=    1> LatitudeRef                 [2 =ASCII         2]  = 'N'
  <0x0002=    2> Latitude                    [5 =RATIONAL      3]  = @0x2f4=756
  <0x0003=    3> LongitudeRef                [2 =ASCII         2]  = 'W\000'
  <0x0004=    4> Longitude                   [5 =RATIONAL      3]  = @0x30c=780
  **** next IFD offset 0
  ============= VALUES, GPS IFD ============
  Latitude                    = 42,40,16.212
  Longitude                   = 72,33,1.548
Using the search feature on Google Maps, putting in "42 40 16.212 N 72 33 1.548 W" results in a pushpin right in the middle of Bernardston.
200: Multi-arch encapsulation
This was a JPG file embedded in a MACH-O multi-architecture executable (MACH-O is used by Mac OSX). We identified this to be the case using the unix strings command. The string "JFIF" is part of the JPG file header.
$ strings forensics200-5be06945190cf5ff9722d0b5f12e0013
Simply stripping off the first 68 bytes (likely the size of a full MACH-O header) we were able to make the file load as a JPG file. The resulting picture showed the key, which was a restaurant sign (indicated by it being circled): IDYLWOOD GRILL
Doing this analysis on an OSX machine is made much easier by using the Mach-O tools:
$ file forensics200-5be06945190cf5ff9722d0b5f12e0013
forensics200-5be06945190cf5ff9722d0b5f12e0013: Mach-O universal binary with 3 architectures
forensics200-5be06945190cf5ff9722d0b5f12e0013 (for architecture sparc): JPEG image data, JFIF standard 1.01
forensics200-5be06945190cf5ff9722d0b5f12e0013 (for architecture i386):  Mach-O executable i386
forensics200-5be06945190cf5ff9722d0b5f12e0013 (for architecture ppc):   Mach-O executable ppc
$ ditto -arch sparc forensics200-5be06945190cf5ff9722d0b5f12e0013 forensics200.jpg
300: Let's play Operation
After ungzipping, the output from "file" just said "data". Turning to "strings", the output was much more interesting. The interesting bits seen:
$ strings forensics300-e130c3621118e4b891fbceb67e2c94cc.dd
<< /Type /Page /Parent 10 0 R /Resources 3 0 R /Contents 2 0 R /MediaBox
[0 0 612 792] >>
Googling "pool_guid top_guid" makes it look like this file is a ZFS dump. The second chunk makes it look like a VMWare virtual disk. The third chunk, however, stands out as a PostScript or PDF file of some kind. Ignoring the horror that might be reading a VMWare image off a ZFS filesystem, we turned to Scalpel.
"Scalpel" is a forensic tool for carving files out of a larger file--be it a disk image or Word doc--based on the header and/or footer of the files. For example, a JPG has a known header of "0xffd8ffe00010" and footer "0xffd9". To carve out the embedded files found in the strings output, we checked for filenames, modified the "scalpel.conf" to only carve out PDFs, and recovered two PDF files:
$ zcat forensics300-e130c3621118e4b891fbceb67e2c94cc.dd.gz | strings | grep -i pdf
$ perl -pi -e 's/^#\s+pdf/pdf/' scalpel.conf
$ scalpel -c scalpel.conf forensics300-e130c3621118e4b891fbceb67e2c94cc.dd
Carve lists built.  Workload:
pdf with header "\x25\x50\x44\x46" and footer "\x25\x45\x4f\x46\x0d" --> 0 files
pdf with header "\x25\x50\x44\x46" and footer "\x25\x45\x4f\x46\x0a" --> 2 files
Carving files from image.
The PDFs were copies of the frontpage from the Kenshoto website with the statement: 'The key for CTF 2007 Quals is: "in the other file"' and 'The key for CTF 2007 Quals is: *******************'. The key was covered by a black box but the text could be highlighted, copied, and pasted into another window for viewing.
400: What time is it?
The file is a tar.gz file, which unpacks into the "f400" directory, with the binary, the encrypted key, and a product manual.
First, by playing with the application, we find that it: Moving on to the disassembly, we see the big clue for this one: the random number generator is initialized only with the system time. i.e. the output (%eax) from time() is the only argument (push %eax) to srandom(), or, simply "srandom(time())". As seen with "objdump -d awesomeify":
8048787:       e8 44 fe ff ff          call   80485d0 <time@plt>
804878c:       83 c4 04                add    $0x4,%esp
804878f:       50                      push   %eax
8048790:       e8 5b fe ff ff          call   80485f0 <srandom@plt>
As a result, if it's the same time, the random output will be the same. The only question was how many bytes are read before starting the encryption. Assuming that the time stamp on the output file matches the time the srandom() was called, we just repeatedly reset the system clock while brute-forced a few byte lengths until something pops out.
The time stamp from the original file:
$ stat -t "%Y%m%d%H%M.%S" key.enc
87 565264 -r-------- 1 nops nops 2258139 27 "200705101437.23" "200705101437.23" "200706071815.44" "200705101437.23" 4096 4 0 key.enc
Short brute-forcer to find key length:
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
        # clean up from prior runs
        rm -f key.enc.dec key.enc.enc

        # reset system time to key.enc's modification time
        date 200705101437.23 >/dev/null

        # get key for this time stamp
        OUT=$(./awesomeify -b $i key.enc)

        # convert from text to byte stream
        KEY=$(echo "$OUT" | cut -d" " -f4 | perl -ne 'while (s/(..)/print chr(hex($1))/eg) {};')

        # stuff calculated key back into awesomeify
        echo "$KEY" | ./awesomeify -D -b $i key.enc

        # look for words
        WORDS=$(cat key.enc.dec | strings -n 7)
        if [ -n "$WORDS" ]; then
                # report words
                echo "At length $i: '$WORDS'"
Which the spits out the needed key:
At length 8: 'infinitybitfromsupercipher'
(8, as it turns out, is the default key length when not specifying -b.)
500: Windows offset hell
Based on the program being named 'foo.exe', we correctly assumed this was a Windows memory image. Various tools can carve up such a dump, and we set out to use them. The basic process here is to identify the Windows version, find the process, find its virtual/physical address maps, find the physical offset to the string, and then calculate the virtual location.
First, we found out what Windows version the target system was running (or, in later steps, we could have just run all the ptfinder tool versions until one worked). Carvey's told us it was Windows 2000:
$ perl forensics500-88b33eb4b8a2b9a49a632394ba746088.dd
ostest - parse dd-style RAM Dump to determine OS (v.0.1_20060914)
        Idle check   : 2000
        System check : 2000

OS guess based on System and Idle checks:
Next, ptfinder for Windows 2000 ( tells us that foo.exe's DirectoryTableBase is 0x00b6c000 (reported as PDB in ptfinder's output):
$ perl ./ forensics500-88b33eb4b8a2b9a49a632394ba746088.dd
No.  Type PID    TID    Time created        Time exited         Offset     PDB        Remarks
---- ---- ------ ------ ------------------- ------------------- ---------- ---------- ----------------
  14 Proc    728        2007-05-10 01:23:24                     0x00167860 0x00b6c000 foo.exe
We now use that address with memdump to dump out foo.exe's memory and map which results in two files: and 0xb6c000.mem. is a mapping of the physical offsets to their virtual addresses:
$ perl ./ forensics500-88b33eb4b8a2b9a49a632394ba746088.dd 0x00b6c000
$ cat
 virt. addr.  file offset     size
------------ ------------ --------
     0x10000            0   0x1000
     0x20000       0x1000   0x1000
    0x12e000       0x2000   0x1000
  0xc04ff000      0xc1000   0x1000
  0xc0502000      0xc2000   0x1000
Finally, we locate the string we're looking for:
$ strings --radix=x 0xb6c000.mem | grep "forensics challenge"
  1f034 forensics challenge
  7c034 forensics challenge
A quick look into shows the file offset of 0x1f000 is 0x407000:
$ grep 0x1f000
    0x407000      0x1f000   0x1000
This means our virtual address would be 0x407034 or 0x00407034 per Kenshoto's notation requirement. (0x1f034 - 0x1f000 + 0x407000 == 0x00407034)

ctf 2007 quals