Skip to content

Commit

Permalink
add padded prefix in final bridge to old-5
Browse files Browse the repository at this point in the history
  • Loading branch information
zardus committed Sep 28, 2024
1 parent 3567e62 commit 758b392
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 0 deletions.
30 changes: 30 additions & 0 deletions cryptography/cpa-prefix-pad/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
The previous challenge ignored something very important: [_padding_](https://en.wikipedia.org/wiki/Padding_(cryptography)#Byte_padding).
AES has a 128-bit (16 byte) block size.
This means that input to the algorithm _must_ be 16 bytes long, and any input shorter than that must be _padded_ to 16 bytes by having data added to the plaintext before encryption.
When the ciphertext is decrypted, the result must be _unpadded_ (e.g., the added padding bytes must be removed) to recover the original plaintext.

_How_ to pad is an interesting question.
For example, you could pad with null bytes (`0x00`).
But what if your data has null bytes at the end?
They might be erroneously removed during unpadding, leaving you with a plaintext different than your original!
This would not be good.

One padding standard (and likely the most popular) is PKCS7, which simply pads the input with bytes all containing a value equal to the number of bytes padded.
If one byte is added to a 15-byte input, it contains the value `0x01`, two bytes added to a 14-byte input would be `0x02 0x02`, and the 15 bytes added to a 1-byte input would all have a value `0x0f`.
During unpadding, PKCS7 looks at the value of the last byte of the block and removes that many bytes.
Simple!

But wait...
What if exactly 16 bytes of plaintext are encrypted (e.g., no padding needed), but the plaintext byte has a value of `0x01`?
Left to its own devices, PKCS7 would chop off that byte during unpadding, leaving us with a corrupted plaintext.
The solution to this is slightly silly: if the last block of the plaintext is exactly 16 bytes, we add a block of _all_ padding (e.g., 16 padding bytes, each with a value of `0x10`).
PKCS7 removes the whole block during unpadding, and the sanctity of the plaintext is preserved at the expense of a bit more data.

Anyways, the previous challenge explicitly disabled this last case, which would have the result of popping in a "decoy" ciphertext block full of padding as you tried to push the very first suffix byte to its own block.
This challenge pads properly.
Watch out for that "decoy" block, and go solve it!

----
**NOTE:**
The full-padding block will *only* appear when the last block of plaintext perfectly fills 16 bytes.
It'll vanish when one more byte is appended (replaced with the padded new block containing the last byte of plaintext), but will reappear when the new block reaches 16 bytes in length.
27 changes: 27 additions & 0 deletions cryptography/cpa-prefix-pad/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/opt/pwn.college/python

from base64 import b64encode, b64decode
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Random import get_random_bytes

flag = open("/flag", "rb").read().strip()

key = get_random_bytes(16)
cipher = AES.new(key=key, mode=AES.MODE_ECB)

for n in range(31337):
print("")
print("Choose an action?")
print("1. Encrypt chosen plaintext.")
print("2. Prepend something to the flag.")
if (choice := int(input("Choice? "))) == 1:
pt = input("Data? ").strip().encode()
elif choice == 2:
pt = input("Data? ").strip().encode() + flag
else:
break

padded_pt = pad(pt, cipher.block_size)
ct = cipher.encrypt(padded_pt)
print(f"Result: {b64encode(ct).decode()}")
4 changes: 4 additions & 0 deletions cryptography/cpa-prefix/DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ The core attack is the same as before, it just involves more data massaging.
Keep in mind that a typical pwn.college flag is somewhere upwards of 50 bytes long.
This is four blocks (three full and one partial), and the length can vary slightly.
You will need to experiment with how many bytes you must prepend to push even one of the end characters to its own block.

**HINT:**
Keep in mind that blocks are 16 bytes long!
After you leak the last 16 bytes, you'll be looking at the second-to-last block, and so on.
2 changes: 2 additions & 0 deletions cryptography/module.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ challenges:
name: AES-ECB-CPA-Suffix
- id: cpa-prefix
name: AES-ECB-CPA-Prefix
- id: cpa-prefix-pad
name: AES-ECB-CPA-Prefix-2
- id: level-5
name: level5
- id: level-6
Expand Down

0 comments on commit 758b392

Please sign in to comment.