ForbiddenBITS CTF 2013 – Poir 150 Write up

We were provided with a pcap capture. The capture has mostly HTTP traffic, which seems to be the transfer of a file named key.7z. The HTTP client, which is Python’s urllib, requests the file in very small chunks, using the “Range: bytes=xx-yy” header.

To make things more complicated, the HTTP client requests the byte ranges without any particular order, and it also requests overlapping ranges. These details make it harder to extract the key.7z file from the pcap capture.


So I applied the following filter in Wireshark, in order to show just the HTTP responses from the server:

http && ip.src ==

Now we have 500 packets displayed in Wireshark. So I exported the packet details to PDML format: File -> Export Packet Dissections -> As XML – PDML (Packet details), setting the “Packet details” combo box to “All expanded”. That will generate an XML file containing all the info for every one of the 500 packets we’ve filtered…except for the body of the HTTP responses :(.

But, fortunately, we can export the body of each HTTP response by using the menu File -> Export Objects -> HTTP -> Save all. This will generate a file for every HTTP response with the content of that response. Since we have 500 HTTP responses, that command will generate 500 files, named key(0).7z, key(1).7z, key(2).7z … key(0).7z corresponds to the first HTTP response, key(1).7z to the second response, and so on.

So I wrote the following Python script, which parses the PDML file and reconstructs the key.7z file:

import xml.dom.minidom as minidom
import sys

def main():
    counter = 0
    contents = {}
    FILE_SIZE = 1354
    file_as_array = range(FILE_SIZE)

    doc = minidom.parse('pdml.xml')
    top_element = doc.documentElement
    packets = top_element.getElementsByTagName('packet')
    print "%d packets in PDML." % len(packets)

    #Traverse each one of the packets
    for packet in packets:
        http = None
        #Grab the packet's HTTP layer
        for protocol in packet.getElementsByTagName("proto"):
            if protocol.getAttribute("name") == "http":
                http = protocol

        if not http:
            print "Could not find HTTP protocol in packet %d!" % counter

        content_range = None
        #Grab the "Content-Range: bytes" header from the HTTP response
        for field in http.getElementsByTagName("field"):
            if field.getAttribute("show").startswith("Content-Range: bytes"):
                content_range = field

        if not content_range:
            print "Could not find Content-Range header in packet %d!" % counter

        filerange = content_range.getAttribute("show")
        start_pos = filerange.find("Content-Range: bytes ")
        if start_pos == -1:
            print "Could not find the 'Content-Range: bytes ' string!"

        filerange = filerange[start_pos + len("Content-Range: bytes "):]

        end_pos = filerange.find('/')
        if end_pos == -1:
            print "Could not find the '-' string!"

        filerange = filerange[:end_pos]

        #Grab the beginning and end offsets from the range
        beginning, end = filerange.split('-')

        #Sanity check: Beginning and end of the range should be ints
            beginning = int(beginning)
            end = int(end)
        except ValueError:
            print "Could not convert one of the strings (%s, %s) to integer!" % (beginning, end)

        #Read the content of the file corresponding to this HTTP response
        filename = "all_packets/key(%d).7z" % counter
        f = open(filename, "rb")
        filedata =

        #Sanity check: the length of the data from the file should be equal to the range
        if len(filedata) != (end - beginning + 1):
            print "ERROR: len(filedata)[%d] != (end-beginning+1)[%d]. , packet %d" % (len(filedata), end-beginning+1, counter)

        #Update the file_as_array, at the corresponding range, with the content of the HTTP response
        file_as_array[beginning:end+1] = list(filedata)
        #Sanity check: the length of file_as_array must always be equal to 1354
        if len(file_as_array) != FILE_SIZE:
            print "len(file_as_array)[%d] != FILE_SIZE, packet %d" % (len(file_as_array), counter)
        counter += 1

    #Just another sanity check...
    if counter != 500:
        print "ERROR: counter = %d, expected = 500" % counter

    #Reconstruct the key.7z file
    print "Writing 7zip file..."
    f = open('key.7z', 'wb')


After running the script, we get the key.7z file. After decompressing it, we have an image file named key.png, which is just a blank image:


I loaded the image into GIMP, opened the Alien Map, and started experimenting with the Red, Green and Blue frequency and phaseshift values. Some letters started to appear in the image. After switching from RGB Color Model to HSL Color Model in the Alien Map everything was more clear:


So this is the PNG image after modifying the Hue/Saturation/Luminance values using the Alien Map in HSL Color Mode:


So the flag for this challenge was: PIoneer_Of_Invisible_Road_By_BuGeun


7 thoughts on “ForbiddenBITS CTF 2013 – Poir 150 Write up

  1. Notice that you could also just use wget on the source location of the key.
    Which I did, but couldn’t play enough on colours. But it seems that if you just use gimp with Colors > Auto > Stretch Contrast you can get the output!

  2. @fser @alterpub: I tried to do that, but the source location gave me a 404, that’s why I had to go the hard way. May be you solved it early and later it key.7z was removed from the hosting server?

  3. Pingback: ForbiddenBITS CTF 2013 | stephnix

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s