この大会は2016/10/19 19:00(JST)~2016/10/20 19:00(JST)に開催されました。
今回もチームで参戦。結果は453点で574チーム中80位でした。
自分で解けた問題をWriteupとして書いておきます。
cryptolocker (Crypto 200)
問題はODTファイルがランサムウェアで暗号化されてしまったので、それを復号することのようだ。暗号処理のコードが与えられているので、コードを読んでみる。
入力パスワードは8バイト。パスワードを2バイトずつ区切ってsha256を算出し、AES暗号の鍵4つを決定する。その鍵を使って、平文を4回暗号化する処理になっている。
そこで復号データを確認し、パディング文字とその数が合っているものを総当たりで探す。その際、暗号データは必ず32の倍数のサイズになり、IV(16バイト)を先頭に付けているので、パディング長は16になり、最後の平文に復号するときは、ODTの最初の2バイトはPKとなることをチェックする。コードは以下の通り。
import hashlib from Crypto.Cipher import AES def decrypt(key, enc): iv = enc[:AES.block_size] cipher = AES.new(key, AES.MODE_CBC, iv) return cipher.decrypt(enc[AES.block_size:]) def _unpad(s): return s[:-ord(s[len(s)-1:])] def chk_odt(s): if s[0:2] != 'PK': return False return True def chk_pad(s): num = ord(s[-1:]) if num != 16: return False for i in range(num - 1): if s[-i-2:-i-1] != chr(num): return False return True with open('flag.encrypted', 'rb') as f: ciphertext = f.read() found = 0 for i in range(256): for j in range(256): key_str = chr(i) + chr(j) key = hashlib.sha256(key_str).digest() dec = decrypt(key, ciphertext) if chk_pad(dec): three = _unpad(dec) found = 1 break if found == 1: break found = 0 for i in range(256): for j in range(256): key_str = chr(i) + chr(j) key = hashlib.sha256(key_str).digest() dec = decrypt(key, three) if chk_pad(dec): two = _unpad(dec) found = 1 break if found == 1: break found = 0 for i in range(256): for j in range(256): key_str = chr(i) + chr(j) key = hashlib.sha256(key_str).digest() dec = decrypt(key, two) if chk_pad(dec): one = _unpad(dec) found = 1 break if found == 1: break found = 0 for i in range(256): for j in range(256): key_str = chr(i) + chr(j) key = hashlib.sha256(key_str).digest() dec = decrypt(key, one) if chk_odt(dec): plaintext = _unpad(dec) found = 1 break if found == 1: break with open('flag', 'wb') as f: f.write(plaintext)
上記コードを実行し、復号したデータをflagファイルに保存する。
$ file flag flag: OpenDocument Text
Wordでこのファイルを開くと、フラグが書かれていることがわかる。
flag{v3ry_b4d_crypt0_l0ck3r}