[๐Ÿฅˆ Silver 4] ์†Œ์ˆ˜๋ฅผ ๋ชจ๋“ˆ๋Ÿฌ๋กœ ์“ด ์ œ๊ณฑ PRNG์—์„œ ์‹œ๋“œ ๋˜์ฐพ๊ธฐ โ€” DreamHack the present ํ’€์ด

2026-08-17ยท1๋ถ„ ์ฝ๊ธฐยท

[๐Ÿฅˆ Silver 4] ์†Œ์ˆ˜๋ฅผ ๋ชจ๋“ˆ๋Ÿฌ๋กœ ์“ด ์ œ๊ณฑ PRNG์—์„œ ์‹œ๋“œ ๋˜์ฐพ๊ธฐ โ€” DreamHack the present ํ’€์ด

ํ”Œ๋ž˜๊ทธ๋ฅผ ์‹œ๋“œ๋กœ ๋„ฃ์€ ์ œ๊ณฑ ๋˜๋จน์ž„ PRNG๊ฐ€ ๋‘ ๋ฒˆ์งธ ์ถœ๋ ฅ๋งŒ ๊ณต๊ฐœํ•œ๋‹ค. ๋ชจ๋“ˆ๋Ÿฌ๊ฐ€ ๋‘ ์†Œ์ˆ˜์˜ ๊ณฑ์ด ์•„๋‹ˆ๋ผ 512๋น„ํŠธ ์†Œ์ˆ˜ ํ•˜๋‚˜๋ผ์„œ, ์ œ๊ณฑ๊ทผ์ด ๊ฑฐ๋“ญ์ œ๊ณฑ ํ•œ ๋ฒˆ์œผ๋กœ ๊ณ„์‚ฐ๋œ๋‹ค. ๋„ค์ œ๊ณฑ๊ทผ ํ›„๋ณด๋ฅผ ์ „๋ถ€ ๊ตฌํ•˜๊ณ  ์ด์ฐจ์ž‰์—ฌ ํŒ์ •์œผ๋กœ ๊ฐ€์ง€๋ฅผ ์ณ๋‚ด๋‹ˆ ๋‚จ์€ ๊ฑด ๋‘ ๊ฐœ๋ฟ์ด์—ˆ๊ณ  ๊ทธ์ค‘ ํ•˜๋‚˜๊ฐ€ ๊ทธ๋Œ€๋กœ ํ”Œ๋ž˜๊ทธ์˜€๋‹ค.

๋ฌธ์ œ: DreamHack โ€” the present ๋ถ„๋ฅ˜: crypto ๋‚œ์ด๋„: ๐Ÿฅˆ Silver 4 FLAG: DH{M3rry_ChrI5tM4s_And_4_h4pPy_N3w_y34R_3verY0n3}

์„ ๋ฌผ์ƒ์ž๋ฅผ ์—ด์—ˆ๋”๋‹ˆ ํฌ๋ฆฌ์Šค๋งˆ์Šค CTF ์ดˆ๋Œ€๊ถŒ๊ณผ ์•”ํ˜ธํ™”๋œ ์ชฝ์ง€, ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์ชฝ์ง€๋ฅผ ๋งŒ๋“  ์ฝ”๋“œ๊ฐ€ ๊ฐ™์ด ๋“ค์–ด ์žˆ์—ˆ๋‹ค๋Š” ์„ค์ •์ด๋‹ค. ๋ฌธ์ œ ํŽ˜์ด์ง€์—๋„ "ํ•ด๋‹น ๋ฌธ์ œ๋Š” 2022 Christmas CTF ์— ์ถœ์ œ๋œ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค"๋ผ๊ณ  ๋ถ™์–ด ์žˆ๋‹ค.

๋ฐ›์€ ์ฝ”๋“œ๋Š” ์Šค๋ฌด ์ค„์ด ์•ˆ ๋œ๋‹ค. ์งง์€ crypto ๋ฌธ์ œ๋Š” ๋Œ€๊ฐœ ๋‘˜ ์ค‘ ํ•˜๋‚˜์ธ๋ฐ, ์ˆ˜ํ•™์ ์œผ๋กœ ๋šซ๋ ค ์žˆ๊ฑฐ๋‚˜ ํŒŒ๋ผ๋ฏธํ„ฐ ํ•˜๋‚˜๊ฐ€ ์ž˜๋ชป ๊ณจ๋ผ์ ธ ์žˆ๋‹ค. ์ด ๋ฌธ์ œ๋Š” ํ›„์ž๋‹ค.

๋“œ๋ฆผํ•ต the present ๋ฌธ์ œ ํŽ˜์ด์ง€ โ€” 2022 Christmas CTF ์ถœ์ œ์ž‘์ด๋ผ๋Š” ์•ˆ๋‚ด์™€ ๋ฌธ์ œ ์„ค๋ช…, ํŒŒ์ผ ๋ฐ›๊ธฐ ๋ฒ„ํŠผ์ด ๋ณด์ธ๋‹ค
๋“œ๋ฆผํ•ต the present ๋ฌธ์ œ ํŽ˜์ด์ง€ โ€” 2022 Christmas CTF ์ถœ์ œ์ž‘์ด๋ผ๋Š” ์•ˆ๋‚ด์™€ ๋ฌธ์ œ ์„ค๋ช…, ํŒŒ์ผ ๋ฐ›๊ธฐ ๋ฒ„ํŠผ์ด ๋ณด์ธ๋‹ค


๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…the present
๋‚œ์ด๋„๐Ÿฅˆ Silver 4
๋ถ„๋ฅ˜crypto
์ œ๊ณต ํŒŒ์ผprob.py (์•”ํ˜ธํ™” ์ฝ”๋“œ), output.txt (M, rand)
์„œ๋ฒ„์—†์Œ โ€” ์™„์ „ ์˜คํ”„๋ผ์ธ
ํ•ต์‹ฌ ์ทจ์•ฝ์ ์ œ๊ณฑ ๋˜๋จน์ž„ PRNG์˜ ๋ชจ๋“ˆ๋Ÿฌ๊ฐ€ ํ•ฉ์„ฑ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ์†Œ์ˆ˜ ํ•˜๋‚˜
์‚ฌ์šฉ ๋„๊ตฌPython(pycryptodome), OpenSSL, SageMath

ํ๋ฆ„์„ ํ•œ ์ค„๋กœ ์ค„์ด๋ฉด ์ด๋ ‡๋‹ค. ํ”Œ๋ž˜๊ทธ๋ฅผ ์ •์ˆ˜๋กœ ๋ฐ”๊ฟ” ์‹œ๋“œ๋กœ ๋„ฃ๊ณ  ์ œ๊ณฑ์„ ๋‘ ๋ฒˆ ๋Œ๋ฆฐ ๊ฐ’์ด rand๋กœ ๊ณต๊ฐœ๋˜๋‹ˆ, ์ œ๊ณฑ๊ทผ์„ ๋‘ ๋ฒˆ ๊ฑฐ์Šฌ๋Ÿฌ ์˜ฌ๋ผ๊ฐ€๋ฉด ์‹œ๋“œ๊ฐ€ ๋‚˜์˜จ๋‹ค. ๊ทธ ์ œ๊ณฑ๊ทผ์ด ๊ณ„์‚ฐ ๊ฐ€๋Šฅํ•œ ์ด์œ ๊ฐ€ ์ด ๋ฌธ์ œ์˜ ์ „๋ถ€๋‹ค.



๐Ÿ”ฌ ์ •์ฐฐ โ€” ๋ฐ›์€ ๊ฑด ์ฝ”๋“œ ํ•œ ์žฅ๊ณผ ์ˆซ์ž ๋‘ ๊ฐœ

์••์ถ•์„ ํ’€๋ฉด ํŒŒ์ผ์ด ๋‘˜๋ฟ์ด๋‹ค. ์‹คํ–‰ ํŒŒ์ผ๋„, ์„œ๋ฒ„ ์ฃผ์†Œ๋„ ์—†๋‹ค.

ls -l extracted && file extracted/*

๋ฌธ์ œ ๋ฐฐํฌ๋ณธ ํŒŒ์ผ ๋ชฉ๋ก โ€” output.txt์™€ prob.py ๋‘ ๊ฐœ๋ฟ์ด๊ณ  file ๋ช…๋ น์€ ๊ฐ๊ฐ ASCII text์™€ Python script๋กœ ์ธ์‹ํ•œ๋‹ค
๋ฌธ์ œ ๋ฐฐํฌ๋ณธ ํŒŒ์ผ ๋ชฉ๋ก โ€” output.txt์™€ prob.py ๋‘ ๊ฐœ๋ฟ์ด๊ณ  file ๋ช…๋ น์€ ๊ฐ๊ฐ ASCII text์™€ Python script๋กœ ์ธ์‹ํ•œ๋‹ค

์•”ํ˜ธํ™” ์ฝ”๋“œ๋ถ€ํ„ฐ ๋ณธ๋‹ค.

cat extracted/prob.py

prob.py ์ „์ฒด ์†Œ์Šค ํ™”๋ฉด โ€” PRNG ํด๋ž˜์Šค์˜ next๊ฐ€ seed๋ฅผ ์ œ๊ณฑํ•ด M์œผ๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€๋ฅผ ๋Œ๋ ค์ฃผ๊ณ  main์—์„œ next๋ฅผ ๋‘ ๋ฒˆ ํ˜ธ์ถœํ•œ๋‹ค
prob.py ์ „์ฒด ์†Œ์Šค ํ™”๋ฉด โ€” PRNG ํด๋ž˜์Šค์˜ next๊ฐ€ seed๋ฅผ ์ œ๊ณฑํ•ด M์œผ๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€๋ฅผ ๋Œ๋ ค์ฃผ๊ณ  main์—์„œ next๋ฅผ ๋‘ ๋ฒˆ ํ˜ธ์ถœํ•œ๋‹ค

๋‚ด์šฉ์€ ์ด๊ฒŒ ์ „๋ถ€๋‹ค.

from Crypto.Util.number import *
 
flag = bytes_to_long(b'DH{?????????????????????????????????????????????}')
 
class PRNG():
    def __init__(self, seed):
        self.seed = seed
        self.M = getStrongPrime(512)
 
    def next(self):
        self.seed = self.seed ** 2 % self.M
        return self.seed
 
 
 
prng = PRNG(flag)
 
prng.next()
print(f'M = {prng.M}')
print(f'rand = {prng.next()}')

์ฝ์„ ๊ณณ์€ ์„ธ ๊ตฐ๋ฐ๋‹ค.

  • PRNG(flag) โ€” ์‹œ๋“œ๊ฐ€ ๋‚œ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ํ”Œ๋ž˜๊ทธ ์ž์ฒด๋‹ค. ๋ณต์› ๋Œ€์ƒ์ด ๊ณง ๋‚ด๋ถ€ ์ƒํƒœ๋‹ค.

  • next() โ€” ์ƒํƒœ ์ „์ด๊ฐ€ seed = seedยฒ mod M ํ•˜๋‚˜๋ฟ์ด๋‹ค. ์ถœ๋ ฅ ๊ฐ€๊ณต๋„, ๋น„ํŠธ ์ž๋ฅด๊ธฐ๋„ ์—†๋‹ค. ์ƒํƒœ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฑ‰๋Š”๋‹ค.

  • next() ํ˜ธ์ถœ์ด ๋‘ ๋ฒˆ โ€” ํ•œ ๋ฒˆ์€ ๋ฐ˜ํ™˜๊ฐ’์„ ๋ฒ„๋ฆฌ๊ณ , ๋‘ ๋ฒˆ์งธ ๊ฒƒ๋งŒ ์ถœ๋ ฅํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ๊ณต๊ฐœ๋œ ๊ฐ’์€ flagยฒ๊ฐ€ ์•„๋‹ˆ๋ผ flagโด mod M์ด๋‹ค. ์ด ํ•œ ์ค„์„ ํ˜๋ ค ์ฝ์œผ๋ฉด ๋’ค์—์„œ ์กฐ์šฉํžˆ ํ‹€๋ฆฐ๋‹ค.

์ˆซ์ž ์ชฝ๋„ ํ™•์ธํ•œ๋‹ค.

cat extracted/output.txt

cat ์œผ๋กœ ์—ฐ output.txt โ€” 155์ž๋ฆฌ ์‹ญ์ง„์ˆ˜ M ํ•œ ์ค„๊ณผ ๊ทธ๋ณด๋‹ค ์กฐ๊ธˆ ์ž‘์€ rand ํ•œ ์ค„์ด ์ „๋ถ€์ด๊ณ , ํžŒํŠธ๊ฐ€ ๋  ๋‹ค๋ฅธ ๊ฐ’์€ ๋“ค์–ด ์žˆ์ง€ ์•Š๋‹ค
cat ์œผ๋กœ ์—ฐ output.txt โ€” 155์ž๋ฆฌ ์‹ญ์ง„์ˆ˜ M ํ•œ ์ค„๊ณผ ๊ทธ๋ณด๋‹ค ์กฐ๊ธˆ ์ž‘์€ rand ํ•œ ์ค„์ด ์ „๋ถ€์ด๊ณ , ํžŒํŠธ๊ฐ€ ๋  ๋‹ค๋ฅธ ๊ฐ’์€ ๋“ค์–ด ์žˆ์ง€ ์•Š๋‹ค

์ •๋ฆฌํ•˜๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์•„๋Š” ๊ฒƒ์€ M, rand = flagโด mod M ๋‘ ๊ฐœ๊ณ , ๊ตฌํ•  ๊ฒƒ์€ flag๋‹ค.


๐Ÿงฉ ๋ฐฐ๊ฒฝ โ€” ์ œ๊ณฑ ๋˜๋จน์ž„ PRNG๋Š” ์™œ ์•ˆ์ „ํ•˜๋‹ค๊ณ  ํ•˜๋‚˜

x โ† xยฒ mod N ํ˜•ํƒœ์˜ ๋‚œ์ˆ˜ ์ƒ์„ฑ๊ธฐ๋Š” Blum Blum Shub(BBS)๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์ž˜ ์•Œ๋ ค์ ธ ์žˆ๋‹ค. ์ƒํƒœ๋ฅผ ์ œ๊ณฑํ•ด์„œ ๋‹ค์Œ ์ƒํƒœ๋กœ ์“ฐ๊ณ , ๋ณดํ†ต์€ ์ตœํ•˜์œ„ ๋น„ํŠธ ๋ช‡ ๊ฐœ๋งŒ ์ถœ๋ ฅ์œผ๋กœ ํ˜๋ฆฐ๋‹ค.

์ด ๊ตฌ์กฐ์˜ ์•ˆ์ „์„ฑ์€ ์ „์ ์œผ๋กœ ๋ชจ๋“ˆ๋Ÿฌ N์ด ๋‘ ์†Œ์ˆ˜์˜ ๊ณฑ์ด๋ผ๋Š” ๋ฐ์„œ ๋‚˜์˜จ๋‹ค. ํ•ฉ์„ฑ์ˆ˜ ์œ„์—์„œ ์ œ๊ณฑ๊ทผ์„ ๊ตฌํ•˜๋Š” ๋ฌธ์ œ๋Š” N์„ ์ธ์ˆ˜๋ถ„ํ•ดํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ๋‚œ์ด๋„๋ผ, ์ธ์ˆ˜๋ฅผ ๋ชจ๋ฅด๋ฉด ํ•œ ์Šคํ…๋„ ๋˜๋Œ๋ฆด ์ˆ˜ ์—†๋‹ค. ๋ผ๋นˆ(Rabin) ์•”ํ˜ธ๊ฐ€ "์ธ์ˆ˜๋ถ„ํ•ด๋งŒํผ ์–ด๋ ต๋‹ค"๊ณ  ๋งํ•  ๋•Œ์˜ ๊ทธ ์„ฑ์งˆ์ด๋‹ค.

๋ฐ˜๋Œ€๋กœ ๋ชจ๋“ˆ๋Ÿฌ๊ฐ€ ์†Œ์ˆ˜ ํ•˜๋‚˜๋ฉด ์ด์•ผ๊ธฐ๊ฐ€ ๋๋‚œ๋‹ค. ์†Œ์ˆ˜ p์—์„œ๋Š” ์–ด๋–ค ์ˆ˜๊ฐ€ ์ œ๊ณฑ์ˆ˜์ธ์ง€ ์˜ค์ผ๋Ÿฌ ํŒ์ •๋ฒ•์œผ๋กœ ์ฆ‰์‹œ ์•Œ ์ˆ˜ ์žˆ๊ณ , ์ œ๊ณฑ๊ทผ๋„ Tonelliโ€“Shanks ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ๋‹คํ•ญ์‹œ๊ฐ„์— ๋‚˜์˜จ๋‹ค. ๋˜๋จน์ž„์„ ๋ช‡ ๋ฒˆ ๋Œ๋ ธ๋“  ๊ทธ ํšŸ์ˆ˜๋งŒํผ ๊ฑฐ์Šฌ๋Ÿฌ ์˜ฌ๋ผ๊ฐ€๋ฉด ๋œ๋‹ค.

์ด ๋ฌธ์ œ์˜ M์€ getStrongPrime(512)๋กœ ๋งŒ๋“ค์–ด์กŒ๋‹ค. ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ "๊ฐ•ํ•œ ์†Œ์ˆ˜"์ง€, ๋‘ ์†Œ์ˆ˜์˜ ๊ณฑ์ด ์•„๋‹ˆ๋‹ค.

๋ฌธ์ œ์˜ ์•”ํ˜ธํ™” ํ๋ฆ„๊ณผ ๋ณต์› ํ๋ฆ„ ๋‹ค์ด์–ด๊ทธ๋žจ โ€” ์œ„์ชฝ์€ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋‘ ๋ฒˆ ์ œ๊ณฑํ•ด rand๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ •, ์•„๋ž˜์ชฝ์€ ์ œ๊ณฑ๊ทผ์„ ๋‘ ๋ฒˆ ์ทจํ•ด ํ”Œ๋ž˜๊ทธ๋กœ ๋Œ์•„์˜ค๋Š” ๊ฒฝ๋กœ
๋ฌธ์ œ์˜ ์•”ํ˜ธํ™” ํ๋ฆ„๊ณผ ๋ณต์› ํ๋ฆ„ ๋‹ค์ด์–ด๊ทธ๋žจ โ€” ์œ„์ชฝ์€ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋‘ ๋ฒˆ ์ œ๊ณฑํ•ด rand๋ฅผ ๋งŒ๋“œ๋Š” ๊ณผ์ •, ์•„๋ž˜์ชฝ์€ ์ œ๊ณฑ๊ทผ์„ ๋‘ ๋ฒˆ ์ทจํ•ด ํ”Œ๋ž˜๊ทธ๋กœ ๋Œ์•„์˜ค๋Š” ๊ฒฝ๋กœ



๐Ÿ’ฃ ํ•ต์‹ฌ โ€” ์†Œ์ˆ˜๋ฉด ์ œ๊ณฑ๊ทผ์ด ๊ฑฐ๋“ญ์ œ๊ณฑ ํ•œ ๋ฒˆ

๋จผ์ € M์ด ์ •๋ง ์†Œ์ˆ˜์ธ์ง€๋ถ€ํ„ฐ ํ™•์ธํ–ˆ๋‹ค. ์ฝ”๋“œ๊ฐ€ getStrongPrime์„ ๋ถ€๋ฅธ๋‹ค๊ณ  ํ•ด์„œ ๋ฐฐํฌ๋œ ์ˆซ์ž๊ฐ€ ๊ทธ๋ ‡๋‹ค๋Š” ๋ณด์žฅ์€ ์—†์œผ๋‹ˆ, ๋ฐ›์€ ๊ฐ’์„ ์ง์ ‘ ํŒ์ •ํ•œ๋‹ค. OpenSSL์— ์†Œ์ˆ˜ ํŒ์ •๊ธฐ๊ฐ€ ๋ถ™์–ด ์žˆ์–ด ๊ทธ๋Œ€๋กœ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

openssl prime $(grep -oP "(?<=M = )\d+" extracted/output.txt)

openssl prime ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” output.txt์—์„œ ๋ฝ‘์•„๋‚ธ 155์ž๋ฆฌ M์„ 16์ง„์ˆ˜๋กœ ๋‹ค์‹œ ๋ณด์—ฌ์ฃผ๋ฉฐ is prime ์ด๋ผ๊ณ  ํŒ์ •ํ•œ๋‹ค
openssl prime ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” output.txt์—์„œ ๋ฝ‘์•„๋‚ธ 155์ž๋ฆฌ M์„ 16์ง„์ˆ˜๋กœ ๋‹ค์‹œ ๋ณด์—ฌ์ฃผ๋ฉฐ is prime ์ด๋ผ๊ณ  ํŒ์ •ํ•œ๋‹ค

์†Œ์ˆ˜์ธ ๊ฑธ ํ™•์ธํ–ˆ์œผ๋ฉด ๋‹ค์Œ์€ M mod 4๋‹ค. ์ด ๊ฐ’์ด 3์ด๋ฉด Tonelliโ€“Shanks๋ฅผ ๋Œ๋ฆด ํ•„์š”๋„ ์—†๋‹ค. ์ด์ฐจ์ž‰์—ฌ a์˜ ์ œ๊ณฑ๊ทผ์ด ๊ฑฐ๋“ญ์ œ๊ณฑ ํ•œ ๋ฒˆ์œผ๋กœ ๋‚˜์˜ค๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

r  = a^((M+1)/4) mod M
rยฒ = a^((M+1)/2) = a ยท a^((M-1)/2) = a ยท 1 = a      (a ๊ฐ€ ์ด์ฐจ์ž‰์—ฌ์ผ ๋•Œ)

๋งˆ์ง€๋ง‰ ๋“ฑ์‹์ด ์„ฑ๋ฆฝํ•˜๋Š” ๊ฑด a๊ฐ€ ์ด์ฐจ์ž‰์—ฌ๋ผ ์˜ค์ผ๋Ÿฌ ํŒ์ •๋ฒ•์—์„œ a^((M-1)/2) โ‰ก 1์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์‹ค์ œ๋กœ ์ด ๋ฌธ์ œ์˜ M์€ M mod 4 = 3์ด๊ณ , rand๋„ ์ด์ฐจ์ž‰์—ฌ๋‹ค.

์—ฌ๊ธฐ์„œ ํ•œ ๋ฒˆ ๋” ์ƒ๊ฐํ•  ๊ฒŒ ์žˆ๋‹ค. ์ œ๊ณฑ๊ทผ์€ ํ•ญ์ƒ ๋‘ ๊ฐœ(r๊ณผ M-r)๋ผ, ๋‘ ๋‹จ๊ณ„๋ฅผ ๊ฑฐ์น˜๋ฉด ํ›„๋ณด๊ฐ€ ๋„ค ๊ฐœ๋กœ ๋ถˆ์–ด๋‚  ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์‹ค์ œ๋กœ๋Š” ๋‘ ๊ฐœ๋งŒ ๋‚˜์˜จ๋‹ค.

M mod 4 = 3์ด๋ฉด -1์ด ๋น„์ž‰์—ฌ๋‹ค. ๊ทธ๋ž˜์„œ ๋ถ€ํ˜ธ๊ฐ€ ๋‹ค๋ฅธ ๋‘ ๊ทผ s์™€ M-s ์ค‘ ์ •ํ™•ํžˆ ํ•œ์ชฝ๋งŒ ๋‹ค์‹œ ์ด์ฐจ์ž‰์—ฌ๊ฐ€ ๋˜๊ณ , ๋‚˜๋จธ์ง€ ํ•œ์ชฝ์€ ์ œ๊ณฑ๊ทผ ์ž์ฒด๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ฐ€์ง€ ํ•˜๋‚˜๊ฐ€ ๊ทธ ์ž๋ฆฌ์—์„œ ๋Š๊ธด๋‹ค.

๋„ค์ œ๊ณฑ๊ทผ ํ›„๋ณด ๋ถ„๊ธฐ ๋‹ค์ด์–ด๊ทธ๋žจ โ€” rand์˜ ๋‘ ์ œ๊ณฑ๊ทผ ์ค‘ ์ด์ฐจ์ž‰์—ฌ์ธ ์ชฝ๋งŒ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ์ด์–ด์ง€๊ณ  ๋น„์ž‰์—ฌ ๊ฐ€์ง€๋Š” ์ œ๊ณฑ๊ทผ์ด ์—†์–ด ๋Š๊ธด๋‹ค
๋„ค์ œ๊ณฑ๊ทผ ํ›„๋ณด ๋ถ„๊ธฐ ๋‹ค์ด์–ด๊ทธ๋žจ โ€” rand์˜ ๋‘ ์ œ๊ณฑ๊ทผ ์ค‘ ์ด์ฐจ์ž‰์—ฌ์ธ ์ชฝ๋งŒ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ์ด์–ด์ง€๊ณ  ๋น„์ž‰์—ฌ ๊ฐ€์ง€๋Š” ์ œ๊ณฑ๊ทผ์ด ์—†์–ด ๋Š๊ธด๋‹ค

์‚ด์•„๋‚จ์€ ๋‘ ํ›„๋ณด๋ฅผ ๊ฐ€๋ฅด๋Š” ๊ฒƒ๋„ ๊ฐ„๋‹จํ•˜๋‹ค. ํ”Œ๋ž˜๊ทธ๋Š” 49๋ฐ”์ดํŠธ, ์ฆ‰ 392๋น„ํŠธ๋ผ 512๋น„ํŠธ ๋ชจ๋“ˆ๋Ÿฌ๋ณด๋‹ค ํ•œ์ฐธ ์ž‘๋‹ค. ๋‚˜๋จธ์ง€ ์—ฐ์‚ฐ์œผ๋กœ ์ž˜๋ ค๋‚˜๊ฐ„ ์ •๋ณด๊ฐ€ ์—†๋‹ค๋Š” ๋œป์ด๊ณ , ๋™์‹œ์— ํ›„๋ณด ์ค‘ ํ•˜๋‚˜๋งŒ 391๋น„ํŠธ๋ผ๋Š” ๋œป์ด๊ธฐ๋„ ํ•˜๋‹ค. ๋‚˜๋จธ์ง€ ํ•˜๋‚˜๋Š” 512๋น„ํŠธ๋กœ ๊ธธ์ด๋ถ€ํ„ฐ ์–ด๊ธ‹๋‚œ๋‹ค.


๐Ÿ› ์‚ฝ์งˆ โ€” next()๋ฅผ ๋ช‡ ๋ฒˆ ์…Œ๋‚˜

โ–ถ๐Ÿ› ์‚ฝ์งˆ โ€” ์ œ๊ณฑ๊ทผ์„ ํ•œ ๋ฒˆ๋งŒ ์ทจํ•˜๋ฉด ๋ญ๊ฐ€ ๋‚˜์˜ค๋‚˜

prng.next()๊ฐ€ ์ถœ๋ ฅ ์—†์ด ํ˜ธ์ถœ๋˜๋Š” ์ค„์€ ํ™”๋ฉด์ƒ ์กด์žฌ๊ฐ์ด ๊ฑฐ์˜ ์—†๋‹ค. rand = flagยฒ mod M์ด๋ผ๊ณ  ์ฝ์–ด ๋ฒ„๋ฆฌ๋ฉด ์ œ๊ณฑ๊ทผ์„ ํ•œ ๋ฒˆ๋งŒ ์ทจํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ๊ทธ๋•Œ ์†์— ๋ญ๊ฐ€ ๋‚จ๋Š”์ง€ ์‹ค์ œ๋กœ ์ฐ์–ด ๋ดค๋‹ค. ๋ค์œผ๋กœ "๊ฐ€์ง€ ํ•˜๋‚˜๊ฐ€ ๋Š๊ธด๋‹ค"๋Š” ์•ž์˜ ์ฃผ์žฅ๋„ ๊ฐ™์ด ํ™•์ธํ•œ๋‹ค.

#!/usr/bin/env python3
"""์ œ๊ณฑ๊ทผ์„ ํ•œ ๋ฒˆ๋งŒ ์ทจํ•˜๋ฉด ๋ฌด์—‡์ด ๋‚˜์˜ค๋‚˜ โ€” next() ํ˜ธ์ถœ ํšŸ์ˆ˜๋ฅผ ์„ธ๋Š” ์ด์œ 
 
prob.py ๋Š” print ํ•˜๊ธฐ ์ „์— prng.next() ๋ฅผ ํ•œ ๋ฒˆ ๋ฒ„๋ฆฐ๋‹ค. ๊ทธ ํ•œ ์ค„์„ ๋ชป ๋ณด๊ณ 
rand = flag^2 ๋ผ๊ณ  ์ฝ์œผ๋ฉด ์ œ๊ณฑ๊ทผ์„ ํ•œ ๋ฒˆ๋งŒ ์ทจํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, ๊ทธ๋•Œ ์†์— ๋‚จ๋Š” ๊ฐ’์ด
๋ฌด์—‡์ธ์ง€ ์‹ค์ œ๋กœ ์ฐ์–ด ๋ณธ๋‹ค. ๋ค์œผ๋กœ {s, M-s} ์ค‘ ํ•˜๋‚˜๋งŒ ๋‹ค์‹œ ์ œ๊ณฑ๊ทผ์„ ๊ฐ€์ง„๋‹ค๋Š”
๊ฒƒ(= ํ›„๋ณด๊ฐ€ ๋‘ ๋ฐฐ๋กœ ๋Š˜์ง€ ์•Š๋Š” ์ด์œ )๋„ ๊ฐ™์ด ํ™•์ธํ•œ๋‹ค.
"""
import re
import pathlib
from Crypto.Util.number import long_to_bytes, bytes_to_long
 
HERE = pathlib.Path(__file__).resolve().parent
FLAG = b"DH{M3rry_ChrI5tM4s_And_4_h4pPy_N3w_y34R_3verY0n3}"
 
text = (HERE / "extracted" / "output.txt").read_text()
M = int(re.search(r"M\s*=\s*(\d+)", text).group(1))
rand = int(re.search(r"rand\s*=\s*(\d+)", text).group(1))
 
s = pow(rand, (M + 1) // 4, M)  # M % 4 == 3 ์ด๋ผ ์ œ๊ณฑ๊ทผ์ด ๊ฑฐ๋“ญ์ œ๊ณฑ ํ•œ ๋ฒˆ
for label, v in (("s      ", s), ("M - s  ", M - s)):
    raw = long_to_bytes(v)
    qr = pow(v, (M - 1) // 2, M) == 1
    print(f"[{label}] {v.bit_length()}๋น„ํŠธ  ์ด์ฐจ์ž‰์—ฌ={qr}  "
          f"์ถœ๋ ฅ๊ฐ€๋Šฅ={all(32 <= c < 127 for c in raw)}  ์•ž16B={raw[:16].hex()}")
 
print()
print(f"[ํ™•์ธ] s == flag^2 mod M ?  {s == pow(bytes_to_long(FLAG), 2, M)}")
print("       ํ•œ ๋ฒˆ๋งŒ ์ทจํ•œ ์ œ๊ณฑ๊ทผ์€ flag ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ฒ„๋ ค์ง„ ์ฒซ next() ์˜ ๊ฒฐ๊ณผ๋‹ค")
python3 wrong_once.py

wrong_once.py ์‹คํ–‰ ํ™”๋ฉด โ€” ์ œ๊ณฑ๊ทผ์„ ํ•œ ๋ฒˆ๋งŒ ์ทจํ•œ ๊ฐ’์ด 512๋น„ํŠธ ์ด์ฐจ์ž‰์—ฌ์ด๊ณ  ์ถœ๋ ฅ ๊ฐ€๋Šฅํ•œ ๋ฌธ์ž๊ฐ€ ์•„๋‹ˆ๋ฉฐ ๋ฒ„๋ ค์ง„ ์ฒซ next ๊ฒฐ๊ณผ์™€ ์ผ์น˜ํ•œ๋‹ค
wrong_once.py ์‹คํ–‰ ํ™”๋ฉด โ€” ์ œ๊ณฑ๊ทผ์„ ํ•œ ๋ฒˆ๋งŒ ์ทจํ•œ ๊ฐ’์ด 512๋น„ํŠธ ์ด์ฐจ์ž‰์—ฌ์ด๊ณ  ์ถœ๋ ฅ ๊ฐ€๋Šฅํ•œ ๋ฌธ์ž๊ฐ€ ์•„๋‹ˆ๋ฉฐ ๋ฒ„๋ ค์ง„ ์ฒซ next ๊ฒฐ๊ณผ์™€ ์ผ์น˜ํ•œ๋‹ค

ํ•œ ๋ฒˆ๋งŒ ์ทจํ•œ ์ œ๊ณฑ๊ทผ์€ 512๋น„ํŠธ์งœ๋ฆฌ ์•„๋ฌด ์˜๋ฏธ ์—†๋Š” ๋ฐ”์ดํŠธ์—ด์ด๊ณ , ๋งˆ์ง€๋ง‰ ์ค„์ด ํ™•์ธํ•ด ์ฃผ๋“ฏ ๊ทธ ๊ฐ’์€ ์ •ํ™•ํžˆ flagยฒ mod M โ€” ์ฝ”๋“œ๊ฐ€ ๋ฒ„๋ฆฐ ์ฒซ next()์˜ ๊ฒฐ๊ณผ๋‹ค. ํ”Œ๋ž˜๊ทธ ํ˜•์‹์ด ์•„๋‹ˆ๋ผ๊ณ  "๋ณตํ˜ธ๊ฐ€ ํ‹€๋ ธ๋‚˜" ์˜์‹ฌํ•˜๋ฉฐ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋‹ค์‹œ ๋œฏ์„ ๊ฒŒ ์•„๋‹ˆ๋ผ, ๊ทธ๋ƒฅ ํ•œ ๋ฒˆ ๋” ์ œ๊ณฑ๊ทผ์„ ์ทจํ•˜๋ฉด ๋˜๋Š” ์ƒํ™ฉ์ด์—ˆ๋‹ค.

๊ฐ™์€ ์ถœ๋ ฅ์—์„œ M - s๊ฐ€ ๋น„์ž‰์—ฌ(์ด์ฐจ์ž‰์—ฌ=False)๋กœ ์ฐํžˆ๋Š” ๊ฒƒ๋„ ๋ณด์ธ๋‹ค. ์ด ๊ฐ€์ง€์—์„œ๋Š” ๋‹ค์Œ ์ œ๊ณฑ๊ทผ์„ ๊ตฌํ•  ์ˆ˜ ์—†์œผ๋‹ˆ ํ›„๋ณด๊ฐ€ ๋„ค ๊ฐœ๋กœ ๋Š˜์ง€ ์•Š๋Š”๋‹ค.


๐ŸŽฏ ์ต์Šคํ”Œ๋กœ์ž‡ โ€” ๋„ค์ œ๊ณฑ๊ทผ ๋‘ ๊ฐœ ์ค‘ ์ฝํžˆ๋Š” ์ชฝ

์—ฌ๊ธฐ๊นŒ์ง€ ์˜ค๋ฉด ์ฝ”๋“œ๋Š” ์งง๋‹ค. output.txt๋ฅผ ํŒŒ์‹ฑํ•˜๊ณ , ์ œ๊ณฑ๊ทผ์„ ๋‘ ๋ฒˆ ์ทจํ•˜๊ณ , ๋‚˜์˜จ ํ›„๋ณด๋ฅผ ๋ฐ”์ดํŠธ๋กœ ๋˜๋Œ๋ ค DH{๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ฅด๋ฉด ๋์ด๋‹ค.

ํ›„๋ณด ๊ฒ€์ฆ์€ ๋ˆˆ์œผ๋กœ ํ•˜์ง€ ์•Š๊ณ  ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ง์ ‘ ํ•œ๋‹ค. ๊ฐ ํ›„๋ณด๋ฅผ ๋ฌธ์ œ์˜ ์•”ํ˜ธํ™” ๊ทธ๋Œ€๋กœ ๋„ค์ œ๊ณฑํ•ด์„œ rand์™€ ๊ฐ™์€์ง€ ๋Œ€์กฐํ•˜๋Š” ์ค„(pow(t, 4, M) == rand)์ด ๊ทธ ์—ญํ• ์ด๋‹ค. ํ”Œ๋ž˜๊ทธ ํ˜•์‹๋งŒ ๋ณด๊ณ  ๊ณ ๋ฅด๋ฉด ํ˜•์‹์ด ์šฐ์—ฐํžˆ ๋งž๋Š” ๊ฐ’์— ์†์„ ์—ฌ์ง€๊ฐ€ ๋‚จ๋Š”๋‹ค.

#!/usr/bin/env python3
"""the present (DreamHack, Silver 4, crypto) โ€” flag ๋ณต๊ตฌ
 
prob.py ๋Š” flag ๋ฅผ ์ •์ˆ˜๋กœ ๋งŒ๋“ค์–ด PRNG ์˜ seed ๋กœ ๋„ฃ๊ณ  next() ๋ฅผ ๋‘ ๋ฒˆ ๋ถ€๋ฅธ๋‹ค.
next() ๊ฐ€ seed <- seed^2 mod M ์ด๋ฏ€๋กœ ์ถœ๋ ฅ๋œ rand ๋Š” flag^4 mod M ์ด๋‹ค.
M ์ด getStrongPrime(512) ๋กœ ๋งŒ๋“  '์†Œ์ˆ˜'๋ผ์„œ 4์ œ๊ณฑ๊ทผ์€ ๊ณง๋ฐ”๋กœ ๊ณ„์‚ฐ๋œ๋‹ค.
 
M % 4 == 3 ์ด๋ฉด QR x ์˜ ์ œ๊ณฑ๊ทผ์ด x^((M+1)/4) mod M ๋กœ ํ•œ ๋ฒˆ์— ๋‚˜์˜ค๊ณ ,
-1 ์ด ๋น„์ž‰์—ฌ๋ผ {s, M-s} ์ค‘ ์ •ํ™•ํžˆ ํ•˜๋‚˜๋งŒ ๋‹ค์‹œ QR ์ด ๋œ๋‹ค. ๊ทธ ํ•˜๋‚˜๋งŒ ๋”ฐ๋ผ๊ฐ€๋ฉด
4์ œ๊ณฑ๊ทผ ํ›„๋ณด๋Š” ๋‘ ๊ฐœ(t, M-t)๋ฟ์ด๋ผ ๋ธŒ๋ฃจํŠธํฌ์Šค๊ฐ€ ํ•„์š” ์—†๋‹ค.
"""
import re
import pathlib
from Crypto.Util.number import long_to_bytes
 
HERE = pathlib.Path(__file__).resolve().parent
OUT = HERE / "extracted" / "output.txt"
 
 
def load_output(path):
    """output.txt ์—์„œ M ๊ณผ rand ๋ฅผ ์ฝ๋Š”๋‹ค."""
    text = path.read_text()
    M = int(re.search(r"M\s*=\s*(\d+)", text).group(1))
    rand = int(re.search(r"rand\s*=\s*(\d+)", text).group(1))
    return M, rand
 
 
def is_qr(x, p):
    """์˜ค์ผ๋Ÿฌ ํŒ์ •๋ฒ•: x ๊ฐ€ p ์˜ ์ด์ฐจ์ž‰์—ฌ์ธ๊ฐ€."""
    return pow(x, (p - 1) // 2, p) == 1
 
 
def sqrt_mod_p3mod4(x, p):
    """p % 4 == 3 ์ธ ์†Œ์ˆ˜์—์„œ x ์˜ ์ œ๊ณฑ๊ทผ ๋‘ ๊ฐœ. QR ์ด ์•„๋‹ˆ๋ฉด ๋นˆ ๋ฆฌ์ŠคํŠธ."""
    if x % p == 0:
        return [0]
    if not is_qr(x, p):
        return []
    r = pow(x, (p + 1) // 4, p)
    return [r, p - r]
 
 
def fourth_roots(x, p):
    """x ์˜ 4์ œ๊ณฑ๊ทผ ์ „๋ถ€ (p % 4 == 3 ๊ฐ€์ •)."""
    roots = []
    for s in sqrt_mod_p3mod4(x, p):
        roots += sqrt_mod_p3mod4(s, p)
    return sorted(set(roots))
 
 
def main():
    M, rand = load_output(OUT)
    assert M % 4 == 3, "์ด ์Šคํฌ๋ฆฝํŠธ๋Š” M mod 4 == 3 ์ „์šฉ ์ง€๋ฆ„๊ธธ์„ ์“ด๋‹ค"
    print(f"[*] M    ({M.bit_length()}๋น„ํŠธ, M mod 4 = {M % 4}) = {M}")
    print(f"[*] rand ({rand.bit_length()}๋น„ํŠธ) = {rand}")
    print(f"[*] rand ๋Š” ์ด์ฐจ์ž‰์—ฌ์ธ๊ฐ€? {is_qr(rand, M)}")
 
    roots = fourth_roots(rand, M)
    print(f"[*] 4์ œ๊ณฑ๊ทผ ํ›„๋ณด {len(roots)}๊ฐœ")
    flag = None
    for i, t in enumerate(roots):
        raw = long_to_bytes(t)
        ok = pow(t, 4, M) == rand  # ๋ฌธ์ œ์˜ ์•”ํ˜ธํ™”๋ฅผ ๊ทธ๋Œ€๋กœ ๋˜๋Œ๋ ค ์ž์ฒด ๊ฒ€์ฆ
        printable = all(32 <= c < 127 for c in raw)
        head = raw[:16] if printable else raw[:16].hex()
        print(f"    [{i}] {t.bit_length():3d}๋น„ํŠธ  ๊ฒ€์‚ฐ={ok}  ์ถœ๋ ฅ๊ฐ€๋Šฅ={printable}  ์•ž๋ถ€๋ถ„={head}")
        if ok and printable and raw.startswith(b"DH{") and raw.endswith(b"}"):
            flag = raw.decode()
 
    if flag:
        print(f"\n[+] FLAG = {flag}")
    else:
        print("\n[-] ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ํ›„๋ณด๊ฐ€ ์—†๋‹ค")
 
 
if __name__ == "__main__":
    main()
python3 solve.py

solve.py ์‹คํ–‰ ํ™”๋ฉด โ€” ๋„ค์ œ๊ณฑ๊ทผ ํ›„๋ณด ๋‘ ๊ฐœ๊ฐ€ ๋ชจ๋‘ ๊ฒ€์‚ฐ์„ ํ†ต๊ณผํ•˜๊ณ  391๋น„ํŠธ ํ›„๋ณด๋งŒ ์ถœ๋ ฅ ๊ฐ€๋Šฅํ•œ ๋ฐ”์ดํŠธ๋ผ์„œ ํ”Œ๋ž˜๊ทธ๋กœ ํ™•์ •๋œ๋‹ค
solve.py ์‹คํ–‰ ํ™”๋ฉด โ€” ๋„ค์ œ๊ณฑ๊ทผ ํ›„๋ณด ๋‘ ๊ฐœ๊ฐ€ ๋ชจ๋‘ ๊ฒ€์‚ฐ์„ ํ†ต๊ณผํ•˜๊ณ  391๋น„ํŠธ ํ›„๋ณด๋งŒ ์ถœ๋ ฅ ๊ฐ€๋Šฅํ•œ ๋ฐ”์ดํŠธ๋ผ์„œ ํ”Œ๋ž˜๊ทธ๋กœ ํ™•์ •๋œ๋‹ค

ํ›„๋ณด๋Š” ์˜ˆ์ƒ๋Œ€๋กœ ๋‘ ๊ฐœ๋‹ค. ๋‘˜ ๋‹ค ๋„ค์ œ๊ณฑํ•˜๋ฉด rand๊ฐ€ ๋˜์ง€๋งŒ, 391๋น„ํŠธ์งœ๋ฆฌ๋งŒ ์ „๋ถ€ ์ถœ๋ ฅ ๊ฐ€๋Šฅํ•œ ๋ฌธ์ž๋กœ ๋–จ์–ด์ง„๋‹ค. 512๋น„ํŠธ ์ชฝ์€ e5fba265โ€ฆ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ฐ”์ดํŠธ์—ด์ด๋ผ ๋ณผ ๊ฒƒ๋„ ์—†๋‹ค.

ํ”Œ๋ž˜๊ทธ๋Š” DH{M3rry_ChrI5tM4s_And_4_h4pPy_N3w_y34R_3verY0n3}. Christmas CTF ์ถœ์ œ์ž‘์ด๋ผ๋Š” ๋ฌธ์ œ ํŽ˜์ด์ง€ ์•ˆ๋‚ด์™€ ๋‚ด์šฉ์ด ๋งž์•„๋–จ์–ด์ง„๋‹ค.



๐Ÿงช ๊ต์ฐจ ๊ฒ€์ฆ โ€” SageMath๋กœ ํ•œ ๋ฒˆ ๋”

์ง์ ‘ ์ง  ์ œ๊ณฑ๊ทผ ๋ฃจํ‹ด์€ ์ง€๋ฆ„๊ธธ(M mod 4 = 3)์— ๊ธฐ๋Œ€๊ณ  ์žˆ์–ด์„œ, ๊ทธ ๊ฐ€์ •์ด ํ‹€๋ ธ๋‹ค๋ฉด ์กฐ์šฉํžˆ ์—‰๋šฑํ•œ ๊ฐ’์„ ๋ฑ‰๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ๊ณ„์‚ฐ ๋Œ€์ˆ˜ ์‹œ์Šคํ…œ์œผ๋กœ ๊ฐ™์€ ๊ฐ’์„ ๋…๋ฆฝ์ ์œผ๋กœ ๊ตฌํ•ด ๋Œ€์กฐํ–ˆ๋‹ค. Sage์˜ nth_root(4, all=True)๋Š” ์ง€๋ฆ„๊ธธ์„ ์“ฐ์ง€ ์•Š๊ณ  ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ ๋„ค์ œ๊ณฑ๊ทผ์„ ์ „๋ถ€ ์ฐพ์•„ ์ค€๋‹ค.

# verify.sage โ€” SageMath ๋กœ ๋…๋ฆฝ ๊ฒ€์ฆ
#  1) M ์ด ์ •๋ง ์†Œ์ˆ˜์ธ๊ฐ€ (์†Œ์ˆ˜๋ฉด ์ œ๊ณฑ๊ทผ์ด ๋‹คํ•ญ์‹œ๊ฐ„์— ๊ณ„์‚ฐ๋œ๋‹ค)
#  2) rand ์˜ 4์ œ๊ณฑ๊ทผ์„ Sage ์˜ nth_root ๋กœ ์ „๋ถ€ ๊ตฌํ•ด solve.py ๊ฒฐ๊ณผ์™€ ๋Œ€์กฐ
 
M = 12045184283108733282877246918018456795629932264538806786957088609817236359725183272225617590814403324378457302095025664842376149624271113548645334599894051
rand = 321609188014782817530081099017386870899897457530475334351599692725892773281779472409503390776505529932157130216692885548245652532540341302761980076422023
 
print("is_prime(M)      =", is_prime(M))
print("M.nbits()        =", Integer(M).nbits())
print("M mod 4          =", M % 4)
print("rand is QR       =", kronecker(rand, M) == 1)
 
roots = Integers(M)(rand).nth_root(4, all=True)
print("๋„ค์ œ๊ณฑ๊ทผ ๊ฐœ์ˆ˜     =", len(roots))
 
for r in sorted(Integer(x) for x in roots):
    raw = int(r).to_bytes((r.nbits() + 7) // 8, "big")
    ok = power_mod(int(r), 4, M) == rand
    tag = raw.decode() if all(32 <= c < 127 for c in raw) else raw.hex()
    print(f"  {r.nbits():3d}๋น„ํŠธ  ๋„ค์ œ๊ณฑ_๊ฒ€์‚ฐ:{ok}  ->  {tag}")
sage verify.sage

SageMath๋กœ ๊ฒ€์ฆํ•œ ํ™”๋ฉด โ€” is_prime์ด True, M mod 4๊ฐ€ 3, rand๊ฐ€ ์ด์ฐจ์ž‰์—ฌ์ด๋ฉฐ ๋„ค์ œ๊ณฑ๊ทผ ๊ฐœ์ˆ˜๊ฐ€ 2๊ฐœ๋กœ solve.py ๊ฒฐ๊ณผ์™€ ์ผ์น˜ํ•œ๋‹ค
SageMath๋กœ ๊ฒ€์ฆํ•œ ํ™”๋ฉด โ€” is_prime์ด True, M mod 4๊ฐ€ 3, rand๊ฐ€ ์ด์ฐจ์ž‰์—ฌ์ด๋ฉฐ ๋„ค์ œ๊ณฑ๊ทผ ๊ฐœ์ˆ˜๊ฐ€ 2๊ฐœ๋กœ solve.py ๊ฒฐ๊ณผ์™€ ์ผ์น˜ํ•œ๋‹ค

is_prime(M) = True, M mod 4 = 3, ๋„ค์ œ๊ณฑ๊ทผ ๊ฐœ์ˆ˜ 2๊ฐœ, ๊ทธ๋ฆฌ๊ณ  ๊ทธ์ค‘ ํ•˜๋‚˜๊ฐ€ ๊ฐ™์€ ํ”Œ๋ž˜๊ทธ. ์†์œผ๋กœ ์ง  ์ง€๋ฆ„๊ธธ๊ณผ Sage์˜ ์ผ๋ฐ˜ ๋ฃจํ‹ด์ด ๊ฐ™์€ ๋‹ต์— ๋„๋‹ฌํ–ˆ๋‹ค.

์ฐธ๊ณ ๋กœ kronecker(rand, M) == 1์€ ์˜ค์ผ๋Ÿฌ ํŒ์ •๋ฒ•๊ณผ ๊ฐ™์€ ์–˜๊ธฐ๋‹ค. Sage์—์„œ๋Š” ๋ฅด์žฅ๋“œ๋ฅด ๊ธฐํ˜ธ๋ฅผ ๋ฐ”๋กœ ๋ถ€๋ฅผ ์ˆ˜ ์žˆ์–ด ๊ฑฐ๋“ญ์ œ๊ณฑ์„ ์ง์ ‘ ๋Œ๋ฆด ํ•„์š”๊ฐ€ ์—†๋‹ค.


๐Ÿ” ์žฌํ˜„ โ€” ํŒŒ์ผ ๋‘ ๊ฐœ๋ฉด ๋

์ด ๋ฌธ์ œ๋Š” ์„œ๋ฒ„๋„ VM๋„ ํ•„์š” ์—†์–ด์„œ, ๋ฐฐํฌ๋ณธ๊ณผ ์Šคํฌ๋ฆฝํŠธ๋งŒ ์žˆ์œผ๋ฉด ์–ธ์ œ๋“  ๋‹ค์‹œ ๋Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค. ์žฌํ˜„ ์Šคํฌ๋ฆฝํŠธ๋Š” ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ˆˆ์œผ๋กœ ๋Œ€์กฐํ•˜๊ฒŒ ๋‘์ง€ ์•Š๊ณ  ๋ฌธ์ œ.json์— ๊ธฐ๋ก๋œ ์ •๋‹ต๊ณผ ์ง์ ‘ ๋น„๊ตํ•œ๋‹ค.

#!/usr/bin/env bash
# the present (DreamHack Silver 4, crypto) โ€” ํ•œ ๋ฐฉ ์žฌํ˜„.
#   extracted/output.txt ์˜ (M, rand) ๋งŒ์œผ๋กœ flag ๋ฅผ ๋ณต์›ํ•˜๊ณ  ์ •๋‹ต๊ณผ ๋Œ€์กฐํ•œ๋‹ค.
#   ์™ธ๋ถ€ ์„œ๋ฒ„ยทVM ์ด ํ•„์š” ์—†๋Š” ์™„์ „ ์˜คํ”„๋ผ์ธ ๋ฌธ์ œ๋ผ ์ด ์Šคํฌ๋ฆฝํŠธ๋งŒ์œผ๋กœ ๋๋‚œ๋‹ค.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
 
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ œ.json'))['flag'])" 2>/dev/null || echo '')
 
python3 -c "import Crypto" 2>/dev/null || { echo "pycryptodome ์ด ํ•„์š”ํ•˜๋‹ค: pip install pycryptodome"; exit 1; }
 
OUT=$(timeout 120 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

reproduce.sh ์‹คํ–‰ ํ™”๋ฉด โ€” solve.py ์ถœ๋ ฅ์„ ๊ทธ๋Œ€๋กœ ๋ณด์—ฌ์ค€ ๋’ค ๋ณต์›ํ•œ ํ”Œ๋ž˜๊ทธ๊ฐ€ ๊ธฐ๋ก๋œ ์ •๋‹ต๊ณผ ์ผ์น˜ํ•ด PASS๋กœ ๋๋‚œ๋‹ค
reproduce.sh ์‹คํ–‰ ํ™”๋ฉด โ€” solve.py ์ถœ๋ ฅ์„ ๊ทธ๋Œ€๋กœ ๋ณด์—ฌ์ค€ ๋’ค ๋ณต์›ํ•œ ํ”Œ๋ž˜๊ทธ๊ฐ€ ๊ธฐ๋ก๋œ ์ •๋‹ต๊ณผ ์ผ์น˜ํ•ด PASS๋กœ ๋๋‚œ๋‹ค

๋กœ์ปฌ ํ™˜๊ฒฝ: Ubuntu 25.10, Python 3.13.7(pycryptodome 3.23.0), OpenSSL 3.5.3, SageMath 10.9. Sage๋Š” nth_root ๊ต์ฐจ๊ฒ€์ฆ์—๋งŒ ์“ฐ์ด๋‹ˆ ์—†์–ด๋„ solve.py๋Š” ๊ทธ๋Œ€๋กœ ๋Œ์•„๊ฐ„๋‹ค.



๐Ÿ“ ๊ฒฐ๋ก 

๋ชจ๋“ˆ๋Ÿฌ ํ•˜๋‚˜๋ฅผ ์ž˜๋ชป ๊ณ ๋ฅด๋ฉด ๊ตฌ์กฐ ์ „์ฒด๊ฐ€ ๋ฌด๋„ˆ์ง„๋‹ค

x โ† xยฒ mod N ๋˜๋จน์ž„์€ ๊ทธ ์ž์ฒด๋กœ๋Š” ๋ฉ€์ฉกํ•œ ์„ค๊ณ„๋‹ค. ๋ฌด๋„ˆ์ง„ ๊ฑด N์„ ๋‘ ์†Œ์ˆ˜์˜ ๊ณฑ์ด ์•„๋‹ˆ๋ผ ์†Œ์ˆ˜ ํ•˜๋‚˜๋กœ ์žก์€ ์ง€์ ์ด๊ณ , ๋‚˜๋จธ์ง€ ์ฝ”๋“œ๋Š” ์•„๋ฌด ์ž˜๋ชป์ด ์—†๋‹ค. ์•”ํ˜ธ ๊ตฌํ˜„์„ ๋ณผ ๋•Œ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ด๋ฆ„๋ณด๋‹ค ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋งŒ๋“ค์–ด์ง€๋Š”์ง€๋ฅผ ๋จผ์ € ๋ณด๋Š” ์ด์œ ๋‹ค.

๋˜๋Œ๋ฆด ํšŸ์ˆ˜๋Š” ์ถœ๋ ฅ์ด ์•„๋‹ˆ๋ผ ํ˜ธ์ถœ ํšŸ์ˆ˜๊ฐ€ ์ •ํ•œ๋‹ค

๊ณต๊ฐœ๋œ ๊ฐ’์€ ํ•˜๋‚˜์˜€์ง€๋งŒ next()๋Š” ๋‘ ๋ฒˆ ๋ถˆ๋ ธ๋‹ค. ์ถœ๋ ฅ ๊ฐœ์ˆ˜๋งŒ ์„ธ๊ณ  ์ œ๊ณฑ๊ทผ์„ ํ•œ ๋ฒˆ๋งŒ ์ทจํ•˜๋ฉด ๋ฒ„๋ ค์ง„ ์ค‘๊ฐ„ ์ƒํƒœ๋ฅผ ์†์— ์ฅ๊ณ  "๋ณตํ˜ธ๊ฐ€ ์•ˆ ๋œ๋‹ค"๋ฉฐ ํ—ค๋งค๊ฒŒ ๋œ๋‹ค. ์ƒํƒœ ๊ธฐ๊ณ„๋ฅผ ๋˜๋Œ๋ฆด ๋•Œ๋Š” ํ™”๋ฉด์— ์ฐํžŒ ๊ฐ’์ด ๋ช‡ ๋ฒˆ์งธ ์ƒํƒœ์ธ์ง€๋ถ€ํ„ฐ ์„ธ๋Š” ๊ฒŒ ๋งž๋‹ค.

ํ›„๋ณด๊ฐ€ ์—ฌ๋Ÿฟ์ด๋ฉด ๊ฒ€์‚ฐ์œผ๋กœ ์ขํžŒ๋‹ค

๋„ค์ œ๊ณฑ๊ทผ์€ ์›๋ฆฌ์ƒ ์—ฌ๋Ÿฌ ๊ฐœ์ผ ์ˆ˜ ์žˆ๋‹ค. ํ”Œ๋ž˜๊ทธ ํ˜•์‹์œผ๋กœ ๊ณ ๋ฅด๋Š” ๊ฑด ํŽธํ•˜์ง€๋งŒ, ๊ทธ ์ „์— ํ›„๋ณด๋ฅผ ๋ฌธ์ œ์˜ ์•”ํ˜ธํ™” ๊ทธ๋Œ€๋กœ ๋‹ค์‹œ ๋Œ๋ ค ์›๋ณธ๊ณผ ๋งž๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒŒ ๋จผ์ €๋‹ค. ์ด ๋ฌธ์ œ์—์„œ๋Š” pow(t, 4, M) == rand ํ•œ ์ค„์ด ๊ทธ ์—ญํ• ์„ ํ–ˆ๊ณ , ๊ธธ์ด(391๋น„ํŠธ ๋Œ€ 512๋น„ํŠธ)๊ฐ€ ๋ณด์กฐ ์ง€ํ‘œ๊ฐ€ ๋๋‹ค.

๋ฐฉ์–ด ์ชฝ์—์„œ ์ •๋ฆฌํ•˜๋ฉด

BBS ๊ณ„์—ด ์ƒ์„ฑ๊ธฐ๋ฅผ ์“ธ ๊ฑฐ๋ผ๋ฉด ๋ชจ๋“ˆ๋Ÿฌ๋Š” ๋ฐ˜๋“œ์‹œ ๋‘ ๊ฐœ์˜ ํฐ ์†Œ์ˆ˜ ๊ณฑ(๊ฐ ์†Œ์ˆ˜๋Š” 4๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€๊ฐ€ 3์ธ Blum ์†Œ์ˆ˜)์ด์–ด์•ผ ํ•˜๊ณ , ์ธ์ˆ˜๋Š” ํ๊ธฐํ•ด์•ผ ํ•œ๋‹ค. ์ƒํƒœ๋ฅผ ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅํ•˜๋Š” ๊ฒƒ๋„ ๋ฌธ์ œ๋‹ค. ์›๋ž˜ BBS๋Š” ์ƒํƒœ ์ „์ฒด๊ฐ€ ์•„๋‹ˆ๋ผ ์ตœํ•˜์œ„ ๋น„ํŠธ ์†Œ์ˆ˜๋งŒ ํ˜๋ฆฌ๋Š”๋ฐ, ์ด ์ฝ”๋“œ๋Š” seed๋ฅผ ํ†ต์งธ๋กœ ๋ฑ‰์–ด์„œ ๋˜๋Œ๋ฆด ์ง€์ ์„ ๊ทธ๋Œ€๋กœ ์•Œ๋ ค ์ค€๋‹ค.

์‹œ๋“œ์— ๋น„๋ฐ€๊ฐ’์„ ๋„ฃ๋Š” ๊ฒƒ ์—ญ์‹œ ์œ„ํ—˜ํ•˜๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ํ”Œ๋ž˜๊ทธ๊ฐ€ ์‹œ๋“œ์˜€๊ธฐ ๋•Œ๋ฌธ์— ์ƒํƒœ ๋ณต์›์ด ๊ณง ๋น„๋ฐ€ ๋ณต์›์ด ๋๋‹ค. ๋น„๋ฐ€์€ ์‹œ๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ํ‚ค๋กœ ๋‹ค๋ฃจ๊ณ , ์ƒ์„ฑ๊ธฐ ์ƒํƒœ๋Š” ๋น„๋ฐ€๊ณผ ๋ถ„๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.

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

Comments

๋Œ“๊ธ€

0๊ฐœ

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

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

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅ‡ Gold 2] ๋‘ ๋ฐฐ์  x ์ขŒํ‘œ ํ•˜๋‚˜๋กœ ํ‚ค ๋‘ ๊ฐœ ๋˜์ฐพ๊ธฐ โ€” DreamHack Elliptic Curve Basic ํ’€์ด
blog

[๐Ÿฅ‡ Gold 2] ๋‘ ๋ฐฐ์  x ์ขŒํ‘œ ํ•˜๋‚˜๋กœ ํ‚ค ๋‘ ๊ฐœ ๋˜์ฐพ๊ธฐ โ€” DreamHack Elliptic Curve Basic ํ’€์ด

NIST P-256 ์œ„์˜ ๋žœ๋ค ์  P์™€ ๊ทธ ๋‘ ๋ฐฐ์  2P๋ฅผ 10์Œ ๋ฝ‘์•„, ๋‘ ์ ์˜ x ์ขŒํ‘œ๋งŒ key1ยทkey2์— ๋Œ€ํ•œ 1์ฐจ์‹์œผ๋กœ ํ˜๋ฆฐ๋‹ค. ์ ๋„ y ์ขŒํ‘œ๋„ ๊ณต๊ฐœ๋˜์ง€ ์•Š์ง€๋งŒ y๊ฐ€ ์ œ๊ณฑ์œผ๋กœ๋งŒ ๋“ฑ์žฅํ•˜๋Š” ๋•๋ถ„์— ๋‘ x ์ขŒํ‘œ๋Š” ํ•˜๋‚˜์˜ ์œ ๋ฆฌํ•จ์ˆ˜๋กœ ๋ฌถ์ธ๋‹ค. key2๋ฅผ ์†Œ๊ฑฐํ•ด 7์ฐจ ๋‹คํ•ญ์‹์„ ์„ธ์šฐ๊ณ  GF(p) ๊ทผ์„ ์ „๋ถ€ ๊ตฌํ•œ ๋’ค, ๋‚จ์€ ์—ฌ๋Ÿ ์Œ์œผ๋กœ ๊ฐ€์งœ ํ›„๋ณด ๋‘˜์„ ๊ฑธ๋Ÿฌ๋ƒˆ๋‹ค.
#dreamhack#ctf#crypto+5
2026-08-19#dreamhack +4
[๐Ÿฅˆ Silver 4] SubBytes ํ•˜๋‚˜ ๋นผ๋ฉด AES๊ฐ€ ๊ทธ๋ƒฅ 1์ฐจ์‹์ด ๋œ๋‹ค โ€” DreamHack No sub please! ํ’€์ด
blog

[๐Ÿฅˆ Silver 4] SubBytes ํ•˜๋‚˜ ๋นผ๋ฉด AES๊ฐ€ ๊ทธ๋ƒฅ 1์ฐจ์‹์ด ๋œ๋‹ค โ€” DreamHack No sub please! ํ’€์ด

AES์˜ ๋„ค ์—ฐ์‚ฐ ์ค‘ ๋น„์„ ํ˜•์ธ ๊ฒƒ์€ SubBytes ํ•˜๋‚˜๋ฟ์ด๋‹ค. ๊ทธ๊ฑธ ๋นผ๋ฉด ๋‚จ์€ ShiftRowsยทMixColumnsยทAddRoundKey๊ฐ€ ๋ชจ๋‘ GF(2) ์œ„์˜ ์„ ํ˜•ยท์•„ํ•€ ์—ฐ์‚ฐ์ด๋ผ ์•”ํ˜ธ ์ „์ฒด๊ฐ€ Mยทv ๋”ํ•˜๊ธฐ ์ƒ์ˆ˜ d ๊ผด๋กœ ์ฃผ์ €์•‰๋Š”๋‹ค. ์„œ๋ฒ„๋Š” enc(secret) ํ•˜๋‚˜๋งŒ ๋ณตํ˜ธ๋ฅผ ๊ฑฐ๋ถ€ํ•˜์ง€๋งŒ, XOR์ด enc(secret)์ด ๋˜๋Š” ๋‹ค๋ฅธ ์„ธ ๋ธ”๋ก์„ ๋Œ€์‹  ๋ณตํ˜ธ์‹œํ‚ค๋ฉด ์ƒ์ˆ˜ํ•ญ์ด ํ™€์ˆ˜ ๋ฒˆ ๋‚จ์•„ secret์ด ๊ทธ๋Œ€๋กœ ๋‚˜์˜จ๋‹ค. ๋ณตํ˜ธ ์งˆ์˜ ์„ธ ๋ฒˆ์œผ๋กœ ๋๋‚ฌ๋‹ค.
#dreamhack#ctf#crypto+7
2026-08-14#dreamhack +4
[๐Ÿฅˆ Silver 3] nonce ๋ฅผ ์ƒ์„ฑ์ž์—์„œ ํ•œ ๋ฒˆ๋งŒ ๋งŒ๋“ค๋ฉด ๊ฐœ์ธํ‚ค๊ฐ€ ํ†ต์งธ๋กœ ์ƒˆ์–ด๋‚˜์˜จ๋‹ค โ€” DreamHack Textbook-DSA ํ’€์ด
blog

[๐Ÿฅˆ Silver 3] nonce ๋ฅผ ์ƒ์„ฑ์ž์—์„œ ํ•œ ๋ฒˆ๋งŒ ๋งŒ๋“ค๋ฉด ๊ฐœ์ธํ‚ค๊ฐ€ ํ†ต์งธ๋กœ ์ƒˆ์–ด๋‚˜์˜จ๋‹ค โ€” DreamHack Textbook-DSA ํ’€์ด

์„œ๋ช… ์„œ๋ฒ„์˜ DSA ๊ฐ€ nonce k ๋ฅผ __init__ ์—์„œ ๋”ฑ ํ•œ ๋ฒˆ ๋งŒ๋“ค์–ด ๋ชจ๋“  ์„œ๋ช…์— ๋Œ๋ ค ์“ด๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ๊ทธ k ๊ฐ€ ๊ฐœ์ธํ‚ค์˜ ์—ญ์›์ด๋ผ s ๊ฐ€ x ์˜ 2์ฐจ์‹์ด ๋˜๊ณ , ์„œ๋ช… ๋‘ ๊ฐœ๋ฅผ ๋นผ๋ฉด 2์ฐจํ•ญ์ด ํ†ต์งธ๋กœ ์‚ฌ๋ผ์ ธ ๋‚˜๋ˆ—์…ˆ ํ•œ ๋ฒˆ์— ๊ฐœ์ธํ‚ค๊ฐ€ ๋‚˜์˜จ๋‹ค. ๋ณต์›ํ•œ ํ‚ค๋กœ ์„œ๋ฒ„๊ฐ€ ์„œ๋ช…ํ•ด ์ฃผ์ง€ ์•Š๋Š” token ์„ ์ง์ ‘ ์„œ๋ช…ํ•ด ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฐ›๋Š”๋‹ค.
#dreamhack#ctf#crypto+6
2026-08-13#dreamhack +4