๋ฌธ์ : DreamHack โ uncommon e ๋ถ๋ฅ: Crypto ๋์ด๋: ๐ฅ Silver 3 FLAG:
DH{7e532757ebddde149f45c32156e58421e95b5c691b668f1681677af9495f7e36}
RSA ๋ฌธ์ ์ธ๋ฐ ์ฃผ๋ ๊ฒ ์ข ํน์ดํ๋ค. ๋ชจ๋๋ฌ์ค N ํ๋์, ์ง์๊ฐ e1, e2 ๋ ๊ฐ, ๊ทธ๋ฆฌ๊ณ ๊ฐ์ ํ๋๊ทธ๋ฅผ ๊ฐ๊ฐ์ผ๋ก ์ํธํํ ์ํธ๋ฌธ c1, c2 ๊ฐ ์ ๋ถ๋ค. ์์๋, ๊ฐ์ธํค๋ ์๋ค.
์ง์๋ฅผ ๋ ๊ฐ๋ ์ค๋ค๋ ๊ฒ ์ค๋ง๋ฆฌ๋ค. ๊ฐ์ N, ๊ฐ์ ํ๋ฌธ์ ์๋ก ๋ค๋ฅธ ์ง์๋ก ๋ ๋ฒ ์ํธํํ๋ค๋ฉด ๊ณตํต ๋ชจ๋๋ฌ์ค ๊ณต๊ฒฉ(Common Modulus Attack) ์ด ๋ฐ๋ก ๋ ์ค๋ฅธ๋ค. ์ฌ๊ธฐ์ ์ด ๋ฌธ์ ๋ "e ๊ฐ uncommon ํ๋ค"๋ ์ด๋ฆ๊ฐ์ ํ๋ ํจ์ ์ ํ๋ ๋ ์น์๋ค.
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | uncommon e |
| ๋์ด๋ | ๐ฅ Silver 3 |
| ๋ถ๋ฅ | Crypto (RSA) |
| ์ ๊ณต ํ์ผ | prob.py, output.txt |
| ํต์ฌ ์ทจ์ฝ์ | ๊ฐ์ Nยทํ๋ฌธ์ gcd ๊ฐ 1์ธ ๋ ์ง์๋ก ์ํธํ โ ๊ณตํต ๋ชจ๋๋ฌ์ค ๊ณต๊ฒฉ |
ํ์ผ๋ง์ผ๋ก ๋๋๋ ์คํ๋ผ์ธ ๋ฌธ์ ๋ผ ์๋ฒ๋ ํ์ ์๋ค. output.txt ์ ๋ฐํ N, e1, e2, c1, c2 ๋ง์ผ๋ก ํ๋ฌธ์ ๋ณต์ํ๋ค.
ํ์ด ํ๋ฆ์ ์งง๋ค. ์์ค์์ ์ ๊ฐ๋ณ ๋ณตํธ๊ฐ ๋งํ ์๋์ง ํ์ธํ๊ณ โ gcd(e1, e2) = 1 ์ ๊ทผ๊ฑฐ๋ก ํ์ฅ ์ ํด๋ฆฌ๋๋ฅผ ๋๋ ค โ c1^a ยท c2^b ๋ก ํ๋ฌธ์ ๋ฐ๋ก ๋ฝ๋๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ RSA ์ ๊ณต๊ฐ ์ง์ e
RSA ๋ ๊ณต๊ฐํค (N, e) ๋ก ์ํธํํ๊ณ ๊ฐ์ธํค d ๋ก ๋ณตํธํ๋ค.
์ํธํ : c = m^e mod N
๋ณตํธํ : m = c^d mod N์ฌ๊ธฐ์ d ๋ e ์ ๋ชจ๋๋ฌ ์ญ์, ์ฆ d = eโปยน mod ฯ(N) ์ด๋ค. ฯ(N) = (p-1)(q-1) ์ ์ค์ผ๋ฌ ํ์ด ํจ์๊ณ , ์ด ์ญ์์ด ์กด์ฌํ๋ ค๋ฉด gcd(e, ฯ(N)) = 1 ์ด์ด์ผ ํ๋ค. ๊ทธ๋์ ๋ณดํต e = 65537 ๊ฐ์ ์์๋ฅผ ์ด๋ค โ ฯ ์ ์๋ก์์ผ ํ๋ฅ ์ด ๋์ผ๋๊น.
์ด ์กฐ๊ฑด์ด ๊นจ์ง๋ฉด ์ด๋ป๊ฒ ๋ ๊น. gcd(e, ฯ(N)) > 1 ์ด๋ฉด e ๋ ฯ ์์ ์ญ์์ด ์๋ค. ๊ฐ์ธํค d ์์ฒด๋ฅผ ๋ง๋ค ์ ์์ผ๋, ๊ทธ ์ง์๋ก ์ํธํ๋ ์ํธ๋ฌธ์ ํ์ค์ ์ธ ๋ฐฉ๋ฒ์ผ๋ก๋ ๋ณตํธ๊ฐ ์ ๋๋ค.

์ด ๋ฌธ์ ์ด๋ฆ์ด "uncommon e" ์ธ ์ด์ ๊ฐ ์ฌ๊ธฐ ์๋ค. ํํ(common) e = 65537 ์ ์ ์ฐ๊ณ , ์ผ๋ถ๋ฌ ฯ ์ ๊ณต์ฝ์๋ฅผ ๊ฐ๋ ์ง์๋ฅผ ๊ณจ๋๋ค. ๊ฐ๋ณ ๋ณตํธ๋ฅผ ๋ง์ผ๋ ค๋ ์ฅ์น๋ค.
๐ฌ ์ฝ๋ ์ ์ฐฐ
prob.py ๋ฅผ ์ด์ด ์ง์๋ฅผ ์ด๋ป๊ฒ ๊ณ ๋ฅด๋์ง ๋ณธ๋ค.
from Crypto.Util.number import getPrime, GCD, bytes_to_long
FLAG = b"DH{????????????????????????}"
FLAG = bytes_to_long(FLAG)
while True:
p = getPrime(1024)
q = getPrime(1024)
N = p * q
for e1 in range(0x100, 0x10001):
if GCD(p - 1, e1) >= 0x100: # much better!
break
for e2 in range(0x100, 0x10001):
if GCD(q - 1, e2) >= 0x100: # This is nice!!
break
if GCD(e1, e2) == 1: # UN-UN common == common :P
break
FLAG_enc1 = pow(FLAG, e1, N)
FLAG_enc2 = pow(FLAG, e2, N)
์ฃผ์์ ๋ฅ์ฒญ์ค๋ฌ์ด ๋งํฌ("Stop generating that stupid COMMON e")์ ๋ฌ๋ฆฌ, ์ฝ๋๊ฐ ํ๋ ์ผ์ ๋ช ํํ๋ค.
e1์gcd(p-1, e1) โฅ 0x100์ธ ์ฒซ ๊ฐ โ ์ฆp-1๊ณผ 256 ์ด์์ ๊ณต์ฝ์๋ฅผ ๊ฐ๋๋ค.e2๋gcd(q-1, e2) โฅ 0x100์ธ ์ฒซ ๊ฐ โq-1๊ณผ 256 ์ด์์ ๊ณต์ฝ์๋ฅผ ๊ฐ๋๋ค.ฯ(N) = (p-1)(q-1)์ด๋ฏ๋ก ๋ ์ง์ ๋ชจ๋ ฯ ์ ํฐ ๊ณต์ฝ์๋ฅผ ๊ฐ๋๋ค. ๊ฐ๋ณ ๋ณตํธ๋ ๋งํ๋ค.- ๊ทธ๋ฐ๋ฐ ๋ง์ง๋ง ์กฐ๊ฑด,
gcd(e1, e2) == 1์ ๋ง์กฑํ ๋๊น์ง ์์๋ฅผ ๋ค์ ๋ฝ๋๋ค.
๋ง์ง๋ง ์ค์ด ์ค์ค๋ก ๋ฌธ์ ์ด์ด ์ค๋ค. output.txt ์ ๊ณต๊ฐ๊ฐ ํฌ๊ธฐ์ ์ง์์ ์ ์ฒด๋ฅผ ํ์ธํด ๋ณด์.
# peek.py โ output.txt ์ ์ฐฐ
from Crypto.Util.number import GCD
# ... N, e1, e2, c1, c2 ๋ฅผ ์ฝ์ด ํฌ๊ธฐ์ ์์ธ์๋ถํด ์ถ๋ ฅ ...
print(f"N : {N.bit_length()} bit")
print(f"e1 : {e1} = {factor(e1)}")
print(f"e2 : {e2} = {factor(e2)}")
print(f"GCD(e1, e2) : {GCD(e1, e2)}")
N ์ 2048๋นํธ(1024๋นํธ ์์ ๋ ๊ฐ์ ๊ณฑ), e1 = 26107 ์ ์์, e2 = 416 = 2โตยท13. ๊ทธ๋ฆฌ๊ณ gcd(e1, e2) = 1. ๊ฐ์ N ๊ณผ ๊ฐ์ ํ๋ฌธ์ ์๋ก์์ธ ๋ ์ง์๋ก ์ํธํํ ๊ทธ๋ฆผ์ด ์์ฑ๋๋ค. ๊ณตํต ๋ชจ๋๋ฌ์ค ๊ณต๊ฒฉ์ด ๊ทธ๋๋ก ๋ค์ด๋ง๋ ์กฐ๊ฑด์ด๋ค.
๐ ์ฝ์ง โ ๊ฐ๋ณ ๋ณตํธ๋ถํฐ ์๋
โถ๐ ์ฝ์ง โ ํ ์ํธ๋ฌธ๋ง ๋ถ์ก๊ณ d ๋ฅผ ๊ตฌํ๋ ค๋ค ๋งํ
์ฒ์์ ์ต๊ด๋๋ก "์ผ๋จ ํ๋๋ง ํ์" ์ถ์๋ค. c1 ํ๋๋ง ์์ด๋ d1 = e1โปยน mod ฯ ๋ง ๊ตฌํ๋ฉด ๋ณตํธ๋๋๊น. ๋ฌธ์ ๋ ๋ ๊ฐ์ง๋ค.
N์ ์ธ์๋ถํดํด์ผ ฯ ๋ฅผ ์๋๋ฐ, 1024๋นํธ ์์ ๋ ๊ฐ๋ผ ์ธ์๋ถํด๋ ๋ถ๊ฐ๋ฅํ๋ค.- ์ค๋ น ฯ ๋ฅผ ์๋ค ์ณ๋,
gcd(e1, ฯ) โฅ 0x100์ด๋ผe1์ ์ญ์ ์์ฒด๊ฐ ์กด์ฌํ์ง ์๋๋ค.
๋ ๋ฒ์งธ๊ฐ ์ง์ง ๋ฒฝ์ด๋ค. ์์ ์์๋ก prob.py ์ ์ง์ ์ ํ ๋ก์ง์ ๊ทธ๋๋ก ์ฌํํด์ ํ์ธํด ๋ดค๋ค.
# toy_uncommon.py โ ์์ ์์๋ก prob.py ํค ์์ฑ์ ์ฌํ
while True:
p = getPrime(256); q = getPrime(256)
for e1 in range(0x100, 0x10001):
if GCD(p - 1, e1) >= 0x100: break
for e2 in range(0x100, 0x10001):
if GCD(q - 1, e2) >= 0x100: break
if GCD(e1, e2) == 1: break
phi = (p - 1) * (q - 1)
print("GCD(e1, phi) =", GCD(e1, phi)) # 1 ์ด ์๋๋ค
inverse(e1, phi) # ValueError
inverse(e1, phi) ๋ base is not invertible for the given modulus ๋ก ์ฃฝ๋๋ค. ๋จ์ผ ์ง์๋ก๋ ๋ณตํธ ํค d ๋ฅผ ๋ชป ๋ง๋ ๋ค. c1, c2 ๋ฅผ ๋ฐ๋ก๋ฐ๋ก ๋ถ์ก๋ ์ ๊ทผ์ ์์ฒ์ ์ผ๋ก ๋งํ ์๋ค๋ ๋ป์ด๋ค.
๊ทธ๋ฌ๋ ๋ ์ํธ๋ฌธ์ ํจ๊ป ์จ์ผ ํ๋ค.
๐ฃ ํต์ฌ โ Common Modulus Attack
๊ฐ์ ๋ชจ๋๋ฌ์ค N, ๊ฐ์ ํ๋ฌธ m, ์๋ก์์ธ ๋ ์ง์. ์ด ์ธ ์กฐ๊ฑด์ด ๋ชจ์ด๋ฉด ๊ฐ์ธํค ์์ด ํ๋ฌธ์ด ๋์จ๋ค.
gcd(e1, e2) = 1 ์ด๋ฉด ํ์ฅ ์ ํด๋ฆฌ๋ ์๊ณ ๋ฆฌ์ฆ์ผ๋ก ๋ค์์ ๋ง์กฑํ๋ ์ ์ a, b ๋ฅผ ๊ตฌํ ์ ์๋ค(๋ฒ ์ฃผ ํญ๋ฑ์).
aยทe1 + bยทe2 = 1์ด์ ๋ ์ํธ๋ฌธ์ ๊ฐ๊ฐ a, b ์ ๊ณฑํด ๊ณฑํ๋ฉด ์ง์๊ฐ ๋ํด์ง๋ค.
c1^a ยท c2^b = (m^e1)^a ยท (m^e2)^b
= m^(aยทe1 + bยทe2)
= m^1
= m (mod N)ฯ(N) ๋, ๊ฐ๋ณ ์ง์์ ์ญ์ d ๋ ๋ฑ์ฅํ์ง ์๋๋ค. ์ค์ง ์ง์์ ์ ์ ์ ํ๊ฒฐํฉ๋ง ์ด์ฉํ๊ธฐ ๋๋ฌธ์, ์์์ ๊ฐ๋ณ ๋ณตํธ๋ฅผ ๋ง์๋ "e ์ ฯ ์ ๊ณต์ฝ์" ์ฅ์น๊ฐ ์ฌ๊ธฐ์ ์๋ฌด ์ํฅ์ ๋ชป ์ค๋ค. ์ถ์ ์๊ฐ ๊ฑธ์ด ๋ ์๋ฌผ์ ์ ๋ฌด๊ดํ ๋ฌธ์ผ๋ก ๋ค์ด๊ฐ๋ ์
์ด๋ค.
ํ ๊ฐ์ง ์ค๋ฌด ํฌ์ธํธ. a, b ์ค ํ๋๋ ์์๊ฐ ๋์จ๋ค(์ฌ๊ธฐ์ a = -173). ์์ ์ง์๋ ๊ทธ๋๋ก ๊ณ์ฐํ ์ ์์ผ๋, ํด๋น ์ํธ๋ฌธ์ ๋ชจ๋๋ฌ ์ญ์์ ์ทจํ ๋ค ์์ ์ง์๋ก ๋ฐ๊ฟ ๊ฑฐ๋ญ์ ๊ณฑํ๋ค. c1^(-173) ์ (c1โปยน mod N)^173 ์ผ๋ก ์ฒ๋ฆฌํ๋ค.

๐ฏ Full Exploit
๊ณต๊ฒฉ์ ๊ทธ๋๋ก ์คํฌ๋ฆฝํธ๋ก ์ฎ๊ธด๋ค. ํ์ฅ ์ ํด๋ฆฌ๋๋ก ๊ณ์๋ฅผ ๊ตฌํ๊ณ , ์์ ์ง์๋ ์ญ์์ผ๋ก ์ฒ๋ฆฌํด c1^a ยท c2^b mod N ์ ๊ณ์ฐํ๋ฉด ๋์ด๋ค.
from Crypto.Util.number import long_to_bytes
# output.txt ์ ๊ณต๊ฐ๊ฐ (์ผ๋ถ ์๋ต)
N = 18564839028340970630632687927085732690660...978989538340075110803 # 2048-bit
e1 = 26107
e2 = 416
c1 = 75270794888358245501765894323847809211380...194072814312710
c2 = 55534679163927793023196503211616823602913...257879728088984
def egcd(a, b):
if b == 0:
return a, 1, 0
g, x, y = egcd(b, a % b)
return g, y, x - (a // b) * y
def signed_pow(c, e, n):
if e < 0: # ์์ ์ง์๋ ๋ชจ๋๋ฌ ์ญ์์ผ๋ก
c = pow(c, -1, n)
e = -e
return pow(c, e, n)
g, a, b = egcd(e1, e2) # aยทe1 + bยทe2 = gcd(e1,e2) = 1
assert g == 1
m = (signed_pow(c1, a, N) * signed_pow(c2, b, N)) % N
print(long_to_bytes(m).decode())
a = -173, b = 10857. ๋ ์ํธ๋ฌธ์ ์กฐํฉํ๋ ํ๋ฌธ์ด ๊ทธ๋๋ก ๋จ์ด์ง๋ค.
DH{7e532757ebddde149f45c32156e58421e95b5c691b668f1681677af9495f7e36}โ ๊ฒ์ฆ
์๋ฒ๊ฐ ์๋ ๋ฌธ์ ๋ผ "์ ๋ง ์ด๊ฒ ๋ง๋" ์ถ์ผ๋ฉด ์ค์ค๋ก ํ์ธํ ์ ์๋ค. ๋ณต์ํ ํ๋ฌธ์ ๋ค์ e1, e2 ๋ก ์ํธํํด์ output.txt ์ c1, c2 ์ ๊ฐ์์ง ๋ณด๋ฉด ๋๋ค.
from Crypto.Util.number import bytes_to_long
FLAG = b"DH{7e532757ebddde149f45c32156e58421e95b5c691b668f1681677af9495f7e36}"
m = bytes_to_long(FLAG)
print("m^e1 mod N == c1 :", pow(m, e1, N) == c1)
print("m^e2 mod N == c2 :", pow(m, e2, N) == c2)
๋ ๋ค True. ๋ณต์ํ ํ๋ฌธ์ด ๋ ์ํธ๋ฌธ์ ๋ชจ๋ ์ฌํํ๋, ์๋ฒ์ ๋ฌผ์ด๋ณด์ง ์์๋ ์ ๋ต์ด ํ์คํ๋ค. ์ค์ ๋ก ์ด ํ๋๊ทธ๋ฅผ ์ ์ถํด ํต๊ณผํ๋ค.
๐ ๊ฒฐ๋ก
๊ฐ์ ๋ชจ๋๋ฌ์ค๋ก ๊ฐ์ ํ๋ฌธ์ ๋ ๋ฒ ์ํธํํ๋ฉด ์ ๋๋ค.
์ด ๋ฌธ์ ์ ๋ฐฉ์ด ์ฅ์น๋ "e ๋ฅผ ฯ ์ ๊ณต์ฝ์๋ฅผ ๊ฐ๊ฒ ๊ณจ๋ผ ๊ฐ๋ณ ๋ณตํธ๋ฅผ ๋ง๋๋ค"์๋ค. ๋ฐ์ ์์ฒด๋ ๋์์ง ์๋ค. ๊ทธ ์ง์ ํ๋๋ง ๋๊ณ ๋ณด๋ฉด ์ ๋ง๋ก d ๊ฐ ์กด์ฌํ์ง ์์ผ๋๊น. ํ์ง๋ง ๊ฐ์ N, ๊ฐ์ m ์ผ๋ก ์ํธ๋ฌธ์ ํ๋ ๋ ๋ด์ฃผ๋ ์๊ฐ, ๊ณต๊ฒฉ์๋ ๊ฐ์ธํค๋ฅผ ๊ตฌํ ํ์๊ฐ ์์ด์ง๋ค. ๋ ์ง์๊ฐ ์๋ก์์ด๊ธฐ๋ง ํ๋ฉด ์ง์์ ์ ํ๊ฒฐํฉ์ผ๋ก ํ๋ฌธ์ด ๋์จ๋ค.
๊ณตํต ๋ชจ๋๋ฌ์ค ๊ณต๊ฒฉ์ e ๋ ฯ ์ ์ฑ์ง๊ณผ ๋ฌด๊ดํ๋ค.
๋ฒ ์ฃผ ๊ณ์ aยทe1 + bยทe2 = 1 ๋ง ์์ผ๋ฉด ์ฑ๋ฆฝํ๋ค. ๊ทธ๋์ "e ๋ฅผ ์ด์ํ๊ฒ ๊ณจ๋์ผ๋ ์์ ํ๊ฒ ์ง"๋ผ๋ ์ง๊ด์ด ํตํ์ง ์๋๋ค. ์ง์๋ฅผ ์๋ฌด๋ฆฌ ํน์ดํ๊ฒ ์ก์๋, ๊ฐ์ ํ๋ฌธ์ ์๋ก์์ธ ๋ ์ง์๋ก ๋
ธ์ถํ๋ ๊ตฌ์กฐ ์์ฒด๊ฐ ์ทจ์ฝ์ ์ด๋ค.
์ค๋ฌด์์์ ๊ตํ์ ๋จ์ํ๋ค. ์ฌ๋ฌ ์์ ์์๊ฒ ๊ฐ์ ๋ฉ์์ง๋ฅผ ๋ณด๋ผ ๋ ๋ชจ๋๋ฌ์ค๋ฅผ ์ฌ์ฌ์ฉํ์ง ๋ง ๊ฒ, ๊ทธ๋ฆฌ๊ณ ๊ฐ์ ํค ์ฌ๋ฃ๋ก ๊ฐ์ ํ๋ฌธ์ ์ฌ๋ฌ ๋ฒ ์ํธํํ์ง ๋ง ๊ฒ. ํ์ค RSA-OAEP ์ฒ๋ผ ๋งค๋ฒ ์์ ํจ๋ฉ์ ์์ผ๋ฉด "๊ฐ์ ํ๋ฌธ"์ด๋ผ๋ ์ ์ ์์ฒด๊ฐ ๊นจ์ ธ ์ด๋ฐ ๊ณต๊ฒฉ์ด ํตํ์ง ์๋๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ