この大会は2020/2/29 18:00(JST)~2020/3/1 18:00(JST)に開催されました。
今回もチームで参戦。結果は831点で273チーム中72位でした。
自分で解けた問題をWriteupとして書いておきます。
Babycrypt (Reverse)
あまりバイナリのアセンブリ等の解析はせずに、動作的な暗号の特徴から推測することにした。
$ ./bcry key: 11111111 [-] Error key size! $ ./bcry key: 1111111111111111 text: 123 Encoded: 313433 $ ./bcry key: 1111111111111112 text: 123 Encoded: 313433 $ ./bcry key: 3333333333333333 text: 123 Encoded: 353433 $ ./bcry key: 3333333333333333 text: 156 Encoded: 353938 $ ./bcry key: 0000000000000000 text: 178 Encoded: 313738 $ ./bcry key: 0000000000000000 text: 1jj Encoded: 318a8a
1文字ずつ対応しているようだ。
$ ./bcry key: 0000000000000000 text: test_test_test_test_test Encoded: 748573749f748573749f748573749f748573749f74857374 $ ./bcry key: 1111111111111111 text: test_test_test_test_test Encoded: 768573769f768573769f768573769f768573769f76857376
$ ./bcry
key: 1111111111111111
text: qwertyuiopasdfgh
Encoded: 71778574767975898f7281738688878a
$ ./bcry key: aaaaaaaaaaaaaaaa text: aaaaaaaaaaaaaaaa Encoded: 61616161616161616161616161616161 $ ./bcry key: aaaaaaaaaaaaaaaa text: bbbbbbbbbbbbbbbb Encoded: 64646464646464646464646464646464 $ ./bcry key: aaaaaaaaaaaaaaaa text: cccccccccccccccc Encoded: 63636363636363636363636363636363 $ ./bcry key: aaaaaaaaaaaaaaaa text: dddddddddddddddd Encoded: 66666666666666666666666666666666 $ ./bcry key: aaaaaaaaaaaaaaaa text: eeeeeeeeeeeeeeee Encoded: 65656565656565656565656565656565 $ ./bcry key: aaaaaaaaaaaaaaaa text: ffffffffffffffff Encoded: 68686868686868686868686868686868 $ ./bcry key: 0000000000000000 text: aaaaaaaaaaaaaaaa Encoded: 81818181818181818181818181818181
ここまで見てきて、keyとtextでxorしたものをkeyに足していると推測できる。
key: %key% text: test_test_test_test_test Encoded: 7685737a9f7895737a9f84857b769f7a657b769f78898378 key: %key% text: qwertyuiopasdfgh Encoded: 717785747885858d6f7e917364686776 key: %key% text: skIllaoInasJjklqo19akq9k13k45k69alq1 Encoded: 7393a992708d8fad708d83aa7273707d6f3939856b7d398bb53b8b34b573b6c5618e7135
暗号と平文が1対1対応していないので、上記の条件で満たす鍵すべてに対して、可能性のあるフラグをリストにしてみる。
def decrypt_char(c, key): code = (int(c, 16) - ord(key)) ^ ord(key) if code >= 0 and code < 256: return chr(code) else: return 'x' pt = [ 'test_test_test_test_test', 'qwertyuiopasdfgh', 'skIllaoInasJjklqo19akq9k13k45k69alq1'] ct = [ '7685737a9f7895737a9f84857b769f7a657b769f78898378', '717785747885858d6f7e917364686776', '7393a992708d8fad708d83aa7273707d6f3939856b7d398bb53b8b34b573b6c5618e7135'] enc_flag = '8185748f7b3b3a3565454584b8babbb8b441323ebc8b3a86b5899283b9c2c56d64388889b781' keys = [] for i in range(len(enc_flag) / 2): for code in range(32, 127): found = True if i < len(pt[1]): for j in range(3): if (code ^ ord(pt[j][i])) + code != int(ct[j][i*2:i*2+2], 16): found = False elif i < len(pt[0]): for j in [0, 2]: if (code ^ ord(pt[j][i])) + code != int(ct[j][i*2:i*2+2], 16): found = False elif i < len(pt[2]): for j in [2]: if (code ^ ord(pt[j][i])) + code != int(ct[j][i*2:i*2+2], 16): found = False if found: keys.append((i, chr(code))) flags = [] for i in range(len(enc_flag) / 2): if i == 0: target = 'A' elif i == 1: target = 'e' elif i == 2: target = 'r' elif i == 3: target = 'o' elif i == 4: target = '{' elif i == len(enc_flag) / 2 - 1: target = '}' else: target = '0123456789abcdef' decs = [] for j in keys: if j[0] == i: dec = decrypt_char(enc_flag[i*2:i*2+2], j[1]) if dec in target: if dec not in decs: decs.append(dec) if i == 0: tmps = decs else: tmps = [] for flag in flags: for dec in decs: tmps.append(flag + dec) flags = tmps for flag in flags: print flag
実行結果は以下の通り。
Aero{381a95d003649088c8f1abc989ad6fe7} Aero{381a95d003649088c8f1abc989ab6fe7} Aero{381a95d003649088c8f1abc189ad6fe7} Aero{381a95d003649088c8f1abc189ab6fe7} Aero{381a95d003649088c8f1ebc989ad6fe7} Aero{381a95d003649088c8f1ebc989ab6fe7} Aero{381a95d003649088c8f1ebc189ad6fe7} Aero{381a95d003649088c8f1ebc189ab6fe7} Aero{381a95d003629088c8f1abc989ad6fe7} Aero{381a95d003629088c8f1abc989ab6fe7} Aero{381a95d003629088c8f1abc189ad6fe7} Aero{381a95d003629088c8f1abc189ab6fe7} Aero{381a95d003629088c8f1ebc989ad6fe7} Aero{381a95d003629088c8f1ebc989ab6fe7} Aero{381a95d003629088c8f1ebc189ad6fe7} Aero{381a95d003629088c8f1ebc189ab6fe7}★
上記リストの最後でフラグが通った。正当な解き方ではなさそうだが、とりあえず良しとする。
Aero{381a95d003629088c8f1ebc189ab6fe7}
Old Crypto Server (Crypto)
$ nc tasks.aeroctf.com 44323 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit > 1 Enter cipher key: 1234567890abcdef Enter data: 111 {+} Encryption result: b'sOLO+RhZo8dU5ZEQ3k4VRw==' 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit > 2 Enter cipher key: 1234567890abcdef Enter ciphertext(in base64): sOLO+RhZo8dU5ZEQ3k4VRw== {+} Decryption result: b'111\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
サーバの処理概要は以下の通り。
■1 ・key、dataを指定 ・AES-ECB暗号(paddingは\x00埋め) dataをkeyで暗号化->base64 ■2 ・key、dataを指定 ・dataをbase64デコード ・AES-ECB復号 dataをkeyで復号 ■3 ・saltを指定 ・BASE64(AES-ECB暗号(salt + flag + pading))
saltの長さを増やしてみる。
$ nc tasks.aeroctf.com 44323 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit > 3 Enter salt: 1 {+} Encrypted secret: b'UvDjXUCMhBi1+lUb4ztm3fdWlMqhCNDhedvtq3vOpe5PDtL1hss3iKEPEe2BTAj0' 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit > 3 Enter salt: 11 {+} Encrypted secret: b'3MQm2PkBxtv5fOQov/tLQUUljJ51RIuC0Hbbz7i+BOHqWhABnQ8+UMoU/72NPH30' 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit > 3 Enter salt: 111 {+} Encrypted secret: b'Tx5EDWeNxRM6jddLbB8YJdZCV9VsK/nIc/4xpA73TcWjkimiGfR07YcueoaEO9cS' 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit > 3 Enter salt: 1111 {+} Encrypted secret: b'R4DO+oWKdq48+TwdFx2M+BIL6FnhxLocKq5DSthL5x2v/NLDSfgQZlCpwfSVloHA' 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit > 3 Enter salt: 11111 {+} Encrypted secret: b'zm+seM/Ynu2o4A8mpAW3SPFVOD7LLtu349PRzzO+1JvzosXkIL6Cj+NBVwJva6HY' 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit > 3 Enter salt: 111111 {+} Encrypted secret: b'yOf+91h/YFPH54xM51kFOQznJVJxJd5oY9IYpiPFHEeJYcqveu9bxXSSTQWm83mT' 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit > 3 Enter salt: 1111111 {+} Encrypted secret: b'9QJxrhAJHKN12f8IT5Q73C6NARu5EDjxl8xDOaeG9l9vys+iWg0ivDkU7OhHBcS9' 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit > 3 Enter salt: 11111111 {+} Encrypted secret: b'h/JmgvVhJLjFTkU9pkWCZSJIXlG7kcS8FV4/CbBQvq5IL7xMi8Cr9gk3ZWTjkC5/' 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit > 3 Enter salt: 111111111 {+} Encrypted secret: b'XTAh8QRosBhMmUufQQp0mIE5aIti/o8nF61c+n5zyobk27CjlHerCT4pWBT8ZWrI' 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit > 3 Enter salt: 1111111111 {+} Encrypted secret: b'ZqxZgLF/c1ZPz1xlmpq4JCHrjeUZYAVxCpb/Xl83z4jrfI3JbJK08hB3hZ2h117mb1rxX7a53SZS1QVcPNLiZg==' 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit
flagの形式からもその長さは38バイトとわかる。
0123456789abcdef SSSSSSSSSSFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF PPPPPPPPPPPPPPPP 0123456789abcdef 0 ?PPPPPPPPPPPPPPP0 SSSSSSSSSSSFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FPPPPPPPPPPPPPPP4 0123456789abcdef 1 ?FPPPPPPPPPPPPPP0 SSSSSSSSSSSSFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFPPPPPPPPPPPPPP4 0123456789abcdef 2 ?FFPPPPPPPPPPPPP0 SSSSSSSSSSSSSFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFPPPPPPPPPPPPP4 0123456789abcdef 3 ?FFFPPPPPPPPPPPP0 SSSSSSSSSSSSSSFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFPPPPPPPPPPPP4 0123456789abcdef 4 ?FFFFPPPPPPPPPPP0 SSSSSSSSSSSSSSSF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFPPPPPPPPPPP4 0123456789abcdef 5 ?FFFFFPPPPPPPPPP0 SSSSSSSSSSSSSSSS FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFPPPPPPPPPP4 0123456789abcdef 6 ?FFFFFFPPPPPPPPP0 SSSSSSSSSSSSSSSS SFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFPPPPPPPPP4 : 0123456789abcdef 14 ?FFFFFFFFFFFFFFP0 SSSSSSSSSSSSSSSS SSSSSSSSSFFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFP4 0123456789abcdef 15 ?FFFFFFFFFFFFFFF0 SSSSSSSSSSSSSSSS SSSSSSSSSSFFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF4 PPPPPPPPPPPPPPPP 0123456789abcdef 16 ?FFFFFFFFFFFFFFF0 SSSSSSSSSSSSSSSS SSSSSSSSSSSFFFFF FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF4 FPPPPPPPPPPPPPPP
フラグを1文字ずつはみ出させ、ブロック単位で暗号化したデータが一致するものを探すことを続けることによってフラグを割り出す。
import socket from base64 import b64encode, b64decode def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) def pad(data): return data + '\x00' * (16 - (len(data) % 16)) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('tasks.aeroctf.com', 44323)) chars = '0123456789abcdefAro{}' #flag = '' flag = '3a76ed3b98bae1e79169b3495f47a}' # <- 接続が不安定だったため、接続が切れたら、そのときまでに分かったフラグを入れながら実行。 for i in range(len(flag), 38): for c in chars: print '[+] flag =', flag if len(flag) < 15: salt = pad(c + flag) + 'S' * (i + 11) else: salt = c + flag[:15] + 'S' * (i + 11) data = recvuntil(s, '> ') print data + '3' s.sendall('3\n') data = recvuntil(s, ': ') print data + salt s.sendall(salt + '\n') data = recvuntil(s, '\n').rstrip() print data enc = b64decode(data.split(' ')[-1][2:-1]) if enc[16*0:16*1] == enc[16*4:16*5]: flag = c + flag break print flag
実行結果は以下の通り。
: [+] flag = ero{5013a76ed3b98bae1e79169b3495f47a} 1. Encrypt 2. Decrypt 3. Get server secret 4. Exit > 3 Enter salt: Aero{5013a76ed3bSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS {+} Encrypted secret: b'KNKValNs6lx7ENPtvxKAPXPUjKD1BluvFjW1DErLsg5z1Iyg9QZbrxY1tQxKy7IOc9SMoPUGW68WNbUMSsuyDijSlWpTbOpcexDT7b8SgD0cmpNN3YyBK6DkPjsKrR5fr/8oWbpTSDk5qHlA69M+Iw==' Aero{5013a76ed3b98bae1e79169b3495f47a}
Aero{5013a76ed3b98bae1e79169b3495f47a}