PlaidCTF 2012 – Practical Packets 200 – Torrents Writeup
"It turns out that robots, like humans, are cheap and do not like paying for their movies and music. We were able to intercept some torrent downloads but are unsure what the file being downloaded was. Can you figure it out?"
We get a pcap file with the P2P part of a BitTorrent exchange between 2 peers.
Hopefully Wireshark has a fairly complete BitTorrent dissector which we will use to get the "pieces" of data.
According to the protocol specification, data is transferred in pieces that have an index and an offset. We need to extract each pieces with its index and offset and a Python script will reorder them.
import sys
import struct
if len(sys.argv) ==2:
print "Parsing "+str(sys.argv[1])
else:
print "Usage: python "+sys.argv[0]+" file.pcap"
exit(10)
pcap=file(sys.argv[1],"r")
out=file(sys.argv[1]+".hex","w")
data = {}
for p in pcap:
a = p.split("|")
index = int(a[0], 16)
offset = int(a[1], 16)
print 'Index', index, 'Offset', offset
order = "%08x" % index + "_" + "%08x" % offset
data[order] = a[2].split(":")
for key in sorted(data.iterkeys()):
print key + "\n"
for b in data[key]:
out.write(chr(int(b,16)))
pcap.close()
out.close()
$ file torrents.dump.hex
torrents.dump.hex: bzip2 compressed data, block size = 900k
$ mkdir key ; cd key ; tar xvfj ../torrents.dump.hex
key.mp3
key.txt
$ cat key.txt
t0renz0_v0n_m4tt3rh0rn
The key is: t0renz0_v0n_m4tt3rh0rn
PS: notice the useless MP3 file just to make the archive and so the bittorrent transfer bigger
Insomni’hack 2012 Network PCAP Write-ups
In this challenge we 2 files: 1 PCAP, 1 Python
The PCAP file contains a dialog between a client and server that goes like this:
> = client to server
< = server to client
> 8e67bb26b358e2ed20fe552ed6fb832f397a507d:3daf723376f823eceeb314c8fa60e47b1ba23633
< 5f367ff47fff772986cca54219fa167175353dc7
> 78be5fe51f264a4067463bad57022348
< 3290452b9a9f6d18523347dd1daa54a1e09195a7
> 94817a6b1d833e1ffb4fcb2aa7dd14143dc5759e>_<8b060ba4b309e73abd079d8f0128056c07b78cad
< Bienvenue, superuser. Le lieu du rendez-vous n'est pas encore defini.
The original Python file goes like this:
import SocketServer
import socket, sys, hashlib, random
if __name__ == "__main__":
HOST, PORT = sys.argv[1], 9999
USER = raw_input("Utilisateur : ").strip()
PASS = raw_input("Mot de passe : ").strip()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
try:
data = hashlib.sha1(USER).hexdigest()+":"+hashlib.sha1(hashlib.md5(hashlib.sha1(PASS).hexdigest()+hashlib.sha1(PASS).hexdigest()+hashlib.md5(USER).hexdigest()).hexdigest()).hexdigest()
sock.sendall(data + "\n")
challenge = sock.recv(41).strip()
sock.sendall(hashlib.md5(hashlib.sha1(PASS).hexdigest()+"SuperSalt"+hashlib.md5(USER+challenge).hexdigest()).hexdigest() + "\n")
challenge = sock.recv(41).strip()
sock.sendall(hashlib.sha1(PASS).hexdigest()+">_<"+hashlib.sha1(challenge+hashlib.md5(USER).hexdigest()).hexdigest() + "\n")
print sock.recv(1024)
except:
print sock.recv(1024)
sock.close()
We need to get updated information on the meeting location so we need to do a replay attack on this service.
We are going to map the network stream to the python code:
Handshake
> 8e67bb26b358e2ed20fe552ed6fb832f397a507d:3daf723376f823eceeb314c8fa60e47b1ba23633
sha1(user) : sha1(sha1(pass)+sha1(pass)+md5(user))
Challenge 1:
< 5f367ff47fff772986cca54219fa167175353dc7
> 78be5fe51f264a4067463bad57022348
md5(sha1(pass)+"SuperSalt"+md5(user+challenge))
Challenge 2:
< 3290452b9a9f6d18523347dd1daa54a1e09195a7
> 94817a6b1d833e1ffb4fcb2aa7dd14143dc5759e>_<8b060ba4b309e73abd079d8f0128056c07b78cad
sha1(pass)+">_<"+sha1(challenge+md5(user))
We can deduce:
sha1(user) = 8e67bb26b358e2ed20fe552ed6fb832f397a507d
sha1(pass) = 94817a6b1d833e1ffb4fcb2aa7dd14143dc5759e
Google bruteforcing sha1(user) gives "superuser", which match the plain text welcome message "Bienvenue, superuser". We can also Google bruteforce the challenges but that's useless.
user = "superuser"
challenge1 = "4283399"
challenge2 = "3593819"
So we know USER, now we need PASS. But we only have sha1(pass) and it cannot be Google bruteforced.
But do we really need to know PASS ? If you look closely in the code above, you will see we only use the sha1 of PASS, never anything else. Since we know USER, we can carry a replay attack. We remove all reference to PASS and put directly the sha1(pass) string.
import SocketServer
import socket, sys, hashlib, random
if __name__ == "__main__":
HOST, PORT = "10.13.37.12", 9999
USER = "superuser"
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
try:
sock.sendall("8e67bb26b358e2ed20fe552ed6fb832f397a507d:3daf723376f823eceeb314c8fa60e47b1ba23633" + "\n")
challenge = sock.recv(41).strip()
sock.sendall(hashlib.md5("94817a6b1d833e1ffb4fcb2aa7dd14143dc5759eSuperSalt"+hashlib.md5(USER+challenge).hexdigest()).hexdigest() + "\n")
challenge = sock.recv(41).strip()
sock.sendall("94817a6b1d833e1ffb4fcb2aa7dd14143dc5759e>_<"+hashlib.sha1(challenge+hashlib.md5(USER).hexdigest()).hexdigest() + "\n")
print sock.recv(1024)
except:
print sock.recv(1024)
sock.close()
All done.
