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