Key Exchange - cryptography - cryptopia

Writeup - bylal

This is the script that solved:

#!/usr/bin/env python3

from pwn import *
from hashlib import sha256
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import os

HOST = "0.cloud.chals.io"
PORT = 26625
N_BYTES = 1024

def recv_exact(io, n):
    data = b""
    while len(data) < n:
        chunk = io.recv(n - len(data))
        if not chunk:
            raise EOFError(f"Connection closed early while reading {n} bytes.")
        data += chunk
    return data

def int_from_n_bytes(b):
    return int.from_bytes(b, byteorder="big")

def int_to_n_bytes(x, n_bytes=N_BYTES):
    out = x.to_bytes(n_bytes, byteorder="big")
    if len(out) != n_bytes:
        raise ValueError(f"Integer too large to encode in {n_bytes} bytes.")
    return out

def main():
    io = remote(HOST, PORT)
    print(f"[+] Connected to {HOST}:{PORT}")

    raw_p = recv_exact(io, N_BYTES)
    raw_g = recv_exact(io, N_BYTES)
    raw_A = recv_exact(io, N_BYTES)

    p = int_from_n_bytes(raw_p)
    g = int_from_n_bytes(raw_g)
    A = int_from_n_bytes(raw_A)

    # Pick random 2 ≤ b ≤ p-1
    b = int.from_bytes(os.urandom(256), byteorder="big") % (p - 2) + 2
    B = pow(g, b, p)

    io.send(int_to_n_bytes(B, N_BYTES))
    print(f"[+] Sent our public key (B = g^b mod p).")

    encrypted = io.recvall(timeout=5)
    if len(encrypted) < 16:
        raise RuntimeError("Did not receive a full IV+ciphertext!")
    iv = encrypted[:16]
    ciphertext = encrypted[16:]
    print(f"[+] Received {len(encrypted)} bytes (16‐byte IV + {len(ciphertext)}‐byte ciphertext).")

    k = pow(A, b, p)
    k_bytes = k.to_bytes((k.bit_length() + 7)//8, byteorder="big")
    key = sha256(k_bytes).digest()

    cipher = AES.new(key, AES.MODE_CBC, iv)
    plain_padded = cipher.decrypt(ciphertext)
    try:
        plaintext = unpad(plain_padded, AES.block_size)
    except ValueError as e:
        raise RuntimeError(f"Unpadding failed: {e}")

    # write the decrypted PNG to disk
    with open("flag.png", "wb") as f:
        f.write(plaintext)
    print("[+] Decrypted PNG saved as flag.png. Open it to read the flag.")

    io.close()

if __name__ == "__main__":
    main()