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()