## problem

A template of man-in-the-middle attack is given. Fill blanks.

## solution

Attack to the ECDH. The point is: use CBC mode for the AES, and the initialization vector is decided by comparing to the flag format SECCON{...}.

## note

konjo solves almost all. I’ve only found about CBC mode.

## implementation

exploit.py:

#!/usr/bin/python3
import mitm
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = "mitm.pwn.seccon.jp"
s.connect((host, 8000))

def fixed(msg):
data = s.recv(len(msg))
assert data

fixed(b"[dev0 to dev1]:")
data = s.recv(120)
fixed(b"\n[dev1 to dev0]: OK\n")

fixed(b"[dev1 to dev0]:")
data = s.recv(120)
fixed(b"\n[dev0 to dev1]: OK\n")

fixed(b"[KBKDF: SHA256, Encryption: AES]\n")
mitm.derive_keys()  ## derive keys

fixed(b"[dev0 to dev1]:")
data = s.recv(256)

ct = mitm.mitm(data)  ## todo
s.send(ct)

fixed(b"\n[dev1 to dev0]: OK\n")
fixed(b"[dev1 to dev0]:")

data = s.recv(256)
mitm.decrypt(data)  ## todo


mitm.py:

# Python Version: 3.x
# https://pypi.python.org/pypi/cryptography
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.asymmetric import ec

endian = 'big'

my_q = ec.generate_private_key(ec.SECP384R1(), default_backend())
peer_q = [ None, None ]
# recv
x = int.from_bytes(data[24 :][: 48], endian)
y = int.from_bytes(data[24 + 48 :], endian)
peer_q[i] = ec.EllipticCurvePublicNumbers(x, y, ec.SECP384R1()).public_key(default_backend())
# send
my_q_x = my_q.public_key().public_numbers().x
my_q_y = my_q.public_key().public_numbers().y
return header + my_q_x.to_bytes(48, endian) + my_q_y.to_bytes(48, endian)

def sha256(data):
digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(data)
return digest.finalize()
shared_key = [ None, None ]
def derive_keys():
for i in range(2):
digest = sha256(my_q.exchange(ec.ECDH(), peer_q[i]))
shared_key[i] = Cipher(algorithms.AES(digest), modes.CBC(b'0000000000000000'), default_backend())
def run_crypto(cryptor, data):
buf = bytearray(4098)
len_crypted = cryptor.update_into(data, buf)
return bytes(buf[: len_crypted]) + cryptor.finalize()

def mitm(data):
data = run_crypto(shared_key[0].decryptor(), data)
print(data)
data = run_crypto(shared_key[1].encryptor(), data)
return data

def decrypt(data):
data = run_crypto(shared_key[1].decryptor(), data)
print(data)