*UPDATE: Padcheck source is now available on GitHub: https://github.com/Tripwire/padcheck
Since August, I’ve spent countless hours studying CBC padding oracle attacks toward the development of a new scan tool called padcheck
. Using this tool, I was able to identify thousands of popular domains which could be targeted by an active network adversary (i.e. MiTM) to hijack authenticated HTTPS sessions.
The underlying vulnerabilities break down into two main categories which I have named Zombie POODLE and GOLDENDOODLE. These names specifically refer to the techniques for exploiting a variety of underlying flaws in the MAC-Then-Pad-Then-Encrypt scheme used for TLS CBC ciphers. Zombie POODLE exploits a ‘MAC validity’ oracle whereas GOLDENDOODLE exploits a ‘pad validity’ oracle. These attacks are described in more detail in separate blog posts for Zombie POODLE
In this post, I will provide background on how I came to develop padcheck
and identify several vulnerable implementations.
The story of how I came to discover these vulnerabilities technically began in October 2017 when Hanno Böck and Juraj Somorovsky invited me to collaborate with them on assessing the prevalence and exploitability of Bleichenbacher oracle threats on the Internet. This research went on to be called ROBOT and it won the 2018 Pwnie award in the ‘Best Cryptographic Attack’ category.
Being involved in this research really opened my eyes to just how badly the TLS standards have failed to displace insecure technology.
In the case of ROBOT, the specifications repeatedly added increasingly unruly countermeasures for Bleichenbacher’s attack rather than simply deprecating the use of PKCS#1 v1.5 (or RSA encryption based key exchange). After this, I was excited to investigate more TLS flaws and, after several lengthy discussions with Hanno, I decided to read up on CBC padding oracle attacks.
Accepting our Pwnie at Black Hat 2018
In the week following Black Hat, I began reviewing CBC padding oracles and specifically the POODLE attack. I had previously researched POODLE TLS to add a check for IP360, but this time around I had a new revelation. Specifically, I noted that the prevalent testing methodology for detecting POODLE TLS did not match the behavior of an actual exploit.
The common technique for detecting POODLE TLS is to simply connect to a server using a client TLS stack modified to use SSLv3 padding. If the server accepts the handshake, it must be vulnerable. This technique should always work to identify servers which mistakenly use the SSLv3 unpadding process in a TLS session. In practice, since Finished is the only encrypted handshake message prior to TLSv1.3, this means that the client is sending the Finished message in a malformed record.
As outlined in a previous blog post
, POODLE attacks do not actually involve these malformed Finished messages.
While we mostly discuss TLS holistically as its own protocol, it is technically comprised of the handshake protocol, alert protocol, change cipher spec protocol, and application data protocol
. This test for POODLE TLS examines the way the server behaves based on a padding error in the handshake protocol, but the actual POODLE attack involves the manipulation of application data records after a normal handshake. This revelation gave me the idea that some TLS stacks likely respond differently to padding errors in the application data versus in the Finished message.
In order to create a new tool for this purpose, I used Adam Langley’s POODLE TLS tool
and Golang patch
as a starting point. I was familiar with this tool at the time of its original publication and it had caught my attention because of the way Langley demonstrated being able to easily modify low-level behavior in the crypto/tls module. Specifically, Langley had modified the TLS stack to include a BreakCBCPadding
Despite having virtually no prior experience working with Go, within a day or two I was able to patch Go so that it would only break the padding on HTTP data and would support several modes of broken padding. The initial test cases I included were:
- Valid Padding with Invalid MAC
- Invalid Padding with Missing MAC
- Invalid Padding with Valid MAC
- Incomplete Padding with Missing MAC
NOTE: Tests #2 and #4 initially only used a single block of padding regardless of padding length. This was changed in March to extend the padding to span multiple blocks in light of other attack models such as BEAST. A fifth test was also added to cover the 0-byte record OpenSSL padding oracle identified by Somorovsky, Merget, and Aviram
The last step was to update the test script to prepare ‘block-aligned’ requests (e.g. requests requiring a full block of padding) and then send this request with each padding mode while recording response lengths and error codes. After all test cases are performed, the results are compared and inconsistencies are deemed vulnerable. Ideally, a host should only be considered vulnerable if it responds consistently across 3 or more test iterations.
Analyzing a Cisco Padding Oracle
The first thing I did with this new scanning tool was to point it at an internal lab VERT uses for testing new scan logic. The scan expectedly flagged several systems known to have POODLE TLS, but I was surprised to find that it also flagged a Cisco ASA running software version 9.1(6). Per Cisco’s advisory
, this version was not vulnerable to the POODLE TLS (aka POODLE BITES or CVE-2014-8730) attack.
Upon further investigation, I noted that the unique response was from test case #1. The ASA would appropriately abort the connection for any malformed padding but had no TLS error when the MAC was invalid. Instead, the device returned an HTTP protocol error encrypted within a proper TLS record. (The error is presumably because the request had 15 bytes of trailing binary data which were originally MAC bytes.)
I quickly found that this was caused by a previously discovered vulnerability termed ‘MACE’ by Yngve Nysaeter Pettersen from TLS Prober Labs
who had reported the defect. This issue is tracked as CVE-2015-4458. The surprising aspect of this was that nobody seemed to realize the full impact of the behavior. Descriptions from both NIST and Cisco only indicate that it would enable spoofing, but from my perspective it seemed clear that this could be exploited in a manner similar to POODLE.
With POODLE, the attacker can work backward based on observing when a manipulated ciphertext has a correct MAC which, therefore, implies a particular padding length based on the block size for the selected cipher. This Cisco ASA, however, is revealing whether the manipulated ciphertext has valid padding regardless of the MAC validity. This means that if the ASA doesn’t reject the manipulated TLS record, the corresponding plaintext must have ended with 0x00. (It could also end with 0x01|0x01 or 0x02|0x02|0x02 and so on, but the valid 0-length padding should happen randomly about once per 256 attempts whereas the valid 1-length padding will only happen about once out of every 65,535 attempts so it is far less likely.)
With this in mind, I set out to prove my theory that CVE-2015-4458 could be exploited to hijack an authenticated ASA WebVPN/SSLVPN session. I crafted a very primitive TLS/TCP proxy in Python making use of logic I had previously created
for detecting an older OpenSSL vulnerability. Using cURL as a client, I confirmed that I could make targeted modifications to the plaintext.
For example, here is the result from XOR’ing ciphertext block 3 with 0x01:
Note that the 3rd
block, which contained part of the Host and User-Agent headers now has random bytes but the “A” (0x41) byte from the Accept header at offset 0x3F has been changed to “@” (0x40).
Decrypting Bytes with CVE-2015-4458
The next step was to embed some secret value into the request and then attempt to decipher it as MiTM. I prepared a request in cURL such that it would have some secret bytes at a known offset and would have a full block of padding as required for POODLE. The proxy script was then updated to translocate the block with the ‘secret’ in place of the padding block. If the server didn’t respond with an error, the script would log the ciphertext blocks and attempt to calculate a byte of the secret value. (Refer to my earlier post for more details on this.
I started cURL in a while loop and sat back to wait for the attack to succeed. I expected that it would take an average of 256 intercepted connections before one of them would randomly end with 0x00.
I waited… And waited… And waited some more, but nothing happened.
I’m still not entirely sure why this didn’t work, but while staring into the dark abyss of my empty terminal window, I had another revelation. My revelation this time was that I didn’t actually have to just wait and hope for the bytes to magically align. Instead, it occurred to me that, since the ASA fails to validate the MAC bytes anyway, I could actually use these bytes to influence the decryption process. Specifically, my thought was that this would allow me to use the oracle to test guessed values for the secret bytes. (I later learned that this is exactly what Serge Vaudenay described in 2002
Put into words, this means that the attacker will trigger a request such that they can predict the offset of some unknown byte to be decrypted. The attacker will then intercept this TLS record and alter it so that the interesting ciphertext block replaces the last ciphertext block. The attacker will then make an iterative guess as to the value of the unknown byte and use this to calculate what the corresponding byte which would have been output from the block cipher decryption of the targeted block.
It is important to remember that output value from decrypting the ciphertext block is only an intermediary value in the CBC decryption process.
The value will be XOR’d with the previous ciphertext block and so the attacker will also XOR their guess with the corresponding byte from the previous ciphertext block. The last byte of the second to last ciphertext block will then be set to this calculated value. The application of XOR between the block cipher output and the previous ciphertext (during decryption by the server) will then force the final byte (e.g. padding length) to 0 if the guess was correct.
This is depicted in the illustration below.
If the attacker’s guess was correct, the Cisco ASA responds with some encrypted application data. The attacker does not know what is in the response, but the lack of a TLS alert confirms that the padding was valid and that the attacker’s guess was most likely correct and they can move on to guessing the next byte. There is, however, a small (1 in 65536) chance that a valid 1-byte pad, 0x01|0x01, is formed. This can be confirmed with a follow-up request to the oracle by attempting to construct a longer well-formed padding block.
This application of Vaudenay’s padding oracle attack to modern TLS implementation flaws is what I’ve come to call GOLDENDOODLE. For more information, check out the following additional blog posts:
I would like to thank Robert Merget, Juraj Somorovsky, and Nimrod Aviram and mention that they have also prepared a far more extensive padding oracle scanner based on the TLS Attacker framework. Although we largely worked independently, I was able to learn a tremendous amount by collaborating with them and comparing notes. More information about new padding oracles we’ve found is also on GitHub at https://github.com/RUB-NDS/TLS-Padding-Oracles