この大会は2025/6/14 9:00(JST)~2025/6/16 9:00(JST)に開催されました。
今回もチームで参戦。結果は269点で1090チーム中196位でした。
自分で解けた問題をWriteupとして書いておきます。
Sanity Check (misc)
問題にフラグが書いてあった。
.;,;.{it_seems_you_started_this_ctf_'sane'._Lets_see_if_you_can_end_it_as_sane_as_you_started.}
Success (rev)
フラグの各文字のASCIIコードについて、条件がたくさんコードとして記載されている。z3でこの条件を満たすものを探す。
#!/usr/bin/env python3 from z3 import * x = [BitVec('x%d' % i, 8) for i in range(39)] s = Solver() s.add(x[37] * x[15] == 3366) s.add(x[8] + x[21] == 197) s.add(x[8] * x[13] == 9215) s.add(x[0] * x[3] == 2714) s.add(x[3] + x[21] == 159) s.add(x[1] * x[20] == 5723) s.add(x[6] | x[37] == 105) s.add(x[11] * x[7] == 11990) s.add(x[29] & x[25] == 100) s.add(x[16] | x[29] == 127) s.add(x[20] - x[6] == -8) s.add(x[21] + x[20] == 197) s.add(x[2] + x[36] == 77) s.add(x[35] * x[11] == 3630) s.add(x[4] * x[3] == 2714) s.add(x[35] ^ x[6] == 72) s.add(x[25] + x[24] == 221) s.add(x[14] * x[36] == 3465) s.add(x[15] - x[11] - 148 == -156) s.add(x[37] + x[17] == 138) s.add(x[1] ^ x[38] == 70) s.add(x[9] + x[29] == 212) s.add(x[30] - x[10] == 7) s.add(x[10] + x[33] == 206) s.add(x[7] * x[15] == 11118) s.add(x[28] * x[14] * 55 == 641025) s.add(x[7] | x[4] | 216 == 255) s.add(x[24] + x[4] == 151) s.add(x[2] * x[30] == 4928) s.add(x[5] + x[22] == 224) s.add(x[18] | x[36] == 127) s.add(x[13] + x[34] == 195) s.add(x[9] | x[17] == 111) s.add(x[12] * x[9] == 10403) s.add(x[25] ^ x[27] == 23) s.add(x[13] ^ x[34] == 59) s.add(x[18] + x[31] == 200) s.add(x[17] + x[32] == 213) s.add(x[2] * x[12] == 4444) s.add(x[24] * x[31] == 11025) s.add(x[5] * x[0] == 5658) s.add(x[10] + x[32] + 228 == 441) s.add(x[35] * x[0] == 1518) s.add(x[30] | x[8] == 113) s.add(x[28] - x[34] == 11) s.add(x[26] * x[14] == 9975) s.add(x[31] * x[22] == 10605) s.add(x[26] * x[32] * 239 == 2452140) s.add(x[28] * x[38] == 13875) s.add(x[18] + x[16] == 190) s.add(x[27] + x[26] + 96 == 290) s.add(x[22] - x[38] == -24) s.add(x[33] + x[5] == 224) s.add(x[19] * x[16] == 10355) s.add(x[27] + x[1] == 158) s.add(x[33] + x[12] == 202) s.add(x[19] * x[23] == 10355) r = s.check() assert r == sat m = s.model() flag = '' for i in range(39): flag += chr(m[x[i]].as_long()) print(flag)
.;,;.{imagine_if_i_made_it_compiled!!!}
saas (crypto)
サーバの処理概要は以下の通り。
・p, q: 512ビット素数(4で割って3余る素数) ・n = p * q ・e = 0x10001 ・以下繰り返し ・l: 数値入力(n未満) ・f(l)を表示 ・[-1 or 1] * pow(l, (p + 1) // 4, p)) * pow(q, -1, p) * q + [-1 or 1]* pow(x, (q + 1) // 4, q)) % q * pow(p, -1, q) * p) % n ・例外発生の場合、繰り返し終了 ・m: 0以上n-1以下ランダム整数 ・mを表示 ・s: 数値入力(n未満) ・pow(s, e, n)がmと一致する場合、フラグを表示
fはRabin暗号の暗号文から復号した平文4つのうち1つを返す関数。4つの平文は以下の構成になっている。
x % p = a or p - a x % q = b or p - b
つまり、この4パターンを入手し和を取れば、pまたはqで割り切れ、nがわかれば、p, qを算出できる。またこの4パターンを2乗したものの差分はすべてnで割った余りが同じことから、差分はnの倍数になる。このことからnを算出することができる。
あとはmを復号した値を入力すれば、フラグが得られる。
#!/usr/bin/env python3 import socket import itertools from Crypto.Util.number import * def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) e = 0x10001 skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM) skt.connect(('smiley.cat', 34987)) l = 1234567890 fs = [] while True: data = recvuntil(skt, b'>>> ') print(data + str(l)) skt.sendall(str(l).encode() + b'\n') data = recvuntil(skt, b'\n').rstrip() print(data) f = int(data) if f not in fs: fs.append(f) if len(fs) == 4: break n_diffs = [] pq_sums = [] for x in itertools.combinations(fs, 2): n_diff = x[0]**2 - x[1]**2 if n_diff < 0: n_diff = - n_diff n_diffs.append(n_diff) pq_sum = x[0] + x[1] pq_sums.append(pq_sum) n = n_diffs[0] for i in range(1, len(n_diffs)): n = GCD(n, n_diffs[i]) print('[+] n:', n) for pq_sum in pq_sums: p = GCD(n, pq_sum) if p != n and p > 1: q = n // p break assert isPrime(p) and isPrime(q) assert p * q == n print('[+] p:', p) print('[+] q:', q) data = recvuntil(skt, b'>>> ') print(data) skt.sendall(b'\n') data = recvuntil(skt, b'\n').rstrip() print(data) m = int(data.split(' ')[-1]) phi = (p - 1) * (q - 1) d = inverse(e, phi) s = pow(m, d, n) data = recvuntil(skt, b'>>> ') print(data + str(s)) skt.sendall(str(s).encode() + b'\n') flag = recvuntil(skt, b'\n').rstrip() print('[*] flag:', flag)
実行結果は以下の通り。
>>> 1234567890 127722842213254781586758118684092238397393185259276147993025398605525862184256373296403848636462229709741964536718951797518422802798598464901547686908419039576878935261246456642814164465894139537321215407347584868502171742909965330104192907434951104657421937230523031700997087197322067669408662927234080437793 >>> 1234567890 127722842213254781586758118684092238397393185259276147993025398605525862184256373296403848636462229709741964536718951797518422802798598464901547686908419039576878935261246456642814164465894139537321215407347584868502171742909965330104192907434951104657421937230523031700997087197322067669408662927234080437793 >>> 1234567890 127722842213254781586758118684092238397393185259276147993025398605525862184256373296403848636462229709741964536718951797518422802798598464901547686908419039576878935261246456642814164465894139537321215407347584868502171742909965330104192907434951104657421937230523031700997087197322067669408662927234080437793 >>> 1234567890 78664478740681499692319087595291919473817744878657053483957455528162675465817222772526318856207718627945389036576053127711744514115305494457036853261234963319426526119075184517636156361235338700510427537683625632948320110267596806842012629732807313873138356547580413510434477487107717879787420003156106874774 >>> 1234567890 78664478740681499692319087595291919473817744878657053483957455528162675465817222772526318856207718627945389036576053127711744514115305494457036853261234963319426526119075184517636156361235338700510427537683625632948320110267596806842012629732807313873138356547580413510434477487107717879787420003156106874774 >>> 1234567890 20408450663577306220960565505123811029757564073724874383459511493607715510879570689633900800809074993374468940597444998025412356056751961234998661366523679746576089933167638972870446340151040652890354912831658344078833754931042605332603199166587992105449744499222633903353009914547121268208633834038948184316 >>> 1234567890 127722842213254781586758118684092238397393185259276147993025398605525862184256373296403848636462229709741964536718951797518422802798598464901547686908419039576878935261246456642814164465894139537321215407347584868502171742909965330104192907434951104657421937230523031700997087197322067669408662927234080437793 >>> 1234567890 20408450663577306220960565505123811029757564073724874383459511493607715510879570689633900800809074993374468940597444998025412356056751961234998661366523679746576089933167638972870446340151040652890354912831658344078833754931042605332603199166587992105449744499222633903353009914547121268208633834038948184316 >>> 1234567890 127722842213254781586758118684092238397393185259276147993025398605525862184256373296403848636462229709741964536718951797518422802798598464901547686908419039576878935261246456642814164465894139537321215407347584868502171742909965330104192907434951104657421937230523031700997087197322067669408662927234080437793 >>> 1234567890 78664478740681499692319087595291919473817744878657053483957455528162675465817222772526318856207718627945389036576053127711744514115305494457036853261234963319426526119075184517636156361235338700510427537683625632948320110267596806842012629732807313873138356547580413510434477487107717879787420003156106874774 >>> 1234567890 127722842213254781586758118684092238397393185259276147993025398605525862184256373296403848636462229709741964536718951797518422802798598464901547686908419039576878935261246456642814164465894139537321215407347584868502171742909965330104192907434951104657421937230523031700997087197322067669408662927234080437793 >>> 1234567890 127722842213254781586758118684092238397393185259276147993025398605525862184256373296403848636462229709741964536718951797518422802798598464901547686908419039576878935261246456642814164465894139537321215407347584868502171742909965330104192907434951104657421937230523031700997087197322067669408662927234080437793 >>> 1234567890 78664478740681499692319087595291919473817744878657053483957455528162675465817222772526318856207718627945389036576053127711744514115305494457036853261234963319426526119075184517636156361235338700510427537683625632948320110267596806842012629732807313873138356547580413510434477487107717879787420003156106874774 >>> 1234567890 20408450663577306220960565505123811029757564073724874383459511493607715510879570689633900800809074993374468940597444998025412356056751961234998661366523679746576089933167638972870446340151040652890354912831658344078833754931042605332603199166587992105449744499222633903353009914547121268208633834038948184316 >>> 1234567890 69466814136150588115399596593924129953333004454343968892527454570970902229318721213511430581063586075171044440740343667832090644740044931679509495013707756004028499075338911098048454444809841489701142782495617579632685387573411128594783476868731782889733325182165252093915619624761471057829876758116921747335 [+] n: 148131292876832087807718684189216049427150749333001022376484910099133577695135943986037749437271304703116433477316396795543835158855350426136546348274942719323455025194414095615684610806045180190211570320179243212581005497841007935436796106601539096762871681729745665604350097111869188937617296761273028622109 [+] p: 12994863769080445444956473863693438014393267441883615652557349834273542929427594116294599753100607393407058517000679280000153703736049646154634417358353271 [+] q: 11399218607377081506878251793659642361763796476173610996370248132946849766826824305608308057473070214093635307637733305584406945506580266026695494652544779 >>> m = 138493488194585558083953817230565762236813958181169796808795590463157957874931271307355745502031794178451229114514256866106939506487143844690822167628866196957522738182388444235061690030499408912921869018459839415042592915774886456248300860069628291212293385227375134618185112299349503194909738987760057729173 >>> 99374095421206923683714405213892497296558223891898043656557296501409629024479597016208401618833626219781276289405194145693597103870453980802480326399350941538646247982770356398130461356675532670154224569254989694290378744673507997031138134003417272424449963478938587556637939524368485448889980480117162874942 [*] flag: .;,;.{squares_as_a_service_est_like_the_dawn_of_time}
.;,;.{squares_as_a_service_est_like_the_dawn_of_time}
never enough (crypto)
暗号化処理の概要は以下の通り。
・danger = 624*32 ・given = [] ・key = "" ・danger // 20 - 16回、以下繰り返し ・x: ランダム32ビット整数 ・keyにx % 2**12を文字列として結合 ・given: x >> 12を追加 ・key = key[:100] ・key: keyのsha256ダイジェスト ・givenを出力 ・flagを"\x00"でパディングし、keyでAES ECBモード暗号化したものを16進数表記で出力
Mersenne Twsiterの問題。givenは982個の情報がある。下12ビットが欠落しているので、関係する値の間で総当たりする。
乱数をr0~r981とすると、以下のようになる。
・r0の最上位ビットはわかっている。 ・r1の最上位ビット以外の値とr397の値がわかっていれば、r624の値がわかる。 ・r624の上位20ビットが合っているものを探す。
つまりブルートフォースのケーズは2**24である。乱数の1個目だけは特定できないため、AES暗号の復号を行い、フラグの形式になるものを探す。
#!/usr/bin/env python3 from Crypto.Cipher import AES from hashlib import sha256 def untemper(rand): rand ^= rand >> 18; rand ^= (rand << 15) & 0xefc60000; a = rand ^ ((rand << 7) & 0x9d2c5680); b = rand ^ ((a << 7) & 0x9d2c5680); c = rand ^ ((b << 7) & 0x9d2c5680); d = rand ^ ((c << 7) & 0x9d2c5680); rand = rand ^ ((d << 7) & 0x9d2c5680); rand ^= ((rand ^ (rand >> 11)) >> 11); return rand def temper(st): y = st y ^= y >> 11 y ^= (y << 7) & 0x9d2c5680 y ^= (y << 15) & 0xefc60000 y ^= y >> 18 return y def get_st624(st0, st1, st397): n = st0 & 0x80000000 n += st1 & 0x7fffffff st = st397 ^ (n >> 1) if n % 2 != 0: st ^= 0x9908b0df return st def get_st624_0(st1, st397): st_list = [] for x in [0, 0x80000000]: n = x + (st1 & 0x7fffffff) st = st397 ^ (n >> 1) if n % 2 != 0: st ^= 0x9908b0df st_list.append(st) return st_list danger = 624 * 32 with open('out.txt', 'r') as f: params = f.read().splitlines() given = eval(params[0]) enc_flag = bytes.fromhex(params[1]) rnd = [-1] * (danger // 20 - 16 - 624) rnd[0] = given[0] << 12 key_without_head = '' for i in range(danger // 20 - 16 - 624): r0 = rnd[i] st0 = untemper(r0) r1_part = given[i + 1] << 12 r397_part = given[i + 397] << 12 found = False for j in range(2**12): r1 = r1_part + j st1 = untemper(r1) for k in range(2**12): r397 = r397_part + k st397 = untemper(r397) if i == 0: st624_list = get_st624_0(st1, st397) for st624 in st624_list: if temper(st624) >> 12 == given[i + 624]: found = True rnd[i + 1] = r1 key_without_head += str(r1 % 2**12) break else: r624 = get_st624(st0, st1, st397) if temper(r624) >> 12 == given[i + 624]: found = True rnd[i + 1] = r1 key_without_head += str(r1 % 2**12) if found: break if found: break if len(key_without_head) >= 100: break print('[+] key part:', key_without_head) for i in range(2**12): key = str(i) + key_without_head key = key[:100] key = sha256(key.encode()).digest() cipher = AES.new(key, AES.MODE_ECB) flag = cipher.decrypt(enc_flag) if flag.startswith(b'.;,;.{'): flag = flag.rstrip(b'\x00').decode() print('[*] flag:', flag) break
実行結果は以下の通り。
[+] key part: 122034203256216122924163875116925356934163689349935828615933987262515679539414703525201051123827853062 [*] flag: .;,;.{never_enough_but_you_gotta_just_make_more_or_something_idk_im_not_a_motivational_speaker_but_you_get_the_idea}
.;,;.{never_enough_but_you_gotta_just_make_more_or_something_idk_im_not_a_motivational_speaker_but_you_get_the_idea}