[๐Ÿฅ‰ Bronze 3] ์•ฝ๊ด€ 27๊ฐœ ์กฐ๋ฌธ์— ์ˆจ์€ 36๊ธ€์ž โ€” DreamHack ์„œ๋น„์Šค ์ด์šฉ ์•ฝ๊ด€ ํ’€์ด

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

[๐Ÿฅ‰ Bronze 3] ์•ฝ๊ด€ 27๊ฐœ ์กฐ๋ฌธ์— ์ˆจ์€ 36๊ธ€์ž โ€” DreamHack ์„œ๋น„์Šค ์ด์šฉ ์•ฝ๊ด€ ํ’€์ด

hex ํ•œ ์ค„์งœ๋ฆฌ ํ…์ŠคํŠธ ํŒŒ์ผ์„ ๋˜๋Œ๋ฆฌ๋ฉด UTF-8 ์ด ์•„๋‹ˆ๋ผ EUC-KR ๋กœ ์“ฐ์ธ ์ด์šฉ์•ฝ๊ด€์ด ๋‚˜์˜จ๋‹ค. ๋ณธ๋ฌธ์—๋Š” "์ด๊ฑด ํ”Œ๋ž˜๊ทธ๊ฐ€ ์•„๋‹ˆ๋‹ค" ๋ผ๊ณ  ๋Œ€๋†“๊ณ  ์ ํžŒ ๋ฏธ๋ผ๊ฐ€ ๋ฐ•ํ˜€ ์žˆ๊ณ , ์ง„์งœ ํ”Œ๋ž˜๊ทธ๋Š” ํ•œ๊ธ€๊ณผ ๊ตฌ๋ถ„์ด ์•ˆ ๊ฐ€๋Š” ์ „๊ฐ ASCII 36๊ธ€์ž๋กœ ์กฐ๋ฌธ ์‚ฌ์ด์— ์„ž์—ฌ ์žˆ๋‹ค. NFKC ์ •๊ทœํ™”๊ฐ€ ๋งˆ์ง€๋ง‰ ์—ด์‡ ๋‹ค.

๋ฌธ์ œ: DreamHack โ€” ์„œ๋น„์Šค ์ด์šฉ ์•ฝ๊ด€ ๋ถ„๋ฅ˜: misc / crypto ๋‚œ์ด๋„: ๐Ÿฅ‰ Bronze 3 FLAG: DH{Do_Y0u_Know_EUC-KR_enc0ding?}

๋ฌธ์ œ ์„ค๋ช…์€ ์ด๋ ‡๋‹ค.

๋“œ๋ฆผ์ด๋Š” ๋“œ๋ฆผํ•ต์— ์ ์šฉํ•  ์ƒˆ ์„œ๋น„์Šค ์ด์šฉ ์•ฝ๊ด€์„ ์ž‘์„ฑํ–ˆ์–ด์š”. ๊ทธ๋Ÿฐ๋ฐ ์ด์šฉ ์•ฝ๊ด€์ด ์ €์žฅ๋œ ํŒŒ์ผ์ด ์ž˜๋ชป๋œ ๋ฐฉ์‹์œผ๋กœ ์ธ์ฝ”๋”ฉ๋˜์–ด ๋“œ๋ฆผ์ด๊ฐ€ ๋‹นํ™ฉ์Šค๋Ÿฌ์›Œํ•˜๊ณ  ์žˆ์–ด์š”. ์—ฌ๋Ÿฌ๋ถ„์ด ๋“œ๋ฆผ์ด๋ฅผ ๋„์™€ ์ด์šฉ ์•ฝ๊ด€์„ ๋ณต์›ํ•ด ์ฃผ์„ธ์š”.

๋ฐ›์€ ๊ฑด tos.txt ํ•˜๋‚˜. ์„œ๋ฒ„๋„ ์—†๊ณ  ๋ฐ”์ด๋„ˆ๋ฆฌ๋„ ์—†๋‹ค.

"์ž˜๋ชป๋œ ๋ฐฉ์‹์œผ๋กœ ์ธ์ฝ”๋”ฉ" ์ด๋ผ๋Š” ํ•œ ๋ฌธ์žฅ์ด ๋ฌธ์ œ์˜ ์ „๋ถ€์ธ๋ฐ, ์—ฌ๊ธฐ์„œ ๋งํ•˜๋Š” ์ธ์ฝ”๋”ฉ์ด ํ•œ ๊ฒน์ด ์•„๋‹ˆ๋‹ค. ๊ฒ‰์˜ hex ๋ฅผ ๋ฒ—๊ธฐ๋ฉด ๋‘ ๋ฒˆ์งธ ์ธ์ฝ”๋”ฉ์ด ๋‚˜์˜ค๊ณ , ๊ทธ๊ฑธ ๋ฒ—๊ธฐ๋ฉด ์„ธ ๋ฒˆ์งธ๊ฐ€ ๋‚˜์˜จ๋‹ค. ๋งˆ์ง€๋ง‰ ๊ฒน์€ ๋ˆˆ์œผ๋กœ๋Š” ์ ˆ๋Œ€ ์•ˆ ๋ณด์ธ๋‹ค.

file extracted/tos.txt && wc -c extracted/tos.txt && head -c 96 extracted/tos.txt && echo

file ๊ณผ wc ๋กœ ๋ณธ tos.txt ๊ธฐ๋ณธ ์ •๋ณด โ€” ASCII text ์ด๊ณ  51050์ž์งœ๋ฆฌ ๋งค์šฐ ๊ธด ์ค„ ํ•˜๋‚˜์— CRLF ์ค„๋ฐ”๊ฟˆ์„ ์“ด๋‹ค. ์•ž๋ถ€๋ถ„์„ ์ฐ์–ด ๋ณด๋ฉด Encoded toc ๋ผ๋Š” ํ—ค๋” ๋’ค์— 16์ง„์ˆ˜ ๋ฌธ์ž์—ด์ด ์ด์–ด์ง„๋‹ค
file ๊ณผ wc ๋กœ ๋ณธ tos.txt ๊ธฐ๋ณธ ์ •๋ณด โ€” ASCII text ์ด๊ณ  51050์ž์งœ๋ฆฌ ๋งค์šฐ ๊ธด ์ค„ ํ•˜๋‚˜์— CRLF ์ค„๋ฐ”๊ฟˆ์„ ์“ด๋‹ค. ์•ž๋ถ€๋ถ„์„ ์ฐ์–ด ๋ณด๋ฉด Encoded toc ๋ผ๋Š” ํ—ค๋” ๋’ค์— 16์ง„์ˆ˜ ๋ฌธ์ž์—ด์ด ์ด์–ด์ง„๋‹ค

ํŒŒ์ผ์€ 51,071๋ฐ”์ดํŠธ์ธ๋ฐ file ์ด "very long lines (51050)" ์ด๋ผ๊ณ  ์•Œ๋ ค ์ค€๋‹ค. ์ฆ‰ ์‚ฌ์‹ค์ƒ ํ•œ ์ค„์งœ๋ฆฌ ํŒŒ์ผ์ด๋‹ค. ์•ž๋จธ๋ฆฌ์— Encoded toc (hex) ๋ผ๋Š” ํ—ค๋”๊ฐ€ ๋ถ™์–ด ์žˆ๋‹ค. tos ๋ฅผ toc ๋กœ ์ ์–ด ๋‘” ๊ฑด ์ถœ์ œ์ž์˜ ์˜คํƒ€๋กœ ๋ณด์ธ๋‹ค.


๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…์„œ๋น„์Šค ์ด์šฉ ์•ฝ๊ด€
๋‚œ์ด๋„๐Ÿฅ‰ Bronze 3
๋ถ„๋ฅ˜misc / crypto
์ œ๊ณต ํŒŒ์ผtos.txt (ASCII, 51,071 B)
์„œ๋ฒ„์—†์Œ โ€” ์™„์ „ ์˜คํ”„๋ผ์ธ
ํ•ต์‹ฌ ๊ธฐ๋ฒ•hex โ†’ EUC-KR โ†’ ์ „๊ฐ(fullwidth) ASCII โ†’ NFKC ์ •๊ทœํ™”
ํ•จ์ •๋ณธ๋ฌธ์— DH{...} ๋ชจ์–‘์˜ ๋ฏธ๋ผ๊ฐ€ ๋ฐ•ํ˜€ ์žˆ๋‹ค

ํ’€์ด ํ๋ฆ„์€ ์งง๋‹ค. hex ๋ฅผ ๋˜๋Œ๋ฆฌ๊ณ , UTF-8 ์ด ์•„๋‹ˆ๋ผ EUC-KR ๋กœ ์ฝ๊ณ , ๋ณต์›๋œ ์•ฝ๊ด€์—์„œ ํ•œ๊ธ€ ์‚ฌ์ด์— ์„ž์ธ ์ „๊ฐ ๋ผํ‹ด ๋ฌธ์ž๋งŒ ๊ณจ๋ผ๋‚ด NFKC ๋กœ ์ •๊ทœํ™”ํ•˜๋ฉด ๋๋‚œ๋‹ค.

์งง์ง€๋งŒ ๊ฐ ๋‹จ๊ณ„๋งˆ๋‹ค ํ•œ ๋ฒˆ์”ฉ ๊ฑธ๋ ค ๋„˜์–ด์งˆ ์ž๋ฆฌ๊ฐ€ ์žˆ๋‹ค.



๐Ÿงฉ ๋ฐฐ๊ฒฝ โ€” EUC-KR ์ด๋ผ๋Š” "์ž˜๋ชป๋œ ์ธ์ฝ”๋”ฉ"

์ง€๊ธˆ ํ•œ๊ตญ์–ด ํ…์ŠคํŠธ๋Š” ๊ฑฐ์˜ ์ „๋ถ€ UTF-8 ์ด๋‹ค. ํ•˜์ง€๋งŒ UTF-8 ์ด์ „์—๋Š” EUC-KR(๊ณผ ๊ทธ ํ™•์žฅ์ธ CP949)์ด ์‚ฌ์‹ค์ƒ ํ‘œ์ค€์ด์—ˆ๊ณ , ๊ด€๊ณต์„œ ๋ฌธ์„œ๋‚˜ ์˜ค๋ž˜๋œ ์‹œ์Šคํ…œ์ด ๋ฑ‰๋Š” ํŒŒ์ผ์—๋Š” ์•„์ง๋„ ๋‚จ์•„ ์žˆ๋‹ค.

๋‘˜์˜ ์ฐจ์ด๋Š” ๋‹จ์ˆœํ•˜๋‹ค.

ํ•œ๊ธ€ ํ•œ ๊ธ€์žASCII ํ•œ ๊ธ€์ž์ฒซ ๋ฐ”์ดํŠธ
UTF-83๋ฐ”์ดํŠธ1๋ฐ”์ดํŠธ0xEA~0xED
EUC-KR2๋ฐ”์ดํŠธ1๋ฐ”์ดํŠธ0xB0~0xC8 ๋“ฑ

"์ž˜๋ชป ์ธ์ฝ”๋”ฉ๋๋‹ค" ๋Š” ๋ฌธ์ œ ์„ค๋ช…์€ ์‚ฌ์‹ค ์ฝ๋Š” ์ชฝ์ด ์ธ์ฝ”๋”ฉ์„ ์ž˜๋ชป ๊ณ ๋ฅด๋ฉด ๊นจ์ง„๋‹ค๋Š” ๋œป์ด๋‹ค. ํŒŒ์ผ ์ž์ฒด๋Š” ๋ฉ€์ฉกํ•˜๊ณ , EUC-KR ๋กœ ์ฝ์–ด์•ผ ํ•  ๊ฑธ UTF-8 ๋กœ ์ฝ์œผ๋ฉด ์ฒซ ๋ฐ”์ดํŠธ๋ถ€ํ„ฐ ์‹คํŒจํ•œ๋‹ค.

์—ฌ๊ธฐ๊นŒ์ง€๋Š” ํ”ํ•œ ์ด์•ผ๊ธฐ๋‹ค. ์ด ๋ฌธ์ œ๊ฐ€ ํ•œ ๊ฑธ์Œ ๋” ๋‚˜๊ฐ€๋Š” ์ง€์ ์€ ๋’ค์— ๋‚˜์˜จ๋‹ค.


๐Ÿ”ฌ ์ •์ฐฐ โ€” hex ํ•œ ์ค„์„ ๋œฏ์–ด๋ณธ๋‹ค

๋จผ์ € ํŒŒ์ผ์˜ ์‹ค์ œ ๋ฐ”์ดํŠธ๋ฅผ ๋ณธ๋‹ค.

xxd -l 224 extracted/tos.txt

xxd ๋กœ ๋ณธ tos.txt ์•ž 224๋ฐ”์ดํŠธ โ€” Encoded toc ํ—ค๋”์™€ CRLF ๋‘ ๊ฐœ๊ฐ€ ์ง€๋‚˜๋ฉด 0x30 ๋ถ€ํ„ฐ 0x66 ์‚ฌ์ด ๊ฐ’๋งŒ ์ด์–ด์ง„๋‹ค. ์ฆ‰ ํŒŒ์ผ ๋ณธ๋ฌธ์€ 0-9a-f ๋กœ๋งŒ ์ด๋ค„์ง„ 16์ง„์ˆ˜ ํ…์ŠคํŠธ๋‹ค
xxd ๋กœ ๋ณธ tos.txt ์•ž 224๋ฐ”์ดํŠธ โ€” Encoded toc ํ—ค๋”์™€ CRLF ๋‘ ๊ฐœ๊ฐ€ ์ง€๋‚˜๋ฉด 0x30 ๋ถ€ํ„ฐ 0x66 ์‚ฌ์ด ๊ฐ’๋งŒ ์ด์–ด์ง„๋‹ค. ์ฆ‰ ํŒŒ์ผ ๋ณธ๋ฌธ์€ 0-9a-f ๋กœ๋งŒ ์ด๋ค„์ง„ 16์ง„์ˆ˜ ํ…์ŠคํŠธ๋‹ค

0d 0a 0d 0a ๋กœ ํ—ค๋”๊ฐ€ ๋๋‚˜๊ณ , ๊ทธ ๋’ค๋กœ๋Š” 3039, 6166 ๋งŒ ๋‚˜์˜จ๋‹ค. ASCII ๋กœ 09, af. ํ—ค๋” ๋ง๋Œ€๋กœ hex ๋‹ค.

51,050์ž๋‹ˆ๊นŒ ๋˜๋Œ๋ฆฌ๋ฉด 25,525๋ฐ”์ดํŠธ๊ฐ€ ๋‚˜์˜จ๋‹ค. ์ง์ˆ˜๋ผ ์ž˜๋ ค ์žˆ์ง€๋„ ์•Š๋‹ค.

๋˜๋Œ๋ฆฐ ๋ฐ”์ดํŠธ๋Š” ๋ฌด์Šจ ์ธ์ฝ”๋”ฉ์ธ๊ฐ€

hex ๋ฅผ ํ’€๊ณ  ๋‚˜์„œ ๋ฐ”๋กœ .decode() ๋ฅผ ๋ถ€๋ฅด๋ฉด ์•ˆ ๋œ๋‹ค. ์–ด๋–ค ์ฝ”๋ฑ์ด ๋งž๋Š”์ง€๋ถ€ํ„ฐ ํ™•์ธํ•œ๋‹ค. probe_encoding.py ๋ฅผ ๋งŒ๋“ค์–ด ํ›„๋ณด๋ฅผ ํ•˜๋‚˜์”ฉ ์ฐ”๋Ÿฌ ๋ดค๋‹ค.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""hex ๋ฅผ ๋˜๋Œ๋ฆฐ ๋’ค ์–ด๋–ค ๋ฌธ์ž ์ธ์ฝ”๋”ฉ์ธ์ง€ ์‹ค์ œ๋กœ ์ฐ”๋Ÿฌ๋ณธ๋‹ค.
 
๋ถ€์ˆ˜ ํšจ๊ณผ๋กœ extracted/tos.bin (hex ๋ฅผ ํ‘ผ ๋‚ ๋ฐ”์ดํŠธ) ์„ ๋‚จ๊ธด๋‹ค.
iconv ๊ฐ™์€ ์™ธ๋ถ€ ๋„๊ตฌ๋กœ ๊ต์ฐจํ™•์ธํ•  ๋•Œ ์ด ํŒŒ์ผ์„ ์“ด๋‹ค.
"""
import binascii
from pathlib import Path
 
import chardet
 
HERE = Path(__file__).resolve().parent
TOS = HERE / "extracted" / "tos.txt"
BIN = HERE / "extracted" / "tos.bin"
 
raw = TOS.read_bytes()
hex_line = raw.split(b"\r\n")[2]
blob = binascii.unhexlify(hex_line)
BIN.write_bytes(blob)
print(f"[hex] {len(hex_line)}์ž -> {len(blob)}๋ฐ”์ดํŠธ, {BIN.name} ๋กœ ์ €์žฅ")
print(f"[hex] ์•ž 16๋ฐ”์ดํŠธ : {blob[:16].hex(' ')}")
print()
 
for enc in ("utf-8", "utf-16-le", "euc-kr", "cp949", "johab"):
    try:
        s = blob.decode(enc)
    except UnicodeDecodeError as e:
        print(f"[{enc:<9}] FAIL  {e.reason} @ offset {e.start}")
    except LookupError:
        print(f"[{enc:<9}] SKIP  ์ฝ”๋ฑ ์—†์Œ")
    else:
        print(f"[{enc:<9}] OK    {len(s)}๊ธ€์ž | ์ฒซ ์ค„ = {s.splitlines()[0]}")
 
print()
guess = chardet.detect(blob)
print(f"[chardet] encoding={guess['encoding']} confidence={guess['confidence']:.2f} language={guess['language']}")
python3 probe_encoding.py

probe_encoding.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” utf-8 ์€ offset 0 ์—์„œ invalid start byte ๋กœ ์‹คํŒจํ•˜๊ณ  utf-16-le ์™€ johab ๋„ ๊นจ์ง„๋‹ค. euc-kr ๊ณผ cp949 ๋งŒ 15016๊ธ€์ž๋กœ ๋””์ฝ”๋“œ๋˜๋ฉฐ ์ฒซ ์ค„์ด ์ด์šฉ์•ฝ๊ด€์ด๋‹ค. chardet ๋„ EUC-KR ์„ ์‹ ๋ขฐ๋„ 0.99 ๋กœ ์ง€๋ชฉํ•œ๋‹ค
probe_encoding.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” utf-8 ์€ offset 0 ์—์„œ invalid start byte ๋กœ ์‹คํŒจํ•˜๊ณ  utf-16-le ์™€ johab ๋„ ๊นจ์ง„๋‹ค. euc-kr ๊ณผ cp949 ๋งŒ 15016๊ธ€์ž๋กœ ๋””์ฝ”๋“œ๋˜๋ฉฐ ์ฒซ ์ค„์ด ์ด์šฉ์•ฝ๊ด€์ด๋‹ค. chardet ๋„ EUC-KR ์„ ์‹ ๋ขฐ๋„ 0.99 ๋กœ ์ง€๋ชฉํ•œ๋‹ค

UTF-8 ์€ ์ฒซ ๋ฐ”์ดํŠธ(0xc0)์—์„œ ๋ฐ”๋กœ ์ฃฝ๋Š”๋‹ค. 0xc0 ์€ UTF-8 ์—์„œ ์•„์˜ˆ ์“ฐ์ด์ง€ ์•Š๋Š” ๊ฐ’์ด๋‹ค. EUC-KR ๊ณผ CP949 ๋Š” ๋‘˜ ๋‹ค ํ†ต๊ณผํ•˜๊ณ  ์ฒซ ์ค„์ด ์ด์šฉ์•ฝ๊ด€ ์œผ๋กœ ์ฝํžŒ๋‹ค. chardet ๋„ EUC-KR ์„ ์‹ ๋ขฐ๋„ 0.99 ๋กœ ์ฐ๋Š”๋‹ค.

CP949 ๋Š” EUC-KR ์˜ ์ƒ์œ„ํ˜ธํ™˜์ด๋ผ ๋‘˜ ๋‹ค ๋˜๋Š” ๊ฒŒ ์ •์ƒ์ด๋‹ค. ์ด ํŒŒ์ผ์—๋Š” CP949 ํ™•์žฅ ์˜์—ญ ๊ธ€์ž๊ฐ€ ์—†๋‹ค๋Š” ๋œป์ด๊ธฐ๋„ ํ•˜๋‹ค.

ํŒŒ์ด์ฌ ๋ง๊ณ  ์™ธ๋ถ€ ๋„๊ตฌ๋กœ๋„ ํ™•์ธ

ํ•œ ๋„๊ตฌ๋งŒ ๋ฏฟ๊ธฐ๋ณด๋‹ค ๊ณ„์—ด์ด ๋‹ค๋ฅธ ๊ฒƒ์œผ๋กœ ํ•œ ๋ฒˆ ๋” ๋ณธ๋‹ค. iconv ๋Š” glibc ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ณ€ํ™˜๊ธฐ๋‹ค.

iconv -f UTF-8 -t UTF-8 extracted/tos.bin | wc -c; iconv -f EUC-KR -t UTF-8 extracted/tos.bin | sed -n "1,4p"

iconv ๋กœ ๊ต์ฐจํ™•์ธํ•œ ๊ฒฐ๊ณผ โ€” UTF-8 ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” 0 ์œ„์น˜์— ์ž˜๋ชป๋œ ์ž…๋ ฅ ์ˆœ์„œ์—ด์ด ์žˆ๋‹ค๋ฉฐ 0๋ฐ”์ดํŠธ๋ฅผ ๋‚ด๋†“๊ณ , EUC-KR ์—์„œ UTF-8 ๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด ์ด์šฉ์•ฝ๊ด€ ์ œ1์žฅ ์ด์น™ ์ œ1์กฐ ๋ชฉ์  ์กฐ๋ฌธ์ด ์ •์ƒ์ ์ธ ํ•œ๊ธ€๋กœ ๋‚˜์˜จ๋‹ค
iconv ๋กœ ๊ต์ฐจํ™•์ธํ•œ ๊ฒฐ๊ณผ โ€” UTF-8 ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” 0 ์œ„์น˜์— ์ž˜๋ชป๋œ ์ž…๋ ฅ ์ˆœ์„œ์—ด์ด ์žˆ๋‹ค๋ฉฐ 0๋ฐ”์ดํŠธ๋ฅผ ๋‚ด๋†“๊ณ , EUC-KR ์—์„œ UTF-8 ๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด ์ด์šฉ์•ฝ๊ด€ ์ œ1์žฅ ์ด์น™ ์ œ1์กฐ ๋ชฉ์  ์กฐ๋ฌธ์ด ์ •์ƒ์ ์ธ ํ•œ๊ธ€๋กœ ๋‚˜์˜จ๋‹ค

iconv -f UTF-8 -t UTF-8 ์€ "์ด ๋ฐ”์ดํŠธ์—ด์ด ์œ ํšจํ•œ UTF-8 ์ธ๊ฐ€" ๋ฅผ ๋ฌป๋Š” ๊ด€์šฉ์ ์ธ ๋ฐฉ๋ฒ•์ด๋‹ค. 0๋ฐ”์ดํŠธ๋ฅผ ๋‚ด๋†“๊ณ  ์˜ค๋ฅ˜๋กœ ๋๋‚œ๋‹ค. EUC-KR ๋กœ ์ง€์ •ํ•˜๋ฉด ์•ฝ๊ด€์ด ๊ทธ๋Œ€๋กœ ๋‚˜์˜จ๋‹ค.

์ฃผ์‹ํšŒ์‚ฌ ํ‹ฐ์˜ค๋ฆฌํ•œ๊ตญ(์ดํ•˜ 'ํšŒ์‚ฌ')๊ฐ€ ์ œ๊ณตํ•˜๋Š” Dreamhack ์„œ๋น„์Šค... โ€” ์ง„์งœ ์•ฝ๊ด€์ฒ˜๋Ÿผ ์ƒ๊ธด ๋ฌธ์„œ๋‹ค. ๋ฌธ์ œ ์„ค๋ช…์ด "์ด ์Šคํ† ๋ฆฌ๋Š” ํ”ฝ์…˜" ์ด๋ผ๊ณ  ๋ชป๋ฐ•์•„ ๋’€์œผ๋‹ˆ ๋‚ด์šฉ์€ ๊ทธ๋Œ€๋กœ ๋ฏฟ์„ ๊ฒŒ ์•„๋‹ˆ๋‹ค.



๐Ÿ› ์‚ฝ์งˆ โ€” ๋‘ ๋ฒˆ ํ—›์งš์—ˆ๋‹ค

์ฒซ ๊ฒน์„ ๋ฒ—๊ธฐ๊ณ  ๋‚˜๋ฉด 15,016๊ธ€์ž์งœ๋ฆฌ ์•ฝ๊ด€์ด ์†์— ๋“ค์–ด์˜จ๋‹ค. ์—ฌ๊ธฐ์„œ DH ๋ฅผ ์ฐพ์œผ๋ฉด ๋ญ”๊ฐ€ ๋‚˜์˜จ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๊ทธ๊ฒŒ ํ•จ์ •์ด์—ˆ๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ 1 โ€” ๋Œ€๋†“๊ณ  ๋ฐ•ํ˜€ ์žˆ๋˜ ๋ฏธ๋ผ ํ”Œ๋ž˜๊ทธ

๋ณต์›๋œ ๋ณธ๋ฌธ์„ DH{ ๋กœ ์ฐพ์œผ๋ฉด ๋”ฑ ํ•œ ๊ตฐ๋ฐ ๊ฑธ๋ฆฐ๋‹ค. ์ œ27์กฐ๋‹ค.

iconv -f EUC-KR -t UTF-8 extracted/tos.bin | grep -a -n -A1 "์ œ27์กฐ"

iconv ์™€ grep ์œผ๋กœ ์ฐพ์€ ์ œ27์กฐ โ€” ์กฐ๋ฌธ ์ œ๋ชฉ์ด ํ”Œ๋ž˜๊ทธ์ด๊ณ  ๋ฐ”๋กœ ๋‹ค์Œ ์ค„์— DH ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ฌธ์ž์—ด ํ•˜๋‚˜๊ฐ€ ์ ํ˜€ ์žˆ๋Š”๋ฐ, ๊ทธ ๋ฌธ์žฅ์ด ์ด ๋ฌธ์ž์—ด์€ ํ”Œ๋ž˜๊ทธ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค ๋ผ๊ณ  ์Šค์Šค๋กœ ๋ฐํžˆ๊ณ  ์žˆ๋‹ค
iconv ์™€ grep ์œผ๋กœ ์ฐพ์€ ์ œ27์กฐ โ€” ์กฐ๋ฌธ ์ œ๋ชฉ์ด ํ”Œ๋ž˜๊ทธ์ด๊ณ  ๋ฐ”๋กœ ๋‹ค์Œ ์ค„์— DH ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ฌธ์ž์—ด ํ•˜๋‚˜๊ฐ€ ์ ํ˜€ ์žˆ๋Š”๋ฐ, ๊ทธ ๋ฌธ์žฅ์ด ์ด ๋ฌธ์ž์—ด์€ ํ”Œ๋ž˜๊ทธ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค ๋ผ๊ณ  ์Šค์Šค๋กœ ๋ฐํžˆ๊ณ  ์žˆ๋‹ค

์กฐ๋ฌธ ์ œ๋ชฉ์ด ์ œ27์กฐ (ํ”Œ๋ž˜๊ทธ) ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ณธ๋ฌธ์€ ์ด๋ ‡๊ฒŒ ์ ํ˜€ ์žˆ๋‹ค.

๋ฌธ์ž์—ด DH{New_Dreamhack_Terms_Of_Service} ๋Š” ํ”Œ๋ž˜๊ทธ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

์ฝ๊ณ  ๋‚˜์„œ์•ผ ์›ƒ์—ˆ๋‹ค. ํ˜•์‹๋„ ๋งž๊ณ  ๋‚ด์šฉ๋„ ๊ทธ๋Ÿด๋“ฏํ•ด์„œ ๊ทธ๋ƒฅ ๋˜์ ธ ๋ณผ ๋ป”ํ–ˆ๋‹ค. ์ถœ์ œ์ž๊ฐ€ ์นœ์ ˆํ•˜๊ฒŒ ์•„๋‹ˆ๋ผ๊ณ  ์ ์–ด ๋‘” ๋•์— ์ œ์ถœ ์‹œ๋„๋Š” ์•ˆ ํ–ˆ์ง€๋งŒ, "์ฐพ์•˜๋‹ค" ๋กœ ๋๋‚ด๋ฉด ์•ˆ ๋œ๋‹ค๋Š” ์‹ ํ˜ธ๋ฅผ ์—ฌ๊ธฐ์„œ ๋ฐ›์€ ์…ˆ์ด๋‹ค.

grep ์— -a ๋ฅผ ๋ถ™์ธ ๊ฒƒ๋„ ์ด์œ ๊ฐ€ ์žˆ๋‹ค. ์ด ์ŠคํŠธ๋ฆผ์—๋Š” CR ์ด ์„ž์—ฌ ์žˆ์–ด์„œ GNU grep ์ด ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ํŒ์ •ํ•ด ๋ฒ„๋ฆฌ๋Š” ์ผ์ด ์žˆ์—ˆ๋‹ค. -a ์—†์ด ๋Œ๋ฆฌ๋ฉด ๋งค์น˜๊ฐ€ ์žˆ๋Š”๋ฐ๋„ ์กฐ์šฉํžˆ ์ข…๋ฃŒ์ฝ”๋“œ 1 ๋กœ ๋๋‚œ๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ 2 โ€” 0xA3 ์„ ๊ทธ๋ƒฅ grep ํ–ˆ๋‹ค๊ฐ€ ์˜คํƒ 79๊ฐœ

๋’ค์— ๋‚˜์˜ค์ง€๋งŒ ์ง„์งœ ํ”Œ๋ž˜๊ทธ๋Š” EUC-KR ์˜ A3 ํ–‰์— ๋“ค์–ด ์žˆ๋‹ค. ๊ทธ๊ฑธ ์•Œ๊ณ  ๋‚˜์„œ "๊ทธ๋Ÿผ 0xA3 ๋ฐ”์ดํŠธ๋งŒ ์„ธ๋ฉด ๋˜๊ฒ ๋„ค" ํ•˜๊ณ  ๋ฐ”์ดํŠธ ๋‹จ์œ„๋กœ ํ›‘์—ˆ๋”๋‹ˆ ์—‰๋šฑํ•œ ๊ฐ’์ด ๋‚˜์™”๋‹ค.

runs = [m for m in re.finditer(rb'(?:\xa3.)+', b)]   # 43๊ฐœ โ€” ๋Œ€๋ถ€๋ถ„ ๊ฐ€์งœ

EUC-KR ์€ ๊ฐ€๋ณ€ ๊ธธ์ด๋‹ค. 0xA3 ์€ ์ „๊ฐ ๊ธ€์ž์˜ ์„ ๋‘ ๋ฐ”์ดํŠธ์ด๊ธฐ๋„ ํ•˜์ง€๋งŒ, ์ „ํ˜€ ๋‹ค๋ฅธ ํ•œ๊ธ€์˜ ๋’ท๋ฐ”์ดํŠธ๋กœ๋„ ๋‚˜์˜จ๋‹ค. ํŒŒ์ผ ์ „์ฒด์—์„œ 0xA3 ๋ฐ”์ดํŠธ๋Š” 79๊ฐœ์ธ๋ฐ ๊ทธ ์ค‘ ์‹ค์ œ ์„ ๋‘๋Š” 36๊ฐœ๋ฟ์ด๋‹ค.

์ •๊ทœ์‹์œผ๋กœ ๋ฐ”์ดํŠธ๋ฅผ ํ›‘์œผ๋ฉด ์ด ๋‘˜์„ ๊ตฌ๋ถ„ํ•˜์ง€ ๋ชปํ•œ๋‹ค. ์ฒซ ๋ฐ”์ดํŠธ๋ถ€ํ„ฐ ์ˆœ์„œ๋Œ€๋กœ ๊ฑธ์œผ๋ฉด์„œ "์ง€๊ธˆ ์ด ๋ฐ”์ดํŠธ๊ฐ€ ๊ธ€์ž์˜ ์‹œ์ž‘์ธ๊ฐ€" ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค. solve.py ์˜ walk_euckr() ๊ฐ€ ๊ทธ ์ผ์„ ํ•œ๋‹ค.

์ด๋Ÿฐ ์ข…๋ฅ˜์˜ ์ฐฉ๊ฐ์€ UTF-8 ์—์„œ๋Š” ์ž˜ ์•ˆ ์ƒ๊ธด๋‹ค. UTF-8 ์€ ํ›„์† ๋ฐ”์ดํŠธ๊ฐ€ ํ•ญ์ƒ 10xxxxxx ๋ผ ๋ฐ”์ดํŠธ๋งŒ ๋ด๋„ ์„ ๋‘์ธ์ง€ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ ์„ค๊ณ„๋ผ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. EUC-KR ์—๋Š” ๊ทธ๋Ÿฐ ์žฅ์น˜๊ฐ€ ์—†๋‹ค.


๐Ÿ’ฃ ํ•ต์‹ฌ โ€” ํ•œ๊ธ€ ์‚ฌ์ด์— ์„ž์ธ ์ „๊ฐ ASCII

๋ฏธ๋ผ์— ์†์ง€ ์•Š์œผ๋ ค๋ฉด "์•ฝ๊ด€ ๋ณธ๋ฌธ์— ์žˆ์–ด์„œ๋Š” ์•ˆ ๋  ๊ฒƒ" ์„ ์ฐพ์•„์•ผ ํ•œ๋‹ค. ์ •์ƒ์ ์ธ ํ•œ๊ตญ์–ด ์•ฝ๊ด€์ด๋ผ๋ฉด ๊ธ€์ž๋Š” ํ•œ๊ธ€ ์•„๋‹ˆ๋ฉด ASCII๋‹ค. ๊ทธ ๋ฐ–์˜ ๊ฒƒ์ด ๋‚จ์œผ๋ฉด ๊ทธ๊ฒŒ ์‹ฌ์–ด์ง„ ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ ๊ธ€์ž๋ฅผ ์ข…๋ฅ˜๋ณ„๋กœ ์„ธ๋Š” charscan.py ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""๋ณต์›๋œ ์•ฝ๊ด€ ๋ณธ๋ฌธ์˜ ๊ธ€์ž๋ฅผ ์ข…๋ฅ˜๋ณ„๋กœ ์„ธ์–ด 'ํŠ€๋Š” ๊ธ€์ž' ๋ฅผ ์ฐพ์•„๋‚ธ๋‹ค.
 
์•ฝ๊ด€์€ ํ•œ๊ธ€ + ASCII ๋กœ๋งŒ ์ด๋ค„์ ธ์•ผ ์ •์ƒ์ด๋‹ค. ๊ทธ ๋ฐ–์˜ ๊ธ€์ž๊ฐ€ ๋‚จ์œผ๋ฉด
๊ทธ๊ฒŒ ๊ณง ์ถœ์ œ์ž๊ฐ€ ์‹ฌ์–ด ๋‘” ๊ฒƒ์ด๋‹ค.
"""
import unicodedata
from collections import Counter
from pathlib import Path
 
HERE = Path(__file__).resolve().parent
BIN = HERE / "extracted" / "tos.bin"
 
text = BIN.read_bytes().decode("euc-kr")
 
 
def kind(ch: str) -> str:
    o = ord(ch)
    if o < 0x80:
        return "ASCII"
    if 0xAC00 <= o <= 0xD7A3:
        return "ํ•œ๊ธ€"
    if 0xFF01 <= o <= 0xFF5E:
        return "์ „๊ฐASCII"
    return "๊ธฐํƒ€"
 
 
tally = Counter(kind(c) for c in text)
print(f"๋ณธ๋ฌธ {len(text)}๊ธ€์ž")
for k, n in tally.most_common():
    print(f"  {k:<9} {n:>6}")
print()
 
print("ํ•œ๊ธ€/ASCII ๊ฐ€ ์•„๋‹Œ ๊ธ€์ž (๋งŽ์€ ์ˆœ):")
odd = Counter(c for c in text if kind(c) not in ("ASCII", "ํ•œ๊ธ€"))
for ch, n in odd.most_common(12):
    print(f"  U+{ord(ch):04X}  {ch}  x{n:<3} {unicodedata.name(ch, '?')}")
print(f"  ... ์ด {len(odd)}์ข…")
print()
 
hits = [i for i, c in enumerate(text) if kind(c) == "์ „๊ฐASCII"]
print(f"์ „๊ฐ ASCII {len(hits)}๊ธ€์ž, ์œ„์น˜ {hits[0]}~{hits[-1]}")
print(f"๊ทธ ๊ตฌ๊ฐ„ ์›๋ฌธ : {text[hits[0]:hits[-1] + 1]}")
python3 charscan.py

charscan.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ๋ณธ๋ฌธ 15016๊ธ€์ž ์ค‘ ํ•œ๊ธ€์ด 10369๊ฐœ ASCII ๊ฐ€ 4507๊ฐœ์ธ๋ฐ ์ „๊ฐ ASCII ๊ฐ€ 36๊ฐœ ์„ž์—ฌ ์žˆ๋‹ค. ์œ ๋‹ˆ์ฝ”๋“œ ์ด๋ฆ„์„ ํ•จ๊ป˜ ์ฐ์–ด ๋ณด๋ฉด FULLWIDTH LATIN CAPITAL LETTER K ๊ฐ™์€ ํ•ญ๋ชฉ์ด ๋ณด์ด๊ณ , ๋งˆ์ง€๋ง‰ ์ค„์— ์ „๊ฐ ๊ตฌ๊ฐ„ ์›๋ฌธ์ด ํ†ต์งธ๋กœ ๋“œ๋Ÿฌ๋‚œ๋‹ค
charscan.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ๋ณธ๋ฌธ 15016๊ธ€์ž ์ค‘ ํ•œ๊ธ€์ด 10369๊ฐœ ASCII ๊ฐ€ 4507๊ฐœ์ธ๋ฐ ์ „๊ฐ ASCII ๊ฐ€ 36๊ฐœ ์„ž์—ฌ ์žˆ๋‹ค. ์œ ๋‹ˆ์ฝ”๋“œ ์ด๋ฆ„์„ ํ•จ๊ป˜ ์ฐ์–ด ๋ณด๋ฉด FULLWIDTH LATIN CAPITAL LETTER K ๊ฐ™์€ ํ•ญ๋ชฉ์ด ๋ณด์ด๊ณ , ๋งˆ์ง€๋ง‰ ์ค„์— ์ „๊ฐ ๊ตฌ๊ฐ„ ์›๋ฌธ์ด ํ†ต์งธ๋กœ ๋“œ๋Ÿฌ๋‚œ๋‹ค

15,016๊ธ€์ž ์ค‘ ์ „๊ฐ ASCII ๊ฐ€ ์ •ํ™•ํžˆ 36๊ธ€์ž๋‹ค. ์œ„์น˜๋„ 11743~11791 ํ•œ ๊ตฐ๋ฐ์— ๋ชฐ๋ ค ์žˆ๋‹ค. ๊ทธ ๊ตฌ๊ฐ„์„ ๊ทธ๋Œ€๋กœ ์ฐ์–ด ๋ณด๋ฉด ์ด๋ ‡๊ฒŒ ๋‚˜์˜จ๋‹ค.

โ‘ค ํ”Œ๋ž˜๊ทธ๋Š” ๋‹ค์Œ ๋ฌธ์ž์—ด์„ ๏ผฎ๏ผฆ๏ผซ๏ผฃ ์ •๊ทœํ™”ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค : ๏ผค๏ผจ๏ฝ›๏ผค๏ฝ๏ผฟ๏ผน๏ผ๏ฝ•๏ผฟโ€ฆ๏ฝ

์ œ18์กฐ (์œ ๋ฃŒ ์ฝ˜ํ…์ธ ) ์˜ ๋‹ค์„ฏ ๋ฒˆ์งธ ํ•ญ์ด๋‹ค. ์œ ๋ฃŒ ์ฝ˜ํ…์ธ  ์กฐ๋ฌธ ํ•œ๋ณตํŒ์— ํ”Œ๋ž˜๊ทธ ์•ˆ๋‚ด๊ฐ€ ๋ผ์–ด ์žˆ๋‹ค. โ‘ ~โ‘ช ๊ฐ™์€ ์›๋ฌธ์ž์™€ โ€˜โ€™ ๊ฐ™์€ ์ธ์šฉ๋ถ€ํ˜ธ๋„ "๊ธฐํƒ€" ๋กœ ์žกํžˆ๋Š”๋ฐ, ๊ทธ๊ฑด ์•ฝ๊ด€ ๋ฌธ์„œ์— ์›๋ž˜ ํ”ํ•œ ๊ฒƒ๋“ค์ด๋ผ ๊ฐ™์ด ๋‚˜์˜ฌ ๋ฟ ๋‹จ์„œ๋Š” ์•„๋‹ˆ๋‹ค.

๏ผฎ๏ผฆ๏ผซ๏ผฃ ๋ผ๋Š” ์ง€์‹œ์–ด ์ž์ฒด๋„ ์ „๊ฐ์œผ๋กœ ์“ฐ์—ฌ ์žˆ๋‹ค๋Š” ๊ฒŒ ์žฌ๋ฐŒ๋‹ค. ์ „๊ฐ ๊ตฌ๊ฐ„ 36๊ธ€์ž = ์ง€์‹œ์–ด 4๊ธ€์ž + ํ”Œ๋ž˜๊ทธ 32๊ธ€์ž๋‹ค.

์„ธ ๊ฒน ๊ตฌ์กฐ ๋„์‹ โ€” hex ASCII 51050์ž๋ฅผ unhexlify ํ•˜๋ฉด EUC-KR ๋ฐ”์ดํŠธ 25525๊ฐœ๊ฐ€ ๋˜๊ณ  ๊ทธ๊ฒƒ์„ ๋””์ฝ”๋“œํ•˜๋ฉด ์•ฝ๊ด€ ๋ณธ๋ฌธ์ด ๋œ๋‹ค. ๋งˆ์ง€๋ง‰ ๊ฒน์˜ ์ „๊ฐ ๋ฌธ์ž์—ด์„ NFKC ์ •๊ทœํ™”ํ•˜๋ฉด ํ”Œ๋ž˜๊ทธ๋‹ค. ์•„๋ž˜ ์นธ์€ D ๊ฐ€ 1๋ฐ”์ดํŠธ์ธ ๋ฐ˜๋ฉด ์ „๊ฐ D ๋Š” a3 c4 ๋‘ ๋ฐ”์ดํŠธ์ž„์„ ๋Œ€๋น„ํ•œ๋‹ค
์„ธ ๊ฒน ๊ตฌ์กฐ ๋„์‹ โ€” hex ASCII 51050์ž๋ฅผ unhexlify ํ•˜๋ฉด EUC-KR ๋ฐ”์ดํŠธ 25525๊ฐœ๊ฐ€ ๋˜๊ณ  ๊ทธ๊ฒƒ์„ ๋””์ฝ”๋“œํ•˜๋ฉด ์•ฝ๊ด€ ๋ณธ๋ฌธ์ด ๋œ๋‹ค. ๋งˆ์ง€๋ง‰ ๊ฒน์˜ ์ „๊ฐ ๋ฌธ์ž์—ด์„ NFKC ์ •๊ทœํ™”ํ•˜๋ฉด ํ”Œ๋ž˜๊ทธ๋‹ค. ์•„๋ž˜ ์นธ์€ D ๊ฐ€ 1๋ฐ”์ดํŠธ์ธ ๋ฐ˜๋ฉด ์ „๊ฐ D ๋Š” a3 c4 ๋‘ ๋ฐ”์ดํŠธ์ž„์„ ๋Œ€๋น„ํ•œ๋‹ค

์™œ ๋ˆˆ์— ์•ˆ ๋„์—ˆ๋‚˜

์ „๊ฐ(fullwidth) ๋ฌธ์ž๋Š” ์œ ๋‹ˆ์ฝ”๋“œ U+FF01~U+FF5E ์— ์žˆ๋Š”, ASCII ๋ฅผ ํ•œ๊ธ€ ํญ์— ๋งž์ถฐ ๋‹ค์‹œ ๊ทธ๋ฆฐ ๊ธ€์ž๋‹ค. ๏ผค ์™€ D ๋Š” ์˜๋ฏธ์ƒ ๊ฐ™์€ ๊ธ€์ž์ธ๋ฐ ์ฝ”๋“œํฌ์ธํŠธ๊ฐ€ ๋‹ค๋ฅด๋‹ค.

EUC-KR ์—์„œ๋Š” ์ด๊ฒŒ ๋” ์–„๊ถ‚๋‹ค. ์ „๊ฐ ASCII ๋Š” A3 ํ–‰์— ํ†ต์งธ๋กœ ๋“ค์–ด ์žˆ๊ณ , ๋’ท๋ฐ”์ดํŠธ๊ฐ€ ์ •ํ™•ํžˆ ASCII | 0x80 ์ด๋‹ค.

๊ธ€์ž์œ ๋‹ˆ์ฝ”๋“œEUC-KR ๋ฐ”์ดํŠธ
DU+004444 (1๋ฐ”์ดํŠธ)
๏ผคU+FF24a3 c4 (2๋ฐ”์ดํŠธ)
{U+007B7b (1๋ฐ”์ดํŠธ)
๏ฝ›U+FF5Ba3 fb (2๋ฐ”์ดํŠธ)

์ฆ‰ ์ „๊ฐ์œผ๋กœ ์“ฐ์ธ ํ”Œ๋ž˜๊ทธ๋Š” EUC-KR ์•ˆ์—์„œ ํ•œ๊ธ€๊ณผ ๋˜‘๊ฐ™์€ 2๋ฐ”์ดํŠธ ๊ธ€์ž๋กœ ์กด์žฌํ•œ๋‹ค. grep DH ๋กœ๋„ ์•ˆ ์žกํžˆ๊ณ , strings ๋กœ๋„ ์•ˆ ์žกํžŒ๋‹ค. ์‹ค์ œ ๋ฐ”์ดํŠธ๋ฅผ ๋ณด๋ฉด ๋ถ„๋ช…ํ•˜๋‹ค.

xxd -s 19968 -l 144 extracted/tos.bin

xxd ๋กœ ๋ณธ ํ”Œ๋ž˜๊ทธ ๊ตฌ๊ฐ„ ์›๋ณธ ๋ฐ”์ดํŠธ โ€” ์•ž์ชฝ์€ c6a3 cba3 c320 ๊ฐ™์€ ํ‰๋ฒ”ํ•œ ํ•œ๊ธ€ ๋ฐ”์ดํŠธ์ธ๋ฐ ์˜คํ”„์…‹ 0x4e1a ๋ถ€ํ„ฐ a3c4 a3c8 a3fb ์ฒ˜๋Ÿผ a3 ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์Œ์ด ์ค„์ค„์ด ์ด์–ด์ง„๋‹ค. ์ด a3 ํ–‰ ๊ตฌ๊ฐ„์ด ๋๋‚˜๋ฉด ๋‹ค์‹œ 0d0a ์™€ ํ•œ๊ธ€ ๋ฐ”์ดํŠธ๋กœ ๋Œ์•„๊ฐ„๋‹ค
xxd ๋กœ ๋ณธ ํ”Œ๋ž˜๊ทธ ๊ตฌ๊ฐ„ ์›๋ณธ ๋ฐ”์ดํŠธ โ€” ์•ž์ชฝ์€ c6a3 cba3 c320 ๊ฐ™์€ ํ‰๋ฒ”ํ•œ ํ•œ๊ธ€ ๋ฐ”์ดํŠธ์ธ๋ฐ ์˜คํ”„์…‹ 0x4e1a ๋ถ€ํ„ฐ a3c4 a3c8 a3fb ์ฒ˜๋Ÿผ a3 ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์Œ์ด ์ค„์ค„์ด ์ด์–ด์ง„๋‹ค. ์ด a3 ํ–‰ ๊ตฌ๊ฐ„์ด ๋๋‚˜๋ฉด ๋‹ค์‹œ 0d0a ์™€ ํ•œ๊ธ€ ๋ฐ”์ดํŠธ๋กœ ๋Œ์•„๊ฐ„๋‹ค

0x4e1a ๋ถ€ํ„ฐ a3c4 a3c8 a3fb a3c4 a3ef ... ๊ฐ€ ์ด์–ด์ง„๋‹ค. a3c4 โ†’ 0xc4 - 0x80 = 0x44 โ†’ D. a3c8 โ†’ H. a3fb โ†’ {. ๋’ท๋ฐ”์ดํŠธ์—์„œ 0x80 ๋งŒ ๋นผ๋ฉด ๊ทธ๋Œ€๋กœ ASCII ๊ฐ€ ๋œ๋‹ค.

์ด ๊ทœ์น™ ๋•๋ถ„์— ์œ ๋‹ˆ์ฝ”๋“œ ํ…Œ์ด๋ธ” ์—†์ด๋„ ๋‹ต์„ ๋‚ผ ์ˆ˜ ์žˆ๋‹ค โ€” EUC-KR ์ŠคํŠธ๋ฆผ์„ ๊ฑธ์œผ๋ฉด์„œ ์„ ๋‘ ๋ฐ”์ดํŠธ๊ฐ€ 0xA3 ์ธ ๊ธ€์ž๋งŒ ๋ชจ์œผ๋ฉด ๋œ๋‹ค. solve.py ๋Š” ์ด ๊ฒฝ๋กœ๋ฅผ ๊ต์ฐจ๊ฒ€์ฆ์šฉ์œผ๋กœ ๋”ฐ๋กœ ๋Œ๋ฆฐ๋‹ค.

๋งˆ์ง€๋ง‰ ์—ด์‡  โ€” NFKC

๋ณธ๋ฌธ์ด ์‹œํ‚ค๋Š” NFKC ๋Š” ์œ ๋‹ˆ์ฝ”๋“œ ์ •๊ทœํ™” ํ˜•์‹ ์ค‘ ํ•˜๋‚˜๋‹ค. NFC ๊ฐ€ "๋ชจ์–‘์ด ๊ฐ™์€ ๊ฒƒ์„ ํ•ฉ์น˜๋Š”" ์ˆ˜์ค€์ด๋ผ๋ฉด, NFKC ๋Š” ํ˜ธํ™˜ ๋ฌธ์ž๋ฅผ ๋Œ€ํ‘œ ๋ฌธ์ž๋กœ ์ ‘๋Š”๋‹ค. ์ „๊ฐ ๏ผค โ†’ D, โ‘  โ†’ 1, ๏ฌ ํ•ฉ์ž โ†’ fi ๊ฐ™์€ ๋ณ€ํ™˜์ด ์—ฌ๊ธฐ์„œ ์ผ์–ด๋‚œ๋‹ค.

ํŒŒ์ด์ฌ์—์„œ๋Š” unicodedata.normalize("NFKC", s) ํ•œ ์ค„์ด๋‹ค. ์ „๊ฐ 32๊ธ€์ž๋ฅผ ์—ฌ๊ธฐ์— ํ†ต๊ณผ์‹œํ‚ค๋ฉด ํ‰๋ฒ”ํ•œ ASCII ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋œ๋‹ค.


๐ŸŽฏ ๊ต์ฐจํ™•์ธ โ€” CyberChef ๋กœ ๊ฐ™์€ ๋‹ต์ด ๋‚˜์˜ค๋Š”๊ฐ€

๋‚ด ์Šคํฌ๋ฆฝํŠธ๋งŒ์œผ๋กœ ๊ฒฐ๋ก ์„ ๋‚ด๋ฉด ์Šคํฌ๋ฆฝํŠธ ๋ฒ„๊ทธ์™€ ์ •๋‹ต์„ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ž˜์„œ ์™„์ „ํžˆ ๋‹ค๋ฅธ ๊ตฌํ˜„์œผ๋กœ ํ•œ ๋ฒˆ ๋” ํ™•์ธํ–ˆ๋‹ค. CyberChef ๋Š” ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋„๋Š” ์ธ์ฝ”๋”ฉ ๋ณ€ํ™˜ ๋„๊ตฌ๋‹ค.

ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋“  ๊ตฌ๊ฐ„์˜ hex 238์ž๋ฅผ ์ž˜๋ผ From Hex โ†’ Decode text (EUC Korean 51949) ๋ ˆ์‹œํ”ผ์— ๋„ฃ์—ˆ๋‹ค. ๋ ˆ์‹œํ”ผ์™€ ์ž…๋ ฅ์€ URL ํ•ด์‹œ์— ์‹ค๋ ค ์žˆ์–ด ์ฃผ์†Œ๋งŒ ์—ด๋ฉด ๊ทธ๋Œ€๋กœ ์žฌํ˜„๋œ๋‹ค.

CyberChef ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด โ€” ์ฃผ์†Œ์ฐฝ์— From_Hex ์™€ Decode_text EUC Korean 51949 ๋ ˆ์‹œํ”ผ๊ฐ€ ์‹ค๋ ค ์žˆ๊ณ  Recipe ํŒจ๋„์— ๋‘ ๋‹จ๊ณ„๊ฐ€ ์Œ“์—ฌ ์žˆ๋‹ค. Output ์—๋Š” ํ•ด๋‹น ์กฐ๋ฌธ๊ณผ ์ „๊ฐ ํ”Œ๋ž˜๊ทธ๊ฐ€ ํญ ๋„“์€ ๊ธ€์ž๋กœ ์ถœ๋ ฅ๋œ๋‹ค
CyberChef ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด โ€” ์ฃผ์†Œ์ฐฝ์— From_Hex ์™€ Decode_text EUC Korean 51949 ๋ ˆ์‹œํ”ผ๊ฐ€ ์‹ค๋ ค ์žˆ๊ณ  Recipe ํŒจ๋„์— ๋‘ ๋‹จ๊ณ„๊ฐ€ ์Œ“์—ฌ ์žˆ๋‹ค. Output ์—๋Š” ํ•ด๋‹น ์กฐ๋ฌธ๊ณผ ์ „๊ฐ ํ”Œ๋ž˜๊ทธ๊ฐ€ ํญ ๋„“์€ ๊ธ€์ž๋กœ ์ถœ๋ ฅ๋œ๋‹ค

Output ์„ ๋ณด๋ฉด ๊ธ€์ž ์‚ฌ์ด๊ฐ€ ์œ ๋‚œํžˆ ๋ฒŒ์–ด์ ธ ์žˆ๋‹ค. ๊ทธ๊ฒŒ ์ „๊ฐ์ด๋ผ๋Š” ์ฆ๊ฑฐ๋‹ค. ๋ธŒ๋ผ์šฐ์ € ํฐํŠธ๋กœ ๋ Œ๋”ํ•ด๋„ ๏ผค๏ผจ๏ฝ› ๋Š” DH{ ๋ณด๋‹ค ๋ˆˆ์— ๋„๊ฒŒ ๋„“๋‹ค. ํŒŒ์ด์ฌ์ด ์ค€ ๋‹ต๊ณผ ๊ฐ™์€ ๋ฌธ์ž์—ด์ด ๋‚˜์™”์œผ๋‹ˆ ์ฝ”๋ฑ ์„ ํƒ์€ ํ‹€๋ฆฌ์ง€ ์•Š์•˜๋‹ค.



๐Ÿš€ Full Exploit

์ง€๊ธˆ๊นŒ์ง€์˜ ํŒ๋‹จ์„ ํ•œ ํŒŒ์ผ๋กœ ๋ฌถ์—ˆ๋‹ค. tos.txt ๋งŒ ์žˆ์œผ๋ฉด ํ”Œ๋ž˜๊ทธ๊นŒ์ง€ ๊ฐ„๋‹ค.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
DreamHack "์„œ๋น„์Šค ์ด์šฉ ์•ฝ๊ด€" (Bronze 3, misc/crypto) โ€” solve
 
tos.txt ๋Š” ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ๋‹ค.
 
    Encoded toc (hex)\r\n
    \r\n
    c0ccbfebbee0b0fc0d0ac1a631c0e5...      <- 51050์ž ํ•œ ์ค„
 
ํ’€์ด๋Š” ์„ธ ๊ฒน์ด๋‹ค.
  1) ํ—ค๋”๋ฅผ ๋–ผ๊ณ  hex ๋ฅผ ๋ฐ”์ดํŠธ๋กœ ๋˜๋Œ๋ฆฐ๋‹ค.
  2) ๊ทธ ๋ฐ”์ดํŠธ๋Š” UTF-8 ์ด ์•„๋‹ˆ๋ผ EUC-KR ์ด๋‹ค (= ๋ฌธ์ œ๊ฐ€ ๋งํ•˜๋Š” "์ž˜๋ชป๋œ ์ธ์ฝ”๋”ฉ").
  3) ๋ณต์›๋œ ์•ฝ๊ด€ ๋ณธ๋ฌธ ์•ˆ์—์„œ flag ๋Š” '์ „๊ฐ(fullwidth) ๋ผํ‹ด' ์œผ๋กœ ์‹ฌ์–ด์ ธ ์žˆ๋‹ค.
     ๋ณธ๋ฌธ ์ œ18์กฐ โ‘ค ๊ฐ€ "๋‹ค์Œ ๋ฌธ์ž์—ด์„ NFKC ์ •๊ทœํ™”ํ•œ ๊ฒƒ" ์ด๋ผ๊ณ  ์•Œ๋ ค ์ค€๋‹ค.
 
EUC-KR ์—์„œ ์ „๊ฐ ASCII ๋Š” A3 ํ–‰์— ์žˆ๊ณ , ๋’ท๋ฐ”์ดํŠธ๋Š” ์ •ํ™•ํžˆ (ASCII | 0x80) ์ด๋‹ค.
  'D' = 0x44        ->  '๏ผค' = 0xA3 0xC4
๊ทธ๋ž˜์„œ EUC-KR ์ŠคํŠธ๋ฆผ์„ ๊ฑธ์œผ๋ฉฐ ์„ ๋‘ ๋ฐ”์ดํŠธ๊ฐ€ 0xA3 ์ธ ๊ธ€์ž๋งŒ ๋ชจ์œผ๋ฉด
์œ ๋‹ˆ์ฝ”๋“œ ํ…Œ์ด๋ธ” ์—†์ด๋„ ๊ฐ™์€ ๋‹ต์ด ๋‚˜์˜จ๋‹ค (verify_a3_row ๊ฐ€ ๊ทธ๊ฑธ ํ™•์ธํ•œ๋‹ค).
"""
import binascii
import sys
import unicodedata
from pathlib import Path
 
HERE = Path(__file__).resolve().parent
TOS = HERE / "extracted" / "tos.txt"
 
 
def load_hex(path: Path) -> bytes:
    """ํ—ค๋” 2์ค„์„ ๋–ผ๊ณ  hex ๋ณธ๋ฌธ๋งŒ ๋ฐ”์ดํŠธ๋กœ ๋˜๋Œ๋ฆฐ๋‹ค."""
    raw = path.read_bytes()
    parts = raw.split(b"\r\n")
    hex_line = parts[2]
    print(f"[1] {path.name}: {len(raw)}๋ฐ”์ดํŠธ, ํ—ค๋”={parts[0].decode()!r}")
    print(f"[1] hex ๋ณธ๋ฌธ {len(hex_line)}์ž -> {len(hex_line)//2}๋ฐ”์ดํŠธ")
    return binascii.unhexlify(hex_line)
 
 
def pick_encoding(blob: bytes) -> str:
    """UTF-8 ์€ ๊นจ์ง€๊ณ  EUC-KR ๋กœ๋งŒ ์ฝํžŒ๋‹ค๋Š” ๊ฒƒ์„ ์‹ค์ œ๋กœ ๋ณด์—ฌ ์ค€๋‹ค."""
    for enc in ("utf-8", "euc-kr"):
        try:
            blob.decode(enc)
        except UnicodeDecodeError as e:
            print(f"[2] {enc:<7} ๋””์ฝ”๋“œ ์‹คํŒจ - {e.reason} (offset {e.start})")
        else:
            print(f"[2] {enc:<7} ๋””์ฝ”๋“œ ์„ฑ๊ณต")
            good = enc
    return good
 
 
def walk_euckr(blob: bytes):
    """EUC-KR ์ŠคํŠธ๋ฆผ์„ ๊ฑธ์œผ๋ฉฐ (๋ฐ”์ดํŠธ์—ด, ๊ธ€์ž) ๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ๋‚ด๋†“๋Š”๋‹ค.
 
    ๋ฐ”์ดํŠธ๋ฅผ ๊ทธ๋ƒฅ grep ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค. 0xA3 ์€ ๋‹ค๋ฅธ ํ•œ๊ธ€์˜ '๋’ท๋ฐ”์ดํŠธ' ๋กœ๋„
    ๋‚˜์˜ค๊ธฐ ๋•Œ๋ฌธ์—(ํŒŒ์ผ ์ „์ฒด์— 79๊ฐœ, ๊ทธ ์ค‘ ์„ ๋‘๋Š” 36๊ฐœ๋ฟ) ์„ ๋‘/ํ›„ํ–‰ ๊ตฌ๋ถ„์„ ์ง€์ผœ์•ผ ํ•œ๋‹ค.
    """
    i = 0
    while i < len(blob):
        n = 2 if blob[i] >= 0x80 else 1
        chunk = blob[i:i + n]
        yield chunk, chunk.decode("euc-kr")
        i += n
 
 
def verify_a3_row(blob: bytes) -> str:
    """์„ ๋‘ ๋ฐ”์ดํŠธ๊ฐ€ 0xA3(์ „๊ฐ ASCII ํ–‰)์ธ ๊ธ€์ž๋งŒ ๋ชจ์€๋‹ค."""
    out = []
    for chunk, ch in walk_euckr(blob):
        if len(chunk) == 2 and chunk[0] == 0xA3:
            out.append(chr(chunk[1] - 0x80))
    naive = blob.count(b"\xa3")
    print(f"[5] 0xA3 ์„ ๋‹จ์ˆœ ๋ฐ”์ดํŠธ๋กœ ์„ธ๋ฉด {naive}๊ฐœ (ํ›„ํ–‰๋ฐ”์ดํŠธ ์˜คํƒ ํฌํ•จ)")
    print(f"[5] EUC-KR ์ŠคํŠธ๋ฆผ์„ ๊ฑธ์–ด์„œ ๊ณ ๋ฅธ ์ „๊ฐ ๊ธ€์ž: {len(out)}๊ฐœ")
    return "".join(out)
 
 
def extract_flag(text: str) -> str:
    """์ „๊ฐ ๋ผํ‹ด์ด ์—ฐ์†์œผ๋กœ ์ด์–ด์ง€๋Š” ๊ฐ€์žฅ ๊ธด ๊ตฌ๊ฐ„์„ ๋ฝ‘์•„ NFKC ์ •๊ทœํ™”ํ•œ๋‹ค."""
    runs, cur = [], ""
    for ch in text:
        if 0xFF01 <= ord(ch) <= 0xFF5E:
            cur += ch
        else:
            if cur:
                runs.append(cur)
            cur = ""
    if cur:
        runs.append(cur)
    runs.sort(key=len, reverse=True)
    print(f"[4] ์ „๊ฐ ๊ตฌ๊ฐ„ {len(runs)}๊ฐœ, ๊ฐ€์žฅ ๊ธด ๊ฒƒ {len(runs[0])}์ž")
    print(f"[4] ์›๋ฌธ ๊ทธ๋Œ€๋กœ : {runs[0]}")
    return unicodedata.normalize("NFKC", runs[0])
 
 
def find_decoy(text: str) -> str:
    """์ œ27์กฐ๊ฐ€ '์ด๊ฑด ํ”Œ๋ž˜๊ทธ๊ฐ€ ์•„๋‹ˆ๋‹ค' ๋ผ๊ณ  ๋ชป๋ฐ•์•„ ๋‘” ๋ฏธ๋ผ."""
    decoy = "DH{New_Dreamhack_Terms_Of_Service}"
    line = next(l for l in text.splitlines() if decoy in l)
    print(f"[3] ๋ณธ๋ฌธ์— ๋ฐ•ํžŒ ๋ฏธ๋ผ {decoy}")
    print(f"[3] ์ œ27์กฐ ์›๋ฌธ : {line.strip()}")
    return decoy
 
 
def main() -> int:
    blob = load_hex(TOS)
    enc = pick_encoding(blob)
    text = blob.decode(enc)
 
    find_decoy(text)
    flag = extract_flag(text)
 
    a3 = unicodedata.normalize("NFKC", verify_a3_row(blob))
    assert flag in a3, "A3 ํ–‰ ๊ต์ฐจ๊ฒ€์ฆ ์‹คํŒจ"
    print(f"[6] ๊ต์ฐจ๊ฒ€์ฆ(0xA3 ํ–‰ ์ „์ฒด) : {a3}")
 
    print()
    print(f"FLAG: {flag}")
    return 0
 
 
if __name__ == "__main__":
    sys.exit(main())
python3 solve.py

solve.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” hex 51050์ž๋ฅผ 25525๋ฐ”์ดํŠธ๋กœ ๋˜๋Œ๋ฆฌ๊ณ  utf-8 ์€ ์‹คํŒจ euc-kr ์€ ์„ฑ๊ณตํ–ˆ๋‹ค๊ณ  ์ฐ์€ ๋’ค, ๋ฏธ๋ผ ๋ฌธ์ž์—ด๊ณผ ์ œ27์กฐ ์›๋ฌธ์„ ๋ณด์—ฌ ์ค€๋‹ค. ์ด์–ด์„œ ์ „๊ฐ ๊ตฌ๊ฐ„ 32์ž๋ฅผ ๋ฝ‘๊ณ  A3 ํ–‰ ๊ต์ฐจ๊ฒ€์ฆ๊นŒ์ง€ ํ†ต๊ณผํ•œ ๋‹ค์Œ ๋งˆ์ง€๋ง‰ ์ค„์— ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค
solve.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” hex 51050์ž๋ฅผ 25525๋ฐ”์ดํŠธ๋กœ ๋˜๋Œ๋ฆฌ๊ณ  utf-8 ์€ ์‹คํŒจ euc-kr ์€ ์„ฑ๊ณตํ–ˆ๋‹ค๊ณ  ์ฐ์€ ๋’ค, ๋ฏธ๋ผ ๋ฌธ์ž์—ด๊ณผ ์ œ27์กฐ ์›๋ฌธ์„ ๋ณด์—ฌ ์ค€๋‹ค. ์ด์–ด์„œ ์ „๊ฐ ๊ตฌ๊ฐ„ 32์ž๋ฅผ ๋ฝ‘๊ณ  A3 ํ–‰ ๊ต์ฐจ๊ฒ€์ฆ๊นŒ์ง€ ํ†ต๊ณผํ•œ ๋‹ค์Œ ๋งˆ์ง€๋ง‰ ์ค„์— ํ”Œ๋ž˜๊ทธ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค

[5] ์ค„์ด ์‚ฝ์งˆ 2์—์„œ ์ด์•ผ๊ธฐํ•œ ๊ทธ ์ฐจ์ด๋‹ค. 0xA3 ๋ฐ”์ดํŠธ๋Š” 79๊ฐœ์ธ๋ฐ ์‹ค์ œ ์ „๊ฐ ๊ธ€์ž๋Š” 36๊ฐœ. ๋‘ ๊ฒฝ๋กœ(์œ ๋‹ˆ์ฝ”๋“œ ๋ฒ”์œ„๋กœ ๊ณ ๋ฅด๊ธฐ / A3 ํ–‰ ๋ฐ”์ดํŠธ๋กœ ๊ณ ๋ฅด๊ธฐ)๊ฐ€ ๊ฐ™์€ ๋ฌธ์ž์—ด์„ ๋‚ด๋†“์•„์„œ assert ๊ฐ€ ํ†ต๊ณผํ•œ๋‹ค.

[6] ์— NFKC ๋„ค ๊ธ€์ž๊ฐ€ ์•ž์— ๋ถ™์–ด ๋‚˜์˜ค๋Š” ๊ฑด ์ง€์‹œ์–ด๊นŒ์ง€ ์ „๊ฐ์ด๋ผ ๊ฐ™์ด ๋”ธ๋ ค ์˜จ ๊ฒƒ์ด๋‹ค.

ํ”Œ๋ž˜๊ทธ๋Š” ์ด๊ฒƒ์ด๋‹ค.

DH{Do_Y0u_Know_EUC-KR_enc0ding?}

โ™ป๏ธ ์žฌํ˜„

๋ฌธ์ œ ํด๋”๋งŒ ์žˆ์œผ๋ฉด ํ•œ ๋ฒˆ์— ๋‹ค์‹œ ๋Œ์•„๊ฐ€๋„๋ก reproduce.sh ๋ฅผ ๋‚จ๊ฒจ ๋’€๋‹ค. ์ •๋‹ต๊ณผ ๋Œ€์กฐ๊นŒ์ง€ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์Šค์Šค๋กœ ํ•œ๋‹ค.

#!/usr/bin/env bash
# ์„œ๋น„์Šค ์ด์šฉ ์•ฝ๊ด€ (DreamHack Bronze 3, misc/crypto) โ€” ํ•œ ๋ฐฉ ์žฌํ˜„
# tos.txt ๋งŒ ์žˆ์œผ๋ฉด flag ๊นŒ์ง€ ๊ฐ„๋‹ค. ์„œ๋ฒ„ยท๋„คํŠธ์›Œํฌ ๋ถˆํ•„์š”.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
 
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ œ.json'))['flag'])" 2>/dev/null || echo '')
 
if [ ! -f extracted/tos.txt ]; then
  echo "โŒ extracted/tos.txt ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค (challenge_2189.zip ์„ ํ’€์–ด ์ฃผ์„ธ์š”)"
  exit 1
fi
 
# tos.bin(hex ๋ฅผ ํ‘ผ ๋‚ ๋ฐ”์ดํŠธ)์€ probe_encoding.py ๊ฐ€ ๋งŒ๋“ ๋‹ค. solve.py ๋Š” tos.txt ๋งŒ์œผ๋กœ๋„ ๋ˆ๋‹ค.
_probe=$(python3 probe_encoding.py 2>&1) || true
 
OUT=$(timeout 120 python3 solve.py 2>&1) || true
 
echo "$OUT" | tail -8
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | tail -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 ํ‘œ์‹œ๊ฐ€ ์ฐํžˆ๊ณ  ์ข…๋ฃŒ์ฝ”๋“œ 0 ์œผ๋กœ ๋๋‚œ๋‹ค
reproduce.sh ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” solve.py ์ถœ๋ ฅ ๋งˆ์ง€๋ง‰ ์—ฌ๋Ÿ ์ค„์„ ๊ทธ๋Œ€๋กœ ๋ณด์—ฌ ์ค€ ๋‹ค์Œ, ์ •๊ทœ์‹์œผ๋กœ ๋ฝ‘์•„๋‚ธ ํ”Œ๋ž˜๊ทธ์™€ ํ•จ๊ป˜ ์ดˆ๋ก์ƒ‰ PASS ํ‘œ์‹œ๊ฐ€ ์ฐํžˆ๊ณ  ์ข…๋ฃŒ์ฝ”๋“œ 0 ์œผ๋กœ ๋๋‚œ๋‹ค

๊ธฐ๋Œ€๊ฐ’ EXPECT ๋Š” ๊ฐ™์€ ํด๋”์˜ ๋ฌธ์ œ.json ์—์„œ ์ฝ๋Š”๋ฐ, ์ด ํŒŒ์ผ์€ ๋ฐฑ์—… ๋‹จ๊ณ„์—์„œ ์ƒ๊ธด๋‹ค. ์•„์ง ์—†๋Š” ์‹œ์ ์ด๋ผ ์œ„ ์‹คํ–‰์€ "ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฝ‘์•„๋ƒˆ๋Š”๊ฐ€" ๋งŒ ๋ณด๊ณ  ํ†ต๊ณผํ–ˆ๋‹ค. ๋ฌธ์ œ.json ์ด ๋†“์ด๋ฉด ๊ทธ๋•Œ๋ถ€ํ„ฐ๋Š” ๊ฐ’๊นŒ์ง€ ๋Œ€์กฐํ•œ๋‹ค.

grep ์—์„œ tail -1 ์„ ์“ด ์ด์œ ๋„ ์žˆ๋‹ค. ์ถœ๋ ฅ์— DH{ ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ฌธ์ž์—ด์ด ๋‘ ๋ฒˆ ๋‚˜์˜จ๋‹ค โ€” ๋ฏธ๋ผ์™€ ์ง„์งœ. head -1 ๋กœ ์žก์œผ๋ฉด ๋ฏธ๋ผ๋ฅผ ์ •๋‹ต์œผ๋กœ ์ฐฉ๊ฐํ•œ ์ฑ„ ์กฐ์šฉํžˆ ์‹คํŒจํ•œ๋‹ค.

๋กœ์ปฌ ํ™˜๊ฒฝ์€ Ubuntu 25.10 / Python 3.13 / chardet 5.2.0 ์ด๋‹ค. chardet ์€ ์ •์ฐฐ์šฉ์ด๋ผ ์—†์–ด๋„ solve.py ๋Š” ๋ˆ๋‹ค.



๐Ÿ“ ๊ฒฐ๋ก 

๋ฏธ๋ผ๋Š” "ํ˜•์‹์ด ๋งž๋Š” ๊ฒƒ" ์œผ๋กœ ์˜จ๋‹ค.

์ด ๋ฌธ์ œ์—์„œ ๊ฐ€์žฅ ์ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž๋ฆฌ๋Š” ์ธ์ฝ”๋”ฉ์ด ์•„๋‹ˆ๋ผ ์ œ27์กฐ๋‹ค. DH{...} ํ˜•ํƒœ๋ฅผ ์ฐพ์ž๋งˆ์ž ์†์ด ์ œ์ถœ ๋ฒ„ํŠผ์œผ๋กœ ๊ฐ”๋‹ค. ์ถœ์ œ์ž๊ฐ€ ์•„๋‹ˆ๋ผ๊ณ  ์ ์–ด ๋‘์ง€ ์•Š์•˜์œผ๋ฉด ์˜ค๋‹ต ํ•œ ๋ฒˆ์€ ๋จน์—ˆ์„ ๊ฒƒ์ด๋‹ค.

CTF ๋“  ์‹ค๋ฌด๋“ , ํŒจํ„ด ๋งค์นญ์œผ๋กœ ์ฐพ์€ ๊ฒƒ์€ "์ฐพ์•˜๋‹ค" ๊ฐ€ ์•„๋‹ˆ๋ผ "ํ›„๋ณด๋ฅผ ํ•˜๋‚˜ ์–ป์—ˆ๋‹ค" ๋‹ค.

๋ณด์ด์ง€ ์•Š๋Š” ๊ธ€์ž๋Š” ๋ณด์ด๋Š” ๊ธ€์ž์™€ ๊ฐ™์€ ์ทจ๊ธ‰์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

์ „๊ฐ ๏ผค ์™€ ๋ฐ˜๊ฐ D ๋Š” ์‚ฌ๋žŒ ๋ˆˆ์—๋„, ๋Œ€๋ถ€๋ถ„์˜ ํ•„ํ„ฐ์—๋„ ๋‹ค๋ฅธ ๊ธ€์ž๋‹ค. ๊ทธ๋Ÿฐ๋ฐ NFKC ์ •๊ทœํ™”๋ฅผ ๊ฑฐ์น˜๋ฉด ๊ฐ™์•„์ง„๋‹ค. ์ด ์„ฑ์งˆ์€ ์‹ค์ œ ์ทจ์•ฝ์ ์˜ ์žฌ๋ฃŒ๊ฐ€ ๋œ๋‹ค.

๊ฒ€์ฆ์„ ๋จผ์ € ํ•˜๊ณ  ์ •๊ทœํ™”๋ฅผ ๋‚˜์ค‘์— ํ•˜๋ฉด ๊ทธ๋Œ€๋กœ ์šฐํšŒ๊ฐ€ ๋œ๋‹ค. ๏ผœscript๏ผž ๋Š” ํƒœ๊ทธ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•˜์ง€๋งŒ NFKC ๋ฅผ ์ง€๋‚˜๋ฉด <script> ๊ฐ€ ๋œ๋‹ค. ํŒŒ์ด์ฌ ์‹๋ณ„์ž, ํŒŒ์ผ ๊ฒฝ๋กœ, ์‚ฌ์šฉ์ž๋ช… ์ค‘๋ณต ๊ฒ€์‚ฌ์—์„œ ๊ฐ™์€ ํŒจํ„ด์ด ๋ฐ˜๋ณตํ•ด์„œ ๋ฐœ๊ฒฌ๋ผ ์™”๋‹ค.

์ˆœ์„œ๋Š” ์ •๊ทœํ™” โ†’ ๊ฒ€์ฆ์ด๋‹ค. ๋ฐ˜๋Œ€๋กœ ํ•˜๋ฉด ๊ฒ€์‚ฌํ•œ ๋ฌธ์ž์—ด๊ณผ ์‹ค์ œ๋กœ ์“ฐ์ด๋Š” ๋ฌธ์ž์—ด์ด ๋‹ฌ๋ผ์ง„๋‹ค.

์ธ์ฝ”๋”ฉ์„ ์ถ”์ธกํ•˜์ง€ ๋ง๊ณ  ์ธก์ •ํ•œ๋‹ค.

chardet, iconv -f X -t X, CyberChef โ€” ๊ณ„์—ด์ด ๋‹ค๋ฅธ ๋„๊ตฌ ์„ธ ๊ฐœ๊ฐ€ ๊ฐ™์€ ๋‹ต์„ ์ฃผ๋ฉด ๊ทธ๋•Œ ๋ฏฟ๋Š”๋‹ค. ์ด ๋ฌธ์ œ๋Š” EUC-KR ํ•˜๋‚˜๋งŒ ๋งžํžˆ๋ฉด ๋˜๋Š” ์‰ฌ์šด ์ถ•์ด์ง€๋งŒ, ์‹ค๋ฌด์—์„œ ๋งŒ๋‚˜๋Š” ํŒŒ์ผ์€ CP949 ์™€ EUC-KR ์ด ์„ž์—ฌ ์žˆ๊ฑฐ๋‚˜ UTF-8 ๋กœ ๋‘ ๋ฒˆ ์ธ์ฝ”๋”ฉ๋œ ๊ฒƒ๋“ค์ด๋ผ ํ•œ ๋ฒˆ์— ์•ˆ ๋งž๋Š”๋‹ค.

๊ฐ€๋ณ€ ๊ธธ์ด ์ธ์ฝ”๋”ฉ์—์„œ ๋ฐ”์ดํŠธ ๋‹จ์œ„ ๊ฒ€์ƒ‰์€ ํ‹€๋ฆฐ๋‹ค.

0xA3 ์„ ๊ทธ๋ƒฅ ์„ธ๋ฉด 79๊ฐœ, ์‹ค์ œ๋กœ๋Š” 36๊ฐœ์˜€๋‹ค. UTF-8 ์€ ํ›„์† ๋ฐ”์ดํŠธ๊ฐ€ 10xxxxxx ๋กœ ํ‘œ์‹œ๋ผ ์žˆ์–ด ๋ฐ”์ดํŠธ๋งŒ ๋ด๋„ ๊ฒฝ๊ณ„๋ฅผ ์•Œ ์ˆ˜ ์žˆ์ง€๋งŒ EUC-KR ์—๋Š” ๊ทธ๋Ÿฐ ํ‘œ์‹์ด ์—†๋‹ค. ๋ ˆ๊ฑฐ์‹œ ์ธ์ฝ”๋”ฉ์„ ๋‹ค๋ฃฐ ๋•Œ๋Š” ์ŠคํŠธ๋ฆผ์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ฑธ์–ด์•ผ ํ•œ๋‹ค. ์ด๊ฑด ํŒŒ์„œ ๊ตฌํ˜„์—์„œ ๊ฒฝ๊ณ„ ํ˜ผ๋™ ์ทจ์•ฝ์ ์ด ์ƒ๊ธฐ๋Š” ์ž๋ฆฌ์ด๊ธฐ๋„ ํ•˜๋‹ค.

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

Comments

๋Œ“๊ธ€

0๊ฐœ

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

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

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅ‰ Bronze 3] ๊ฐ’์„ ๋ฌป์ง€ ๋ง๊ณ  ์ง€์‹œ๋ฌธ์„ ๋˜ํ’€์ด์‹œํ‚จ๋‹ค โ€” DreamHack baby-ai ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 3] ๊ฐ’์„ ๋ฌป์ง€ ๋ง๊ณ  ์ง€์‹œ๋ฌธ์„ ๋˜ํ’€์ด์‹œํ‚จ๋‹ค โ€” DreamHack baby-ai ํ’€์ด

์›น ์„œ๋ฒ„ ์•ˆ์—์„œ 0.5B ์งœ๋ฆฌ ๋กœ์ปฌ ๋ชจ๋ธ์„ ๋Œ๋ฆฌ๋Š” ์ฑ—๋ด‡ ๋ฌธ์ œ. ํ”Œ๋ž˜๊ทธ๋Š” ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ ์ฒซ ์ค„์— ๊ทธ๋Œ€๋กœ ๋ฐ•ํ˜€ ์žˆ๊ณ , ์ž…๋ ฅ์— adminยทflagยทpassยทsecretยทprivate ์ด ๋“ค์–ด๊ฐ€๋ฉด ๋ชจ๋ธ๊นŒ์ง€ ๊ฐ€์ง€๋„ ๋ชปํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๊ทธ ๋ธ”๋ž™๋ฆฌ์ŠคํŠธ๋Š” ์งˆ๋ฌธ๋งŒ ๋ณธ๋‹ค. ๋น„๋ฐ€์„ ๋ฌป๋Š” ๋Œ€์‹  ์ง€์‹œ๋ฌธ์„ ๊ทธ๋Œ€๋กœ ๋˜ํ’€์ดํ•˜๊ฒŒ ์‹œํ‚ค๋ฉด ํ•œ ์ค„์— ๋‹ค ๋‚˜์˜จ๋‹ค. ํ‚ค๋ฆด ๋ฌธ์ž ํ•œ ๊ธ€์ž๋กœ ๋ถ€๋ถ„๋ฌธ์ž์—ด ๊ฒ€์‚ฌ ์ž์ฒด๋ฅผ ๋น—๋‚˜๊ฐ€๊ฒŒ ํ•˜๋Š” ๋‘ ๋ฒˆ์งธ ๊ฒฝ๋กœ๋„ ๊ฐ™์ด ๋‹ค๋ฃฌ๋‹ค.
#dreamhack#ctf#web+8
2026-07-29#dreamhack +4
[๐Ÿฅ‰ Bronze 4] "๋กœ๋ด‡ ์ธ์ฆ"์ด ์˜คํžˆ๋ ค ๋กœ๋ด‡์—๊ฒŒ ํŒ์„ ๊น”์•„์คฌ๋‹ค โ€” DreamHack Robot Only ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 4] "๋กœ๋ด‡ ์ธ์ฆ"์ด ์˜คํžˆ๋ ค ๋กœ๋ด‡์—๊ฒŒ ํŒ์„ ๊น”์•„์คฌ๋‹ค โ€” DreamHack Robot Only ํ’€์ด

์ˆซ์ž ํ•˜๋‚˜๋ฅผ 3์ดˆ ์•ˆ์— ๊ทธ๋Œ€๋กœ ๋˜๋ฐ›์•„ ํƒ€์ดํ•‘ํ•˜๋ฉด "๋กœ๋ด‡ ์ธ์ฆ"์ด ์™„๋ฃŒ๋œ๋‹ค. ๊ทธ ์ˆซ์ž๋Š” Python random ๋ชจ๋“ˆ์˜ ์›์‹œ ์ถœ๋ ฅ 6๊ฐœ๋ฅผ ์ด์–ด๋ถ™์—ฌ ๊ณ ์ •๋œ ์ƒ์ˆ˜๋กœ XORํ•œ ๊ฐ’์ด๋ผ, ์šฐ๋ฆฌ๊ฐ€ ํ™”๋ฉด์—์„œ ์ฝ๋Š” ์ˆœ๊ฐ„ ๊ทธ 6๊ฐœ ์ˆซ์ž๋ฅผ ๊ทธ๋Œ€๋กœ ์—ญ์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ์ธ์ฆ์€ ์‹คํŒจํ•ด๋„ ๋ช‡ ๋ฒˆ์ด๋“  ๋‹ค์‹œ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ๋‹ค โ€” 104๋ฒˆ๋งŒ ๋ฐ˜๋ณตํ•˜๋ฉด Mersenne Twister์˜ ๋‚ด๋ถ€ ์ƒํƒœ๋ฅผ ์™„์ „ํžˆ ๋ณต์›ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ 624๊ฐœ์˜ ์—ฐ์†๋œ ์›์‹œ๊ฐ’์„ ์ „๋ถ€ ๋ชจ์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค. ๊ทธ ๋’ค๋กœ๋Š” ์„œ๋ฒ„๊ฐ€ ๋ฝ‘์„ ๋„๋ฐ• ๊ฒŒ์ž„์˜ ์ •๋‹ต์„ ๋ฒ ํŒ…ํ•˜๊ธฐ๋„ ์ „์— ์•Œ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.
#dreamhack#ctf#misc+5
2026-07-20#dreamhack +4
[๐Ÿฅ‰ Bronze 3] ๋กœ๋งˆ์ž ์ผ๋ณธ์–ด๋ฅผ ๋‹จ์ผ์น˜ํ™˜์œผ๋กœ ๊ฐ€๋ฆฐ ๋งค์นญ ํผ์ฆ โ€” DreamHack Easy Linguistics ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 3] ๋กœ๋งˆ์ž ์ผ๋ณธ์–ด๋ฅผ ๋‹จ์ผ์น˜ํ™˜์œผ๋กœ ๊ฐ€๋ฆฐ ๋งค์นญ ํผ์ฆ โ€” DreamHack Easy Linguistics ํ’€์ด

์ผ๋ณธ์–ด ๋ช…์‚ฌ๋ฅผ ๋กœ๋งˆ์ž๋กœ ์ ์€ ๋’ค ๋‹จ์ผ์น˜ํ™˜์œผ๋กœ ์•”ํ˜ธํ™”ํ•œ ์—ฌ์„ฏ ๋‹จ์–ด๋ฅผ, ๊ฐ๊ฐ์˜ ๋œป์— ์ง์ง“๋Š” ์–ธ์–ดํ•™ ํผ์ฆ. ๋‘ ๋‹จ์–ด๊ฐ€ ๊ณต์œ ํ•˜๋Š” ์ ‘๋ฏธ์‚ฌ๋ฅผ ์•ต์ปค๋กœ ์‚ผ์•„ ์น˜ํ™˜ํ‘œ๋ฅผ ์„ธ์šฐ๋ฉด yoasobiยทyokazeยทyamiyo ๊ฐ™์€ ์›๋ž˜ ๋‹จ์–ด๊ฐ€ ๋ณต์›๋œ๋‹ค. ์ฒ˜์Œ์—” ๊ธ€์ž ๋‘ ๊ฐœ๊ฐ€ ๋นˆ์นธ์œผ๋กœ ๋‚จ์•˜์ง€๋งŒ, ๋‚˜๋จธ์ง€ ๋Œ€์‘์„ ์ฑ„์šฐ์ž ๋งˆ์ง€๋ง‰ ๋‹จ์–ด๊นŒ์ง€ tobihi(้ฃ›ใณ็ซ, ํŠ€๋Š” ๋ถˆํ‹ฐ)๋ผ๋Š” ์ง„์งœ ์ผ๋ณธ์–ด ๋‹จ์–ด๋กœ ๋”ฑ ๋–จ์–ด์กŒ๋‹ค. ๋งž์ถ˜ ์ˆœ์„œ๊ฐ€ ๊ณง ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋œ๋‹ค.
#dreamhack#ctf#misc+3
2026-07-14#dreamhack +4