この大会は2022/11/12 14:00(JST)~2022/11/13 14:00(JST)に開催されました。
今回もチームで参戦。結果は1072点で726チーム中44位でした。
自分で解けた問題をWriteupとして書いておきます。
pqpq (crypto)
暗号化処理の概要は以下の通り。
・p, q, r: 512ビット素数 ・n = p * q * e ・e = 2 * 65537 ・padding: nのビット長を8で割った値からflagの長さを引いた数の長さのランダム文字列 ・m = bytes_to_long(padding + flag) ・c1p = pow(p, e, n) ・c1q = pow(q, e, n) ・cm = pow(m, e, n) ・c1 = (c1p - c1q) % n ・c2 = pow(p - q, e, n) ・e, n, c1, c2, cmを出力
式を変形してみる。
c1 = p^e - q^e mod n c2 = (p-q)^e mod n e2 = e // 2とする。 c2 = pow((p - q) ** 2, e2, n) = pow(p**2 + q**2, e2, n) = pow(p**2, e2, n) + pow(q**2, e2, n) = (pow(p, e, n) + pow(q, e, n)) % n c1 = (pow(p, e, n) - pow(q, e, n)) % n
c1とc2からpow(p, e, n)、pow(q, e, n)を計算できる。
pow(p, e, n) = (c2 + c1) % n pow(q, e, n) = (c2 - c1) % n
このことから以下により、p, q, rを求める。
p = GCD(pow(p, e, n), n) q = GCD(pow(q, e, n), n) r = n // (p * q)
次にm**2を通常のRSA暗号の復号方法で復号する。その後は、Tonelli-Shanks Algorithmを使って、p, q, rについて、平方剰余を求め、中国人剰余定理により復号する。
#!/usr/bin/env python3 from Crypto.Util.number import * from sympy.ntheory.modular import crt from re import * def legendre(a, p): return pow(a, (p - 1) // 2, p) def tonelli_shanks(a, p): if legendre(a, p) != 1: raise Exception("not a square (mod p)") q = p - 1 s = 0 while q % 2 == 0: q >>= 1 s += 1 for z in range(2, p): if legendre(z, p) == p - 1: break m = s c = pow(z, q, p) t = pow(a, q, p) r = pow(a, (q + 1) // 2, p) t2 = 0 while True: if t == 0: return 0 if t == 1: return r t2 = (t * t) % p for i in range(1, m): if t2 % p == 1: break t2 = (t2 * t2) % p b = pow(c, 1 << (m - i - 1), p) m = i c = (b * b) % p t = (t * c) % p r = (r * b) % p with open('output.txt', 'r') as f: params = f.read().splitlines() e = int(params[0].split(' ')[-1]) n = int(params[1].split(' ')[-1]) c1 = int(params[2].split(' ')[-1]) c2 = int(params[3].split(' ')[-1]) cm = int(params[4].split(' ')[-1]) cp = (c2 + c1) % n cq = (c2 - c1) % n p = GCD(cp, n) q = GCD(cq, n) r = n // (p * q) assert p * q * r phi = (p - 1) * (q - 1) * (r - 1) d = inverse(e // 2, phi) m2 = pow(cm, d, n) ms = [] for i in [p, q, r]: m = tonelli_shanks(m2 % i, i) ms.append([m, i - m]) for mp in ms[0]: for mq in ms[1]: for mr in ms[2]: m = int(crt([p, q, r], [mp, mq, mr])[0]) flag = long_to_bytes(m) if b'SECCON{' in flag: pattern = b'(SECCON{.+})' flag = search(pattern, flag).group(1).decode() print(flag)
SECCON{being_able_to_s0lve_this_1s_great!}