この大会は2022/4/10 3:00(JST)~2022/4/11 3:00(JST)に開催されました。
今回もチームで参戦。結果は1855点で255チーム中54位でした。
自分で解けた問題をWriteupとして書いておきます。
Welcome Quals 2K22 (Welcome)
Discordに入り、#announcementチャネルのメッセージを見ると、フラグが書いてあった。
Securinets{WelCome_T0_QuAls_2k22}
Escrime (Cryptography)
暗号化処理の概要は以下の通り。
・prime: 512bit素数 ・p1, q1: genPrime(prime) ・p2, q2: genPrime(prime) ・n1 = p1*q1 ・n2 = p2*q2 ・e = 65537 ・m1: FLAGの前半の文字列の数値化 ・m2: FLAGの後半の文字列の数値化 ・c1 = pow(m1, e, n1) ・c2 = pow(m2, e, n2) ・n1, n2, e, c1, c2出力 ※genPrime(prime):2*prime*[256bit素数] + 1の素数のペア生成
式を変形してみる。
n1 = p1 * q1 = (2 * prime * a1 + 1) * (2 * prime * b1 + 1) = 2 * prime * (2 * prime * a1 * b1 + a1 + b1) + 1 n2 = p2 * q2 = (2 * prime * a2 + 1) * (2 * prime * b2 + 1) = 2 * prime * (2 * prime * a2 * b2 + a2 + b2) + 1
n1 - 1, n2 - 1のGCDはprimeの倍数。このことからprimeを算出することができる。さらに以下のことが言える。
((n1 - 1) // (2 * prime)) % (2 * prime) = a1 + b1 ((n2 - 1) // (2 * prime)) % (2 * prime) = a2 + b2 phi1 = (p1 - 1) * (q1 - 1) = n1 - (p1 + q1) + 1 = n1 - (2 * prime * (a1 + b1) + 2) + 1 phi2 = (p2 - 1) * (q2 - 1) = n2 - (p2 + q2) + 1 = n2 - (2 * prime * (a2 + b2) + 2) + 1
あとはこのまま復号すればよい。
#!/usr/bin/env python3 from Crypto.Util.number import * n1 = 5285941989924581490741575774796326221790301948671605967204654261159288826022690654909746856601734294076351436205238123432817696904524845143908229601315593896823359605609172777227518764838488130850768836467030938547486936412484230693105639039311878853055295612388722273133638524917106191321503530749409311343663516633298043891444321772817485480644504762143353706512690041092791539952154332856635651319630479019844011333570438615137628705917690349203588170944935681 n2 = 5512656145670579765357132887430527554149315293720001536465226567777071834432904027590899542293511871806792894769506962601330354553170015126601443256295513753986998761021594415121386822360537570074896704547101502955980189351257681515387379761554807684880212096397524725819607628411147885452294832392886405475830663300445429053365129797792206619514994944481130684176571005780217091773969415001961227566026934419626425934895777818074251010427154279687683891897394401 e = 65537 c1 = 3792561290017712418676552700903779226679678307521013229152018077539055935181708693237786486418411190513573593312739874489485768872374239333562352570689090751306553033406629945001093355613620844532659507519582518955178617942044813600181673015763469247380587771641089223066734168709065596269187564842646397647564064090886856491267151338586218098150720579275673440512159074650632829004798635425409766385176472514086448897744502264325566940224093583630788193949908215 c2 = 3222093169881176821995152873609430742364413196826316856495679228145853706169389758246323802005549827444022148276365869623395771621464376723299960525487777645386674088866891887984766934440527885549168365996216682223515034398685244541695223412679979637178695229351272286453267599205874775267533781360269542834699741976380260822746797186755978820611721151719635986648586937891954519919600047846994285652165076540057377973800029963140392459328016771048953153246246886 prime = GCD(n1 - 1, n2 - 1) for i in range(256, 1, -1): if prime % i == 0: prime //= i assert isPrime(prime) a1_plus_b1 = ((n1 - 1) // (2 * prime)) % (2 * prime) a2_plus_b2 = ((n2 - 1) // (2 * prime)) % (2 * prime) phi1 = n1 - (2 * prime * (a1_plus_b1) + 2) + 1 phi2 = n2 - (2 * prime * (a2_plus_b2) + 2) + 1 d1 = inverse(e, phi1) d2 = inverse(e, phi2) m1 = pow(c1, d1, n1) m2 = pow(c2, d2, n2) FLAG = (long_to_bytes(m1) + long_to_bytes(m2)).decode() print(FLAG)
Securinets{G3n3r4t1ng_pr1m3s_1n_4_sp3c1f1c_f0rm_4lm0st_4lw4ys_3nds_b4dly}
AES² (Cryptography)
暗号化処理の概要は以下の通り。
・key, iv1, iv2: ランダム16バイト ・alice_username: ランダム32バイト ・alice_token = get_token(alice_username, iv1, iv2) ・blocks: alice_username16バイトごとのブロック配列 ・enc = '' ・tmp1 = iv1 ・tmp2 = iv2 ・blocksの各ブロックについて以下の処理を実行 ・tmp: blockとtmp1のXORのAES-ECB暗号化 ・_tmp: tmpとtmp2のXORのAES-ECB暗号化 ・enc += _tmp ・tmp1 = _tmp ・tmp2 = tmp ・encを返す。 ・alice_usernameとalice_tokenを16進数表記で出力 ・以下繰り返し ・username: 16進数表記で入力→hexデコード ・token = get_token(username, iv1, iv2) ・usernameとtokenを16進数表記で出力 ・proof(token) == proof(alice_token)の場合、 tokenとalice_tokenが異なれば、フラグが表示される。
Aliceの暗号は以下のようなイメージとなる。
平文1ブロック目 ^ iv1 --(AES暗号)--> M1 ^ iv2 --(AES暗号)--> 暗号1ブロック目 平文2ブロック目 ^ 暗号1ブロック目 --(AES暗号)--> M2 ^ M1 --(AES暗号)--> 暗号2ブロック目
暗号の各ブロックのXORがAliceの暗号の各ブロックのXORと同じになればよい。
平文3ブロック目 ^ 暗号2ブロック目 == 平文2ブロック目 ^ 暗号1ブロック目になる平文3ブロック目を指定する。
さらに平文4ブロック目 ^ 暗号3ブロック目 == 平文2ブロック目 ^ 暗号1ブロック目になる平文4ブロック目を指定する。
こうすれば、以下のようなイメージで、tokenは同じになるはず。
平文1ブロック目 ^ iv1 --(AES暗号)--> M1 ^ iv2 --(AES暗号)--> 暗号1ブロック目 平文2ブロック目 ^ 暗号1ブロック目 --(AES暗号)--> M2 ^ M1 --(AES暗号)--> 暗号2ブロック目 平文3ブロック目 ^ 暗号2ブロック目 --(AES暗号)--> M2 ^ M2 --(AES暗号)--> 暗号3ブロック目 平文4ブロック目 ^ 暗号3ブロック目 --(AES暗号)--> M2 ^ M2 --(AES暗号)--> 暗号4ブロック目(暗号3ブロック目と同じ)
以上を元にスクリプトにする。
#!/usr/bin/env python3 import socket def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) def xor(a, b): return bytes(i ^ j for i, j in zip(a, b)) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('20.233.7.174', 4870)) for _ in range(8): data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data) alice_pt = bytes.fromhex(data.split(' : ')[1].split(' -> ')[0]) alice_ct = bytes.fromhex(data.split(' : ')[1].split(' -> ')[1]) #### 1st try #### pt3 = xor(xor(alice_pt[16:], alice_ct[:16]), alice_ct[16:]) username1 = (alice_pt + pt3).hex() data = recvuntil(s, b': ') print(data + username1) s.sendall(username1.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) token1 = bytes.fromhex(data.split(' : ')[1].split(' -> ')[1]) data = recvuntil(s, b'\n').rstrip() print(data) #### 2nd try #### pt4 = xor(xor(alice_pt[16:], alice_ct[:16]), token1[32:]) username2 = (alice_pt + pt3 + pt4).hex() data = recvuntil(s, b': ') print(data + username2) s.sendall(username2.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data)
実行結果は以下の通り。
┌─────────────────────┐ | ┌──(quals@ctf)-[~] | | └─$ ./AES² | | | | By Aptx | └─────────────────────┘ Alice's creds : c2d4719d0fe7716ea48624ca88ba1ff703e3fe408b0d9e0345f474df1d894f66 -> 192a45970801d87e7acd12034994d2367801ce8b3319918db5f93603021fc8b0 Username : c2d4719d0fe7716ea48624ca88ba1ff703e3fe408b0d9e0345f474df1d894f6662c8755cb015d7f08ac050df560255e0 Your creds : c2d4719d0fe7716ea48624ca88ba1ff703e3fe408b0d9e0345f474df1d894f6662c8755cb015d7f08ac050df560255e0 -> 192a45970801d87e7acd12034994d2367801ce8b3319918db5f93603021fc8b02fe3b9d8f9d4abb7de9f6083a0245aaa Try again! Username : c2d4719d0fe7716ea48624ca88ba1ff703e3fe408b0d9e0345f474df1d894f6662c8755cb015d7f08ac050df560255e0352a020f7ad8edcae1a6065ff439c7fa Your creds : c2d4719d0fe7716ea48624ca88ba1ff703e3fe408b0d9e0345f474df1d894f6662c8755cb015d7f08ac050df560255e0352a020f7ad8edcae1a6065ff439c7fa -> 192a45970801d87e7acd12034994d2367801ce8b3319918db5f93603021fc8b02fe3b9d8f9d4abb7de9f6083a0245aaa2fe3b9d8f9d4abb7de9f6083a0245aaa Hey Alice! Here is your flag b'Securinets{r0ll_y0ur_0wn_structur3_4nd_g3t_c0ll1d3d}'
Securinets{r0ll_y0ur_0wn_structur3_4nd_g3t_c0ll1d3d}