###### 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'`