この大会は2021/8/28 2:00(JST)~2021/8/29 14:00(JST)に開催されました。
今回もチームで参戦。結果は4204点で428チーム中28位でした。
自分で解けた問題をWriteupとして書いておきます。
Welcome (Welcome)
Discordに入り、#welcome-flagチャネルのメッセージを見ると、フラグが書いてあった。
FwordCTF{Welcome_To_FwordCTF_2021}
Leaky Blinders (Cryptography)
サーバの処理概要は以下の通り。
・key: ランダム32バイト文字列 ・フラグを暗号化して表示 ・以下繰り返し ・メニュー表示 ・1を選択した場合 ・msg: ランダム32バイト ・cipher: msgを暗号化 ・cipherとkeyの各文字が異なる場合はcipherを表示 そうでない場合は、leakのメッセージを表示 ・2を選択した場合 ・k: keyを16進表記で入力 ・cipher: 暗号文を16進表記で入力 ・復号(XOR -> AES-ECB復号) →FwordCTFが含まれていたら、フラグを表示 ・3を選択した場合、終了
暗号化の処理概要は以下の通り。
・平文が16バイトの倍数でない場合、パディング ・AES-ECBでkeyを使って暗号化 ・cipherとkeyでXOR(文字列が短い方は繰り返し文字列にする)
2で鍵と暗号文を指定できるので、適当な鍵で"FwordCTF"を暗号化したものを指定すればよい。
#!/usr/bin/env python3 import socket from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) def xor(a, b): return bytearray([a[i % len(a)] ^ b[i % len(b)] for i in range(max(len(a), len(b)))]) def encrypt(msg): aes = AES.new(key, AES.MODE_ECB) if len(msg) % 16 != 0: msg = pad(msg, 16) cipher = aes.encrypt(msg) cipher = xor(cipher, key) return cipher s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('52.149.135.130', 4869)) for _ in range(3): data = recvuntil(s, b'\n').rstrip() print(data) key = b'\x00' * 16 msg = b'FwordCTF' cipher = encrypt(msg) key_hex = key.hex() cipher_hex = cipher.hex() data = recvuntil(s, b'> ') print(data + '2') s.sendall(b'2\n') data = recvuntil(s, b': ') print(data + key_hex) s.sendall(key_hex.encode() + b'\n') data = recvuntil(s, b': ') print(data + cipher_hex) s.sendall(cipher_hex.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data)
実行結果は以下の通り。
Welcome to Enc/Dec Oracle. Here is the encrypted flag : fe4fe57726fd997b8973129764ad10c880a12d48f3f8604cb0ae3219a47ee019bf8670d05a1fb38da5fbd056f67aad4b2e14b32a565544fe41111a0ada03a8019b8ff6dc00caa62b700b6c2d82a9be75 1- Encrypt 2- Decrypt 3- Leave > 2 Key : 00000000000000000000000000000000 Ciphertext : 94d0974bbcfc8739501c6985a3a50500 Well done ! Here is your flag : b'FwordCTF{N3v3r_x0r_w1thout_r4nd0m1s1ng_th3_k3y_0r_m4yb3_s3cur3_y0ur_c0d3}'
FwordCTF{N3v3r_x0r_w1thout_r4nd0m1s1ng_th3_k3y_0r_m4yb3_s3cur3_y0ur_c0d3}
Boombastic (Crypto)
サーバの処理概要は以下の通り。
・p: 1024bit素数 ・secret: 1以上p-1以下ランダム整数 ・以下繰り返し ・メニュー表示 ・1を選択した場合 ・magic_word: json形式で入力 ・"Boombastic"のチケットと一致していたら、フラグを表示 ・2を選択した場合 ・word: ランダム16バイトの16進表記 ・wordのチケットを表示 ・3を選択した場合、終了
チケットの計算は以下の通り。
・y: msgのsha256ダイジェストの数値化 ・r = ((y**2 - 1) * (inverse(secret**2, p))) % p ・s = ((1 + y) * (inverse(secret, p))) % p
inverse(secret, p) = Aとして、式を変形していく。
r = ((y**2 - 1) * (A**2)) % p s = ((1 + y) * A) % p ↓ s**2 % p = ((y**2 + y*2 + 1) * (A**2)) % p = (r + 2 * (y + 1) * (A**2)) % p = (r + 2 * s * A) % p ↓ A = ((s**2 - r) * inverse(s * 2, p)) % p
Aを算出できるので、ここから"Boombastic"のチケットを計算できる。
#!/usr/bin/env python3 import socket from Crypto.Util.number import inverse from json import loads, dumps import hashlib def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) def get_ticket(code, A): y = int(hashlib.sha256(code.encode()).hexdigest(),16) r = ((y**2 - 1) * (A**2)) % p s = ((1 + y) * A) % p return {'s': hex(s), 'r': hex(r), 'p': hex(p)} sc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sc.connect(('52.149.135.130', 4872)) data = recvuntil(sc, b'> ') print(data + '2') sc.sendall(b'2\n') data = recvuntil(sc, b'\n').rstrip() print(data) data = recvuntil(sc, b'\n').rstrip() print(data) ticket = loads(data.split(' : ')[1]) s = int(ticket['s'], 16) r = int(ticket['r'], 16) p = int(ticket['p'], 16) A = ((s**2 - r) * inverse(s * 2, p)) % p magic_word = dumps(get_ticket("Boombastic", A)) data = recvuntil(sc, b'> ') print(data + '1') sc.sendall(b'1\n') data = recvuntil(sc, b': ') print(data + magic_word) sc.sendall(magic_word.encode() + b'\n') data = recvuntil(sc, b'\n').rstrip() print(data)
実行結果は以下の通り。
______________ _(______________() ______ _- | || | |_ _- | || | |_|_ | Boombastic || |______| -_ | || /\ -_|______________|| / \ / \ / \ 1- Enter Cinema 2- Get a ticket 3- Leave > 2 Your ticket : {"s": "0x82f654c827f93d5687273115817c50a5a5eb1f3f52d552a463319968622737864cd35d08ea1aeff856a6d2cd339dfa596944056787254324a395e224369d7ba619300720197935fa763d7aeb88c7475365d43496f75fb1fe819dbc7315c03897235fe7f7ff77e249107ab4be0cee3ce23b9beaa09f9f46eb39db12df4186ef6e", "r": "0xdbada907e4cd5fea81984ba32c377669e55cb1a732a98dc1e61baaeb785293dbff0d963f2c09531ee24019aafb46d7b180547c6b9a9c35f4abf227f08a159a19a90e49c076356ab53693d5ff4e387d780c2c5324e95ed20d33027685cd7c6ac54b692680eb2ec6779468e08e96d28a4dfcadee27437685d0ca5ba41194462a0f", "p": "0xddb53eca8ab4c59353a59b59f40f874105fa5ae78c8b6d0933f1c9fc068c2932e90d564e37f6db498e0c704e91253b5873480ca505907f2ec01ce56342e30248479501c7171c410c575bef1c0d00cf48ca98a968f786a42826e9ec59f8016e09d9421a7e41237831e92b0cb492b786609e53a5b4341e9ad87535a758fbd2b997"} 1- Enter Cinema 2- Get a ticket 3- Leave > 1 Enter the magic word : {"s": "0x66f451a57a0aaa9fa295a49c3829d403deea227b165d94d6d20be8e05a450fa2a2ea7c1ccdbf4f2ef3662462ea6869fd561e2a211977197d9c6e0fc307e3b2b5a684dc615bb7838abe501008853dbf1d14a606fe690274cbecf400ad7a5a1dd54d36bfce7a5a4ebada04be4ea53369b0f2dd631e4c7455d8126ea9023c64c3af", "r": "0x423eb22fbcbce63aa40ea60bc79dd106819b083a2dd6aaf14f36cb8fa3162d94e473bae5b269fb886ec544ddf749433411aaad7efd1a4e8b7d76e889f414720136e75cc6948de85e27f57a925fa2a94ed5951205eb5c2c23bb1a134e3e8ef3df58a60d4bdc5fdff9085982ddcb1c4caf661b8f2b9e7a6fa29cf85fb15f8a851", "p": "0xddb53eca8ab4c59353a59b59f40f874105fa5ae78c8b6d0933f1c9fc068c2932e90d564e37f6db498e0c704e91253b5873480ca505907f2ec01ce56342e30248479501c7171c410c575bef1c0d00cf48ca98a968f786a42826e9ec59f8016e09d9421a7e41237831e92b0cb492b786609e53a5b4341e9ad87535a758fbd2b997"} Here is your flag : FwordCTF{4ct_l1k3_a_V1P_4nd_b3c0m3_a_V1P}, enjoy the movie sir.
FwordCTF{4ct_l1k3_a_V1P_4nd_b3c0m3_a_V1P}