この大会は2020/10/10 9:00(JST)~2020/10/12 9:00(JST)に開催されました。
今回もチームで参戦。結果は1114点で700チーム中158位でした。
自分で解けた問題をWriteupとして書いておきます。
finger-warmup (beginner) (web)
リンク先をアクセスすると、さらに別のリンク先の付いたページになる。スクリプトで、繰り返しリンク先を追っていく。
import requests import re url = 'https://finger-warmup.chals.damctf.xyz/' path = '' i = 1 while True: print 'Round %d' % i i += 1 print url + path r = requests.get(url + path) body = r.text print body if 'dam{' in body: break pattern = 'href=\"(.+)\">' m = re.search(pattern, body) path = m.group(1)
実行結果は以下の通り。
: Round 1000 https://finger-warmup.chals.damctf.xyz/310491iil95gv69b6qpjp Nice clicking, I'm very impressed! Now to go onwards and upwards! <br/><pre>dam{I_hope_you_did_this_manually}</pre>
dam{I_hope_you_did_this_manually}
phase1 (forensics) (malware)
smtpでフィルタリングすると、あやしいファイルが転送されていることがわかる。該当するデータをすべてエクスポートする。エクスポートしたファイルを結合し、sha256の値を確認する。
$ cat *.bin > mal.elf
$ sha256sum mal.elf
fc5333c5d1de963b52e57c512e10cf0e37153ac917b7040cf5f95a628a601b10 mal.elf
dam{fc5333c5d1de963b52e57c512e10cf0e37153ac917b7040cf5f95a628a601b10}
cbc-padding-oracle (crypto)
$ nc chals.damctf.xyz 30327 PADDING ORACLE MENU 1. encrypt 2. decrypt your input? 1 Give me your data, I will encrypt your data like this: AES_CBC_256('A'*64 + [flag] + [your_data]) Length of the input (size >= 32, multiple of 16)? 32 Please give me your input! 11111111111111111111111111111111 Here's the encrypted data, length 128 b'\x1dW*~\x1f\xc1T\x80\xaa\xd9\x8d!\x98\x1e\x97\xd9j\xd4y\xda(\xbb\xb0\xd1\x17\x04\t\xd4\xd0\x1e\xb8\xda\x1b\x9d\xd7N\r_\x80\x1d:\xcc\xef\x9bI\xb7dr\x82\xe1\x84\x81\xe6\xa7/K\x0cFvl\xdf\xe9\x8b2\x01~\x1cX}\xa3\xc2H"?\xacj\xda\xf5i\xca\xf6\xab\xae\x1d\x11\x8b\xd5\xa8\xa6\xa2F\xc5o\xf7=S?Hd\xb7b=\xf5\xc5\xbeA\xa15D\xd2*\xbb\xf7\x0f\xa3w\xe8?\xad\x9a\xc5P\xb1Fx\x92<\xcd'
サーバの暗号化処理は以下の通り。
■encrypt ・平文のサイズを指定する(32以上、16の倍数) ・平文を指定する ・平文をpaddingする。 ・'A'*64 + [flag] + [指定文字列]をAES-CBC暗号化 ・iv + 暗号化データを返す(ivは毎回生成) ■decrypt ・暗号文のサイズを指定する(32以上、16の倍数) ・暗号文を指定する ・AES-CBCで復号(パディングのチェックあり)
CBC Padding Oracle Atackで復号できる。
なぜかencryptとdecryptを同じセッションでできないので、まず適当なデータを指定し、ファイルに保存する。
import socket def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('chals.damctf.xyz', 30327)) data = recvuntil(s, '?\n').rstrip() print data print '1' s.sendall('1\n') data = recvuntil(s, '?\n').rstrip() print data print '32' s.sendall('32\n') data = recvuntil(s, '!\n').rstrip() print data try_pt = '1' * 32 print try_pt s.sendall(try_pt + '\n') data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data enc = eval(data) with open('msg.enc', 'wb') as f: f.write(enc)
次に復号で、CBC Padding Oracle Attackを実行する。末尾が指定データで、途中でパディングが2バイトがわかたため、フラグが入っているブロックのみ実行する。
import socket def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) def str_xor(s1, s2): return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2)) def unpad(s): return s[:-ord(s[-1])] def is_valid(enc): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('chals.damctf.xyz', 30327)) data = recvuntil(s, '?\n').rstrip() print data print '2' s.sendall('2\n') data = recvuntil(s, '?\n').rstrip() print data print '32' s.sendall('32\n') data = recvuntil(s, '!\n').rstrip() print data print enc s.sendall(enc + '\n') data = recvuntil(s, '\n').rstrip() print data if 'ERROR' in data: return False else: return True with open('msg.enc', 'rb') as f: enc = f.read() enc_blocks = [enc[i:i+16] for i in range(0, len(enc), 16)] print len(enc_blocks) tail_pt_blocks = ['1' * 16, '1' * 14 + '\x02' * 2] xor_blocks = [] for i in range(2): xor_blocks.append(str_xor(enc_blocks[-i-2], tail_pt_blocks[-i-1])) for i in range(len(enc_blocks)-3, 4, -1): xor_block = '' for j in range(16): for code in range(256): print '%d - %d - %d: %s' % (i, j, code, xor_block.encode('hex')) print '****', str_xor(xor_block, enc_blocks[i-1][-j:]), '****' try_pre_block = '\x00' * (16 - j - 1) + chr(code) + str_xor(xor_block, chr(j+1)*j) try_cipher = try_pre_block + enc_blocks[i] if is_valid(try_cipher): xor_code = (j+1) ^ code xor_block = chr(xor_code) + xor_block break xor_blocks.append(xor_block) xor_blocks.reverse() msg = '' for i in range(len(xor_blocks)): msg += str_xor(enc_blocks[i+4], xor_blocks[i]) msg = unpad(msg) print msg
実行結果は以下の通り。
: 5 - 15 - 209: 0fb6e00c116b4aa85e53cf11e7b48e **** am{_Or4Cl3_} 11 **** PADDING ORACLE MENU 1. encrypt 2. decrypt your input? 2 Length of the input (size >= 32, multiple of 16)? 32 Please give me your input! ムヲ・{ZクNC゚槫ョ/ソ詭セチ02ルSュD Your input has been successfully decrypted in length 0 dam{_Or4Cl3_} 11111111111111111111111111111111
dam{_Or4Cl3_}