An extensible, high-performance framework for exploiting padding oracles in network-based applications.
A padding oracle occurs in a cryptosystem where the application (the oracle) reveals information about the legitimacy of padded ciphertext. This typically happens in cipher block chaining (CBC) mode.
In CBC mode, the encryption of a block depends on the block preceding it. Before a plaintext block is passed through the block cipher algorithm (like AES), it is XORed with the previous ciphertext block. For the very first block, an Initialization Vector (IV) is used instead.
During decryption, this process is reversed:
- The ciphertext block passes through the block cipher decryption algorithm, resulting in what is called the Intermediate State (or intermediate ciphertext).
- This Intermediate State is then XORed with the preceding ciphertext block (or the IV) to yield the actual plaintext.
[Ciphertext n-1] ──────┐
│ (XOR)
[Ciphertext n] ───[AES Decryption]───> [Intermediate State n] ──> [Plaintext n]
Because block ciphers require equal-length blocks (e.g., 16 bytes for AES), the final plaintext block must be padded. Common standards like PKCS7 use the value of the padding bytes to denote the number of padding bytes added. For example, if 4 bytes of padding are needed, \x04\x04\x04\x04 is appended.
When an application decrypts the data, it checks this padding. If the last byte is \x04, it verifies that the preceding three bytes are also \x04. If they aren't, it throws a "Padding Exception".
A padding oracle vulnerability exists if the application leaks whether the padding was valid or invalid (e.g., via HTTP 500 errors, distinct error messages, or timing differences).
By controlling [Ciphertext n-1] (the IV for the current block) and sending it to the oracle, an attacker can manipulate the XOR operation that produces [Plaintext n].
Because Plaintext = IntermediateState ^ Ciphertext(n-1), we can rearrange the math to:
IntermediateState = Plaintext ^ Ciphertext(n-1)
The Decryption Exploit Flow:
- We send a forged
Ciphertext n-1consisting of 15 random bytes and 1 guessed byte (testing values 0-255). - The server decrypts
Ciphertext ninto its fixedIntermediate State. - The server XORs the
Intermediate Statewith our forgedCiphertext n-1. - If the server does not throw a padding error, we know that the resulting
Plaintextended in a valid padding byte (usually\x01).
Since we know the forged byte we sent (Ciphertext n-1), and we know the resulting plaintext byte must be \x01 (to pass the padding check), we can calculate the secret Intermediate State byte:
Intermediate_Byte = Forged_Byte ^ \x01
Once we have the Intermediate State byte, we simply XOR it with the actual, original Ciphertext n-1 ripped from the network traffic to recover the true Plaintext byte!
By repeating this process from right-to-left, adjusting our forged block to look for \x02\x02, then \x03\x03\x03, we can decrypt the entire ciphertext block without ever knowing the secret AES key.
We can also run this math in reverse. If we want the server to decrypt a block into a specific plaintext (e.g., admin=true), we first recover the Intermediate State of a dummy block (e.g., all As).
Once we know the Intermediate State of the dummy block, we calculate what Ciphertext n-1 should be to produce our desired plaintext:
Forged_Ciphertext(n-1) = IntermediateState ^ Desired_Plaintext
We then prepend this Forged_Ciphertext(n-1) to our dummy block and send it to the server. The server will decrypt it, perform the XOR, and blindly accept our forged plaintext. By chaining these blocks together, we can forge payloads of any length.
To see padoracle in action immediately, you can use the included vulnerable servers in the examples/ directory.
This server hosts a vulnerable endpoint that leaks padding errors via HTTP 500 status codes.
-
Start the vulnerable server:
go run examples/vuln_http_server.go -p 8080
The server will output a sample ciphertext.
-
Run
padoracleto decrypt: In another terminal, use the provided ciphertext to recover the plaintext:go run padoracle.go -u "http://127.0.0.1:8080/?vuln=<PADME>" -c "<PASTE_CIPHERTEXT_HERE>" -bs 16
-
Run
padoracleto encrypt (forge a payload): You can also use the oracle to encrypt arbitrary data without the key:go run padoracle.go -u "http://127.0.0.1:8080/?vuln=<PADME>" -m 1 -p "Forged Payload" -bs 16
The tool will output a forged ciphertext that the server will accept as valid.
This server demonstrates a non-HTTP padding oracle that communicates over raw TCP and leaks errors through a specific string (PADDING_ERROR).
-
Start the TCP server:
go run examples/vuln_tcp_server.go -p 9000
-
Modify
padoracle.gofor TCP: You would need to updateCallOracleto usenet.Dial("tcp", ...)and check for thePADDING_ERRORstring. This is a great exercise to see how extensible the tool is!
There are other padding oracle exploitation tools, but padoracle focuses on speed and concurrency.
padbuster: A classic tool, but written in Perl and can be slow.padding-oracle-attack: A solid Python-based solution, but somewhat inflexible and processes blocks sequentially.
padoracle is fast. It is built in Go and aggressively parallelizes the attack. It decrypts each block independently and concurrently. It also parallelizes the byte-guessing process using safe and efficient goroutines, meaning it can fire off hundreds of asynchronous requests at once. On a test system, it can decrypt 16 blocks of 16-byte ciphertext in under 1.5 minutes (using 100 threads).
padoracle is highly extensible, but it requires minor code modifications to teach it how to talk to your specific target.
First, clone the repository:
git clone https://github.com/bonzitechnology/padoracle.git
cd padoracleTo adapt the tool to your target application, open padoracle.go in your favorite editor. You will need to modify the testpad struct's methods to suit your needs:
EncodePayload: Define how the raw byte payload should be encoded before sending it (e.g., Base64, Hex).DecodeCiphertextPayload: Define how the input ciphertext from the CLI should be decoded into raw bytes.CallOracle: Implement the HTTP request (or any protocol) that sends the payload to the target.CheckResponse: Analyze the HTTP response (status code, body text, etc.) to determine if the padding was valid (true) or invalid (false).
Once modified, build the tool:
go buildThen run it against your target:
./padoracle -u "http://target.com/vuln?data=<PADME>" -c "YOUR_ENCODED_CIPHERTEXT" -bs 16 -T 100Use the -h flag to see all available options (such as -m 1 for encryption mode).
Built as an exercise in lateral thinking and Go concurrency.

