###### tags: `InterKosenCTF2020` # [InterKosenCTF2020|Crypto] padding oracle ## 問題 ソースコードは単純.はじめにフラグをaesで暗号化したものと`iv`をもらえる.その後、こちら側が入力した暗号文を復号してくれ、それがエラーなく動作したがわかる. ## 解法 やることは問題文からも明らかなようにpadding oracle attack.ただ、pad(),unpad()を見ると、pkcs7で見られるような末尾に追加するのではなく先頭に追加している. とは言っても、普通のpadding oracle attackでは後ろからしていく処理を逆にしていけば解ける. ptrlibの実装を参考に改造すればオッケー https://github.com/ptr-yudai/ptrlib/blob/master/ptrlib/crypto/padcbc.py ```python from ptrlib import Socket from binascii import unhexlify, hexlify def padding_oracle_block(decrypt, prev, block, bs): # prev = prev[::-1] plain = bytearray(bs) for i in range(bs): for b in range(256): p = plain[:] for j in range(i): p[j] = plain[j] ^ prev[j] ^ (i+1) p[i] = b # oracle = decrypt(p[::-1] + block) oracle = decrypt(p + block) if oracle is True: plain[i] = (i+1) ^ prev[i] ^ b print("decrypted a byte {}/{}: {}".format(i + 1, bs, plain[i])) break elif oracle is not False: raise ValueError("The function `decrypt` must return True or False") else: raise ValueError("NOT FOUND") # return bytes(plain)[::-1] return bytes(plain) def padding_oracle(decrypt, cipher, *, bs, unknown=b"\x00", iv=None): if len(cipher) % bs != 0: raise ValueError("The length of `cipher` must be a multiple of `bs`") # Split ciphertext into blocks cipher_blocks = [] for i in range(0, len(cipher), bs): cipher_blocks.append(cipher[i: i + bs]) plain_blocks = [None for i in range(len(cipher_blocks))] # Break the cipher for k in range(1, len(cipher_blocks)): # for k in range(len(cipher_blocks) - 1, 0, -1): plain_blocks[k] = padding_oracle_block( decrypt, cipher_blocks[k - 1], cipher_blocks[k], bs ) print( "decrypted a block {}/{}: {}".format( len(cipher_blocks) - k + 1, len(cipher_blocks), plain_blocks[k] )) if iv: plain_blocks[0] = padding_oracle_block(decrypt, iv, cipher_blocks[0], bs) print("decrypted an iv block: {}".format(plain_blocks[0])) else: plain_blocks[0] = unknown * bs return b"".join(plain_blocks) sock = Socket("localhost", 13004) c = unhexlify(sock.recvline().strip()) iv, c = c[:16], c[16:] c_block = [c[i:i+16] for i in range(0, len(c), 16)] # print("[*]", c_block) def try_decrypt(cipher): payload = hexlify(cipher) # print(f"[*] paylaod: {payload}") sock.sendline(payload) msg = sock.recvline().strip() # print(f"[*] msg: {msg}") if msg == b"False": return False return True flag = padding_oracle( try_decrypt, c, bs=16, unknown=b'?', iv=iv, ) print("[+] flag:", flag) ``` `[+] flag: b'\x03\x03\x03Turkey in de straw, turkey in de hay KosenCTF{0r4c13_5urviv35_57i11_n0w} Turkey in de straw, turkey in de hay'`