From 19777b3855d92146a40edafd43243f6b27416426 Mon Sep 17 00:00:00 2001 From: Yan Date: Sun, 29 Sep 2024 00:14:48 -0700 Subject: [PATCH] padding oracle --- cryptography/aes-cbc-poa/.init | 4 ++++ cryptography/aes-cbc-poa/DESCRIPTION.md | 21 +++++++++++++++++++ cryptography/aes-cbc-poa/dispatcher | 18 ++++++++++++++++ cryptography/aes-cbc-poa/worker | 28 +++++++++++++++++++++++++ cryptography/module.yml | 2 ++ 5 files changed, 73 insertions(+) create mode 100755 cryptography/aes-cbc-poa/.init create mode 100644 cryptography/aes-cbc-poa/DESCRIPTION.md create mode 100755 cryptography/aes-cbc-poa/dispatcher create mode 100755 cryptography/aes-cbc-poa/worker diff --git a/cryptography/aes-cbc-poa/.init b/cryptography/aes-cbc-poa/.init new file mode 100755 index 0000000..fc5fecd --- /dev/null +++ b/cryptography/aes-cbc-poa/.init @@ -0,0 +1,4 @@ +#!/bin/bash + +dd if=/dev/urandom of=/challenge/.key bs=16 count=1 +chmod 600 /challenge/.key diff --git a/cryptography/aes-cbc-poa/DESCRIPTION.md b/cryptography/aes-cbc-poa/DESCRIPTION.md new file mode 100644 index 0000000..9ca19bc --- /dev/null +++ b/cryptography/aes-cbc-poa/DESCRIPTION.md @@ -0,0 +1,21 @@ +So you can manipulate the padding... +If you messed up somewhere along the lines of the previous challenge and created an invalid padding, you might have noticed that the worker _crashed_ with an error about the padding being incorrect! + +It turns out that this one crash _completely_ breaks the Confidentiality of the AES-CBC cryptosystem. +Let's dig in... + +Recall that PKCS7 padding adds N bytes with the value N, so if 11 bytes of padding were added, they have the value `0x0b`. +During unpadding, PKCS7 will read the value N of the last byte, make sure that the last N bytes (including that last byte) have that same value, and remove those bytes. +If the value N is bigger than the block size, or the bytes don't all have the value N, most implementations of PKCS7, including the one provided by PyCryptoDome, will error. + +Consider how careful you had to be in the previous level with the padding, and how this required you to know the letter you wanted to remove. +What if you didn't know that letter? +Your random guesses at what to XOR it with would cause an error 255 times out of 256 (as long as you handled the rest of the padding properly, of course), and the one time it did not, by known what the final padding had to be and what your XOR value was, you can recover the letter value! +This is called a [_Padding Oracle Attack_](https://en.wikipedia.org/wiki/Padding_oracle_attack), after the "oracle" (error) that tells you if your padding was correct! + +Of course, once you remove (and learn) the last byte of the plaintext, the second-to-last byte becomes the last byte, and you can attack it! +And when you recover the entire last block, you can simply discard it, making the second-to-last block the last block. +You'll need to slightly adjust this attack for the 16th byte of a block, since there is not padding at all, but I trust in your ability to do so! + +So, what are you waiting for? +Go recover the flag! diff --git a/cryptography/aes-cbc-poa/dispatcher b/cryptography/aes-cbc-poa/dispatcher new file mode 100755 index 0000000..11bbdcb --- /dev/null +++ b/cryptography/aes-cbc-poa/dispatcher @@ -0,0 +1,18 @@ +#!/opt/pwn.college/python + +from base64 import b64encode +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad + +import sys + +key = open("/challenge/.key", "rb").read() +cipher = AES.new(key=key, mode=AES.MODE_CBC) + +if len(sys.argv) > 1 and sys.argv[1] == "flag": + plaintext = open("/flag", "rb").read().strip() +else: + plaintext = b"sleep" + +ciphertext = cipher.iv + cipher.encrypt(pad(plaintext, cipher.block_size)) +print(f"TASK: {b64encode(ciphertext).decode()}") diff --git a/cryptography/aes-cbc-poa/worker b/cryptography/aes-cbc-poa/worker new file mode 100755 index 0000000..4899ed1 --- /dev/null +++ b/cryptography/aes-cbc-poa/worker @@ -0,0 +1,28 @@ +#!/opt/pwn.college/python + +from base64 import b64decode +from Crypto.Cipher import AES +from Crypto.Util.Padding import unpad +from Crypto.Random import get_random_bytes + +import time +import sys + +key = open("/challenge/.key", "rb").read() + +while line := sys.stdin.readline(): + if not line.startswith("TASK: "): + continue + data = b64decode(line.split()[1]) + iv, ciphertext = data[:16], data[16:] + + cipher = AES.new(key=key, mode=AES.MODE_CBC, iv=iv) + plaintext = unpad(cipher.decrypt(ciphertext), cipher.block_size).decode('latin1') + + if plaintext == "sleep": + print("Sleeping!") + time.sleep(1) + elif plaintext == "flag": + print("Not so easy...") + else: + print("Unknown command!") diff --git a/cryptography/module.yml b/cryptography/module.yml index aabc792..116b19d 100644 --- a/cryptography/module.yml +++ b/cryptography/module.yml @@ -37,6 +37,8 @@ challenges: name: AES-CBC Tampering - id: aes-cbc-corrupt-resize name: AES-CBC Resizing +- id: aes-cbc-poa + name: AES-CBC: Padding Oracle Attack - id: level-6 name: DHKE - id: level-7