May 31, 2016 - TJCTF 2016: Torrent

Author: aagallag

Publish Date: 2016-05-31

Category: misc Points: 90 Description:

Help, someone’s sharing flags.

Investigate

For those of you who may be living under a rock and have never heard of BitTorrent, I recommend skimming through the BitTorrent and Torrent file wiki pages. But to give a brief overview, torrent files don’t actually contain the desired file. But rather, they provide information to a Torrent client about how to download the desired file from peers on the internet. But how does your torrent client find suitable peers? Traditionally, the .torrent file contains the address to a torrent tracker that keeps track of who is downloading the file and who is currently sharing the file.

So I started by loading up the file into my torrent application to check and see if it will download anything.

Hmm… Doesn’t seem like the torrent is downloading anything, let’s poke around the torrent properties to see why.

Let’s start by checking out the trackers.

Oh great, there are no trackers. How can the file be downloaded without any trackers? It turns out there have been new protocols applied ontop of torrents to allow peer discovery without trackers, checkout the DHT and PEX wiki pages for more information on these protocols.

Perhaps, to solve this challenge, we have to find the tracker or peer hosting the file. SPOILER ALERT: this was not the correct approach.

By default, I like to keep the peer discovery protocols disabled due to privacy concerns (for those concerned with privacy, you probably shouldn’t be using BitTorrent in the first place). So for the duration of this challenge, I went ahead and enabled them.

Hmm… Still, my client doesn’t download the file. So I checked out the peerlist. After running nmap on some of the peers, I noticed one of them was hosting a torrent tracker. So I tried to set that peer as my tracker, but still, the file didn’t download. After noticing all these peers were hosting different services, and running different OS’s, I realized these peers were unlikely to be hosting the flag, but instead, I’m pretty sure they were other participants in the CTF.

Oops… before I get kicked out of the CTF for hacking other contestents, I decided to take a step back and stop attempting to download the file altogether.

Let’s see what information the *.torrent file reveals about the file.

Hmm, looks like *.torrent file contains the SHA1 hash of the file. This is required by the Torrent client so that once it has finished downloading the file, it can verify the integrity by generating a hash of the downloaded file and comparing it with the expected hash.

What if I try and work backwards from the hash? I could pass a bunch of random data into a hash function until it computes to the same hash. Nope, that would take too much time for any unique string larger than a few bytes.

Good thing I know a little bit about the BitTorrent protocol. My client doesn’t show it, but in addition to the entire file, the sha1-hash for each piece is also stored in the *.torrent file. That way, in the event that your client detects that the downloaded file is corrupt, it doesn’t have to throw out everything and start from scratch. Instead, your client checks the hash of each piece and re-downloads only the piece(s) that failed.

Using this knowledge, I realized that if the pieces are small enough, I could resolve the hash back to the piece a lot quicker than attempting to crack the hash for the entire file.

However, my Torrent client isn’t showing me enough information, I need to find a way to extract more details.

I found this cool Python project that parses torrent files and generates an HTML report, called bittorrent-parser.

To setup and run the tool…

$ git clone https://github.com/Resistor52/bittorrent-parser.git
$ python ./bittorrent-parser/parse-torrent.py flag_d542574c3e963792fe07321fd262fe28e6f5cf0ea126efe01148dd6890b63a4d.torrent 

After runnning the tool, I load up output.html into a web browser.

Wow, I wish this had been my first step, this parser is a lot more precise and verbose than your average torrent client.

I notice that the announce url (torrent tracker) was set to 127.0.0.1, that explains why the file could not be downloaded. Next I see that the exact size of the flag file is only 28 bytes with 14 pieces. Each piece is only 2bytes long! That’s great, cracking the hash for a string whose length we know to only be 2 is WAY quicker than attempting to crack the hash for the entire file.

So now I have to simply figure out which two bytes results in each SHA1-hash. Once I have each 2byte piece, I simply put them together to recreate the original file. Next section will show how I went about doing that.

Code

Since I know the file is only 28 bytes long, I predict that this file doesn’t contain any binary data. So I make the assumption that this is a plaintext ASCII encoded file. So to speedup the cracking process, I only search for values in the ASCII range of 0->126. There are additional modifications that I could have made to ensure that the cracking process would be fast, but this implementation was sufficient for solving the challenge.

#!/usr/bin/env python
import sha

# hashes for each piece of the torrent file
PIECE_HASHES = [
'546b05909706652891a87f7bfe385ae147f61f91',
'589e942e00a7dd64a273deb5041c7ce469f2bad7',
'b411d7823a3c4ee3773cafca1e36b8cfd26655ba',
'f437cb078acc7c6d79873462334a355eddeb9459',
'b504c843b2ef4c55c673be0b1daf3b12c5cf2fe8',
'0699989c219e1d7b336851c646e88a651859d081',
'd8273e2f4a7c0a59554544c6605cdd8b117848aa',
'f2daf7bf8c0100e8421f6a72dd8064cad674813a',
'd0cf1ef21f0ce65584e2453a3fb427f6591adca8',
'1116ef128bb637e2d69e9666bfe6d8a4ef9d2c13',
'408c28a2da80ef8bc57e580ac9ffc7f69b2a0e0e',
'f9fc27b9374ad1e3bf34fdbcec3a4fd632427fed',
'c387c982a132d05cbd5f88840aef2c8157740049',
'b3127725f678ca5b1038b1df45a06f2ff4e1f544',
]


def crack(shahash):
    for x in range(127):
        for y in range(127):
            attempt = chr(x) + chr(y)
            res = sha.new(attempt).hexdigest()
            if res == shahash:
                return attempt

    raise Exception('Failed to crack the hash: ' + shahash)


def main():
    flag = ''
    for i in PIECE_HASHES:
        flag += crack(i)
    print(flag)


if __name__ == "__main__":
    main()

Flag

Run the script to get the flag.

$ ./torrent_hash_cracker.py 
tjctf{pls_2_n0t_fl4g_share}

May 20, 2016 - TU CTF 2016: The Nack

Author: aagallag

Publish Date: 2016-05-20

Category: Forensic Points: 100 Description:

Attached file

Investigate

I load up the capture file into Wireshark and start by checking for HTTP objects with…

File -> Export Objects -> HTTP...

Unfortunately, there aren’t any HTTP objects found.

After checking for HTTP objects, my next step when working with packet captures is usually to inspect the individual TCP streams.

Right Click Packet -> Follow -> TCP Stream

TCP Stream 0

Looks like the traffic may contain a GIF file, but why does it start with ‘GOAT’?

TCP Stream 1

Wireshark lets you quickly switch between the different streams by increasing the Stream counter at the bottom right of the pop-up window. Scrolling through the first couple TCP streams reveals a pattern.

TCP Stream 15

It looks like all of the streams start with the same 4 bytes, ‘GOAT’ followed by the byte 0x01.

Code

Now that I know the pattern, I threw together a simple Python script that looks for all occurances of ‘GOAT’ followed by 0x01 and then writes the proceeding 4 bytes to a file.

#!/usr/bin/env python

# read file contents into memory
f = open('ce6e1a612a1da91648306ace0cf7151e6531abc9.pcapng', 'rb')
content = f.read()
f.close()

#split on 'GOAT' + x01 byte (skipping the front part of file before GOAT starts)
goats = content.split('GOAT\x01')[1:]

#write the TCP data to a new file
f = open('goats.data', 'wb')
for i in goats:
	#data is in first 4 bytes, 5th byte should be null
	assert(i[4] == '\x00')
	data = i[:4]
	f.write(data)

f.close()

print('Goat data extracted...')

Next I wanted to confirm that my initial suspicion was correct about this being a .gif file.

$ file goats.data
goats.data: GIF image data, version 89a, 590 x 225

Great, let’s see what the image looks like…

$ mv goats.data goats.gif

goats.gif

I don’t see a flag there… Maybe it’s hiding in a single frame of the .gif and the flag is flashing too quickly for me to see it. So let’s extract each frame of the gif into seperate, non-moving images.

$ convert goats.gif out%05d.gif
$ ls out000*.gif
out00000.gif  out00005.gif  out00010.gif  out00015.gif  out00020.gif  out00025.gif
out00001.gif  out00006.gif  out00011.gif  out00016.gif  out00021.gif  out00026.gif
out00002.gif  out00007.gif  out00012.gif  out00017.gif  out00022.gif  out00027.gif
out00003.gif  out00008.gif  out00013.gif  out00018.gif  out00023.gif  out00028.gif
out00004.gif  out00009.gif  out00014.gif  out00019.gif  out00024.gif

I check each frame of the gif, the 17th frame(out00016.gif) reveals something interesting…

out00016.gif

Flag

TUCTF{this_transport_layer_is_a_syn}

May 10, 2016 - sCTF 2016 Q1 : The Ducks

Author: dade

Publish Date: 2016-05-10

Category: Web Points: 30 Description:

The ducks and I have an unfinished score to settle.

Investigation

The ducks is a seemingly boring site, only prompting us for a password. But they are kind enough to provide us the source.php document, which shows us the entire source code for the page.

<?php
include("secret.php");
?>
<html>
    <head>
        <title>The Ducks</title>
        <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
    </head>
    <body>
        <div class="container">
            <div class="jumbotron">
                <center>
                    <h1>The Ducks</h1>
                    <?php if ($_SERVER["REQUEST_METHOD"] == "POST") { ?>
                        <?php
                        extract($_POST);
                        if ($pass == $thepassword_123) { ?>
                            <div class="alert alert-success">
                                <code><?php echo $theflag; ?></code>
                            </div>
                        <?php } ?>
                    <?php } ?>
                    <form action="." method="POST">
                        <div class="row">
                            <div class="col-md-6 col-md-offset-3">
                                <div class="row">
                                    <div class="col-md-9">
                                        <input type="password" class="form-control" name="pass" placeholder="Password" />
                                    </div>
                                    <div class="col-md-3">
                                        <input type="submit" class="btn btn-primary" value="Submit" />
                                    </div>
                                </div>
                            </div>
                        </div>
                    </form>
                </center>
            </div>
            <p>
                <center>
                    source at <a href="source.php" target="_blank">/source.php</a>
                </center>
            </p>
        </div>
    </body>
</html>

In source.php we see an if statement that runs if our HTTP verb was POST. Then it extracts the variables from $_POST. Then it compares our $pass variable with the value of $thepassword_123, and if they are equal it will echo us out the flag! We just need to make those two values equal. Luckily, it appears to indiscriminately extract variables from $_POST, so we whip out a tool (Fiddler) to manually send a POST request.

Solution

Instead of just submitting the $pass variable in the body of our POST, let’s also submit $thepassword_123. So our POST looks something like

	POST http://ducks.sctf.michaelz.xyz HTTP/1.1
	Host: http://ducks.sctf.michaelz.xyz
	Content-Type: application/x-www-form-urlencoded;

	pass=Easyas123&thepassword_123=Easyas123

Now we can see what we got back from the server, and under the The Ducks header, we have an alert with the flag in it!

Flag

sctf{maybe_i_shouldn't_have_extracted_everything_huh}