この大会は2023/11/11 14:00(JST)~2023/11/12 14:00(JST)に開催されました。
今回もチームで参戦。結果は1617点で729チーム中18位でした。
自分で解けた問題をWriteupとして書いておきます。
Welcome (welcome)
Discordに入り、#announcementチャネルのメッセージを見ると、フラグが書いてあった。
CakeCTF{hav3_s0m3_cak3_t0_r3fr3sh_y0ur_pa1at3}
simple signature (crypto, warmup)
サーバの処理概要は以下の通り。
・p: 512ビット素数 ・g = 2 ・magic_word = "cake_does_not_eat_cat" ・skey, vkey = keygen() ・x: ランダム2以上p-1未満整数 ・y: ランダム2以上p-1未満整数 ・w: ランダム2以上p-1未満整数 ・v = w * y % (p-1) ・vとp-1は互いに素でなければ、最初からやり直し ・u = (w * x - 1) * inverse(v, p-1) % (p-1) ・(x, y, u), (w, v)を返却 ・p, g, vkeyを表示 ・以下繰り返し ・choice: 入力 ・choiceが"S"の場合 ・message: 入力(magic_wordと同じ文字列はNG) ・sig = sign(h(message), skey) ・m: messageのsha512の数値化 ・x, y, u = skey ・r: ランダム2以上p-1未満整数 ・pow(g, x*m + r*y, p), pow(g, u*m + r, p)を返却 ・sigを表示 ・choiceが"V"の場合 ・message: 入力 ・s: 入力(2以上p-1未満) ・t: 入力(2以上p-1未満) ・verify(h(message), (s, t), vkey)でTrueの場合フラグを表示 ・w, v = vkey ・pow(g, h(message), p) == pow(s, w, p) * pow(t, -v, p) % pを返却
Verifyの等式が成り立つように適当なsを決めて、まずpow(t, v, p)を求める。
pow(t, v, p) = pow(s, w, p) * pow(g, h(message), p)
これをCをとすると、RSA暗号の復号方法で復号する。
phi = p - 1 d = inverse(v, phi) t = pow(C, d, p)
あとはこれらのデータをVerifyで渡してやればフラグが表示される。
#!/usr/bin/env python3 import socket from hashlib import sha512 from Crypto.Util.number import * def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) def h(m): return int(sha512(m.encode()).hexdigest(), 16) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('crypto.2023.cakectf.com', 10444)) data = recvuntil(sock, b'\n').rstrip() print(data) p = int(data.split(' = ')[1]) data = recvuntil(sock, b'\n').rstrip() print(data) g = int(data.split(' = ')[1]) data = recvuntil(sock, b'\n').rstrip() print(data) w, v = eval(data.split(' = ')[1]) magic_word = 'cake_does_not_eat_cat' s = 2 C = pow(s, w, p) * inverse(pow(g, h(magic_word), p), p) % p d = inverse(v, p - 1) t = pow(C, d, p) data = recvuntil(sock, b': ') print(data + 'V') sock.sendall(b'V\n') data = recvuntil(sock, b': ') print(data + magic_word) sock.sendall(magic_word.encode() + b'\n') data = recvuntil(sock, b': ') print(data + str(s)) sock.sendall(str(s).encode() + b'\n') data = recvuntil(sock, b': ') print(data + str(t)) sock.sendall(str(t).encode() + b'\n') data = recvuntil(sock, b'\n').rstrip() print(data) data = recvuntil(sock, b'\n').rstrip() print(data)
実行結果は以下の通り。
p = 10456129348987557481254996071034143684530960425708760133818260187442073806339593567912840424398419865326063623147020711301242111753917733144639229108892563 g = 2 vkey = (447684014132427783133530646893568737263009822339544169775893952481211373586359060289482466832827091848685967445885854739716253040131700049201573721550677, 8075783722883597733009746086093828558359534391870759327760217320597468198350900837320339144395157219846639434878101311943221115540741827787657995188823465) [S]ign, [V]erify: V message: cake_does_not_eat_cat s: 2 t: 6373152920261135618370503836807163046160391795156781879397274826756542381074926856233876326654834958417915167176491140943842127470211792996717173254879234 verified flag = CakeCTF{does_yoshiking_eat_cake_or_cat?}
CakeCTF{does_yoshiking_eat_cake_or_cat?}
ding-dong-ting-ping (crypto)
サーバの処理概要は以下の通り。
・KEY: ランダム16バイト文字列 ・IV: ランダム16バイト文字列 ・aes: 鍵KEYを使ったAES-ECB暗号オブジェクト ・以下繰り返し ・choice: 入力 ・choiceが1の場合 ・register() ・username: base64で入力→デコード ・usernameに"root"が含まれていたら、NG ・cookie = "<PREFIX>|user=<username>|YYYY-MM-DD hh:mm:ss.xxxxxx" ・cookie = encrypt(cookie) ・plain: cookieをパディング ・blocks: plainをブロックごとの配列にしたもの ・ciphers = [IV] ・blocksの各blockについて以下を実行する。 ・block: blockとciphers[-1]のmd5とのXOR ・blockをAES暗号化したものをciphersに追加 ・ciphersを連結したものを返却 ・cookie: cookieをbase64エンコードしたもの ・cookieを表示 ・choiceが2の場合 ・login() ・cookie: 入力 ・cookie = decrypt(b64decode(cookie)) ・cipher: cookieをbase64デコードしたもの ・blocks: cipherをブロックごとの配列にしたもの ・h = md5(blocks[0]).digest() ・plains = [] ・blocksの2個目以降の各blockについて以下を実行する。 ・plainsにblockを復号したものとhをXORしたものを追加 ・h = md5(block).digest() ・plainsを連結し、アンパッドしたものを返却 ・data = cookieを"|"区切りの配列にしたもの ・data[0]がPREFIXでdata[1]が"user="から始まる場合 ・username: data[1]の"="区切りの2個目の文字列 ・time: data[2] ・data[0]がPREFIXでない、またはdata[1]が"user="から始まらない場合 ・認証失敗メッセージを表示して、ログイン処理は終了 ・usernameとtimeを表示 ・usernameが"root"の場合、フラグを表示
PREFIXの長さを調べる。
$ nc crypto.2023.cakectf.com 11111 ===== MENU ===== [1]register [2]login: 1 username(base64): YQ== your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG1f61JT1JDR1MbtmoHS+StYRCqnu5gTslUG6z183w5LTHWTrAuhw9cce31/V0SWElw= ===== MENU ===== [1]register [2]login: 1 username(base64): YWE= your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG0kJ5ZLYV8vNCdmxf7B/Z8XytrtqLtjBpNQtYPplw86jBlFyUiIzE3rw/CqD2DgvWo= ===== MENU ===== [1]register [2]login: 1 username(base64): YWFh your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG19ZS6ASjp0lC1NXPbWmwG1I8wgFh2zHNXoJEKY7gDFLr0N+c6zlKG+/IqdFu3ollw= ===== MENU ===== [1]register [2]login: 1 username(base64): YWFhYQ== your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG1AXH1BvwFwcEIArdqBhn1Fmfbb8Z0d96XAQmjYFM0iVNHII/9PiTaruEUt8tBIiyo= ===== MENU ===== [1]register [2]login: 1 username(base64): YWFhYWE= your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG2pWvrTrc3Y21NNmtYWn7vQ0APqLKL+2TisGce2PYrJGZzBOnB3y0lKfoP5ackgk50= ===== MENU ===== [1]register [2]login: 1 username(base64): YWFhYWFh your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG3HF2sCx8+zOhQam82sIajxPaXgJMietq+yNVKAEHgs+GR4V53HOJRNmPu5Nh3k5k0= ===== MENU ===== [1]register [2]login: 1 username(base64): YWFhYWFhYQ== your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG1jMihl9+0XLRa8MnwX2YavJ44Tl+1orpME2kjvuipz5QCOVfX5WieaOBk6b/gsg+U= ===== MENU ===== [1]register [2]login: 1 username(base64): YWFhYWFhYWE= your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG1WSz6CqKY/3o8d6e430YamQLZpqhfjEKegUFW1VNwz1Kef9puZbQAonNqrJGtJFh0= ===== MENU ===== [1]register [2]login: 1 username(base64): YWFhYWFhYWFh your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG3k/t1ykAROWQUjim/7Yh6Dp+OGXkUcfH5VWxwnL7nUh6EkYGE5VNknYV36jQApwIU= ===== MENU ===== [1]register [2]login: 1 username(base64): YWFhYWFhYWFhYQ== your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG3k/t1ykAROWQUjim/7Yh6Dr0mc3k4tRWL/aU9P0U2S+tyQBqxuv2QgzcoSPKwo+20= ===== MENU ===== [1]register [2]login: 1 username(base64): YWFhYWFhYWFhYWE= your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG3k/t1ykAROWQUjim/7Yh6DWHlo/vwaD1j/+rrpvXH/WrsgQ2O2cUMz4fCnaBMQzVo= ===== MENU ===== [1]register [2]login: 1 username(base64): YWFhYWFhYWFhYWFh your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG3k/t1ykAROWQUjim/7Yh6DoVhpSGIX4Hb6hcgEJif2ZcNIh08U6g5DUSkheXva45c= ===== MENU ===== [1]register [2]login: 1 username(base64): YWFhYWFhYWFhYWFhYQ== your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG3k/t1ykAROWQUjim/7Yh6DIApsaAZIeoP7rcNx1VA9IfxSsCqJhM7Rcp/kf1VLdp4= ===== MENU ===== [1]register [2]login: 1 username(base64): YWFhYWFhYWFhYWFhYWE= your cookie => 5qR1+GZFJtyHTu+UCyKlnWrDhvz9OTuZW5igiAzscG3k/t1ykAROWQUjim/7Yh6DLQTcyR2/PMVXcC6Z3iR4AvMvit2PZhT+oEtHCXTsuldyW7RzSlvD9PS/RVuFDk1T
base64デコードしたusernameが14バイトのときに暗号化データをbase64デコードしたものがIVを含め96バイトになった。
"H"をPREFIX、"P"をパディング文字列と定義すると、以下のようなブロック構成のイメージになる。
0123456789abcdef PT0 HHHHHHHHHHHHHHHH PT1 H|user=aaaaaaaaa PT2 aaaaa|2023-11-12 PT3 hh:mm:ss.xxxxxx PT4 PPPPPPPPPPPPPPPP PT0 ^ md5(IV) --(AES暗号)--> CT0 PT1 ^ md5(CT0) --(AES暗号)--> CT1 PT2 ^ md5(CT1) --(AES暗号)--> CT2 PT3 ^ md5(CT2) --(AES暗号)--> CT3 PT4 ^ md5(CT3) --(AES暗号)--> CT4
作りたい暗号は、例えば以下のブロック構成になる平文に対するもの。
0123456789abcdef PT0 HHHHHHHHHHHHHHHH PT1 H|user=root|202P
rootは暗号化に指定できないので、roosで暗号化する。
0123456789abcdef PT0 HHHHHHHHHHHHHHHH -> CT0 PT1 H|user=roos|2023 -> CT1 PT2 -11-12 hh:mm:ss. -> CT2 PT3 xxxxxxPPPPPPPPPP -> CT3
PT0 ^ md5(IV) --(AES暗号)--> CT0
この部分はくずせない。以下の形にしたい。
0123456789abcdef PT0 HHHHHHHHHHHHHHHH -> CT0 PTA H|user=root|202P -> CTa
PTBの値について以下のようになるようなものを作る。
PTA ^ md5(CT0) = PTB ^ md5(CT1)
以下のブロック構成にして、暗号化する。
0123456789abcdef PT0 HHHHHHHHHHHHHHHH -> CT0 PT1 H|user=roos|202P -> CT1 PTB ???????????????? -> CTb PTC |2023-11-12 hh:m -> CTc PTD m:ss.xxxxxxPPPPP -> CTd
この暗号結果から、IV + CT0 + CTbを指定し復号すれば、HHHHHHHHHHHHHHHHH|user=roos|202 になり、ログインできるはず。ただし、PT1の1バイト目は不明のため、ブルートフォースで復号する。
#!/usr/bin/env python3 import socket from base64 import b64decode, b64encode from hashlib import md5 from Crypto.Util.strxor import strxor def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) real_username = b'root' dummy_username = b'roos' b64_dummy_username = b64encode(dummy_username).decode() s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('crypto.2023.cakectf.com', 11111)) data = recvuntil(s, b': ') print(data + '1') s.sendall(b'1\n') data = recvuntil(s, b': ') print(data + b64_dummy_username) s.sendall(b64_dummy_username.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) ct = b64decode(data.split(' ')[-1]) ct_blocks = [ct[i:i+16] for i in range(0, len(ct), 16)] for code in range(32, 127): pta = bytes([code]) + b'|user=root|202\x01' key0 = md5(ct_blocks[1]).digest() key1 = md5(ct_blocks[2]).digest() ptb = strxor(strxor(pta, key0), key1) try_username = b'roos|2023' + ptb b64_try_username = b64encode(try_username).decode() data = recvuntil(s, b': ') print(data + '1') s.sendall(b'1\n') data = recvuntil(s, b': ') print(data + b64_try_username) s.sendall(b64_try_username.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) ct = b64decode(data.split(' ')[-1]) ct_blocks = [ct[i:i+16] for i in range(0, len(ct), 16)] cookie = b''.join(ct_blocks[:2] + [ct_blocks[3]]) cookie = b64encode(cookie).decode() data = recvuntil(s, b': ') print(data + '2') s.sendall(b'2\n') data = recvuntil(s, b': ') print(data + cookie) s.sendall(cookie.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) if 'unsuccessful' not in data: break data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data)
実行結果は以下の通り。
===== MENU ===== [1]register [2]login: 1 username(base64): cm9vcw== your cookie => TQh+VTNmPnEIRbTcpgmJpUcAcETB+LojSJRCgK2qqmaJ3HFAAdILXhA8zuS6n7vtWFwAo5tcXZ4Q8MeplSFD8+XCeHeYudix1bPnxLj+pkg= ===== MENU ===== [1]register [2]login: 1 username(base64): cm9vc3wyMDIz6whoLcNIiupkzFEYm3N8mA== your cookie => TQh+VTNmPnEIRbTcpgmJpUcAcETB+LojSJRCgK2qqmaJ3HFAAdILXhA8zuS6n7vtBum2woJgdEV9L4qc7NULbw4I99nGMu2uFqjKXPERaMeHYmDes6N9D/7Tsol01I7G ===== MENU ===== [1]register [2]login: 2 cookie: TQh+VTNmPnEIRbTcpgmJpUcAcETB+LojSJRCgK2qqmYG6bbCgmB0RX0vipzs1Qtv Authentication unsuccessful... ===== MENU ===== [1]register [2]login: 1 username(base64): cm9vc3wyMDIz6ghoLcNIiupkzFEYm3N8mA== your cookie => TQh+VTNmPnEIRbTcpgmJpUcAcETB+LojSJRCgK2qqmaJ3HFAAdILXhA8zuS6n7vtZXqMefkT5H6bbVwFX9VEK9hU/r7QNdbQFWeWv3DCl0Cjut3vtZE7MUe5RPpQMzE9 ===== MENU ===== [1]register [2]login: 2 cookie: TQh+VTNmPnEIRbTcpgmJpUcAcETB+LojSJRCgK2qqmZleox5+RPkfpttXAVf1UQr Authentication unsuccessful... ===== MENU ===== [1]register [2]login: 1 username(base64): cm9vc3wyMDIz6QhoLcNIiupkzFEYm3N8mA== your cookie => TQh+VTNmPnEIRbTcpgmJpUcAcETB+LojSJRCgK2qqmaJ3HFAAdILXhA8zuS6n7vt6aTON+SbcWu6aMVl8J8eSG3rJlMTXGpNYoAio8f+4CzYs9R71s+aq/mngH2yjQum ===== MENU ===== [1]register [2]login: 2 cookie: TQh+VTNmPnEIRbTcpgmJpUcAcETB+LojSJRCgK2qqmbppM435Jtxa7poxWXwnx5I Authentication unsuccessful... : : ===== MENU ===== [1]register [2]login: 1 username(base64): cm9vc3wyMDIziQhoLcNIiupkzFEYm3N8mA== your cookie => TQh+VTNmPnEIRbTcpgmJpUcAcETB+LojSJRCgK2qqmaJ3HFAAdILXhA8zuS6n7vti4xWOzOCdY3mGr+r21ztMa4KDDzlyDfr4kf8kWB3wphpZCxLp38ffuqaRwix5Mgu ===== MENU ===== [1]register [2]login: 2 cookie: TQh+VTNmPnEIRbTcpgmJpUcAcETB+LojSJRCgK2qqmaLjFY7M4J1jeYav6vbXO0x Authentication unsuccessful... ===== MENU ===== [1]register [2]login: 1 username(base64): cm9vc3wyMDIziAhoLcNIiupkzFEYm3N8mA== your cookie => TQh+VTNmPnEIRbTcpgmJpUcAcETB+LojSJRCgK2qqmaJ3HFAAdILXhA8zuS6n7vtEPS/ZBbSlO307a6cut8oTpP1TYmeIAmPqeA4Lq3+fDE4soy17RW2zhGHpUAaKr8w ===== MENU ===== [1]register [2]login: 2 cookie: TQh+VTNmPnEIRbTcpgmJpUcAcETB+LojSJRCgK2qqmYQ9L9kFtKU7fTtrpy63yhO Authentication unsuccessful... ===== MENU ===== [1]register [2]login: 1 username(base64): cm9vc3wyMDIzjwhoLcNIiupkzFEYm3N8mA== your cookie => TQh+VTNmPnEIRbTcpgmJpUcAcETB+LojSJRCgK2qqmaJ3HFAAdILXhA8zuS6n7vt16JnvbUEQJEIXKJqEcnhEF64mst2xL8jFgXbEGcVLG8ulRzxGjc6H7gEZn2r6GyT ===== MENU ===== [1]register [2]login: 2 cookie: TQh+VTNmPnEIRbTcpgmJpUcAcETB+LojSJRCgK2qqmbXome9tQRAkQhcomoRyeEQ Hi, root! [registered at 202] Ding-Dong, Ding-Dong, Welcome, root. The ultimate authority has logged in. This is for you => CakeCTF{dongdingdongding-dingdong-dongdingdong-ding}
CakeCTF{dongdingdongding-dingdong-dongdingdong-ding}
Survey (survey)
アンケートに答えたら、フラグが表示された。
CakeCTF{thank_y0u_4_tasting_0ur_n3w_cak3s_this_y3ar}