PlaidCTF’17 – Multicast

We are given a challenge which contains a Sage script, which holds the following lines of code

nbits = 1024
e = 5
flag = open("flag.txt").read().strip()
assert len(flag) <= 64
m = Integer(int(flag.encode('hex'),16))
out = open("data.txt","w")

for i in range(e):
    while True:
        p = random_prime(2^floor(nbits/2)-1, lbound=2^floor(nbits/2-1), proof=False)
        q = random_prime(2^floor(nbits/2)-1, lbound=2^floor(nbits/2-1), proof=False)
        ni = p*q
        phi = (p-1)*(q-1)
        if gcd(phi, e) == 1:

    while True:
        ai = randint(1,ni-1)
        if gcd(ai, ni) == 1:

    bi = randint(1,ni-1)
    mi = ai*m + bi
    ci = pow(mi, e, ni)

…along with a text file containing the encrypted flag. Let us first parse the file and put in a JSON structure

data = {'n' : [n0, n1, ...], 'a' : [a0, a1, ...], ...}

We see that the flag is encrypted several times, up to affine transformation. If we define a polynomial g_i(x) = (a_ix + b_i)^5 - c_i \mod N_i, this has a root g_i(m) = 0 \mod N_i. We could try to find this root using Coppersmith, but it turns out it not possible since the size of (am + b)^5 is larger than N_i. However, due to a publication by Håstad, we can construct the following:

g(x) = \sum \phi_i(N_i) g_i(x)


\phi_i \mod N_j = \begin{cases}0 & \quad \text{if } i \neq j\\1 & \quad \text{when } i = j\\\end{cases}

It is easy to construct \phi_i as

\phi_i = \frac{1}{N_i}\cdot \prod_k N_k \cdot [\prod_k N_k / N_i]^{-1}_{N_i}

using the Chinese Remainder Theorem. Clearly, g(x) also has a root g(m) = 0 \mod (\prod N_i) (this is easy to check!). Since m^5 is strictly smaller than \prod_k N_k, the polynomial roots of g(x) can be found using Coppersmith!

p = data['n'][0] * data['n'][1] * data['n'][2] * data['n'][3] * data['n'][4]
PR. = PolynomialRing(Zmod(p))
f = 0

# compute the target polynomial
for i in range(0, 5):
    q = p / data['n'][i]
    qinv = inverse_mod(q, data['n'][i])
    f = f + q * qinv * ((data['a'][i]*x + data['b'][i])^5 - data['c'][i])

# make f monic
f = f * inverse_mod(f[5], p)

print f.small_roots(X=2^512, beta=1)[0]

The code outputs the long integer representation of


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s