Sharky CTF 2020 | Noisy RSA

from Crypto.Util.number import bytes_to_long, getStrongPrime
from fractions import gcd
from secret import flag
from Crypto.Random import get_random_bytes

def encrypt(number):
    return pow(number,e,N)

def noisy_encrypt(a,m):
    return encrypt(pow(a,3,N)+(m << 24))

e = 3
p = getStrongPrime(512)
q = getStrongPrime(512)
while (gcd(e,(p-1)*(q-1)) != 1):
    p = getStrongPrime(512)
    q = getStrongPrime(512)
N = p * q

print("N : " + str(N) + "\n")
print("e : " + str(e) + "\n")
rand = bytes_to_long(get_random_bytes(64))
ct = []
ct.append(encrypt(rand << 24))
for car in flag:
    ct.append(noisy_encrypt(car,rand))
print(ct)

平文を1文字ずつ暗号化しているRSAだが、 67byte 程度のパディングがついている + 平文を暗号化したものをもう一度暗号化しているのでそのままでは解けない。

また、ヒントとして、パディングだけを暗号化したものも与えられている。

 pad^3 \mod N

 (m^3 + pad)^3 \mod N

↑より、mがわかっていれば Franklin-Reiter Related Message Attackができそう。フラグのフォーマットから m_0sと推測して、次のようにパディングを求める。

パディングがわかれば、あとは平文と暗号文を対応させるテーブルを作るだけ。

from sage.all import *

N = 166340340660877519226247260987065058211371932250499241074633026863387096385721863889904212902371869444274842128829082337552709506851212430322777753756706525557339442213042322434951929492350910439291882068704178535251621706072605607671593003649095340546077718624006859019891494745600258808326203381366558360403
e = 3
cs = eval(open("output.txt").read().strip().split("\n")[4])

b = pow(ord('s'), e, N)
_, x = PolynomialRing(Zmod(N), name='x').objgen()
f1 = x**e - cs[0]
f2 = (b + x)**e - cs[1]

def gcd(a, b):
    while b:
        a, b = b, a % b
    return a.monic()

pad = -gcd(f1, f2).coefficients()[0]

table = {}
for i in range(256):
    c = pow(pow(i, e, N) + pad, e, N)
    table[c] = chr(i)

flag = ''
for c in cs:
    flag += table[c]
print(flag)