Skip to main content
A padding oracle attack is one of the most powerful adaptive chosen-ciphertext attacks in practical cryptography. Given a system that tells you whether a decrypted ciphertext has valid PKCS#7 padding, you can:
  1. Decrypt any ciphertext without knowing the key
  2. Encrypt any chosen plaintext (forge ciphertext)
The oracle can be as subtle as a different HTTP error code, a longer response, or a measurable timing difference.

Prerequisites

  • Target uses AES-CBC (or another CBC mode cipher) with PKCS#7 padding
  • The system exposes a padding validity signal after decryption:
    • Different HTTP status codes (200 OK vs 500 Internal Server Error)
    • Different response bodies (“Invalid padding” vs “Incorrect MAC”)
    • Timing difference between valid and invalid padding

How the attack works

CBC decryption reminder

Let D[i] = Decrypt(C[i]) (the raw block cipher output). Then:
An attacker who controls C[i-1] (the previous ciphertext block) and can query the oracle controls what P[i] decrypts to.

Recovering one byte

To recover the last byte of P[i]:
  1. Send a modified ciphertext where the last byte of C[i-1] is replaced with a guess g.
  2. If the oracle returns “valid padding”, the last byte of P[i] is \x01 (single-byte valid PKCS#7).
  3. Therefore: D[i][15] XOR g = 0x01, so D[i][15] = g XOR 0x01.
  4. Recover the original plaintext byte: P[i][15] = D[i][15] XOR C[i-1][15].

Recovering the full block

Repeat for each byte position, right to left. For position j (counting from the right, starting at 1):
  1. Fix all previously recovered bytes: set them so they decrypt to the target padding value j.
  2. Brute-force the next byte until the oracle signals valid padding.
The above is a simplified single-threaded implementation. Real attacks need to handle the edge case where \x01 padding is accidentally valid due to a coincidental \x02\x02 at the end. Production tools handle this by verifying \x02\x02 explicitly.

Encryption forgery

By reversing the decryption process you can also encrypt arbitrary plaintext:
  1. Choose a target plaintext P' and pad it.
  2. Starting from the last block and working backwards, determine the C[i-1] values that will produce valid decrypted P'[i].
  3. The final computed C[-1] becomes the IV.
This allows you to forge entirely new ciphertext without knowing the key.

Practical tooling

PadBuster

PadBuster automates the attack against web targets.

padbuster (Python)

Manual exploit with pycryptodome

Real-world examples

SSLv3 uses a non-deterministic padding scheme and performs MAC-then-encrypt. An attacker who can inject chosen blocks into a TLS session can exploit the padding validation to recover plaintext cookies, one byte at a time (256 requests per byte on average).Mitigation: disable SSLv3 and TLS 1.0; use TLS 1.2+ with AEAD ciphers.
ASP.NET encrypted ViewState with AES-CBC and returned a generic error page when padding was invalid. Researchers demonstrated full decryption and code execution via crafted ViewState tokens.Mitigation: Microsoft added a customErrors requirement and later deprecated CBC-based ViewState encryption.

Performance

For a 16-byte AES block, the worst case is 256 queries per byte × 16 bytes = 4096 queries per block. In practice the average is ~128 per byte, or ~2048 per block. Multi-threading or binary search on the oracle can significantly reduce this.

Defences