social



BITSCTF 2017 - Woodstock 1/2 writeup

In this double challenge, we're provided with a pcap of some sort of TCP communication between two hosts. Opening the first TCP stream (of three), we see this:

TCP stream 0

...well that was fast. Still, it's just the first flag. The second one is got to be buried a bit deeper.

So this is a capture of some Direct Connect traffic. This particular stream contains a conversation between two users who both seem to have the flag on their shares. In the other two streams, we see some requests for the shares' file lists but no actual files being transmitted:

TCP stream 1 TCP stream 2

Well, at least we can see what's on those shares. Let's use binwalk to extract the bzip2 parts:

tr@karabut.com:~/work/ctf/bitsctf/ws$ binwalk -e ws1_2.pcapng 
...

tr@karabut.com:~/work/ctf/bitsctf/ws$ find _ws1_2.pcapng.extracted/ | xargs grep fl3g\.txt
grep: _ws1_2.pcapng.extracted/: Is a directory
Binary file _ws1_2.pcapng.extracted/8EF.zlib matches
_ws1_2.pcapng.extracted/2C45:   <File Name="fl3g.txt" Size="14" TTH="CA4CMF34SHRUQIBG6MNRDAI5BVT7HQQRTGC7TBA"/>
Binary file _ws1_2.pcapng.extracted/A63.zlib matches
Binary file _ws1_2.pcapng.extracted/B9C.zlib matches

So the fl3g.txt file is only 14 bytes long, and from the description of the challenge we know that the flag's format should be BITSCTF{[a-z0-9_]}. It leaves only 5 symbols unkown, and we know the file's TTH hash, so we probably can bruteforce our way through...

...That is, we could if we also had some additional information from the admin. For whatever reason, the file ended with a newline, so the bruteforced string should have contained just 4 symbols between the curly braces (and a newline of course). I don't think this detail ever appeared on the CTF website, and I didn't try to communicate with the admin directly in the course of the CTF, but let's just pretend we know this already and go forward.

So, TTH is a hash tree algorithm with Tiger hash being its basic block. Problem is, I couldn't find a single working (that is, giving equivalent to DC++'s results) Python TTH implementation -- not many in other languages as well -- and the existing Tiger hash implementations seem to be wildly inconsistent, which got summed up nicely in this StackOverflow post. Luckily, at least we have a tool called RHash out there able to calculate TTH hashes; let's test it and see if it does correspond to the DC++ implementation:

tr@karabut.com:~/work/ctf/bitsctf/ws$ printf "" | rhash --tth -
lwpnacqdbzryxw3vhjvcj64qbznghohhhzwclnq  (stdin)

Looks like it does, according to the post mentioned above. All that's left is get our bruteforcer up and running. We're going to split the task between 8 processes here:

import itertools
import string
from multiprocessing import Process
import subprocess

alphabet = string.lowercase + string.digits + "_"

# the fl3g.txt TTH 
check = "CA4CMF34SHRUQIBG6MNRDAI5BVT7HQQRTGC7TBA".lower()

def force(t):
    for start in alphabet[t::8]:
        for word in itertools.product(alphabet, repeat=3):
            flag = "BITSCTF{" + start + ''.join(word) + "}\n"
            pipe = subprocess.Popen(["rhash", "--tth", "-"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
            pipe.stdin.write(flag)
            if pipe.communicate()[0].split(" ")[0] == check:
                print flag

        print "%s: finished" % 

processes = []

for t in range(8):
    p = Process(target=force, args=(t,))
    p.start()
    processes.append(p)

for p in processes:
    p.join()

And in several minutes we get:

tr@karabut.com:~/work/ctf/bitsctf/ws$ python crack.py 
h: finished
d: finished
f: finished
g: finished
e: finished
c: finished
a: finished
b: finished
l: finished
p: finished
o: finished
m: finished
k: finished
i: finished
n: finished
j: finished
BITSCTF{sw3g}
...

I've also recorded a screencast with (most of) the gory details (Russian only, sorry), which you can view right here: