plaidCTF 2014 - zfs (for400)

For PlaidCTF2014, Eindbazen and fail0verflow joined forces as 0xffa, the Final Fail Alliance.
Don't miss out on other write-ups at Eindbazen's site!
zfs
Forensics (400 pts)
-------------------
The Plague is using state of the art systems for storing his data. 
Our operatives managed to steal a drive from one of his servers, 
but it seems like our haste may have led to some uber-corruption. 
Can you get the data off the drive to track down The Plague?

Sure we can. But where do we start?

Step 0: wget

The challenge description mentions “zfs”, so let’s start downloading the ZFS specs.

$ wget https://maczfs.googlecode.com/files/ZFSOnDiskFormat.pdf &

Meanwhile…

Step 1: strings

Never underestimate my script kiddie abilities:

$ strings -n10 disk

Closely together, we find

  1. “You must dig deeper, this is not the key.” ASCII art
  2. “not_the_key”
  3. “key.xor_encrypted”

Maybe (2) is the file name for (1)? Looks like we need the data for (3) then, and a to-be-found second half.

I should really read the specs, but the download hasn’t finished yet.

Step 2: xxd disk | less

Oh, the directory structure is trailing the payload. Let’s see what else we can find immediately before another directory structure:

41b000  03 00 00 00 00 00 00 80  69 0c 22 00 00 00 00 00  ........i.".....
41b010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41b020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41b030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41b040  08 00 00 00 00 00 00 80  00 00 00 00 00 00 6e 6f  ..............no
41b050  74 5f 74 68 65 5f 6b 65  79 00 00 00 00 00 00 00  t_the_key.......
41b060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41b070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41b080  09 00 00 00 00 00 00 80  00 00 00 00 00 00 78 6f  ..............xo
41b090  72 5f 6b 65 79 00 00 00  00 00 00 00 00 00 00 00  r_key...........
41b0a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41b0b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41b0c0  0a 00 00 00 00 00 00 80  00 00 00 00 00 00 6b 65  ..............ke
41b0d0  79 2e 78 6f 72 5f 65 6e  63 72 79 70 74 65 64 00  y.xor_encrypted.
41b0e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41b0f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41b100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41b110  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41b120  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41b130  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................

At 0x41b000, we see “not_the_key”, “xor_key”, “key.xor_encrypted”. At 0x41ae00 there is a mysterious 0x200 high entropy block.

[...]
41ade0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41adf0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
41ae00  7d 8e 98 9a bd 2f b0 95  d8 22 5a 03 5f 26 1a 02  }..../..."Z._&..
41ae10  75 01 65 02 ad 54 ea 2c  28 c1 4d 7e 67 b2 d5 fe  u.e..T.,(.M~g...
41ae20  84 0c 53 03 2d 29 28 a7  44 96 3d 39 c1 8d 90 18  ..S.-)(.D.=9....
[...]
41afc0  99 c4 7f d5 fa b8 7d d8  fe 71 2b d8 27 3b 05 9c  ......}..q+.';..
41afd0  69 6c 78 73 b6 86 a6 88  52 56 8c a0 22 c9 20 51  ilxs....RV..". Q
41afe0  54 90 5d 1d ed 4a 50 5e  a7 ac 89 8b 09 38 f6 04  T.]..JP^.....8..
41aff0  3f 7b 56 cd 27 9d be b7  34 84 0e 2b 98 20 8a 2a  ?{V.'...4..+. .*

“xor_key” and “key.xor_encrypted” sounds like one-time pad.

I wonder if I really want to learn about the ZFS disk structure, or just xor that mysterious block over the whole file.

Step 3: omg python

import sys

i = open(sys.argv[1], "rb")
i.seek(0x41ae00)   # the mysterious block...
x = i.read(0x200)  # 0x200 bytes in size
i.seek(0)
o = open(sys.argv[2], "wb")

def xor(a, b):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(a, b))

while True:
    b = i.read(len(x))
    if not len(b):
    break
    o.write(xor(b, x))

Naive? Only one way to figure it out:

$ xor.py disk disk.out

Step 4: strings -n20 disk.out

* _________________________
< ZFS_daTa_1s_s4f35t_d4t4 >
 -------------------------
  \                    /' )
   \                 /'   (                          ,
   \             __/'     )                        .' `;
    \    _.-~~~~'          ``---..__             .'   ;
    _.--'  b)                       ``--...____.'   .'
   (     _.      )).      `-._                     <
    `\|\|\|\|)-.....___.-     `-.         __...--'-.'.
      `---......____...---`.___.'----.....'         `.;

Uh yeah. I totally forgot about the download.

Step 5: rm ZFSOnDiskFormat.pdf

Good, we never needed that.