# war(sa)mup - zer0pts CTF 2021 ###### tags: `zer0pts CTF 2021` `crypto` ## overview This is the very simple warmup-level RSA challenge. We're given public key $N, e$ and ciphertext $C_1 = (pad(m))^e \mod n, C_2 = (\lfloor \frac{pad(m) }{2} \rfloor)^e \mod n$. ```python= from Crypto.Util.number import getStrongPrime, GCD from random import randint from flag import flag import os def pad(m: int, n: int): # PKCS#1 v1.5 maybe ms = m.to_bytes((m.bit_length() + 7) // 8, "big") ns = n.to_bytes((n.bit_length() + 7) // 8, "big") assert len(ms) <= len(ns) - 11 ps = b"" while len(ps) < len(ns) - len(ms) - 3: p = os.urandom(1) if p != b"\x00": ps += p return int.from_bytes(b"\x00\x02" + ps + b"\x00" + ms, "big") while True: p = getStrongPrime(512) q = getStrongPrime(512) n = p * q phi = (p-1)*(q-1) e = 1337 if GCD(phi, e) == 1: break m = pad(int.from_bytes(flag, "big"), n) c1 = pow(m, e, n) c2 = pow(m // 2, e, n) print("n =", n) print("e =", e) print("c1=", c1) print("c2=", c2) ``` ## solution Considering $2^eC_2 \equiv (2 * \lfloor \frac{pad(m)}{2} \rfloor)^e \mod n$. This is from RSA's multiplicative homomorphism. If $pad(m)$ is even, $2^eC_2 \equiv (pad(m))^e \equiv C_1 \mod n$. This looks unsolvable. However, if $pad(m)$ is odd value, $2^eC_2 \equiv (2 * \lfloor \frac{pad(m)}{2} \rfloor)^e \equiv (pad(m) - 1)^e \mod n$ since $\lfloor \frac{pad(m)}{2} \rfloor = \frac{pad(m)-1}{2}$. Then we get two cipheretexts and a difference of their plantexts. As $e$ is small[^1], we can apply the **Franklin-Reiter's Related Message Attack**. [^1]: The plagiarism challenge in diceCTF 2021 introduces us the Half-GCD method which solves polynomial GCD faster than euclidean method. So we can solve this challenge if even $e$ is not so small. I didn't know such a great way when I've created this challenge. Only the problem is $pad(m)$ is even or odd. We have 1/2 chance of win. Additionally, as $m$ is the flag, we can guess it ends with `}`. It is odd value, then $m$ and $pad(m)$ is odd value highly probability. ## exploit ```python= def pgcd(a, b): while b: a, b = b, a % b return a.monic() def unpad(m: int): m = int(m).to_bytes((m.bit_length() + 7) // 8, "big") return m.split(b"\x00", 2)[-1] exec(open("output.txt").read()) PR.<x> = PolynomialRing(Zmod(n)) c2_ = c2 * pow(2,e,n)%n f1 = (x)^e - c1 f2 = (x - 1)^e - c2_ m = -pgcd(f1, f2)[0] print(unpad(int(m))) ```