---
key: "crypto-padding-oracle"
title: "Padding oracle attacks"
url: "https://softwaresecurity.com/domains/identity-crypto/encryption/padding-oracle-attacks/"
api: "https://softwaresecurity.com/api/v1/articles/crypto-padding-oracle.json"
domain: "identity-crypto"
topic: "Identity \u0026 cryptography"
type: "Reference"
identifiers: ["CWE-327","CWE-353","CWE-208","A04:2025","V11.2.5","V11.3.3","V11.3.5"]
tags: ["Encryption"]
stages: ["design","build"]
roles: ["developer","appsec-engineer"]
license: "CC-BY-4.0"
license_url: "https://creativecommons.org/licenses/by/4.0/"
code_license: "MIT"
---

# Padding oracle attacks

Use a byte-level CBC example to see how a tiny validity signal becomes plaintext information, then design verification that checks more than the error message.

Canonical URL: https://softwaresecurity.com/domains/identity-crypto/encryption/padding-oracle-attacks/

## In short

Use authenticated encryption, such as AES-GCM or ChaCha20-Poly1305, and release nothing until the tag verifies, so a modified ciphertext is rejected before any padding check can leak. If you must keep CBC, verify a MAC over the IV and ciphertext first (encrypt-then-MAC) in a maintained library. Uniform error messages are not a fix: timing and side effects still leak, and a fallback to the old CBC path brings the oracle back.

- In CBC, changing a byte of one ciphertext block changes the same byte of the next plaintext block.
- Any observable difference between bad padding and other failures, including timing, is an oracle.
- Authenticating before decrypting removes the padding decision from attacker-controlled input.
- Real cases include ASP.NET in 2010 (CVE-2010-3332), Lucky Thirteen and POODLE in TLS.
- After migrating, delete the old CBC decode path instead of falling back to it.

## Verify it

1. Alter the IV, an interior ciphertext byte and the last byte of a valid record: each must fail identically and before any side effect.
2. Compare response codes, sizes and timing distributions for bad-padding and bad-data cases over many requests.
3. Confirm an AEAD failure never triggers a retry through an old CBC decoder.
4. Run `testssl.sh --lucky13 host:443` and confirm no CBC suite is offered.

## What you will learn

- Calculate the CBC byte relation.
- Identify oracle signals beyond error bodies.
- Explain why authentication ordering fixes the boundary.

Audience: Developers who maintain code that decrypts CBC data or tokens, and testers who assess error and timing behavior.

Before you start:

- Understand block encryption, padding, XOR and observable success or failure responses.

Time: About 18 minutes: 4 to read and 14 for the exercise

## One changed byte controls one decrypted byte

CBC decryption computes Pᵢ = Dₖ(Cᵢ) XOR Cᵢ₋₁; for the first block the previous value is the IV. The attacker cannot normally choose Dₖ(Cᵢ), but can alter the previous ciphertext block. That alteration XORs directly into the corresponding plaintext byte. An endpoint that distinguishes valid padding from invalid padding turns this controlled change into a question about unknown plaintext.

For PKCS#7 padding, a final byte 01 represents one padding byte; 02 02 represents two; a full 16-byte padding block contains sixteen 10 hexadecimal bytes. Let the final intermediate byte I = Dₖ(Cᵢ)[last] be `0x6a`. A modified previous byte x = `0x6b` yields I XOR x = `0x01`. If the endpoint reports valid padding, the observer infers I = x XOR `0x01` = `0x6a`. With original previous byte `0x20`, the original plaintext byte is `0x4a`.

The attacker repeats the process for a longer padding suffix, fixing recovered bytes to 02, then 03, and so on. A real attack must handle ambiguous successful probes, padding already present in the original message, block boundaries, and rate limits. The worked byte is enough to explain the leak without presenting a network exploitation script.

## An oracle is observable behavior, not an exception name

The attacker needs ciphertexts accepted for processing, control over relevant bytes, and a repeatable signal related to a secret-dependent validity decision. A distinct HTTP status is an obvious oracle. Response sizes, connection closure, retries, downstream side effects, and statistically distinguishable timing can provide the same information. Identical error text does not establish identical processing.

CBC support alone is not proof of an exploitable oracle. An implementation may authenticate the complete IV and ciphertext before decryption, or use a reviewed constant-time protocol implementation. Conversely, placing an ordinary checksum inside the ciphertext gives no keyed authentication and does not block malicious modifications. An error suppressed by a gateway may still be visible through timing or application behavior.

## Stop unauthenticated inputs before plaintext interpretation

For new record designs, choose authenticated encryption and release no plaintext on tag failure, as in the AEAD record guide at /domains/identity-crypto/encryption/authenticated-record-design/. For a protocol that supports a standardized encrypt-then-MAC construction, verify a MAC covering the relevant framing, IV, and ciphertext before decryption. The construction needs independent keys and unambiguous encoding; bolting a homemade MAC onto an existing format is itself a protocol change.

Legacy CBC protocols can demand complicated constant-time padding and MAC handling. That belongs in a maintained protocol library, not application exception handling: Lucky Thirteen (/domains/identity-crypto/encryption/lucky13-poodle/) showed how hard it is to get right. Rate limiting and uniform errors reduce the observer’s bandwidth but leave the underlying secret-dependent decision intact. A migration must also close old decode paths: trying AEAD first and CBC after any failure resurrects unauthenticated processing.

Operationally, distinguish rejected traffic in internal aggregate telemetry while keeping public failures uniform. Avoid logging decrypted partial records or full bearer tokens. Review the error path under load as well as the happy path; a fallback service or diagnostic endpoint can expose a signal that the main handler hides.

## What should you do today?



- Find every place that decrypts CBC data from outside the trust boundary: cookies, tokens, hidden form fields, API payloads and stored records.
- Move those formats to an AEAD with a version byte, so new writes are authenticated and old records are migrated and retired.
- Where CBC must stay for a while, verify an HMAC over the version, IV and ciphertext with a separate key before decrypting.
- In TLS, offer only TLS 1.3 and TLS 1.2 AEAD suites; Mozilla’s current intermediate profile contains no CBC suites.

## Use a failure matrix and state the limits of timing evidence

Start with valid records, then separately alter the IV, an interior ciphertext byte, the last byte, the tag, and authenticated context. Every invalid authenticated record must fail before parsing or side effects. Record status, body length, connection behavior, and mutation counts. A valid record must still produce the intended plaintext exactly once.

Timing observations need repeated measurements and a stated noise model. Similar average times in a handful of requests cannot prove constant-time execution. Source review and the maintained library’s guarantees are stronger evidence for the intended ordering. The supplied exercise is algebra plus an explicit oracle transcript, not a reproduction of a remote vulnerability.

## Guided practice

A toy CBC decoder leaks whether its last plaintext byte is 01. Use its supplied observations to recover one original byte.

### Supplied material

```text
All values are hexadecimal.
Original previous ciphertext byte = 20.
Probe x=68 → invalid; x=69 → invalid; x=6b → valid 01.
For this fixture, the success is unambiguous and means exactly one-byte padding.
P = I XOR previous; modified P = I XOR x.
```

### 1. Recover the intermediate byte

Compute I from the successful probe.

Worked answer: I = 6b XOR 01 = 6a.

### 2. Recover the original byte

Compute the byte before the attacker changed the previous block.

Worked answer: P = 6a XOR 20 = 4a, ASCII J. This conclusion uses the fixture’s explicit unambiguous-success assumption.

### 3. Evaluate repairs

Compare identical error strings, authenticate-before-decrypt, and longer AES keys.

Worked answer: Identical strings can leave timing or side effects; longer keys do not change CBC malleability. Correct authentication before decryption prevents modified ciphertext from reaching padding processing. Untouched authenticated input must still succeed.

Deliverable: The two XOR calculations and a test matrix naming both the public response and the absence of plaintext-dependent side effects.

Scope: This supplied transcript models one padding decision. It does not execute AES, establish a remote oracle, or prove constant-time behavior.

Articles and teaching material: CC BY 4.0. Original executable code: MIT. Attribution: Software Security.

## Sources

- [OWASP WSTG 4.2: Testing for Padding Oracle](https://wstg.owasp.org/v4.2/4-Web_Application_Security_Testing/09-Testing_for_Weak_Cryptography/02-Testing_for_Padding_Oracle/). WSTG v4.2.
- [Practical Padding Oracle Attacks — Rizzo and Duong, WOOT 2010](https://www.usenix.org/legacy/event/woot10/tech/full_papers/Rizzo.pdf). May 2010.
- [RFC 7366: Encrypt-then-MAC for TLS and DTLS](https://www.rfc-editor.org/info/rfc7366/). September 2014.
- [OWASP Cryptographic Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html)
- [Microsoft Security Bulletin MS10-070: ASP.NET padding oracle (CVE-2010-3332)](https://learn.microsoft.com/en-us/security-updates/securitybulletins/2010/ms10-070)