λ¬Έμ : DreamHack β tiny roots λΆλ₯: Crypto λμ΄λ: π₯ Silver 1 FLAG:
DH{yeaaaahhh!!_C00p3rsM1th_1s_G0d_><}
λ¬Έμ μ€λͺ μ μ΄λ λ€. λλ¦Όμ΄κ° RSA ν€λ₯Ό λ§λ€λ€ 컀νΌλ₯Ό μμ μ λ³΄κ° μ§μμ‘κ³ , μλ‘ λ§λ€λ €λ μ€μ ν€ μΌλΆκ° λ ΈμΆλ κ±Έ λ°κ²¬νλ€λ κ². 컀νΌλ₯Ό μμλ€λ κ² κ³§ "λΉνΈκ° μ§μμ‘λ€"λ λ»μ΄κ³ , κ·Έ μ§μμ§ μλ¦¬κ° μ νν μ΄λκΉμ§μΈμ§κ° μ΄ λ¬Έμ μ μ λΆλ€.

λ¬Έμ κ°μ
| νλͺ© | λ΄μ© |
|---|---|
| λ¬Έμ λͺ | tiny roots |
| λμ΄λ | π₯ Silver 1 |
| λΆλ₯ | Crypto |
| μ 곡 νμΌ | tiny roots.py (μμ± μ€ν¬λ¦½νΈ), output.txt (μΆλ ₯) |
| μλ² | μμ β μ€νλΌμΈ λ¬Έμ |
| μ€ν | Python 3 + PyCryptodome, RSA-2048 |
| ν΅μ¬ κΈ°λ² | Coppersmith known-MSB μΈμλΆν΄ (May) + Howgrave-Graham 격μ + LLL |
νμ΄ νλ¦μ μ§§λ€. p μ μμ 744λΉνΈκ° κ·Έλλ‘ κ³΅κ°λΌ μμΌλ λλ¨Έμ§ 280λΉνΈλ₯Ό λ―Έμ§μ x λ‘ λκ³ f(x) = MSB + x μ μμ κ·Όμ 격μλ‘ μ°Ύλλ€. p κ° λμ€λ©΄ κ·Έλ€μμ κ΅κ³Όμ RSAλ€.
π¬ λ°°ν¬λ³Έ μ μ°°
νμΌμ λ± λ κ°λ€. μμ± μ€ν¬λ¦½νΈμ κ·Έ μΆλ ₯.
ls -l extracted && file extracted/* && head -18 "extracted/tiny roots.py"
μ€ν¬λ¦½νΈ μ λ¬Έμ μ΄κ² λ€λ€.
from Crypto.Util.number import *
FLAG = open('../../flag.txt','rb').read()
FLAG = bytes_to_long(FLAG)
p = getPrime(1024)
q = getPrime(1024)
assert p > q
n = p * q
e = getPrime(128)
beta = 0.4
epsilon = beta ** 2 / 7
upper_bound = p.bit_length()
lower_bound = int(n.bit_length() * (beta ** 2 - epsilon))
MSB = p & (pow(2,upper_bound) - pow(2,lower_bound))
# encryption
ciphertext = pow(FLAG,e,n)
print(f'Here is the flag : {ciphertext}\n')
print(f'I don\'t know the secret key... except some bits of p...\n{MSB}\n')
print(f'But I know the public key !! Can You make it??\n({n},{e})')e κ° 128λΉνΈ μμλΌ μμ μ§μ 곡격μ μ²μλΆν° λ§ν μλ€. μμ λ κ°λ λ
립μ μΌλ‘ λ½μμΌλ κ·Όμ μμ 곡격λ μ λλ€. λ¨λ 건 MSB ν μ€λΏμ΄λ€.
MSB = p & (2^upper_bound - 2^lower_bound) β μ¬κΈ°μ upper_bound λ p.bit_length() λκΉ 1024κ³ , lower_bound λ n μ λΉνΈμ 2048μ beta^2 - epsilon μ κ³±ν κ°μ΄λ€. λ§μ€ν¬κ° λͺ λΉνΈλ₯Ό μ΄λ¦¬κ³ λͺ λΉνΈλ₯Ό μ§μ°λμ§λ λμΌλ‘ μΈμ§ λ§κ³ μ§μ κ³μ°νλ κ² λΉ λ₯΄λ€.
#!/usr/bin/env python3
"""output.txt μμ νλΌλ―Έν°λ₯Ό λ½κ³ , λ¬Έμ μ½λμ λ§μ€ν¬κ° μ€μ λ‘ λͺ λΉνΈλ₯Ό μ§μ λμ§ μΌλ€."""
import re
txt = open('extracted/output.txt').read()
c = int(re.search(r'Here is the flag : (\d+)', txt).group(1))
msb = int(re.search(r'bits of p\.\.\.\s*\n(\d+)', txt).group(1))
n, e = (int(v) for v in re.search(r'\((\d+),(\d+)\)', txt).groups())
beta = 0.4
epsilon = beta ** 2 / 7
upper_bound = 1024 # p.bit_length()
lower_bound = int(n.bit_length() * (beta ** 2 - epsilon))
mask = pow(2, upper_bound) - pow(2, lower_bound)
print(f'n : {n.bit_length()}λΉνΈ')
print(f'e : {e.bit_length()}λΉνΈ = {e}')
print(f'ciphertext : {c.bit_length()}λΉνΈ')
print(f'MSB : {msb.bit_length()}λΉνΈ')
print()
print(f'beta = {beta}')
print(f'epsilon = beta^2/7 = {epsilon:.9f}')
print(f'lower_bound = int({n.bit_length()} * ({beta**2:.2f} - {epsilon:.6f})) '
f'= int({n.bit_length()*(beta**2-epsilon):.4f}) = {lower_bound}')
print()
print(f'mask = 2^{upper_bound} - 2^{lower_bound}')
print(f'mask μμμ 1 μΈ λΉνΈ = {bin(mask).count("1")}κ° (λΉνΈ {lower_bound}..{upper_bound-1})')
print(f'-> p μ μμ {upper_bound-lower_bound}λΉνΈλ₯Ό μκ³ , νμ {lower_bound}λΉνΈλ₯Ό λͺ¨λ₯Έλ€')
print()
print(f'MSB μ λ€μͺ½ 0 λΉνΈ = {(msb & -msb).bit_length()-1}κ° '
f'(νμ {lower_bound}λΉνΈκ° μ§μμ‘κ³ , λ§μΉ¨ κ·Έ μ 6λΉνΈλ 0μ΄μλ€)')
print(f'MSB % 2^{lower_bound} == 0 ? {msb % (1 << lower_bound) == 0}')
print()
print(f'λ―Έμ§μ ν보 = 2^{lower_bound} = {1 << lower_bound}')
print(f'μ΄λΉ 10^9 ν κ²μ¬ν΄λ {(1<<lower_bound)/1e9/3.15e7:.3e} λ
-> μ μλ λΆκ°λ₯')python3 recon.py
μ«μκ° λ€ λμλ€. lower_bound = 280, μ¦ p μ νμ 280λΉνΈκ° μ§μμ§κ³ μμ 744λΉνΈκ° κ·Έλλ‘ κ³΅κ°λλ€.
MSB μ λ€μͺ½ 0λΉνΈκ° 286κ°λ‘ λμ€λ κ² μ κΉ λμ 걸리λλ°, μ΄κ±΄ μ§μμ§ 280λΉνΈ μμͺ½ 6λΉνΈκ° μ°μ°ν 0μ΄μμ λΏμ΄λ€. λ§μ€ν¬κ° μ§μ΄ 건 μ νν 280λΉνΈλ€.

μ 리νλ©΄ μ΄λ λ€.
p = MSB + x μ΄κ³ λ―Έμ§μ λ²μλ 0 <= x < 2^280 μ΄λ€.
2^280 μ λλ΅ 1.9x10^84 λ€. μ μλ λ
ΌμΈλ€.
π§© λ°°κ²½ β μΈμμ μμ λΉνΈλ₯Ό μλ©΄ λ«λ¦°λ€
Coppersmith λ 1996λ μ "λͺ¨λλ¬ λ€νμμ μμ κ·Όμ λ€νμκ°μ μ°Ύμ μ μλ€"λ κ²°κ³Όλ₯Ό λκ³ , May κ° κ·Έκ±Έ μΈμλΆν΄ μͺ½μΌλ‘ μ 리νλ€. μ°λ¦¬κ° μΈ ννλ μ΄κ±°λ€.
Nμ λ―Έμ§ μΈμp >= N^betaμ μ°¨μdeltaμΈ monic λ€νμf(x)μ λν΄,f(x0)κ°mod pμμ 0μ΄ λλx0κ°|x0| < N^(beta^2/delta)λ₯Ό λ§μ‘±νλ©΄ κ·Έx0λNλ§ μκ³ λ λ€νμκ°μ μ°Ύμ μ μλ€.
μ¬κΈ° λμ
νλ©΄ f(x) = MSB + x, μ°¨μ 1, f(x0) = p μ΄λ―λ‘ mod p μμ 0μ΄λ€. p μ q κ° λ λ€ 1024λΉνΈλΌ p λ λλ΅ N^0.5 μ΄κ³ , κ·Έλ¬λ©΄ κ°λΉ κ°λ₯ν λ―Έμ§μλ N^0.25 = 512λΉνΈλ€. μ§μμ§ κ±΄ 280λΉνΈλΏμ΄λΌ 232λΉνΈλ λ¨λλ€. λ¬Έμ μ΄λ¦ tiny roots λ μ΄ "μμ κ·Ό"μ΄μ SageMath μ small_roots λ₯Ό κ°λ¦¬ν¨λ€.
ν΅μ¬ λꡬλ Howgrave-Graham μ 보쑰μ 리λ€. mod p μμ κ·Όμ κ°λ μ μ λ€νμμ κ³μκ° μΆ©λΆν μμΌλ©΄, κ·Έ κ·Όμμ μ μ μμμλ κ°μ΄ 0μ΄ λλ€λ κ². κ·Έλμ mod p λ‘ 0μ΄ λλ λ€νμλ€μ μλ© λ§λ€μ΄ λκ³ , κ·Έ μ μ κ²°ν© μ€ κ³μκ° μμ κ²μ μ°ΎμΌλ©΄ λλ€. κ·Έ "κ³μκ° μμ κ²°ν© μ°ΎκΈ°"κ° κ³§ 격μ μ΅λ¨λ²‘ν° λ¬Έμ κ³ , LLL μ΄ κ·Έκ±Έ κ·Όμ¬λ‘ νμ΄ μ€λ€.

μΆμ²: Wikimedia Commons, public domain
κ·Έλ¦Όμ v1, v2 μ²λΌ κΈΈκ³ κ±°μ ννν κΈ°μ λ₯Ό u1, u2 μ²λΌ μ§§κ³ μ§κ΅μ κ°κΉκ² λ°κΎΈλ κ²μ΄ LLL μ΄λ€. κ°μ 격μλ₯Ό μμ±νλ 벑ν°λ§ μ§§μμ§λ€. μ°λ¦¬κ° μνλ "μμ κ³μ λ€νμ"μ΄ μ νν κ·Έ μ§§μ 벑ν°λ€.
π£ ν΅μ¬ β 3Γ3 격μλ©΄ μΆ©λΆνλ€
m = 1, t = 1 λ‘ μ‘μΌλ©΄ mod p μμ 0μ΄ λλ λ€νμμ΄ μ
λμ¨λ€.
| λ€νμ | mod p μμ 0μΈ μ΄μ |
|---|---|
g0(x) = n | p κ° n μ λλλ€ |
g1(x) = f(x) | f(x0) = p |
h1(x) = xΒ·f(x) | μμ x λ₯Ό κ³±νμ λΏ |
μ΄ μ
μ x β XΒ·x λ‘ μΉνν΄ κ³μ 벑ν°λ‘ λμ΄λμΌλ©΄ μΌκ°νλ ¬μ΄ λλ€. λκ° μ±λΆμ n, X, X^2 μ΄κ³ νλ ¬μμ nΒ·X^3 μ΄λ€.
Howgrave-Graham 쑰건μ LLL μ΅λ¨λ²‘ν°μ λ
Έλ¦μ΄ p^m / βdim λ³΄λ€ μμμΌ νλ€λ κ²μ΄κ³ , LLL μ μ΅λ¨λ²‘ν° λ
Έλ¦μ΄ 2^((d-1)/4) * det^(1/d) μ΄νμμ 보μ₯νλ€. μ«μλ₯Ό λ£μΌλ©΄
log2(det) = 2048 + 3 x 280 = 2888 μ΄κ³ , μ΄κ±Έ μ°¨μ 3μΌλ‘ λλλ©΄ 962.7 μ΄λ€.
μ¬μ κ° 2^60 μ΄μ λ¨λλ€. μ΄λ‘ κ°λ§ λ―Ώμ κ² μλλΌ (m, t) λ₯Ό νμ΄ μ΄λλΆν° μ‘°κ±΄μ΄ μκ³ μ€μ λ‘ μΌλ§λ 걸리λμ§ μ¬ λ΄€λ€.
#!/usr/bin/env python3
"""격μ ν¬κΈ° (m, t) λ₯Ό λ°κΏ κ°λ©° Howgrave-Graham μ‘°κ±΄μ΄ μ±λ¦½νλμ§ κ³μ°νκ³ ,
μμ 격μλ μ€μ λ‘ LLL μ λλ € κ±Έλ¦° μκ°κ³Ό μ΅λ¨λ²‘ν° κΈΈμ΄λ₯Ό μ°λ€.
μ΄λ‘ : 2^((dim-1)/4) * det^(1/dim) < p^m / sqrt(dim)
μ¬κΈ°μ det = X^(m(m+1)/2 + t*m + t(t+1)/2) * n^(m(m+1)/2)
"""
import math
import re
import sys
import time
from lll import lll
txt = open('extracted/output.txt').read()
msb = int(re.search(r'bits of p\.\.\.\s*\n(\d+)', txt).group(1))
n = int(re.search(r'\((\d+),(\d+)\)', txt).group(1))
LOGX, LOGN, LOGP = 280, 2048, 1024 # X=2^280, n 2048λΉνΈ, p 1024λΉνΈ
def build(m, t):
dim = m + t + 1
fp = [[1]]
for _ in range(m):
a, b = fp[-1], [msb, 1]
out = [0] * (len(a) + len(b) - 1)
for i, x in enumerate(a):
for j, y in enumerate(b):
out[i + j] += x * y
fp.append(out)
polys = [[c * pow(n, m - i) for c in fp[i]] for i in range(m + 1)]
polys += [[0] * j + fp[m] for j in range(1, t + 1)]
X = 1 << LOGX
return [[polys[r][k] * pow(X, k) if k < len(polys[r]) else 0
for k in range(dim)] for r in range(dim)]
print(f'{"m":>2} {"t":>2} {"dim":>4} {"log2(det)":>10} {"LLL μν":>9} '
f'{"νμ p^m":>9} νμ μ€μΈ‘')
print('-' * 68)
for m in range(1, 5):
for t in range(0, 4):
dim = m + t + 1
logdet = LOGX * (m * (m + 1) // 2 + t * m + t * (t + 1) // 2) + LOGN * (m * (m + 1) // 2)
lhs = logdet / dim + (dim - 1) / 4 + 0.5 * math.log2(dim)
rhs = LOGP * m
ok = 'ν΅κ³Ό' if lhs < rhs else 'λ―Έλ¬'
measured = ''
if dim <= 4 and lhs < rhs:
st = time.time()
R = lll(build(m, t))
el = time.time() - st
norm2 = sum(c * c for c in R[0])
measured = f'{el:6.2f}s ||v||=2^{(norm2.bit_length()-1)/2:.0f}'
print(f'{m:>2} {t:>2} {dim:>4} {logdet:>10} {lhs:>9.1f} {rhs:>9} '
f' {ok} {measured}')
sys.stdout.flush()python3 lattice_scan.py
νμμ μ½νλ κ² λ κ°μ§λ€.
첫째, t = 0 μΈ μ€μ m μ΄ μ무리 컀λ μ λΆ λ―Έλ¬μ΄λ€. x λ₯Ό κ³±ν μ΄λ λ€νμμ΄ νλλ μμ΄μΌ μ‘°κ±΄μ΄ μ λ€.
λμ§Έ, μ‘°κ±΄μ΄ μλ κ°μ₯ μμ 격μκ° (m, t) = (1, 1) μ 3μ°¨μμ΄λ€. dim μ ν€μ°λ©΄ μ΅λ¨λ²‘ν°λ λ μ§§μμ§μ§λ§(2^931 β 2^900) μ΄μ°¨νΌ νμν 건 2^1023 μ΄νλΌ μλ―Έκ° μκ³ , μμ νμ΄μ¬ LLL λ‘λ λΉμ©λ§ κΈκ²©ν λλ€. μ΄ λ¬Έμ μμ tiny λ κ·Όλ§μ΄ μλλΌ κ²©μμλ ν΄λΉνλ€.

π§± LLL μ μ§μ μ§κΈ°
fpylll μ΄ μ΄ λ¨Έμ μ μμκ³ , μ΄μ°¨νΌ 3μ°¨μμ΄λΌ κ΅³μ΄ λΆμΌ μ΄μ λ μμλ€. νμ€ λΌμ΄λΈλ¬λ¦¬λ§μΌλ‘ μ§°λ€.
μ£Όμν κ² νλ μλ€. κ·Έλ-μλ―ΈνΈλ₯Ό λΆλμμμ μΌλ‘ νλ©΄ μ λλ€. μ±λΆμ΄ 2900λΉνΈμ§λ¦¬λΌ float λ‘λ μ ν¨μ«μκ° ν΅μ§Έλ‘ λ μκ°λ€. Fraction μΌλ‘ μ νν κ³μ°ν΄μΌ νλ€.
#!/usr/bin/env python3
"""νμ€ λΌμ΄λΈλ¬λ¦¬λ§μΌλ‘ λ§λ LLL(Lenstra-Lenstra-Lovasz) 격μ μΆμ½.
Fraction μΌλ‘ κ·Έλ-μλ―ΈνΈλ₯Ό μ νν κ³μ°νλ€. λΆλμμμ μ μ°λ©΄ 2000λΉνΈκ° λλ
μ±λΆμμ μ ν¨μ«μκ° ν΅μ§Έλ‘ λ μκ°λ―λ‘ μ μ/μ 리μ μ°μ μ΄ νμλ€.
μ°¨μμ΄ 10 μ΄νλΌ O(n^4) μ¬κ³μ°μ΄μ΄λ μΆ©λΆν λΉ λ₯΄λ€.
"""
from fractions import Fraction
def _dot(u, v):
return sum(a * b for a, b in zip(u, v))
def _gram_schmidt(B):
n = len(B)
Bs, mu = [], [[Fraction(0)] * n for _ in range(n)]
for i in range(n):
v = [Fraction(x) for x in B[i]]
for j in range(i):
mu[i][j] = _dot(B[i], Bs[j]) / _dot(Bs[j], Bs[j])
v = [v[k] - mu[i][j] * Bs[j][k] for k in range(len(v))]
Bs.append(v)
return Bs, mu
def lll(basis, delta=Fraction(99, 100)):
"""ν λ²‘ν° λ¦¬μ€νΈλ₯Ό λ°μ LLL μΆμ½λ κΈ°μ λ₯Ό λλ €μ€λ€."""
B = [list(map(int, row)) for row in basis]
n = len(B)
Bs, mu = _gram_schmidt(B)
k = 1
while k < n:
for j in range(k - 1, -1, -1):
if abs(mu[k][j]) > Fraction(1, 2):
r = int(round(mu[k][j]))
B[k] = [B[k][i] - r * B[j][i] for i in range(len(B[k]))]
Bs, mu = _gram_schmidt(B)
lhs = _dot(Bs[k], Bs[k])
rhs = (delta - mu[k][k - 1] ** 2) * _dot(Bs[k - 1], Bs[k - 1])
if lhs >= rhs:
k += 1
else:
B[k], B[k - 1] = B[k - 1], B[k]
Bs, mu = _gram_schmidt(B)
k = max(k - 1, 1)
return Bμ§μ μ§ κ±Έ κ·Έλλ‘ λ―Ώκ³ 2000λΉνΈ 격μμ λ°μ΄ λ£κΈ° μ μ, λ΅μ μλ μμ μ λ ₯μμ νμΈνλ€. Cohen μ κ΅κ³Όμ μμ μ, κ±°μ ννν΄μ μΆμ½ ν¨κ³Όκ° λλ ·ν 2μ°¨μ κΈ°μ νλ.
#!/usr/bin/env python3
"""μμ lll.py κ° μ λ§ LLL μΆμ½μ νλμ§ νμΈνλ€.
Cohen μ κ΅κ³Όμ μμ κΈ°μ μ, κ·Έ κΈ°μ κ° μ€μ λ‘ μΆμ½ 쑰건(size-reduced + Lovasz)μ
λ§μ‘±νλμ§κΉμ§ κ°μ΄ κ²μ¬νλ€.
"""
from fractions import Fraction
from lll import lll, _gram_schmidt
def check(B):
Bs, mu = _gram_schmidt(B)
size_ok = all(abs(mu[i][j]) <= Fraction(1, 2)
for i in range(len(B)) for j in range(i))
lovasz_ok = all(sum(x * x for x in Bs[k]) >=
(Fraction(99, 100) - mu[k][k - 1] ** 2) * sum(x * x for x in Bs[k - 1])
for k in range(1, len(B)))
return size_ok, lovasz_ok
for name, B in [('Cohen μμ ', [[1, 1, 1], [-1, 0, 2], [3, 5, 6]]),
('κ±°μ ννν 2μ°¨μ κΈ°μ ', [[201, 37], [140, 26]])]:
R = lll(B)
s, l = check(R)
before = min(sum(x * x for x in r) for r in B)
after = min(sum(x * x for x in r) for r in R)
print(f'{name}')
print(f' μ
λ ₯ {B}')
print(f' μΆλ ₯ {R}')
print(f' μ΅λ¨λ²‘ν° λ
Έλ¦^2 {before} -> {after}')
print(f' size-reduced {s}, Lovasz 쑰건 {l}')python3 lll_check.py
κ΅κ³Όμ μμ λ μλ €μ§ μΆμ½ κΈ°μ κ·Έλλ‘ λμκ³ , λ λ²μ§Έλ λ Έλ¦ μ κ³±μ΄ 20276μμ 50μΌλ‘ λ¨μ΄μ‘λ€. μΆμ½ 쑰건 λ κ°λ μ¬ν κ²μ¬λ‘ ν΅κ³Όνλ€.
π μ½μ§
βΆπ μ½μ§ 1 β 격μλ ν΄μλ‘ μ’μ μ€ μμλ€
μ²μμ (m, t) = (3, 2) λ‘ 6μ°¨μ 격μλ₯Ό μ§°λ€. Coppersmith μ€λͺ
μ νμ "m μ μΆ©λΆν ν¬κ²"κ° λΆμΌλ μ¬μ λ₯Ό λλ κ² μμ νλ€κ³ μκ°νλ€.
2λΆμ΄ μ§λλ μ λλ¬λ€. μμ νμ΄μ¬ Fraction κ·Έλ-μλ―ΈνΈλ₯Ό λ§€ λ¨κ³ λ€μ κ³μ°νλ ꡬνμ΄λΌ μ°¨μμ΄ μ€λ₯΄λ©΄ λΉμ©μ΄ νλ°νλ€. μμ lattice_scan.py λ‘ μ€μΈ‘ν κ² κ·Έλμμλ€.
m t dim log2(det) LLL μν νμ p^m νμ μ€μΈ‘
1 1 3 2888 964.0 1024 ν΅κ³Ό 0.36s
1 2 4 3728 933.8 1024 ν΅κ³Ό 1.28s
2 1 4 7824 1957.8 2048 ν΅κ³Ό 5.50s
2 2 5 8944 1791.0 2048 ν΅κ³Ό 77.53s
3 1 5 15088 3019.8 3072 ν΅κ³Ό 28.49sm μ ν€μ°λ©΄ 쑰건μ λ μ¬μ λ‘κ² μμ§λ§ μ΄ λ¬Έμ μ μ΄λ―Έ μ¬μ κ° μΆ©λΆνλ€. λ―Έμ§μκ° 280λΉνΈλ‘ μ΄λ‘ νκ³ 512λΉνΈμ μ λ° λ¨μ§μ΄λΌ, μ΅μ 격μμμ λ°λ‘ μ‘°κ±΄μ΄ μ λ€. "m μ ν¬κ²"λ λ―Έμ§μκ° νκ³μ λ°μ§ λΆμμ λ νλ μκΈ°λ€.
βΆπ μ½μ§ 2 β λ€νμ GCD κ° μμμμ 무ν루νμ λΉ μ‘λ€
LLL μ 0.36μ΄μ λλλλ° solve.py κ° 5λΆ νμμμκΉμ§ μ λμμλ€. λ²μΈμ λ΄κ° μ§ poly_gcd μλ€.
μ²μ ꡬνμ λλμ
ν λ¨κ³λ₯Ό λ°λ³΅νλ©΄μ while len(b) > 1 or b[0] != 0 λ‘ λμλ€. κ·Έλ°λ° λ λ€νμμ΄ μλ‘μλ©΄ λλ¨Έμ§κ° 0μ΄ μλ μμλ‘ λ¨μ΄μ§λ€. κ·Έλ a λ λ§€λ² 0μ΄ λκ³ b λ κ·Έλλ‘λΌ μ’
λ£ μ‘°κ±΄μ μμ λͺ» λΏλλ€.
LLL κ²°κ³Ό ν μΈ κ° μ€ μ‘°κ±΄μ λ§μ‘±νμ§ λͺ»νλ νμ΄ μμ¬ μμΌλ©΄ κ·Έ μ‘°ν©μ GCD κ° μμκ° λλ€. μ¦ μ΄ κ²½λ‘λ μ μ λμμμλ λ°λμ λ°λλ€.
κ³ μΉ κ±΄ κ΅κ³Όμ μ ν΄λ¦¬λ ννλ‘ λλλ¦° κ²λΏμ΄λ€.
while not (len(b) == 1 and b[0] == 0):
a, b = b, poly_mod(a, b)poly_mod μμμλ λλλ μͺ½μ΄ μμλ©΄(μ°¨μ 0) λλ¨Έμ§λ₯Ό λ°λ‘ [0] μΌλ‘ λλ €μ€λ€. μ΄ λ μ€λ‘ 5λΆμ΄ 0.42μ΄κ° λλ€.
βΆπ μ½μ§ 3 β μ‘°μ©ν ν리λ μΈ κ°μ§
격μ νμ΄λ νλ Έμ λ μμΈλ₯Ό μ λΈλ€. LLL μ λ©μ©‘ν λκ³ μκ°λ μ§§μλ° κ·Όλ§ μ λμ¨λ€. κ·Έλμ "μ΄λ‘ μ΄ μ λ§λ" λ₯Ό μμ¬νκ² λλλ°, μ€μ λ‘λ μ λ€ μ¬μν ꡬν μ€μμλ€. μ¬ν μ€ν¬λ¦½νΈλ‘ λ¨κ²¨ λλ€.
#!/usr/bin/env python3
"""νμ΄ λμ€ μ€μ λ‘ λ°μλ μΈ κ°μ§ ν¨μ μ κ·Έλλ‘ μ¬ννλ€.
μ
λ€ 'μ‘°μ©ν νλ¦°λ€' - μμΈλ μκ³ μ€λ 걸리μ§λ μλλ° κ·Όμ΄ μ λμ¨λ€.
"""
import re
from lll import lll
from solve import coppersmith_known_msb, poly_gcd, poly_trim, _newton_int_root
txt = open('extracted/output.txt').read()
msb = int(re.search(r'bits of p\.\.\.\s*\n(\d+)', txt).group(1))
n = int(re.search(r'\((\d+),(\d+)\)', txt).group(1))
TRUE_X0 = 781182359928297838659720821147531543713572092829500480647209554762229923863457600593
def report(tag, roots):
ok = [r for r in roots if n % (msb + r) == 0]
print(f' -> μ°Ύμ κ·Ό {len(roots)}κ°, n μ λλλ κ² {len(ok)}κ°'
f'{" <== μ±κ³΅" if ok else " <== μ€ν¨"}')
print('[ν¨μ 1] x -> X*x μ€μΌμΌλ§μ λΉΌλ¨Ήκ³ κ²©μλ₯Ό μ§λ©΄')
dim = 3
polys = [[n], [msb, 1], [0, msb, 1]]
B = [[polys[r][k] if k < len(polys[r]) else 0 for k in range(dim)] for r in range(dim)]
R = lll(B)
cand = [poly_trim(list(row)) for row in R]
roots = []
for i in range(dim):
for j in range(i + 1, dim):
g = poly_gcd(cand[i], cand[j])
if len(g) == 2 and (-g[0] / g[1]).denominator == 1:
roots.append(int(-g[0] / g[1]))
r = _newton_int_root(cand[i], 1 << 280)
if r is not None:
roots.append(r)
print(f' LLL μ΅λ¨λ²‘ν° = {[hex(v)[:12] + ".." if abs(v) > 2**40 else v for v in R[0]]}')
report('scale', roots)
print()
print('[ν¨μ 2] t=0 - x λ₯Ό κ³±ν μ΄λ λ€νμμ λ£μ§ μμΌλ©΄ (격μ 2x2)')
print(' Howgrave-Graham 쑰건: 2^1164.8 < 2^1024 μ΄μ΄μΌ νλλ° λ―Έλ¬')
try:
roots = coppersmith_known_msb(n, msb, 1 << 280, m=1, t=0, verbose=False)
except Exception as exc:
roots = []
print(f' μμΈ: {exc!r}')
report('t0', roots)
print()
print('[ν¨μ 3] lower_bound λ₯Ό n(2048λΉνΈ) μ΄ μλλΌ p(1024λΉνΈ) κΈ°μ€μΌλ‘ μ½μΌλ©΄')
wrong = int(1024 * (0.4 ** 2 - 0.4 ** 2 / 7))
print(f' X = 2^{wrong} μΈλ° μ°Έκ° x0 λ {TRUE_X0.bit_length()}λΉνΈλ€')
roots = coppersmith_known_msb(n, msb, 1 << wrong, m=1, t=1, verbose=False)
report('smallX', roots)
print()
print('[λμ‘°] μ λλ‘ μ§ κ²½μ°')
roots = coppersmith_known_msb(n, msb, 1 << 280, m=1, t=1, verbose=False)
report('ok', roots)
print(f' x0 = {roots[0] if roots else "-"}')python3 traps.py
첫째, x β XΒ·x μΉνμ λΉΌλ¨Ήλ κ². μ΄κ² μ μΌ ν·κ°λ¦°λ€. μ€μΌμΌλ§ μμ΄λ 격μλ μ μ§μ΄κ³ LLL λ λλ€. νμ§λ§ LLL μ΄ μ€μ΄λ 건 벑ν°μ μ ν΄λ¦¬λ λ
Έλ¦μΈλ°, Howgrave-Graham μ΄ μꡬνλ 건 "κ·Όμ ν¬κΈ°κΉμ§ κ°μν κ³μ ν¬κΈ°"λ€. X λ₯Ό κ³±ν΄ λμ§ μμΌλ©΄ μ΄ λμ΄ μλ‘ λ€λ₯Έ μμ μ¬λ μ
μ΄λΌ μ§§μ 벑ν°κ° λμλ μΈλͺ¨κ° μλ€.
λμ§Έ, μ΄λ λ€νμ μμ΄ t = 0. 2μ°¨μ 격μλ‘λ 2^1164.8 < 2^1024 λ₯Ό λ§μ‘±ν λ°©λ²μ΄ μλ€. μ€ν μκ°μ΄ μ§§μμ "빨리 λλ¬μΌλ λκ° λκ² μ§" νκ³ λμ΄κ°κΈ° μ½λ€.
μ
μ§Έ, lower_bound λ₯Ό p κΈ°μ€μΌλ‘ μ½λ κ². λ¬Έμ μ½λλ n.bit_length() λ₯Ό μ°λλ° μ΅κ΄μ μΌλ‘ p μ 1024λ‘ μ½μΌλ©΄ X = 2^140 μ΄ λλ€. μ°Έκ° x0 λ 279λΉνΈλΌ μν λ°μ΄κ³ , 격μ 쑰건μ μ€νλ € λ μ¬μ λ‘μμ§λ μ무 κ²½κ³ λ μ λ¬λ€.
π Full Exploit
μ μ‘°κ°λ€μ ν©μΉ μ체μκ²° νμ΄λ€. μμ‘΄μ±μ νμ€ λΌμ΄λΈλ¬λ¦¬μ κ°μ ν΄λμ lll.py λΏμ΄κ³ , λ°°ν¬λ³Έ output.txt λ§ μμΌλ©΄ λμκ°λ€.
#!/usr/bin/env python3
"""tiny roots (DreamHack, Silver 1, crypto) μ체μκ²° νμ΄.
p μ νμ 280λΉνΈλ§ μ§μμ§ RSA 곡κ°ν€μμ p λ₯Ό ν΅μ§Έλ‘ 볡ꡬνλ€.
Coppersmith μ 'known MSB of a factor'(May) λ₯Ό Howgrave-Graham 격μλ‘ μ§μ ꡬμ±νλ€.
μΈλΆ μμ‘΄μ± μμ - νμ€ λΌμ΄λΈλ¬λ¦¬μ κ°μ ν΄λμ lll.py λΏμ΄λ€.
python3 solve.py extracted/output.txt
"""
import re
import sys
from fractions import Fraction
from lll import lll
# ---------------------------------------------------------------- λ€νμ μ νΈ
def poly_mul(a, b):
out = [0] * (len(a) + len(b) - 1)
for i, x in enumerate(a):
if x:
for j, y in enumerate(b):
out[i + j] += x * y
return out
def poly_eval(c, x):
acc = 0
for coeff in reversed(c):
acc = acc * x + coeff
return acc
def poly_trim(c):
while len(c) > 1 and c[-1] == 0:
c.pop()
return c
def poly_mod(a, b):
"""a mod b (μ 리μ κ³μ). b λ 0 μ΄ μλμ΄μΌ νλ€."""
a, db = list(a), len(b) - 1
if db == 0:
return [Fraction(0)]
while len(a) - 1 >= db:
shift, factor = len(a) - 1 - db, a[-1] / b[-1]
for i, coeff in enumerate(b):
a[i + shift] -= factor * coeff
poly_trim(a)
if len(a) == 1 and a[0] == 0:
break
return a
def poly_gcd(a, b):
"""μ 리μ κ³μ λ€νμ GCD. κ³μλ₯Ό Fraction μΌλ‘ μ¬λ € μ νν κ³μ°νλ€."""
a = poly_trim([Fraction(x) for x in a])
b = poly_trim([Fraction(x) for x in b])
while not (len(b) == 1 and b[0] == 0):
a, b = b, poly_mod(a, b)
return [c / a[-1] for c in a] if a[-1] != 0 else a
# --------------------------------------------------- Coppersmith (known MSB)
def coppersmith_known_msb(n, a0, X, m=1, t=1, verbose=True):
"""f(x) = a0 + x κ° n μ λ―Έμ§ μΈμ p λ‘ λλμ΄λ¨μ΄μ§λ |x0| < X λ₯Ό μ°Ύλλ€."""
dim = m + t + 1
# g_i(x) = n^(m-i) * f(x)^i (i = 0..m) / h_j(x) = x^j * f(x)^m (j = 1..t)
fpow = [[1]]
for _ in range(m):
fpow.append(poly_mul(fpow[-1], [a0, 1]))
polys = [[c * pow(n, m - i) for c in fpow[i]] for i in range(m + 1)]
polys += [[0] * j + fpow[m] for j in range(1, t + 1)]
# x -> X*x λ‘ μΉνν΄ κ²©μλ₯Ό μ§ λ€. μ΄ μ€μΌμΌλ§μ΄ μμΌλ©΄ LLL μ΄ "μ§§λ€"κ³ λ³΄λ
# 벑ν°μ Howgrave-Graham μ΄ μꡬνλ "μμ κ³μ"κ° μλ‘ λ€λ₯Έ λ»μ΄ λλ€.
B = [[polys[r][k] * pow(X, k) if k < len(polys[r]) else 0
for k in range(dim)] for r in range(dim)]
det = 1
for r in range(dim):
det *= B[r][r]
if verbose:
print(f'[lattice] dim={dim} (m={m}, t={t})')
print(f'[lattice] log2(det) = {det.bit_length()}')
print(f'[lattice] log2(det)/dim = {det.bit_length() / dim:.1f}')
print(f'[lattice] λͺ©ν p^m = 2^{1024 * m} (p λ 1024λΉνΈ)')
R = lll(B)
roots = []
cand = []
for row in R:
coeffs = [row[k] // pow(X, k) for k in range(dim)]
norm2 = sum(c * c for c in row)
if verbose:
print(f'[lll] ||v|| = 2^{(norm2.bit_length() - 1) / 2:.1f}'
f' (Howgrave-Graham μꡬ: < p^{m}/sqrt({dim}) = 2^{1024 * m - 1})')
cand.append(poly_trim(coeffs))
# μλ‘ λ€λ₯Έ μ§§μ λ²‘ν° λ κ°μ μ΅λ곡μ½λ€νμμ΄ κ³§ (x - x0) λ€.
for i in range(len(cand)):
for j in range(i + 1, len(cand)):
g = poly_gcd(cand[i], cand[j])
if len(g) == 2:
r = -g[0] / g[1]
if r.denominator == 1:
roots.append(int(r))
# 보쑰 κ²½λ‘: κ°μ₯ μ§§μ λ²‘ν° νλλ§μΌλ‘ μ μ λ΄ν΄λ²
for c in cand[:2]:
r = _newton_int_root(c, X)
if r is not None:
roots.append(r)
out = []
for r in roots:
if 0 <= r < X and r not in out and n % (a0 + r) == 0:
out.append(r)
return out
def _newton_int_root(coeffs, X):
d = len(coeffs) - 1
if d < 1:
return None
dc = [i * coeffs[i] for i in range(1, d + 1)]
x = X
for _ in range(4000):
fx = poly_eval(coeffs, x)
if fx == 0:
return x
dfx = poly_eval(dc, x)
if dfx == 0:
return None
nx = x - fx // dfx
if nx == x:
return None
x = nx
return None
# ------------------------------------------------------------------- λ³Έ νμ΄
def main(path='extracted/output.txt'):
txt = open(path).read()
c = int(re.search(r'Here is the flag : (\d+)', txt).group(1))
msb = int(re.search(r'bits of p\.\.\.\s*\n(\d+)', txt).group(1))
n, e = (int(v) for v in re.search(r'\((\d+),(\d+)\)', txt).groups())
# λ¬Έμ μ½λκ° μ§μ΄ λΉνΈ μλ₯Ό κ·Έλλ‘ μ¬ννλ€.
beta = 0.4
epsilon = beta ** 2 / 7
lower_bound = int(n.bit_length() * (beta ** 2 - epsilon))
X = 1 << lower_bound
print(f'[param] n {n.bit_length()}λΉνΈ, e {e.bit_length()}λΉνΈ')
print(f'[param] μ§μμ§ νμ λΉνΈ = {lower_bound} -> X = 2^{lower_bound}')
print(f'[param] μλ €μ§ p μ λΉνΈ = {1024 - lower_bound}')
roots = coppersmith_known_msb(n, msb, X, m=1, t=1)
if not roots:
print('μ€ν¨: κ·Όμ μ°Ύμ§ λͺ»νλ€')
return 1
x0 = roots[0]
p = msb + x0
q = n // p
print(f'[root] x0 = {x0}')
print(f'[root] x0 λ {x0.bit_length()}λΉνΈ (μν {lower_bound}λΉνΈ)')
print(f'[check] n % p == 0 -> {n % p == 0}')
print(f'[check] p * q == n -> {p * q == n}')
d = pow(e, -1, (p - 1) * (q - 1))
m_int = pow(c, d, n)
flag = m_int.to_bytes((m_int.bit_length() + 7) // 8, 'big')
print(f'FLAG: {flag.decode()}')
return 0
if __name__ == '__main__':
sys.exit(main(*sys.argv[1:]))κ·Όμ μ°Ύλ λ§μ§λ§ λ¨κ³λ§ ν μ€ λ§λΆμΈλ€. LLL μ΄ λλ €μ€ μΈ νμ κ°κ° X^k λ‘ λλλ μ μ λ€νμμΌλ‘ λλλ¦¬κ³ , κ·Έμ€ λ κ°μ μ΅λ곡μ½λ€νμμ μ‘μΌλ©΄ (x - x0) λ§ λ¨λλ€. κ³μκ° 2000λΉνΈκΈμ΄λΌ μ 리근 μ λ¦¬λ‘ μΈμλΆν΄νλ 건 νμ€μ μ΄μ§ μμμ, GCD λ‘ 1μ°¨ μΈμλ₯Ό μ§μ λ½λ μͺ½μ ννλ€. μ€ν¨ν λλ₯Ό λλΉν΄ μ μ λ΄ν΄λ² κ²½λ‘λ νλ λκ³ , μ΅μ’
νμ μ n % p == 0 μ΄ νλ€.
python3 solve.py
0.42μ΄. n % p == 0 μ΄ True λ‘ λ¨μ΄μ‘κ³ νλκ·Έκ° κ·Έλλ‘ λμλ€.
DH{yeaaaahhh!!_C00p3rsM1th_1s_G0d_><}β κ΅μ°¨κ²μ¦ β λ€ κ°λλ‘
μ§μ μ§ κ²©μμ μ§μ μ§ LLL λ‘ λμ¨ λ΅μ΄λΌ ν λ² λ νμΈνλ€.
SageMath μ small_roots
κ°μ μΌμ νλ νμ€ κ΅¬νκ³Ό λμ‘°νλ€. Sage μ small_roots λ beta μ epsilon μ λ°μ λ΄λΆμμ m, t λ₯Ό μ νλ―λ‘, λ¬Έμ μ½λκ° μ΄ κ°μ κ·Έλλ‘ λκΈ°λ©΄ λλ€.
# tiny roots (DreamHack, Silver 1, crypto) - SageMath cross-check
# p μ μμ 744λΉνΈκ° κ³΅κ° -> Coppersmith(May) 'known MSB of factor'
import re
from Crypto.Util.number import long_to_bytes
txt = open('extracted/output.txt').read()
c = Integer(re.search(r'Here is the flag : (\d+)', txt).group(1))
msb = Integer(re.search(r'bits of p\.\.\.\s*\n(\d+)', txt).group(1))
n, e = [Integer(v) for v in re.search(r'\((\d+),(\d+)\)', txt).groups()]
beta = 0.4
epsilon = beta ** 2 / 7
X = 2 ** int(n.nbits() * (beta ** 2 - epsilon)) # = 2^280, λ―Έμ§ νμλΉνΈ μν
P.<x> = PolynomialRing(Zmod(n))
f = msb + x # f(x0) = p = 0 mod p
roots = f.monic().small_roots(X=X, beta=beta, epsilon=epsilon)
print('small_roots ->', roots)
x0 = Integer(roots[0])
p = msb + x0
print('p =', p)
print('n % p == 0 ?', n % p == 0)
q = n // p
d = inverse_mod(e, (p - 1) * (q - 1))
m = power_mod(c, d, n)
print('FLAG:', long_to_bytes(int(m)).decode())sage solve_sage.sage
x0 κ° μλ¦ΏμκΉμ§ λκ°μ΄ λμλ€.
openssl μ΄ μΈμ νλ κ°μΈν€μΈκ°
볡ꡬν p, q λ‘ PKCS#1 κ°μΈν€λ₯Ό μμΌλ‘ μ‘°λ¦½ν΄ openssl μκ² νμ μ λ§‘κ²Όλ€. openssl rsa -check λ p μ q μ μμμ±, d, CRT νλΌλ―Έν°μ μ ν©μ±κΉμ§ λ³Έλ€. μ°λ¦¬ νμ΄μ¬ μ½λμ μμ ν λ
립λ κ²μ¦μ΄λ€.
#!/usr/bin/env python3
"""볡ꡬν p, q λ‘ PKCS#1 RSAPrivateKey(PEM)λ₯Ό μ§μ 쑰립νλ€.
DER μ μμΌλ‘ μ§μ λΌμ΄λΈλ¬λ¦¬ λμ μμ΄ λ§λ€κ³ , κ²μ¦μ openssl μκ² λ§‘κΈ΄λ€.
"""
import base64
import re
from solve import coppersmith_known_msb
txt = open('extracted/output.txt').read()
msb = int(re.search(r'bits of p\.\.\.\s*\n(\d+)', txt).group(1))
n, e = (int(v) for v in re.search(r'\((\d+),(\d+)\)', txt).groups())
p = msb + coppersmith_known_msb(n, msb, 1 << 280, m=1, t=1, verbose=False)[0]
q = n // p
assert p * q == n and p > q
d = pow(e, -1, (p - 1) * (q - 1))
def der_len(l):
if l < 0x80:
return bytes([l])
b = l.to_bytes((l.bit_length() + 7) // 8, 'big')
return bytes([0x80 | len(b)]) + b
def der_int(v):
b = v.to_bytes((v.bit_length() + 8) // 8, 'big') if v else b'\x00'
return b'\x02' + der_len(len(b)) + b
body = b''.join(der_int(v) for v in
(0, n, e, d, p, q, d % (p - 1), d % (q - 1), pow(q, -1, p)))
der = b'\x30' + der_len(len(body)) + body
b64 = base64.encodebytes(der).decode().replace('\n', '')
pem = ('-----BEGIN RSA PRIVATE KEY-----\n'
+ '\n'.join(b64[i:i + 64] for i in range(0, len(b64), 64))
+ '\n-----END RSA PRIVATE KEY-----\n')
open('tiny_roots_priv.pem', 'w').write(pem)
print(f'wrote tiny_roots_priv.pem ({len(der)} bytes DER)')#!/usr/bin/env bash
# 볡ꡬν p, q λ‘ λ§λ κ°μΈν€λ₯Ό openssl μ΄ μΈμ νλμ§ λ³Έλ€.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
python3 make_key.py
openssl rsa -in tiny_roots_priv.pem -check -noout
echo "openssl modulus : $(openssl rsa -in tiny_roots_priv.pem -noout -modulus | cut -c9-88)"
python3 -c "
import re
n = int(re.search(r'\((\d+),(\d+)\)', open('extracted/output.txt').read()).group(1))
print('output.txt μ n :', format(n, 'X')[:80])"./verify_openssl.sh
RSA key ok. λͺ¨λλ¬μ€λ output.txt μ n κ³Ό μΌμΉνλ€.
μ΄ μΈμ€ν΄μ€μλ§ λ§μΆ νμ΄κ° μλκ°
λ¬Έμ μ½λμ λκ°μ λ°©μμΌλ‘ RSA μΈμ€ν΄μ€λ₯Ό μλ‘ λ§λ€μ΄ 볡ꡬ 루ν΄μ κ·Έλλ‘ λλ Έλ€. μ΄ λ¬Έμ νλμ μ°μ°ν λ§μ κ² μλλΌλ νμΈμ΄λ€.
#!/usr/bin/env python3
"""λ¬Έμ μ½λμ λκ°μ λ°©μμΌλ‘ RSA μΈμ€ν΄μ€λ₯Ό μλ‘ λ§λ€μ΄ solve.py μ 볡ꡬ 루ν΄μ λλ¦°λ€.
μ΄ λ¬Έμ νλμλ§ λ§μΆ νμ΄κ° μλλΌλ κ²μ νμΈνλ μ©λλ€.
"""
import time
from Crypto.Util.number import getPrime, bytes_to_long, long_to_bytes
from solve import coppersmith_known_msb
ROUNDS = 5
ok = 0
for r in range(1, ROUNDS + 1):
plain = f'DH{{selftest_round_{r}}}'.encode()
m = bytes_to_long(plain)
p = getPrime(1024)
q = getPrime(1024)
if p < q:
p, q = q, p
n, e = p * q, getPrime(128)
beta = 0.4
epsilon = beta ** 2 / 7
upper_bound = p.bit_length()
lower_bound = int(n.bit_length() * (beta ** 2 - epsilon))
MSB = p & (pow(2, upper_bound) - pow(2, lower_bound)) # λ¬Έμ μ½λ κ·Έλλ‘
ct = pow(m, e, n)
st = time.time()
roots = coppersmith_known_msb(n, MSB, 1 << lower_bound, m=1, t=1, verbose=False)
el = time.time() - st
if not roots:
print(f'round {r}: μ€ν¨ ({el:.2f}s)')
continue
pp = MSB + roots[0]
qq = n // pp
d = pow(e, -1, (pp - 1) * (qq - 1))
got = long_to_bytes(pow(ct, d, n))
good = (pp == p and got == plain)
ok += good
print(f'round {r}: {el:.2f}s p 볡ꡬ {pp == p} λ³΅νΈ {got.decode()} '
f'{"OK" if good else "NG"}')
print(f'\n{ok}/{ROUNDS} μ±κ³΅')python3 selftest.py
5ν μ λΆ p λ₯Ό μ νν 볡ꡬνκ³ νλ¬Έλ κ·Έλλ‘ λμλ€. νλΉ 0.4μ΄ μμͺ½μ΄λ€.
ν΄λλ§ μμΌλ©΄ λ€μ λμ€λκ°
λ°°ν¬λ³Έκ³Ό μ€ν¬λ¦½νΈλ§ μμΌλ©΄ ν λ²μ νλκ·ΈκΉμ§ λμ€λλ‘ μ¬ν μ€ν¬λ¦½νΈλ₯Ό λ¨κ²Όλ€.
#!/usr/bin/env bash
# tiny roots (DreamHack Silver 1, crypto) β ν λ°© μ¬ν.
# λ°°ν¬λ³Έ(extracted/output.txt)λ§ μμΌλ©΄ flag κΉμ§ λμ¨λ€. μλ²Β·ν¬λ λ§ λΆνμ.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
EXPECT=$(python3 -c "import json;print(json.load(open('λ¬Έμ .json'))['flag'])" 2>/dev/null || echo '')
OUT=$(timeout 300 python3 solve.py "$@" 2>&1) || true
echo "$OUT" | tail -8
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | head -1)
if [ -n "$FLAG" ] && { [ -z "$EXPECT" ] || [ "$FLAG" = "$EXPECT" ]; }; then
echo; echo "β
PASS $FLAG"; exit 0
fi
echo; echo "β FAIL (μ»μ κ°: '${FLAG:-μμ}' / κΈ°λ: '$EXPECT')"; exit 1./reproduce.sh
π κ²°λ‘
λ ΈμΆ λΉνΈμ "λΉμ¨"μ΄ μμ μ μ μ νλ€
RSA μμμ μμ μ λ°μ΄ μλ €μ§λ©΄ κ·Έ μκ° λμ΄λ€. p κ° λλ΅ N^0.5 μΌ λ Coppersmith κ° κ°λΉνλ λ―Έμ§μλ N^0.25, μ¦ 1024λΉνΈ μμλΌλ©΄ νμ 512λΉνΈκΉμ§ λͺ°λΌλ 볡ꡬλλ€. μ΄ λ¬Έμ λ 280λΉνΈλ§ μ§μ μΌλ μ μ΄μ μ λ° μ΄μ μ¬μ κ° μμλ€.
λΆλΆ λ ΈμΆμ λΆλΆ νΌν΄κ° μλλ€
ν€μ μΌλΆλ§ μλ μ¬κ³ λ ννλ€. λ‘κ·Έμ μ°ν μλ¦° κ°, λ°±μ μμ 볡ꡬλ μ‘°κ°, μ¬μ΄λμ±λλ‘ μ»μ μμ λΉνΈ. λμΉν€ κ°κ°μΌλ‘λ "λͺ λΉνΈ μμ λΏ"μ΄μ§λ§ RSA μμμμλ λ€λ₯΄λ€. μ λ°μ΄ μλ©΄ μ λΆ μ κ²μ΄κ³ , μ λ°λ³΄λ€ μ‘°κΈ λ μλ 격μκ° λλ¨Έμ§λ₯Ό λ©μ΄λ€. μμμ μΌλΆλΌλ λ ΈμΆλ ν€λ νκΈ° λμμΌλ‘ λ΄μΌ νλ€.
격μκ° μ»€μΌ νλ€λ κ°κ°μ μμ¬ν κ²
Coppersmith μ€λͺ
μλ λ "μΆ©λΆν ν° m"μ΄ λΆμ§λ§, μ€μ λ‘ νμν ν¬κΈ°λ λ―Έμ§μκ° μ΄λ‘ νκ³μ μΌλ§λ κ°κΉμ΄μ§λ‘ μ ν΄μ§λ€. μ΄λ² λ¬Έμ μ²λΌ μ¬μ κ° ν¬λ©΄ 3μ°¨μ 격μλ‘ λλλ€. m μ ν€μ°λ©΄ κ³μ°λλ§ νμ¦νκ³ λ΅μ κ·Έλλ‘λ€.
νλ¦° 격μλ μμΈλ₯Ό μ λΈλ€
X μ€μΌμΌλ§ λλ½, μ΄λ λ€νμ λλ½, μν μ€λ
μ
λ€ μ€νμ μ μμ΄κ³ κ²°κ³Όλ§ λΉμ΄ λμ¨λ€. κ·Έλμ 격μ νμ΄μμλ "μ μ λμ§"λ₯Ό μ€λ λΆλ€κΈ° μ μ, 격μ 쑰건μ μ«μλ‘ λ¨Όμ νμΈνλ κ² λ«λ€. log2(det)/dim κ³Ό p^m μ λλν μ°μ΄ 보면 κ·Έ μ리μμ κ°λ¦°λ€.
Comments
λκΈ
λκΈμ λ¨κΈ°λ €λ©΄ λ‘κ·ΈμΈμ΄ νμν΄μ. (λ€μ΄λ² Β· κ΅¬κΈ κ³μ )
λκΈ λΆλ¬μ€λ μ€β¦