Sunday, August 18, 2013

UFO CTF 2013 - Broken brokoli (forensics 100)

This is a task from UFO CTF 2013, which was a sweet mixture of file format stegano, forensics and decoding weird alphabets (though that's probably not a legitimate CTF category).

You were given a single ZIP file (7117 bytes), which contained a single file called flag.rar (3402 bytes). The RAR file on the other hand contained a flag.bmp (180KB), but it was password protected (encrypted) so you couldn't extract it at this point.

Before I go any further, this is a good place to advertise the APPNOTE.TXT - .ZIP File Format Specification.

There are a couple of options where the password could be hidden in a ZIP archive - an extra field, a comment field, space between archived file and the ZIP headers, etc - and in the end it turned out that there is one more ZIP local file header (PK\3\4) in the ZIP archive, meaning that there is another archived file, which is not listed in the central directory. The local file header of this hidden file begins at offset D75h.

Parts of the header were wiped (zeroed), but still some information could be recovered:

50 4B 03 04 14 00 00 00 08 00 B8 63 D4 42 41 B2 68 F4 7F 0D 
00 00 86 34 03 00 07 00 00 00 00 00 00 00 00 00 00 ED DD 3B

The green part is, of course, the magic of the local file header. The yellow if the compression method - 8 means DEFLATE. Furthermore both the compressed size (pink) and the uncompressed size (cyan) were available: D7Fh bytes compressed would get decompressed into 33486h (210054 dec) bytes. Unfortunately the file name has been removed, but it wasn't really needed for anything.

There are several ways to extract this file. One is to add a zlib header in front of the DEFLATE data and just extract it using e.g. Python's zlib. Another would be fixing the local file header and placing it, and the data, in an external file, and then using Java stream ZIP decompression (which totally ignores the central directory and just looks for consequent local file headers from the beginning of the stream). Yet another would be to add a central directory record to describe this file and, again, fix the local file header. For an unknown reason I went for the first solution.

The question of "what exact values to choose for zlib header" can be answered by trying every possible zlib header - it turns out there aren't that many posibilities. The following Python code successfully extracted the hidden file:

import zlib

OFFSET = 0xd9a
SIZE   = 0xd7f
DSIZE  = 0x33486
d = open("brokenarchive.zip", "rb").read()[OFFSET:OFFSET+SIZE]
 
for m in [ "\x78\x01", "\x78\x9C", "\x78\xDA" ]:
  try:
    o = zlib.decompressobj().decompress(m + d, DSIZE)
    open("out.%.2x%.2x" % (ord(m[0]), ord(m[1])), "wb").write(o)
  except:
    pass

The output file turned out to be a BMP image:

The font is of course Wingdings, and after decoding you would get: #Ed&3m@cd0c! - the password to the flag.rar file extracted at the beginning.

As one could predict, the RAR file contained yet another BMP image with more Wingdings:

After decoding you would get flag is helloearthlings!!!!!!!!!, and a 100 points for your team :)

To sum up, a quick fun task if you know the ZIP format... and fluently read/write Wingdings ;>

No comments:

Post a Comment