image.png

from Crypto.Util.number import getPrime, bytes_to_long
try:
	b = int(input())
	m = int.from_bytes(open("flag.txt", "rb").read(), "big")
	if b * 2 < m.bit_length(): input("I'm not really sure about this");exit()
	for _ in range(b):
		n = getPrime(b) * getPrime(b)
		c = pow(m, 65537, n)
		print(f"Data {_ + 1}: {c = }, {n = }")
except: pass

b.py

For each iteration, it encrypts the message m (derived from the contents of "flag.txt") using a newly generated RSA key and prints the encrypted result and the corresponding modulus.

image.png

⇒ Hastad broadcast attack

from sage.all import *
from pwn import *
from Crypto.Util.number import *
from gmpy2 import iroot
from math import prod

CS = []
NS = []
e = 65537

for i in range(300):
    print(i)
    io = remote("host3.dreamhack.games", 17137)
    io.sendline(b'256')
    Cs = []
    Ns = []
    for _ in range(256):
        io.recvuntil(f'Data {_ + 1}: '.encode())
        c, n = io.recvline().strip().decode().split(', ')
        Cs += [int(c[4:])]
        Ns += [int(n[4:])]
    NS += [ZZ(prod(Ns))]
    CS += [ZZ(crt(Cs, Ns))]
    io.close()

me = crt(CS, NS)
m, ok = iroot(me, e)
print(long_to_bytes(m))

with open("./modulus2.txt", "w") as f:
    f.write(str(prod(NS)))
with open("./ciphertext2.txt", "w") as f:
    f.write(str(me))

ex.py

CS stores combined ciphertexts using CRT NSstores combined moduli using CRT

After collecting the ciphertexts and moduli for each set of 256 datasets: