๋ฌธ์ : DreamHack โ crt rsa ๋ถ๋ฅ: Crypto ๋์ด๋: ๐ฅ Silver 3 FLAG:
DH{B45ic_Crt_R54_w1th_A_bit_0f_f3rmat_fact0r}
RSA ๋ฌธ์ ์ธ๋ฐ ์ฃผ๋ ๊ฐ์ด ํ์์ ๋ค๋ฅด๋ค. ๋ชจ๋๋ฌ์ค N, ๊ทธ๋ฆฌ๊ณ dp, dq, qinv ์ธ ๊ฐ, ๋ง์ง๋ง์ผ๋ก ์ํธ๋ฌธ ํ๋. ๊ณต๊ฐ ์ง์ e ๋, ๊ฐ์ธํค d ๋, ์์ pยทq ๋ ์๋ค. ์ด๋ฆ์ด "crt rsa"์ธ ๋งํผ ์ด ์ธ ๊ฐ์ RSA๋ฅผ CRT(์ค๊ตญ์ธ์ ๋๋จธ์ง ์ ๋ฆฌ)๋ก ๋น ๋ฅด๊ฒ ๋ณตํธํ ๋ ์ฐ๋ ํ๋ผ๋ฏธํฐ๋ค์ด๋ค.
ํฌ์ธํธ๋ ๋ ๊ฐ์ง๋ค. ํ๋๋ ์ด dpยทdqยทqinv ๊ฐ ์์ธ์๋ง ์๋ฉด ๊ทธ๋๋ก ๋ณตํธ์ ์ธ ์ ์๋ ๊ฐ์ด๋ผ๋ ๊ฒ, ๋ค๋ฅธ ํ๋๋ ์ถ์ ์๊ฐ ์์๋ฅผ p = nextprime(q + 1) ๋ก ๋ฝ์ ๋ ์์๋ฅผ ๋ถ์ฌ ๋์ ๊ฒ. 2048๋นํธ ๋ชจ๋๋ฌ์ค๋ผ ๊ฒ์ ์ฃผ์ง๋ง, ์ด ๋ ๋ฒ์งธ ์ฌ์ค์ด ์ธ์๋ถํด๋ฅผ ํต์งธ๋ก ์ด์ด ์ค๋ค.
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | crt rsa |
| ๋์ด๋ | ๐ฅ Silver 3 |
| ๋ถ๋ฅ | Crypto |
| ์ ๊ณต ํ์ผ | prob.py, output.txt (Nยทdpยทdqยทqinvยทencrypted flag) |
| ํต์ฌ ์ทจ์ฝ์ | ์ฐ์ ์์ โ Fermat ์ธ์๋ถํด + CRT ํ๋ผ๋ฏธํฐ ์ง์ ๋ณตํธ |
prob.py ๋ flag ๋ฅผ RSA ๋ก ์ํธํํ๊ณ output.txt ์ ๊ณต๊ฐ๊ฐ์ ๋ฑ๋๋ค. ์์ pยทq ์ ๊ฐ์ธํค d ๋ ๋ฌผ๋ก ์จ๊ฒจ์ ธ ์๋ค. ์ฐ๋ฆฌ๊ฐ ํด์ผ ํ ๊ฑด (1) N ์ ์ธ์๋ถํดํ๊ณ , (2) ์ฃผ์ด์ง CRT ํ๋ผ๋ฏธํฐ๋ก ๋ณตํธํ๋ ๊ฒ.
๐งฉ ๋ฐฐ๊ฒฝ โ RSA-CRT ๋ณตํธ๋
์ผ๋ฐ RSA ๋ณตํธ๋ m = c^d mod N ํ ๋ฐฉ์ด์ง๋ง, d ๊ฐ 2048๋นํธ๋ผ ์ด ๊ฑฐ๋ญ์ ๊ณฑ์ ๋๋ฆฌ๋ค. ๊ทธ๋์ ์ค์ ๊ตฌํ์ ๋ชจ๋๋ฌ์ค๋ฅผ ์์ ๋จ์๋ก ์ชผ๊ฐ๋ CRT ์ต์ ํ๋ฅผ ์ด๋ค. ๋ฏธ๋ฆฌ ๋ค์ ์ธ ๊ฐ์ ์ค๋นํด ๋๋ค.
dp = d mod (p - 1)โp์ชฝ ์ง์dq = d mod (q - 1)โq์ชฝ ์ง์qinv = qโปยน mod pโ ๋ ๊ฒฐ๊ณผ๋ฅผ ํฉ์น ๋ ์ฐ๋ ๊ณ์
๋ณตํธ๋ ๊ฐ ์์์์ ๋ฐ๋ก ๊ฑฐ๋ญ์ ๊ณฑํ ๋ค(๋ชจ๋๋ฌ๊ฐ ์ ๋ฐ ํฌ๊ธฐ๋ผ ํจ์ฌ ๋น ๋ฅด๋ค) Garner ๊ณต์์ผ๋ก ํฉ์น๋ค.

์ฌ๊ธฐ์ ์ค์ํ ์ฌ์ค ํ๋. ์ด ๋ณตํธ ๊ณผ์ ์๋ ๊ณต๊ฐ ์ง์ e ๋, ์๋ ๊ฐ์ธํค d ๋ ๋ฑ์ฅํ์ง ์๋๋ค. ํ์ํ ๊ฑด ์์ pยทq ์ ์ด๋ฏธ ์ฃผ์ด์ง dpยทdqยทqinv ๋ฟ์ด๋ค. ์ฆ ์ด ๋ฌธ์ ๋ "๋ณตํธ์ ํ์ํ ์ฌ๋ฃ๋ฅผ ์ด๋ฏธ ๋ค ์คฌ๋๋ฐ, ๋ฑ ํ๋ ์์ธ์๋ง ์ ์ค" ์ํ๋ค. ๊ทธ๋ ๋ค๋ฉด ๋จ์ ์ผ์ N ์ ์ธ์๋ถํดํ๋ ๊ฒ๋ฟ์ด๋ค.
๐ฌ ์ฝ๋ ์ ์ฐฐ
prob.py ๋ถํฐ ๋ณธ๋ค.
์ด ๊ธ์ ๋ช ๋ น์ ๋ชจ๋ ๋ฌธ์ ํด๋ ์ต์์(
extracted/์ ๋ถ๋ชจ) ์์ ์คํํ๋ค.
cat extracted/prob.py
ํต์ฌ์ ์์๋ฅผ ๋ง๋๋ ์ธ ์ค์ด๋ค.
q = getPrime(1024)
p = nextprime(q + 1) # โ q ๋ฐ๋ก ๋ค์ ์์. p ์ q ๊ฐ ๋ถ์ด ์๋ค
N = p * qgetPrime(1024) ๋ก q ๋ฅผ ๋ฝ์ ๋ค, p ๋ฅผ nextprime(q + 1) โ ์ฆ q ๋ฐ๋ก ๋ค์ ์์ โ ๋ก ์ก๋๋ค. 1024๋นํธ ๊ทผ์ฒ์์ ์ฐ์ํ ์์ ์ฌ์ด ๊ฐ๊ฒฉ์ ํ๊ท ์๋ฐฑ ์ ๋๋ผ, p ์ q ๋ ์ฌ์ค์ ๋ฑ ๋ถ์ด ์๋ค. ์ด๊ฒ ์ด ๋ฌธ์ ์ ๊ธ์๋ค.
๊ทธ๋ฆฌ๊ณ ์ถ๋ ฅ๋ถ๋ N, dp, dq, qinv, encrypted flag ๋ง ์ฐ๋๋ค. e ์ d ๋ ํ๋ฉด์ ์๋ค. output.txt ๊ฐ ์ค์ ๋ก ๋ฌด์์ ์ฃผ๋์ง ํฌ๊ธฐ๋ถํฐ ํ์ธํด ๋ดค๋ค.
# inspect_params.py โ output.txt ๊ฐ ์ฃผ๋ ๊ฐ์ ์ ์ฒด ํ์ธ
import re
raw = open("extracted/output.txt").read()
for k in ("N", "dp", "dq", "qinv"):
v = int(re.search(rf"{k}\s*=\s*(\d+)", raw).group(1))
print(f"{k:>4} : {v.bit_length():>4} bits")
c = int(re.search(r"encrypted flag\s*=\s*(\d+)", raw).group(1))
print(f"{'c':>4} : {c.bit_length():>4} bits (encrypted flag)")
N ์ 2048๋นํธ, dpยทdqยทqinv ๋ ์์ ํฌ๊ธฐ(1024๋นํธ๊ธ). ๊ณต๊ฐ ์ง์๋ ๊ฐ์ธํค๋ ์๋ค. ๋ฐฐ๊ฒฝ์์ ์ ๋ฆฌํ ๋๋ก, ์ฃผ์ด์ง ์ธ ๊ฐ์ CRT ๋ณตํธ ํ๋ผ๋ฏธํฐ ๊ทธ ์์ฒด๋ค.
๐ ์ฝ์ง
โถ๐ ์ฝ์ง โ ๊ทธ๋ฅ ์ธ์๋ถํด๋ถํฐ ํ๋ ค๋ค 2048๋นํธ ๋ฒฝ์ ๋งํ
CRT ํ๋ผ๋ฏธํฐ๊ฐ ๋ณตํธ์ ์ฐ์ธ๋ค๋ ๊ฑด ๊ธ๋ฐฉ ๋ณด์์ง๋ง, ๊ทธ๋ฌ๋ ค๋ฉด pยทq ๊ฐ ํ์ํ๋ค. ๊ทธ๋์ "์ผ๋จ N ์ ์ธ์๋ถํดํ์"๋ก ๋ฌ๋ ค๋ค์๋ค. ๋ฌธ์ ๋ N ์ด 2048๋นํธ ๋ฐ์์(semiprime)๋ผ๋ ๊ฒ. ์์ ์์ธ์๋ผ๋ ์๋ ํ์ด๋ดค๋ค.
# naive_factor.py โ ์์ ์์ธ์ ์ค์บ ํ ์ผ๋ฐ ์ธ์๋ถํด๋ก ๋์ด๊ฐ๋ ค๋ ์๋
import re, time
from sympy import primerange
N = int(re.search(r"N\s*=\s*(\d+)", open("extracted/output.txt").read()).group(1))
print(f"N : {N.bit_length()} bits")
t = time.time()
small = None
for pr in primerange(2, 300000):
if N % pr == 0:
small = pr
break
print(f"์์ ์์ธ์ (< 300000) : {small}")
print(f"์ค์บ ์๊ฐ : {time.time() - t:.2f}s")
์์ ์์ธ์๋ ์๋ค. trial division ๋, Pollard rhoยทp-1 ๋ 2048๋นํธ ๋ฐ์์ ์์์๋ ์ฌ์ค์ ๋ฉ์ถ๋ค. ์ฌ๊ธฐ์ "e ๊ฐ ์์ผ๋ dpยทdq ๋ก d ๋ฅผ ๋ณต์ํ ๋ค์ ํ์ค ๋ณตํธ๋ฅผ ํ์"๋ก ๋ฐฉํฅ์ ํ์ด ๋ดค์ง๋ง, d ๋ฅผ ๋ณต์ํ๋ ค ํด๋ ๊ฒฐ๊ตญ p-1, q-1 ์ ์์์ผ ํ๋ ๋ค์ ์ธ์๋ถํด๋ก ๋๋์์จ๋ค. ๋ฑ
๋ฑ
๋๋ค prob.py ์ p = nextprime(q + 1) ํ ์ค๋ก ๋์์๋ค โ ์์๊ฐ ๋ถ์ด ์์ผ๋ฉด ๊ตณ์ด ์ผ๋ฐ ์ธ์๋ถํด๊ฐ ํ์ ์๋ค.
๐ฃ ํต์ฌ โ ์ฐ์ ์์๋ผ Fermat์ด 0์คํ ์ ๋ซ๋ฆฐ๋ค
p ์ q ๊ฐ ๋ถ์ด ์๋ค๋ ๊ฑด Fermat ์ธ์๋ถํด๊ฐ ๋ฑ ๋ง๋ ์ํฉ์ด๋ผ๋ ๋ป์ด๋ค. Fermat ์ธ์๋ถํด๋ ํ์ N ์ ๋ ์ ๊ณฑ์์ ์ฐจ๋ก ๋ณธ๋ค.
N = aยฒ โ bยฒ = (a + b)(a โ b)์ฌ๊ธฐ์ a = (p + q) / 2, b = (p โ q) / 2 ๋ค. ๋ ์์๊ฐ ๊ฐ๊น์ฐ๋ฉด b ๊ฐ ์์ฃผ ์๊ณ , a ๋ โN ๋ฐ๋ก ์์ ์๋ค. ๊ทธ๋์ a ๋ฅผ โโNโ ๋ถํฐ ํ๋์ฉ ์ฌ๋ฆฌ๋ฉฐ aยฒ โ N ์ด ์์ ์ ๊ณฑ(= bยฒ)์ด ๋๋ ์๊ฐ์ ์ฐพ์ผ๋ฉด ๋๋ค. ์์๊ฐ ๋ถ์ด ์์์๋ก ๊ทธ ์๊ฐ์ด ๋นจ๋ฆฌ ์จ๋ค.

์ด ๋ฌธ์ ์์ ์ผ๋ง๋ ๊ฐ๊น์ด์ง ์ง์ ํ์ธํด ๋ดค๋ค. sympy ๋ก ๋์จ ๋ ์์๊ฐ ์ค์ ์์์ธ์ง, ๊ทธ๋ฆฌ๊ณ ์ ๋ง ์ฐ์ ์์(p == nextprime(q + 1))์ธ์ง๊น์ง ๊ฒ์ฆํ๋ค.
# factor_check.py โ Fermat ์ผ๋ก ๊ฐ๋ผ ๋ณด๊ณ ์ฐ์ ์์์ธ์ง ํ์ธ
from math import isqrt
from sympy import isprime, nextprime
import re
N = int(re.search(r"N\s*=\s*(\d+)", open("extracted/output.txt").read()).group(1))
a = isqrt(N)
if a * a < N:
a += 1
steps = 0
while True:
b2 = a * a - N
b = isqrt(b2)
if b * b == b2:
break
a += 1
steps += 1
p, q = a + b, a - b
print(f"Fermat steps : {steps}")
print(f"p * q == N : {p * q == N}")
print(f"p, q ๊ฐ ์์์ธ๊ฐ : {isprime(p)}, {isprime(q)}")
print(f"p == nextprime(q+1): {p == nextprime(q + 1)}")
print(f"p - q : {p - q}")
Fermat steps = 0. ์ฆ a = โโNโ ๊ทธ ๊ฐ์์ ์ด๋ฏธ aยฒ โ N ์ด ์์ ์ ๊ณฑ์ด์๋ค. ๋ ์์ ์ฐจ์ด๋ ๊ฒจ์ฐ 672. 2048๋นํธ ๋ชจ๋๋ฌ์ค๊ฐ ๋จ ํ ๋ฒ์ ์ ๊ณฑ๊ทผ ๊ณ์ฐ์ผ๋ก ๊ฐ๋ผ์ง ๊ฒ์ด๋ค. p == nextprime(q + 1) ๋ True ๋ผ, ์ฐ๋ฆฌ๊ฐ ๋ณต์ํ ์์๊ฐ ์ถ์ ์๊ฐ ๋ง๋ ๊ทธ ์์ ์์ด ๋ง๋ค.
์ด์ ์์ธ์๋ฅผ ์ป์์ผ๋ ๋ฐฐ๊ฒฝ์ CRT ๋ณตํธ๋ฅผ ๊ทธ๋๋ก ๋๋ฆฌ๋ฉด ๋๋ค.
m1 = c^dp mod p
m2 = c^dq mod q
h = qinv ยท (m1 โ m2) mod p # qinv = qโปยน mod p
m = m2 + h ยท q๐ฏ Full Exploit
output.txt ์์ ๊ฐ์ ํ์ฑํด Fermat ์ธ์๋ถํด โ CRT ๋ณตํธ๊น์ง ํ ๋ฒ์ ํ๋ ์คํฌ๋ฆฝํธ๋ค. ๊ฐ์ ํ๋์ฝ๋ฉํ์ง ์๊ณ ๋ฌธ์ ํ์ผ์์ ๊ทธ๋๋ก ์ฝ๋๋ค.
# solve.py
from math import isqrt
from Crypto.Util.number import long_to_bytes
import re, os
here = os.path.dirname(os.path.abspath(__file__))
raw = open(os.path.join(here, "extracted", "output.txt")).read()
vals = {}
for key in ("N", "dp", "dq", "qinv"):
vals[key] = int(re.search(rf"{key}\s*=\s*(\d+)", raw).group(1))
c = int(re.search(r"encrypted flag\s*=\s*(\d+)", raw).group(1))
N, dp, dq, qinv = vals["N"], vals["dp"], vals["dq"], vals["qinv"]
# 1) Fermat factorization: N = a^2 - b^2 = (a+b)(a-b)
a = isqrt(N)
if a * a < N:
a += 1
steps = 0
while True:
b2 = a * a - N
b = isqrt(b2)
if b * b == b2:
break
a += 1
steps += 1
p, q = a + b, a - b
assert p * q == N and p > q
print(f"[+] Fermat steps = {steps} (p-q = {p - q})")
print(f"[+] p = {p}")
print(f"[+] q = {q}")
# 2) RSA-CRT ๋ณตํธ (e, d ๋ถํ์)
m1 = pow(c, dp, p)
m2 = pow(c, dq, q)
h = (qinv * (m1 - m2)) % p
m = m2 + h * q
flag = long_to_bytes(m)
print(f"[+] FLAG = {flag.decode()}")
์ถ๋ ฅ๋ p ์ q ๋ฅผ ๋ณด๋ฉด ์์๋ฆฌ๊ฐ ์ ๋ถ ๊ฐ๊ณ ๋ ์ธ ์๋ฆฌ๋ง ๋ค๋ฅด๋ค(...798109 vs ...797437). ๋์ผ๋ก ๋ด๋ ๋ ์์๊ฐ ๋ถ์ด ์๋ค๋ ๊ฒ ๋ณด์ธ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง ์ค์ ํ๋๊ทธ๊ฐ ๋จ์ด์ง๋ค.
DH{B45ic_Crt_R54_w1th_A_bit_0f_f3rmat_fact0r}โ ๊ฒ์ฆ
ํ๋๊ทธ ๋ฌธ์์ด ์์ฒด๊ฐ ํ์ด๋ฅผ ๊ทธ๋๋ก ์์ฝํ๋ค โ B45ic_Crt_R54_w1th_A_bit_0f_f3rmat_fact0r, ์ฆ "์ฝ๊ฐ์ Fermat ์ธ์๋ถํด๋ฅผ ๊ณ๋ค์ธ ๊ธฐ๋ณธ CRT-RSA". ์ฐ๋ฆฌ๊ฐ ์ด ๋ ๋๊ตฌ(์ฐ์ ์์ Fermat ์ธ์๋ถํด + CRT ํ๋ผ๋ฏธํฐ ๋ณตํธ)๊ฐ ์๋ํ ์ ๋ต ๊ฒฝ๋ก์๋ค๋ ํ์ธ์ด๋ค. ์ด ๋ฌธ์ ๋ needs_vm ์ด ์๋ ์คํ๋ผ์ธ ๋ฌธ์ ๋ผ, ์๋ฒ ์์ด ๋ก์ปฌ์์ ๊ฐ์ ๋ณต์ํด ๊ทธ๋๋ก ์ ์ถํ๋ค.
๐ ๊ฒฐ๋ก โ ๋ฌด์์ด ๋ฌธ์ ์๋
RSA์ ์์ ์ฑ์ "ํฐ ๋ชจ๋๋ฌ์ค๋ ์ธ์๋ถํด๊ฐ ์ด๋ ต๋ค"์ ๊ธฐ๋๋ค. ํ์ง๋ง ๊ทธ๊ฑด ๋ ์์๊ฐ ๋ฌด์์๋ก ๋ฉ๋ฆฌ ๋จ์ด์ ธ ์์ ๋ ์๊ธฐ๋ค. ์ด ๋ฌธ์ ๋ ๋ ๊ณณ์์ ๊ทธ ์ ์ ๋ฅผ ๋ฌด๋๋จ๋ ธ๋ค.
- ์ฐ์ ์์:
p = nextprime(q + 1)์ ํธ์์ ๊ทธ๋ด๋ฏํด ๋ณด์ด์ง๋ง, ๋ ์์๋ฅผ ๋ถ์ฌ ๋์ผ๋ฉดโN๊ทผ์ฒ์ ์์ธ์๊ฐ ๋ชฐ๋ ค Fermat ์ธ์๋ถํด๊ฐ ์ฆ์ ํตํ๋ค. ๋นํธ ์(2048)๋ ๋ฐฉ์ด๊ฐ ๋์ง ๋ชปํ๋ค. ์์๋ ๋ฐ๋์ ๋ ๋ฆฝ์ ์ผ๋ก, ์ถฉ๋ถํ ๋จ์ด์ง๊ฒ ๋ฝ์์ผ ํ๋ค(๊ทธ๋์ ํ์ค ํค ์์ฑ์|p โ q|ํํ์ ๋๋ค). - CRT ํ๋ผ๋ฏธํฐ ๋
ธ์ถ:
dpยทdqยทqinv๋ ๊ฐ์ธํค์ ์ผ๋ถ๋ค. ์ด ๊ฐ๋ค์ ์์ธ์๋ง ํ๋ณด๋๋ฉดeยทd์์ด๋ ๊ณง์ฅ ๋ณตํธ๋ก ์ด์ด์ง๋ค. CRT ์ต์ ํ ๊ฐ์ ๊ฐ์ธํค์ ๋๊ธ์ผ๋ก ์ทจ๊ธํด ์ ๋ ๊ณต๊ฐํ์ง ์์์ผ ํ๋ค.
๋ ์ค์ ์ค ํ๋๋ง ์์ด๋ ์ํํ๋ฐ, ์ด ๋ฌธ์ ๋ ๋์ ๊ฒน์ณ ๋จ๋ค. ๋ฐฉ์ด์์ ๊ตํ์ ๋จ์ํ๋ค โ ์์๋ ๋ ๋ฆฝยท์ถฉ๋ถํ ๊ฐ๊ฒฉ์ผ๋ก, CRT ํ๋ผ๋ฏธํฐ๋ ๊ฐ์ธํค์ฒ๋ผ.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ