この大会は2022/9/17 11:00(JST)~2022/9/19 11:00(JST)に開催されました。
今回もチームで参戦。結果は259点で605チーム中74位でした。
自分で解けた問題をWriteupとして書いておきます。
welcome (Misc)
Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。
flag{wish_you_have_fun_in_2022}
real magic dlog (Crypto)
サーバの処理概要は以下の通り。
・LEN = 17 ・PoW ・magic: ランダム17バイト文字列 ・magic_num: magicの数値化 ・magicを16進数で表示 ・P: 数値入力 ・E: 数値入力 ・data: 16進文字列入力 ・num1: dataを10進数にした数値 ・P >> (384 - LEN * 8) == magic_numで、Pが素数の場合、 ・data2: dataのsha384ダイジェスト(hex) ・num2: data2を10進数にした数値 ・pow(num1, E, P) == num2 % Pの場合、フラグを表示
num1, num2は依存関係にあるので、独立して指定できない。また、Pは条件があるので、P - 1の素因数が小さい値になるよう調整した上で、Eを解く問題となる。
(384 - LEN * 8)ビット分は小さいビットの素数で構成し、残りのビットを条件を満たすよう割り算して算出する。P - 1の素因数がすべて51ビット未満にできるまで、サーバに接続しなおして、条件を満たすまで繰り返す。さらに離散対数問題の解が存在するnum1, num2の組を探す。離散対数問題を解くことができれば、あとはパラメータを入力していけばよい。
#!/usr/bin/env sage import socket import string import re import itertools import sympy from Crypto.Util.number import * from hashlib import sha256, sha384 def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) LEN = 17 while True: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('202.120.7.219', 15555)) data = recvuntil(s, b'\n').rstrip() print(data) pattern = '\+ (.+)\) == (.+)' m = re.search(pattern, data) tail_text = m.group(1) h = m.group(2) data = recvuntil(s, b'\n').rstrip() print(data) chars = string.ascii_letters + string.digits + '!#$%&*-?' for c in itertools.product(chars, repeat=4): head_text = ''.join(c) text = head_text + tail_text if sha256(text.encode()).hexdigest() == h: print(head_text) s.sendall(head_text.encode() + b'\n') break data = recvuntil(s, b'\n').rstrip() print(data) magic = bytes.fromhex(data) magic_num = bytes_to_long(magic) P_base = magic_num << (384 - LEN * 8) prod = 2 for _ in range(9): p = getPrime(27) prod *= p found = False for i in range(1, 257): P_parts = P_base // prod + i P = prod * P_parts + 1 if isPrime(P) and P >> (384 - LEN * 8) == magic_num: if factor(P - 1)[-1][0].nbits() < 51: found = True break if found: break else: s.close() print('[+] P - 1 =', factor(P - 1)) F = GF(P) g = F.multiplicative_generator() while True: num1 = ZZ(g ** randint(1, p - 1)) send_data = hex(num1)[2:] data2 = sha384(send_data.encode()).hexdigest() num2 = int(data2, 16) print('[+] discrete_log() start') try: E = ZZ(discrete_log(F(num2), F(num1))) assert pow(num1, E, P) == num2 % P break except: print('[+] Exception occurred') continue data = recvuntil(s, b'\n').rstrip() print(data) print(hex(P)[2:]) s.sendall(hex(P)[2:].encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) print(hex(E)[2:]) s.sendall(hex(E)[2:].encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) print(send_data) s.sendall(send_data.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data)
実行結果は以下の通り。
: : sha256(XXXX + ns0-1OlPpG&FWMat) == 23909a09dfb9a4efd8b264bae436f05f829bd37339d645cdea6c924abb8f0372 Give me XXXX: z8Xy 584dfa5be19766efe6f0fa3f353214e838 sha256(XXXX + 6Q0vwpiE*5dQhe4R) == 63ba546bb91bda036778db99804144bf54927a28cd4157219d447af13942b6a2 Give me XXXX: nHX# 099cb53f1e49f4ea3e2df88a02d11becb1 sha256(XXXX + FmcSWSPNz7Yp5pVT) == c7a799a3ee9a66f48a8caa5774f57259c764a4be132479b4afdb821bcd049247 Give me XXXX: D&3N ebc82af9cdb736593a0a3c397192255a0e [+] P - 1 = 2 * 3^2 * 5 * 11 * 13 * 271 * 541 * 5903 * 40829 * 1644637 * 68905957 * 76659659 * 86409703 * 90452987 * 92508137 * 93260641 * 96174671 * 98107447 * 105566633 * 3452271289 * 39614162513 [+] discrete_log() start P:> ebc82af9cdb736593a0a3c397192255a0e49f1aefef7a68ed267ea2a0d2d2220b86d41e92f536519e4c75ab9ba0f8c8b E:> 856fb0a054d4f6c2ca56e6ec8897dabdbb602c7a869a92201b14a935428ca955f1f9ff7462fdfac60db8c6bfa5ed171e data:> 2f2488891e2ad3a456fc2d9879083bc7b9077f8105b6e744fe26cef7d93a59ed0460702886b88695a85db694e6a68078 flag{Hope_you_can_solve_by_smoothness_this_time}
flag{Hope_you_can_solve_by_smoothness_this_time}
SURVEY (Misc)
アンケートに答えたら、フラグが表示された。
flag{covid19_steals_the_finals_and_we_hope_to_meet_you_onsite_someday}