Task:
Chisa and Arisu are trying to tell each other two halves of a very important secret! They think they're safe, because they know how cryptography works---but can you learn their terrible, terrible secret? They're available as services at 54.186.6.201:12346 and 54.186.6.201:12345 respectively.
http://bostonkeyparty.net/challenges/mitm2-632e4ecc332baba0943a0c6471dec2c6.tar.bz2
Solution:
This was a nice task. Classic Man-in-the-Middle attack and nothing more. First, i downloaded source files (They are attached to this article - mitm2.zip). There were four files in the archive: arisu.py, chisa.py, mitmlib.py and curve25519.py. As you might guess, curve25519.py contains realization of the elliptic cryptography. So i decided to open arisu.py and chisa.py first, in hope, that they would contain source code of servers running in given ip-addresses. And they indeed contained it.
Servers start:
KEY = ""
CHECK = ""
if __name__ == "__main__":
HOST = sys.argv[1]
PORT = int(sys.argv[2])
KEY = open('chisa.txt', 'r').read()[:-1]
CHECK = open('check.txt', 'r').read()[:-1]
server = ThreadedServer((HOST, PORT), ServerHandler)
server.allow_reuse_address = True
server.serve_forever()
It's same for Arisu - only another key file.
And servers work:
Arisu:
def handle(self):
#if not self.captcha():
#return self.fail("You're a robot!")
self.request.sendall("アリスです")
if not (self.request.recv(50) == "千佐だよ"):
return self.fail("You're not Chisa!")
secretshare1, publicshare1 = mitmlib.mkshare()
to_send = str(publicshare1[0]) + "," + str(publicshare1[1])
self.request.sendall(to_send)
got = self.request.recv(2048).split(',')
publicshare2 = tuple([int(got[0]), int(got[1])])
aeskey = mitmlib.mksecret(secretshare1, publicshare2)
slices = zip(CHECK[0::2], CHECK[1::2])
for a, b in slices:
self.request.sendall(mitmlib.encrypt(aeskey, a))
if b != mitmlib.decrypt(aeskey, self.request.recv(400)):
self.fail("That's wrong!")
self.request.sendall(mitmlib.encrypt(aeskey, "FLAG PART ONE: {" + KEY + "}\n"))
print mitmlib.decrypt(aeskey, self.request.recv(400))
self.request.sendall(mitmlib.encrypt(aeskey, "じゃ!"))
self.request.recv(100)
self.request.close()Chisa:
def handle(self):
#if not self.captcha():
#return self.fail("You're a robot!")
if not (self.request.recv(50) == "アリスです"):
return self.fail("You're not Alice!")
self.request.sendall("千佐だよ")
secretshare2, publicshare2 = mitmlib.mkshare()
got = self.request.recv(2048).split(',')
publicshare1 = tuple([int(got[0]), int(got[1])])
to_send = str(publicshare2[0]) + "," + str(publicshare2[1])
self.request.sendall(to_send)
aeskey = mitmlib.mksecret(secretshare2, publicshare1)
slices = zip(CHECK[0::2], CHECK[1::2])
for a, b in slices:
if a != mitmlib.decrypt(aeskey, self.request.recv(400)):
self.fail("That's wrong!")
self.request.sendall(mitmlib.encrypt(aeskey, b))
print mitmlib.decrypt(aeskey, self.request.recv(400))
self.request.sendall(mitmlib.encrypt(aeskey, "FLAG PART TWO: {" + KEY + "}\n"))
self.request.recv(100)
self.request.sendall(mitmlib.encrypt(aeskey, "じゃ!"))
self.request.close()Thoose parts contain algorithm of conversation. At the beginning Arisu and Chisa send each other something like "hello":
Arisu:
self.request.sendall("アリスです")
if not (self.request.recv(50) == "千佐だよ"):
return self.fail("You're not Chisa!")Chisa:
if not (self.request.recv(50) == "アリスです"):
return self.fail("You're not Alice!")
self.request.sendall("千佐だよ")You might notice, that Arisu starts the conversation and Chisa only replies.
Next step is a public key exchange:
Arisu:
secretshare1, publicshare1 = mitmlib.mkshare()
to_send = str(publicshare1[0]) + "," + str(publicshare1[1])
self.request.sendall(to_send)
got = self.request.recv(2048).split(',')
publicshare2 = tuple([int(got[0]), int(got[1])])
aeskey = mitmlib.mksecret(secretshare1, publicshare2)Chisa:
secretshare2, publicshare2 = mitmlib.mkshare()
got = self.request.recv(2048).split(',')
publicshare1 = tuple([int(got[0]), int(got[1])])
to_send = str(publicshare2[0]) + "," + str(publicshare2[1])
self.request.sendall(to_send)
aeskey = mitmlib.mksecret(secretshare2, publicshare1)Girls just send a public key to each other and then calculate session key.
And the last part: they send some messages to each other, and the last but one contains a flag:
Arisu:
slices = zip(CHECK[0::2], CHECK[1::2])
for a, b in slices:
self.request.sendall(mitmlib.encrypt(aeskey, a))
if b != mitmlib.decrypt(aeskey, self.request.recv(400)):
self.fail("That's wrong!")
self.request.sendall(mitmlib.encrypt(aeskey, "FLAG PART ONE: {" + KEY + "}\n"))
print mitmlib.decrypt(aeskey, self.request.recv(400))
self.request.sendall(mitmlib.encrypt(aeskey, "じゃ!"))
self.request.recv(100)
self.request.close()Chisa:
slices = zip(CHECK[0::2], CHECK[1::2])
for a, b in slices:
if a != mitmlib.decrypt(aeskey, self.request.recv(400)):
self.fail("That's wrong!")
self.request.sendall(mitmlib.encrypt(aeskey, b))
print mitmlib.decrypt(aeskey, self.request.recv(400))
self.request.sendall(mitmlib.encrypt(aeskey, "FLAG PART TWO: {" + KEY + "}\n"))
self.request.recv(100)
self.request.sendall(mitmlib.encrypt(aeskey, "じゃ!"))
self.request.close()Now i've found everythingi need to interfere in communication between Arisu and Chisa to steal a flag. I opened text editor and wrote python script. First, i generated my own key pair like Arisu and Chisa did:
mySecretKey, myPublicKey = mitmlib.mkshare()
Then i sent hello girls:
Arisu = Sock("54.186.6.201:12345", timeout=30)
Chisa = Sock("54.186.6.201:12346", timeout=30)
hello = Arisu(1024)
print "=== Arisu ---> Chisa ==="
print "Hello: " + hello + "\n"
Chisa.send(hello)
sleep(0.25)
hello = Chisa.recv(1024)
print "=== Arisu <--- Chisa ==="
print "Hello: " + hello + "\n"
Arisu(hello)
sleep(0.25)Next step: send my own public key to Chisa and Arisu instead of their own, save their keys and evaluate secret keys for each connection:
ArisuPubKeyArr = Arisu.recv(1024).split(',')
print "=== Arisu ---> Chisa ==="
print "Try to send: " + str(ArisuPubKeyArr[0]) + "," + str(ArisuPubKeyArr[1])
print "Really sent: " + myPublicKeyStr + "\n"
Chisa.send(myPublicKeyStr)
sleep(0.25)
ChisaPubKeyArr = Chisa.recv(1024).split(',')
print "=== Arisu <--- Chisa ==="
print "Try to send: " + str(ChisaPubKeyArr[0]) + "," + str(ChisaPubKeyArr[1])
print "Really sent: " + myPublicKeyStr + "\n"
Arisu.send(myPublicKeyStr)
sleep(0.25)
ArisuPubKey = tuple([int(ArisuPubKeyArr[0]), int(ArisuPubKeyArr[1])])
ChisaPubKey = tuple([int(ChisaPubKeyArr[0]), int(ChisaPubKeyArr[1])])
ArisuAesKey = mitmlib.mksecret(mySecretKey, ArisuPubKey)
ChisaAesKey = mitmlib.mksecret(mySecretKey, ChisaPubKey)And the last part: i have to recieve messages from Arisu, decrypt them on myArisu secret key, reencrypt them on myChisa secret key and send to Chisa, and i have to do the same thing for Chisa's messages:
try: for i in xrange(0,25,1): some_slice = Arisu.recv(1024) print "=== Arisu ---> Chisa ===" print "Try to send: " + some_slice decrypted_slice = mitmlib.decrypt(ArisuAesKey, some_slice) encrypted_slice = mitmlib.encrypt(ChisaAesKey, decrypted_slice) print "Decrypted: " + decrypted_slice print "Really sent: " + encrypted_slice + "\n" Chisa.send(encrypted_slice) sleep(0.35) some_slice = Chisa.recv(1024) print "=== Arisu <--- Chisa ===" print "Try to send: " + some_slice decrypted_slice = mitmlib.decrypt(ChisaAesKey, some_slice) encrypted_slice = mitmlib.encrypt(ArisuAesKey, decrypted_slice) print "Decrypted: " + decrypted_slice print "Really sent: " + encrypted_slice + "\n" Arisu.send(encrypted_slice) sleep(0.35) except Exception, e: print e
The last but one message for Arisu and Chisa is a part of flag:
=== Arisu ---> Chisa ===
Try to send: cSAt1FxoXq5dtROo6YL9VE/Ln95Gd/l6GxDSmsidTuAkbSANizH6b2Y1RXSwss0Z
Decrypted: FLAG PART ONE: {I dunno, }
Really sent: 4VW4ebDKPVXVDDzoiYjvjf1Q4VtX/pZP63WUrsRfqzRlEOCy0Q0ryOVFACILC0ai
=== Arisu <--- Chisa ===
Try to send: 3RZwKsSLLWMKwLlEr/h8X7xousf6B59UuiU+R+sMwL/uwHI2yY9KoyM9wpL/jsS4U4QlrcPzEiMrK7IMXFs38F8xEKilV6fx0DcnCs6ZvME=
Decrypted: FLAG PART TWO: {go fanwank something!}
Really sent: bqW46ykWazNT+gR9ykJHgAWNVa5HrrDhmzXNjekgpebPB43a+0y3bvFkaAZFCZdMP5pMpgpa/rUDX2gD01w5ItYcDLWQXzbpd4hO/OTr8Zg=Flag: I dunno, go fanwank something!
Sports brands | Air Jordan 1 Retro High OG 'University Blue' — Ietp