Skip to content Skip to navigation

MITM II: Electric Boogaloo (Crypto 200)

Category: 

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!

Attachments: