[๐Ÿฅˆ Silver 3] ๊ฐ™์€ N, ๋‘ ์ง€์ˆ˜๋กœ ์•”ํ˜ธํ™”๋œ RSA โ€” DreamHack uncommon e ํ’€์ด

2026-07-14ยท1๋ถ„ ์ฝ๊ธฐยท

[๐Ÿฅˆ Silver 3] ๊ฐ™์€ N, ๋‘ ์ง€์ˆ˜๋กœ ์•”ํ˜ธํ™”๋œ RSA โ€” DreamHack uncommon e ํ’€์ด

๊ฐ™์€ ๋ชจ๋“ˆ๋Ÿฌ์Šค N ์•„๋ž˜ ๊ฐ™์€ ํ‰๋ฌธ์„ ์„œ๋กœ ๋‹ค๋ฅธ ๋‘ ์ง€์ˆ˜ e1, e2 ๋กœ ์•”ํ˜ธํ™”ํ•œ RSA ๋ฌธ์ œ. ์ถœ์ œ์ž๊ฐ€ e ๋ฅผ ์ผ๋ถ€๋Ÿฌ ฯ†(N) ๊ณผ ๊ณต์•ฝ์ˆ˜๋ฅผ ๊ฐ–๊ฒŒ ๊ณจ๋ผ ๊ฐœ๋ณ„ ๋ณตํ˜ธ๋ฅผ ๋ง‰์•˜์ง€๋งŒ, gcd(e1, e2)=1 ์ด๋ผ ๊ณตํ†ต ๋ชจ๋“ˆ๋Ÿฌ์Šค ๊ณต๊ฒฉ์ด ๊ทธ๋Œ€๋กœ ํ†ตํ•œ๋‹ค. ํ™•์žฅ ์œ ํด๋ฆฌ๋“œ๋กœ aยทe1 + bยทe2 = 1 ์„ ๊ตฌํ•ด c1^aยทc2^b ๋กœ ํ‰๋ฌธ์„ ๋ณต์›ํ–ˆ๋‹ค.

๋ฌธ์ œ: 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 ์ž์ฒด๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์—†์œผ๋‹ˆ, ๊ทธ ์ง€์ˆ˜๋กœ ์•”ํ˜ธํ™”๋œ ์•”ํ˜ธ๋ฌธ์€ ํ‘œ์ค€์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ๋ณตํ˜ธ๊ฐ€ ์•ˆ ๋œ๋‹ค.

RSA ๋ณตํ˜ธ ํ‚ค d ๋Š” gcd(e, ฯ†)=1 ์ผ ๋•Œ๋งŒ ์กด์žฌํ•œ๋‹ค โ€” ์ด ๋ฌธ์ œ๋Š” e ๋ฅผ ์ผ๋ถ€๋Ÿฌ ฯ† ์™€ ๊ณต์•ฝ์ˆ˜๋ฅผ ๊ฐ–๊ฒŒ ๊ณจ๋ผ ๊ฐœ๋ณ„ ๋ณตํ˜ธ๋ฅผ ๋ง‰์•˜๋‹ค
RSA ๋ณตํ˜ธ ํ‚ค d ๋Š” gcd(e, ฯ†)=1 ์ผ ๋•Œ๋งŒ ์กด์žฌํ•œ๋‹ค โ€” ์ด ๋ฌธ์ œ๋Š” e ๋ฅผ ์ผ๋ถ€๋Ÿฌ ฯ† ์™€ ๊ณต์•ฝ์ˆ˜๋ฅผ ๊ฐ–๊ฒŒ ๊ณจ๋ผ ๊ฐœ๋ณ„ ๋ณตํ˜ธ๋ฅผ ๋ง‰์•˜๋‹ค

์ด ๋ฌธ์ œ ์ด๋ฆ„์ด "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)
cat ์œผ๋กœ ์—ฐ prob.py โ€” 1024๋น„ํŠธ ์†Œ์ˆ˜ ๋‘ ๊ฐœ๋กœ N ์„ ๋งŒ๋“  ๋’ค p-1, q-1 ๊ณผ์˜ ๊ณต์•ฝ์ˆ˜๊ฐ€ 0x100 ์ด์ƒ์ธ ์ง€์ˆ˜ e1, e2 ๋ฅผ ์ผ๋ถ€๋Ÿฌ ๊ณจ๋ผ ๊ฐ™์€ FLAG ๋ฅผ ๋‘ ๋ฒˆ ์•”ํ˜ธํ™”ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค
cat ์œผ๋กœ ์—ฐ prob.py โ€” 1024๋น„ํŠธ ์†Œ์ˆ˜ ๋‘ ๊ฐœ๋กœ N ์„ ๋งŒ๋“  ๋’ค p-1, q-1 ๊ณผ์˜ ๊ณต์•ฝ์ˆ˜๊ฐ€ 0x100 ์ด์ƒ์ธ ์ง€์ˆ˜ e1, e2 ๋ฅผ ์ผ๋ถ€๋Ÿฌ ๊ณจ๋ผ ๊ฐ™์€ FLAG ๋ฅผ ๋‘ ๋ฒˆ ์•”ํ˜ธํ™”ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค

์ฃผ์„์˜ ๋Šฅ์ฒญ์Šค๋Ÿฌ์šด ๋งํˆฌ("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)}")
python3 peek.py โ€” N ์€ 2048๋น„ํŠธ, e1=26107(์†Œ์ˆ˜), e2=416=2^5ยท13, gcd(e1,e2)=1
python3 peek.py โ€” N ์€ 2048๋น„ํŠธ, e1=26107(์†Œ์ˆ˜), e2=416=2^5ยท13, gcd(e1,e2)=1

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
python3 toy_uncommon.py โ€” gcd(e1,ฯ†)โ‰ 1 ์ด๋ผ inverse(e1,ฯ†) ๊ฐ€ ValueError, ๋‹จ์ผ ์ง€์ˆ˜๋กœ๋Š” d ๋ฅผ ๋ชป ๋งŒ๋“ ๋‹ค
python3 toy_uncommon.py โ€” gcd(e1,ฯ†)โ‰ 1 ์ด๋ผ inverse(e1,ฯ†) ๊ฐ€ ValueError, ๋‹จ์ผ ์ง€์ˆ˜๋กœ๋Š” d ๋ฅผ ๋ชป ๋งŒ๋“ ๋‹ค

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 ์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.

Common Modulus Attack โ€” ๊ฐ™์€ N ์•„๋ž˜ ๋‘ ์•”ํ˜ธ๋ฌธ์„ ๋ฒ ์ฃผ ๊ณ„์ˆ˜๋กœ ์กฐํ•ฉํ•ด ํ‰๋ฌธ m ์„ ๋ณต์›
Common Modulus Attack โ€” ๊ฐ™์€ N ์•„๋ž˜ ๋‘ ์•”ํ˜ธ๋ฌธ์„ ๋ฒ ์ฃผ ๊ณ„์ˆ˜๋กœ ์กฐํ•ฉํ•ด ํ‰๋ฌธ m ์„ ๋ณต์›

๐ŸŽฏ 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())
python3 solve.py โ€” a=-173, b=10857 ๋กœ c1^aยทc2^b ๋ฅผ ๊ณ„์‚ฐํ•ด FLAG ๋ณต์›
python3 solve.py โ€” a=-173, b=10857 ๋กœ c1^aยทc2^b ๋ฅผ ๊ณ„์‚ฐํ•ด FLAG ๋ณต์›

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)
python3 verify.py ์‹คํ–‰ ํ™”๋ฉด โ€” ๋ณต์›ํ•œ ํ‰๋ฌธ์„ e1, e2 ๋กœ ๊ฐ๊ฐ ๋‹ค์‹œ ์•”ํ˜ธํ™”ํ•ด output.txt ์˜ c1, c2 ๋ฅผ ๋ชจ๋‘ ์žฌํ˜„ํ–ˆ๊ณ , ๋งˆ์ง€๋ง‰ ์ค„์— MATCH ๊ฐ€ ์ฐํžŒ๋‹ค
python3 verify.py ์‹คํ–‰ ํ™”๋ฉด โ€” ๋ณต์›ํ•œ ํ‰๋ฌธ์„ e1, e2 ๋กœ ๊ฐ๊ฐ ๋‹ค์‹œ ์•”ํ˜ธํ™”ํ•ด output.txt ์˜ c1, c2 ๋ฅผ ๋ชจ๋‘ ์žฌํ˜„ํ–ˆ๊ณ , ๋งˆ์ง€๋ง‰ ์ค„์— MATCH ๊ฐ€ ์ฐํžŒ๋‹ค

๋‘˜ ๋‹ค True. ๋ณต์›ํ•œ ํ‰๋ฌธ์ด ๋‘ ์•”ํ˜ธ๋ฌธ์„ ๋ชจ๋‘ ์žฌํ˜„ํ•˜๋‹ˆ, ์„œ๋ฒ„์— ๋ฌผ์–ด๋ณด์ง€ ์•Š์•„๋„ ์ •๋‹ต์ด ํ™•์‹คํ•˜๋‹ค. ์‹ค์ œ๋กœ ์ด ํ”Œ๋ž˜๊ทธ๋ฅผ ์ œ์ถœํ•ด ํ†ต๊ณผํ–ˆ๋‹ค.



๐Ÿ“ ๊ฒฐ๋ก 

๊ฐ™์€ ๋ชจ๋“ˆ๋Ÿฌ์Šค๋กœ ๊ฐ™์€ ํ‰๋ฌธ์„ ๋‘ ๋ฒˆ ์•”ํ˜ธํ™”ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

์ด ๋ฌธ์ œ์˜ ๋ฐฉ์–ด ์žฅ์น˜๋Š” "e ๋ฅผ ฯ† ์™€ ๊ณต์•ฝ์ˆ˜๋ฅผ ๊ฐ–๊ฒŒ ๊ณจ๋ผ ๊ฐœ๋ณ„ ๋ณตํ˜ธ๋ฅผ ๋ง‰๋Š”๋‹ค"์˜€๋‹ค. ๋ฐœ์ƒ ์ž์ฒด๋Š” ๋‚˜์˜์ง€ ์•Š๋‹ค. ๊ทธ ์ง€์ˆ˜ ํ•˜๋‚˜๋งŒ ๋†“๊ณ  ๋ณด๋ฉด ์ •๋ง๋กœ d ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋‹ˆ๊นŒ. ํ•˜์ง€๋งŒ ๊ฐ™์€ N, ๊ฐ™์€ m ์œผ๋กœ ์•”ํ˜ธ๋ฌธ์„ ํ•˜๋‚˜ ๋” ๋‚ด์ฃผ๋Š” ์ˆœ๊ฐ„, ๊ณต๊ฒฉ์ž๋Š” ๊ฐœ์ธํ‚ค๋ฅผ ๊ตฌํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ง„๋‹ค. ๋‘ ์ง€์ˆ˜๊ฐ€ ์„œ๋กœ์†Œ์ด๊ธฐ๋งŒ ํ•˜๋ฉด ์ง€์ˆ˜์˜ ์„ ํ˜•๊ฒฐํ•ฉ์œผ๋กœ ํ‰๋ฌธ์ด ๋‚˜์˜จ๋‹ค.

๊ณตํ†ต ๋ชจ๋“ˆ๋Ÿฌ์Šค ๊ณต๊ฒฉ์€ e ๋‚˜ ฯ† ์˜ ์„ฑ์งˆ๊ณผ ๋ฌด๊ด€ํ•˜๋‹ค.

๋ฒ ์ฃผ ๊ณ„์ˆ˜ aยทe1 + bยทe2 = 1 ๋งŒ ์žˆ์œผ๋ฉด ์„ฑ๋ฆฝํ•œ๋‹ค. ๊ทธ๋ž˜์„œ "e ๋ฅผ ์ด์ƒํ•˜๊ฒŒ ๊ณจ๋ž์œผ๋‹ˆ ์•ˆ์ „ํ•˜๊ฒ ์ง€"๋ผ๋Š” ์ง๊ด€์ด ํ†ตํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ง€์ˆ˜๋ฅผ ์•„๋ฌด๋ฆฌ ํŠน์ดํ•˜๊ฒŒ ์žก์•„๋„, ๊ฐ™์€ ํ‰๋ฌธ์„ ์„œ๋กœ์†Œ์ธ ๋‘ ์ง€์ˆ˜๋กœ ๋…ธ์ถœํ•˜๋Š” ๊ตฌ์กฐ ์ž์ฒด๊ฐ€ ์ทจ์•ฝ์ ์ด๋‹ค.

์‹ค๋ฌด์—์„œ์˜ ๊ตํ›ˆ์€ ๋‹จ์ˆœํ•˜๋‹ค. ์—ฌ๋Ÿฌ ์ˆ˜์‹ ์ž์—๊ฒŒ ๊ฐ™์€ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ ๋•Œ ๋ชจ๋“ˆ๋Ÿฌ์Šค๋ฅผ ์žฌ์‚ฌ์šฉํ•˜์ง€ ๋ง ๊ฒƒ, ๊ทธ๋ฆฌ๊ณ  ๊ฐ™์€ ํ‚ค ์žฌ๋ฃŒ๋กœ ๊ฐ™์€ ํ‰๋ฌธ์„ ์—ฌ๋Ÿฌ ๋ฒˆ ์•”ํ˜ธํ™”ํ•˜์ง€ ๋ง ๊ฒƒ. ํ‘œ์ค€ RSA-OAEP ์ฒ˜๋Ÿผ ๋งค๋ฒˆ ์ž„์˜ ํŒจ๋”ฉ์„ ์„ž์œผ๋ฉด "๊ฐ™์€ ํ‰๋ฌธ"์ด๋ผ๋Š” ์ „์ œ ์ž์ฒด๊ฐ€ ๊นจ์ ธ ์ด๋Ÿฐ ๊ณต๊ฒฉ์ด ํ†ตํ•˜์ง€ ์•Š๋Š”๋‹ค.

์ด ๊ธ€์ด ๋„์›€์ด ๋๋‚˜์š”?

Comments

๋Œ“๊ธ€

0๊ฐœ

๋Œ“๊ธ€์„ ๋‚จ๊ธฐ๋ ค๋ฉด ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ด์š”. (๋„ค์ด๋ฒ„ ยท ๊ตฌ๊ธ€ ๊ณ„์ •)

๋Œ“๊ธ€ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘โ€ฆ

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅˆ Silver 4] e ๊ฐ€ (p-1) ์„ ๋‚˜๋ˆ„๋Š” RSA โ€” AMM ์œผ๋กœ e์ฐจ๊ทผ ๊ตฌํ•˜๊ธฐ โ€” DreamHack special_rsa_parameter ํ’€์ด
blog

[๐Ÿฅˆ Silver 4] e ๊ฐ€ (p-1) ์„ ๋‚˜๋ˆ„๋Š” RSA โ€” AMM ์œผ๋กœ e์ฐจ๊ทผ ๊ตฌํ•˜๊ธฐ โ€” DreamHack special_rsa_parameter ํ’€์ด

๊ณต๊ฐœ์ง€์ˆ˜ e=257 ์ด (p-1) ์„ ๋”ฑ ํ•œ ๋ฒˆ ๋‚˜๋ˆˆ๋‹ค. gcd(e, ฯ†(N)) ์ด 1 ์ด ์•„๋‹ˆ๋ผ d=eโปยน mod ฯ† ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์•„ ํ‰๋ฒ”ํ•œ RSA ๋ณตํ˜ธ๊ฐ€ ๋ง‰ํžŒ๋‹ค. pยทq ๊ฐ€ ์ด๋ฏธ ์ฃผ์–ด์กŒ์œผ๋‹ˆ CRT ๋กœ mod p / mod q ๋กœ ์ชผ๊ฐ  ๋’ค, e์ฐจ๊ทผ์ด ์œ ์ผํ•œ mod q ๋Š” ๊ทธ๋Œ€๋กœ ํ’€๊ณ , e์ฐจ๊ทผ์ด 257๊ฐœ์ธ mod p ๋Š” AMM(Adleman-Manders-Miller) ์œผ๋กœ ์ „๋ถ€ ๊ตฌํ•ด CRT ๋กœ ๋‹ค์‹œ ํ•ฉ์นœ๋‹ค. 257๊ฐœ ํ›„๋ณด ์ค‘ BISC ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ‰๋ฌธ์ด flag.
#dreamhack#ctf#crypto+6
2026-07-23#dreamhack +4
[๐Ÿฅˆ Silver 3] 2048๋น„ํŠธ RSA๊ฐ€ 0์Šคํ…์— ๊ฐˆ๋ผ์ง€๋Š” ์ด์œ  โ€” DreamHack crt rsa ํ’€์ด
blog

[๐Ÿฅˆ Silver 3] 2048๋น„ํŠธ RSA๊ฐ€ 0์Šคํ…์— ๊ฐˆ๋ผ์ง€๋Š” ์ด์œ  โ€” DreamHack crt rsa ํ’€์ด

๊ณต๊ฐœ ์ง€์ˆ˜ e ๋„ ๊ฐœ์ธํ‚ค d ๋„ ์ฃผ์ง€ ์•Š๊ณ  dp, dq, qinv ์™€ ์•”ํ˜ธ๋ฌธ๋งŒ ์ฃผ๋Š” RSA-CRT ๋ฌธ์ œ. ๋‘ ์†Œ์ˆ˜๋ฅผ p = nextprime(q + 1) ๋กœ ๋งŒ๋“ค์–ด ์‚ฌ์‹ค์ƒ ์—ฐ์† ์†Œ์ˆ˜๋ผ, 2048๋น„ํŠธ ๋ชจ๋“ˆ๋Ÿฌ์Šค์ธ๋ฐ๋„ Fermat ์ธ์ˆ˜๋ถ„ํ•ด๋กœ 0์Šคํ…์— ๊ฐˆ๋ผ์ง„๋‹ค. dpยทdqยทqinv ๋Š” ๊ทธ ์ž์ฒด๊ฐ€ CRT ๋ณตํ˜ธ ํŒŒ๋ผ๋ฏธํ„ฐ๋ผ, ์†Œ์ธ์ˆ˜๋งŒ ์•Œ๋ฉด eยทd ๋ณต์› ์—†์ด ๊ณง์žฅ ํ‰๋ฌธ์„ ์–ป๋Š”๋‹ค.
#dreamhack#ctf#crypto+4
2026-07-22#dreamhack +4
[๐Ÿฅˆ Silver 3] ๊ฐ’์„ ๋ฐ”๊พธ๋ฉด ๊ฒ€์‚ฌ๋ฅผ ํ”ผํ•ด๊ฐ„๋‹ค โ€” DreamHack Textbook-RSA ํ’€์ด
blog

[๐Ÿฅˆ Silver 3] ๊ฐ’์„ ๋ฐ”๊พธ๋ฉด ๊ฒ€์‚ฌ๋ฅผ ํ”ผํ•ด๊ฐ„๋‹ค โ€” DreamHack Textbook-RSA ํ’€์ด

์•”ํ˜ธํ™”๋œ ํ”Œ๋ž˜๊ทธ๋ฅผ ๊ทธ๋Œ€๋กœ ์„œ๋ฒ„์— ๋˜๋Œ๋ ค ๋ณตํ˜ธํ™”๋ฅผ ์š”์ฒญํ•˜๋Š” ๋ป”ํ•œ ์‹œ๋„๋Š” "Do not cheat !"๋กœ ๋ฐ”๋กœ ๋ง‰ํžŒ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ ๊ฒ€์‚ฌ๋Š” ๋”ฑ ์ˆซ์ž ํ•˜๋‚˜๋งŒ ๋น„๊ตํ•  ๋ฟ์ด๋‹ค. RSA๋Š” ๊ณฑ์…ˆ์— ๋Œ€ํ•ด ์ค€๋™ํ˜•(homomorphic)์ด๋ผ๋Š” ์„ฑ์งˆ์ด ์žˆ์–ด์„œ, ์•”ํ˜ธ๋ฌธ์— ์•Œ๋ ค์ง„ ๊ฐ’์„ ๊ณฑํ•ด ์™„์ „ํžˆ ๋‹ค๋ฅธ ์ˆซ์ž๋กœ ๋‘”๊ฐ‘์‹œํ‚ค๋ฉด ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•˜๋ฉด์„œ๋„ ์›๋ž˜ ๊ฐ’์„ ๋ณต์›ํ•  ์ˆ˜ ์žˆ๋Š” ์ •๋ณด๋ฅผ ๊ทธ๋Œ€๋กœ ์–ป์–ด๋‚ผ ์ˆ˜ ์žˆ๋‹ค.
#dreamhack#ctf#crypto+3
2026-07-20#dreamhack +4