# HumanServer
## Source Code
```pythonimport os, random, hashlib, textwrap, jsonfrom Crypto.Cipher import AESfrom Crypto.Util.Padding import pad, unpadfrom Crypto.Util.number import getPrime, long_to_bytes
from fastecdsa.curve import secp256k1from fastecdsa.point import Point
FLAG = b'union{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}'
CURVE = secp256k1ORDER = CURVE.qG = CURVE.G
print(ORDER)
class EllipticCurveKeyExchange():    def __init__(self):        self.private = random.randint(0,ORDER)        self.public = self.get_public_key()        self.recieved = None        self.nonce = None        self.key = None
    def get_public_key(self):        A = G * self.private        return A
    def send_public(self):        return print(json.dumps({"Px" : self.public.x, "Py" : self.public.y}))
    def receive_public(self, data):        """        Remember to include the nonce for ultra-secure key exchange!        """        Px = int(data["Px"])        Py = int(data["Py"])        self.recieved = Point(Px, Py, curve=secp256k1)        self.nonce = int(data['nonce'])
    def get_shared_secret(self):        """        Generates the ultra secure secret with added nonce randomness        """        assert self.nonce.bit_length() > 64        self.key = (self.recieved * self.private).x ^ self.nonce        print(self.key)
    def check_fingerprint(self, h2: str):        """        If this is failing, remember that you must send the SAME        nonce to both Alice and Bob for the shared secret to match        """        h1 = hashlib.sha256(long_to_bytes(self.key)).hexdigest()        return h1 == h2
    def send_fingerprint(self):        return hashlib.sha256(long_to_bytes(self.key)).hexdigest()
def print_header(title: str):    print('\n\n'+'*'*64+'\n'+'*'+title.center(62)+'*\n'+'*'*64+'\n\n')
def input_json(prompt: str):    data = input(prompt)    try:        return json.loads(data)    except:        print({"error": "Input must be sent as a JSON object"})        exit()
def encrypt_flag(shared_secret: int):    iv = os.urandom(16)    key = hashlib.sha1(long_to_bytes(shared_secret)).digest()[:16]    cipher = AES.new(key, AES.MODE_CBC, iv)    ciphertext = cipher.encrypt(pad(FLAG, 16))
    data = {}    data['iv'] = iv.hex()    data['encrypted_flag'] = ciphertext.hex()    return print(json.dumps(data))
Alice = EllipticCurveKeyExchange()Bob = EllipticCurveKeyExchange()
print_header('Welcome!') message = "Hello! Thanks so much for jumping in to help. Ever since everyone left WhatsApp, we've had a hard time keeping up with communications. We're hoping by outsourcing the message exchange to some CTF players we'll keep the load down on our servers... All messages are end-to-end encrypted so there's no privacy issues at all, we've even rolling out our new ultra-secure key exchange with enhanced randomness! Again, we really appreciate the help, feel free to add this experience to your CV!"welcome = textwrap.fill(message, width=64)          print(welcome)
print_header('Alice sends public key')Alice.send_public()
print_header("Please forward Alice's key to Bob")alice_to_bob = input_json('Send to Bob: ')Bob.receive_public(alice_to_bob)
print_header('Bob sends public key')Bob.send_public()
print_header("Please forward Bob's key to Alice")bob_to_alice = input_json('Send to Alice: ')Alice.receive_public(bob_to_alice) Alice.get_shared_secret()Bob.get_shared_secret()
print_header('Key verification in progress')alice_happy = Alice.check_fingerprint(Bob.send_fingerprint())bob_happy = Bob.check_fingerprint(Alice.send_fingerprint())if not alice_happy or not bob_happy:    print({"error": "Alice and Bob panicked: Potential MITM attack in progress!!"})    exit()
print_header('Alice sends encrypted flag to Bob')encrypt_flag(Alice.key)
```
## Solution
Here we are taking the role of an attacker in between Alice and Bob and we can modify the parameters that they send each other. The curve looks secure as well so we can't really attack that.
The interesting portion is the nonce that they use:
```python    def get_shared_secret(self):        """        Generates the ultra secure secret with added nonce randomness        """        assert self.nonce.bit_length() > 64        self.key = (self.recieved * self.private).x ^ self.nonce        print(self.key)
```
We can see here that they XOR the nonce with the shared key and check that they have the same shared key afterwards.To exploit that I went with the following approach:
- Use the generator of the curve as your key- Send the point that was generated by Alice as the nonce to Bob and vice versa
This means that we get the following
$$(a * G) \oplus B = A \oplus B $$
So now their shared key is basically `A ^ B` and we can then decrypt the flag with that
## Solution Code
```pythonimport os, random, hashlib, textwrap, jsonfrom Crypto.Cipher import AESfrom Crypto.Util.Padding import pad, unpadfrom Crypto.Util.number import getPrime, long_to_bytes
from pwn import *
from fastecdsa.curve import secp256k1from fastecdsa.point import Point
r = remote('134.122.111.232', 54321, level='debug')
def json_recv():    line = r.recvuntil("}")    return json.loads(line.decode())
def json_send(hsh):    request = json.dumps(hsh).encode()    r.sendline(request)
CURVE = secp256k1ORDER = CURVE.qG = CURVE.G
def get_public_key(private):    A = G * private    return A
def get_nonce(private, received):    r = Point(int(received["Px"]), int(received["Py"]), curve=secp256k1)    return (r * private).x
def decrypt_flag(shared_secret: int, iv: str, ciphertext: str):    key = hashlib.sha1(long_to_bytes(shared_secret)).digest()[:16]    cipher = AES.new(key, AES.MODE_CBC, bytes.fromhex(iv))    plaintext = cipher.decrypt(bytes.fromhex(ciphertext))
    return unpad(plaintext, 16).decode()
mykey = get_public_key(1)
r.recvuntil("Alice sends public key")r.recv()
Alice = json_recv()print(Alice)r.recvuntil("Send to Bob: ")
to_bob = {    "Px": mykey.x,    "Py": mykey.y,    "nonce": get_nonce(1, Alice)}
json_send(to_bob)
r.recvuntil("Bob sends public key")r.recv()
Bob = json_recv()print(Bob)r.recvuntil("Send to Alice: ")
to_alice = {    "Px": mykey.x,    "Py": mykey.y,    "nonce": get_nonce(1, Bob)}
json_send(to_alice)
r.recvuntil("Alice sends encrypted flag to Bob")print(r.recvuntil("\n\n"))
flag = json_recv()print(flag)
shared = Alice["Px"] ^ Bob["Px"]
print(decrypt_flag(shared, flag["iv"], flag["encrypted_flag"]))```