In this blog post we will demonstrate how minor peculiarities of the cryptographic implementation had a real-life impact on the security of the product. By exploiting this vulnerability we were able to fabricate arbitrary authentication tokens, allowing us to impersonate any user and effectively break the main functionality of OAM.
The Oracle Access Manager (OAM) is the component of the Oracle Fusion Middleware that handles authentication for all sorts of web applications. In typical scenarios, the web server that provides access to the application is equipped with an authentication component (the Oracle WebGate). When a user requests a protected resource from the web server, it redirects her to an authentication endpoint of the OAM. The OAM then authenticates the user (e.g. with username and password) and redirects her back to the web application. Since all the authentication is handled by a central application, a user only has to authenticate once to access any application protected by the OAM (Single Sign-On).
During a research project, we found that a cryptographic format used by the OAM exhibits a serious flaw. By exploiting this vulnerability, we were able to craft a session token. When a WebGate is presented with this token, it would accept it as a legitimate form of authentication and allow us to access protected resources. What’s more, the session cookie crafting process lets us create a session cookie for an arbitrary username, thus allowing us to impersonate any user known to the OAM.
WHO WAS AFFECTED?
Both currently supported versions, 11g and 12c were affected by this vulnerability – the scenario described here was only verified with version 12c. A simple Google Dork to find OAM installations yields about 11.800 results, some pointing to high-profile organizations (including Oracle itself). Those are only the installations reachable on the Internet.
We responsibly disclosed this vulnerability to Oracle immediately after we identified it in November 2017. Oracle was very responsive and provided a fix with the latest Critical Patch Update (CPU) in April 2018. As this patch was provided in Oracle’s regular update schedule, we expect OAM administrators to have applied the patch by now. If this is not the case for your organization, it’s high time to do so now!1
Moreover, it is advisable to analyze historic logs for the distinct pattern that this attack leaves behind: A successful attack causes a large number of decryption failures resulting from bad padding (javax.crypto.BadPaddingException).
Our security advisory contains further information regarding affected versions, vendor timeline etc.
THE TECHNICAL DETAILS
This section will elaborate on the technical details of the vulnerability. If you are not interested in those, you can skip to the bottom of this article where we provide a demo video showing the attack.
On a more technical level, the following happens during an authentication:
- The user accesses a protected resource.
- The Oracle Webgate component inside the web server answers this request with a redirect to the OAM. An encrypted message (“encquery”) is passed in a URL parameter.
- The user authenticates against the OAM (e.g. with username and password).
- The OAM redirects the user back to the web server. Information about the successful login is passed in the parameter “encreply”.
- The web server redirects the user to the resource that was initially requested. An encrypted authentication token is stored in a cookie (“OAMAuthnCookie”).
- The authentication token in the cookie is used from now on to authenticate the user.
The values in encquery, encreply and the OAMAuthnCookie are (supposed to be) cryptographically protected against modification. This way, when one of these values is received by the OAM or a WebGate, the contents are (supposed to be) assured to be unmodified, even if they come from a user. The OAM uses a single cryptographic format for all these messages. The secret key used for all of these messages is shared between the OAM and the WebGate.
The Cryptographic Format
As already hinted before, the flaw lies in the implementation of the cryptographic format. The algorithm that creates the protected messages processes inputs of key-value pairs and, using a shared secret, produces a base64-encoded output string. The main objective of this format is to provide integrity and authenticity. This is how the algorithm works:
The input is formatted in this way:
salt=<salt> <key>=<value> [...] validate=<base64 hash>
where the salt is a randomly generated value and the parameter validate is a hash over fixed parts of the message (MD5).- This string is then encrypted using a block cipher in CBC mode.
The Flaw
The very first thing that comes to mind when analyzing this format is, that none of the cryptographic algorithms used (i.e. the hash and the CBC block cipher) are intended to provide the main objective – authenticity. One could assume that, although it might not exactly be by the book, attacks are impossible since the attacker does not know the shared secret.
As people with a cryptography background may have noticed, one thing that particularly stands out is the CBC mode. When used incorrectly, it can be broken using a padding oracle attack. A classical padding oracle attack requires an encrypted input and a padding oracle. A padding oracle is a component that discloses whether a provided encrypted string, when it is decrypted has a valid padding. An oracle that discloses this information might not seem useful at first – but it, in fact, allows us to decrypt and encrypt arbitrary messages.
Simply put, a padding is required for block ciphers to be able to encrypt messages with arbitrary lengths. Block ciphers can only deal with blocks of a fixed size (e.g.16 bytes). If we want to e.g. encrypt a message that is 25 bytes long, we would encrypt the first 16 bytes and are then left with 9 bytes. As the block cipher cannot deal with a 9 byte input, we need to append 7 padding bytes. The typical approach to this is to add padding bytes where each byte contains the number of padding bytes (as defined in PKCS#7). E.g. in this case 7 bytes, each containing the value 7 are appended. When no padding would be required, a full padding block is appended (16 bytes, each containing the value 16).
An explanation of the padding oracle attack is beyond the scope of this post – for now, it is sufficient to understand that we need to find a way to figure out if an encrypted string, when decrypted, has proper padding.
To see if a padding oracle attack is possible we need to observe if the system reacts differently to messages that could not be properly de-padded and messages that could be properly de-padded but fail checks afterwards (e.g. if the de-padded text cannot be properly parsed). When we try both test cases against the encquery parameter, the OAM would respond with a “System error” both times – we therefore cannot easily distinguish between the two cases.
Whenever anything doesn’t look right to the OAM a “System error” is shown. So in order for us to distinguish a properly padded message from an improperly padded message, one approach is to make all properly padded messages we use in the attack to look completely legitimate. Obviously, when the OAM encounters a valid message, it would not respond with an error message. If the de-padding fails, we still see the error message.
Making Padding Oracle Work
As it turns out, the OAM ignores any garbage that is appended to the decrypted message if it is separated from the valid message by a space character. We could try to create a valid message that has a space character at the end. We then append the blocks we want to test for padding validity.
A decrypted message with a valid padding would e.g. look like this:
<valid message> <decryption of tested blocks>07070707070707
OAM would first check the padding (which would succeed) and then parse the valid message. It would ignore the rest of the message.
However, a decrypted message with an invalid padding would e.g. look like this:
<valid message> <decryption of tested blocks>8BA09FB1ABC543
OAM would check the padding, find that it is invalid and fail with a system error.
Space: The Final Frontier
So how do we make sure that the valid message is followed by a space character? – with brute force.
First, we need to create a valid message with a length that is exactly divisible by the block length. We need to find a way to influence the plaintext so that the resulting ciphertext meets this criteria. As it turns out, the encquery contains the protected URL that the user initially requested. We can capture the encquery values resulting from accessing URLs of different lengths:
http:// example.com/protected/?
http:// example.com/protected/?a
http:// example.com/protected/?aa
http:// example.com/protected/?a
As soon as the length of the encquery increases by 16 bytes, we know that the length of the encrypted message is divisible by 16 and the last block consists soley of padding bytes:
|---------------|---------------|
< valid msg>PPPP
< valid msg >PPP
< valid msg >PP
< valid msg >P
< valid msg >PPPPPPPPPPPPPPPP
This allows us to discard the last block and continue with the encrypted string that contains only the encrypted string and no padding. We then make sure, that the following block contains a space character at the first position. We can create an encrypted message that contains the valid message without padding, a block we choose as well as the last 2 blocks of the original message (to keep the padding valid). We can arbitrarily choose the block in the encrypted message – we, however, cannot purposefully influence the text the block results to after decryption. Though, we can keep trying random encrypted blocks until the plaintext block matches our needs.
|---------------|---------------|---------------|---------------|
< valid msg >< brute force >< last 2 blocks of orig. msg >
If the decrypted valid message is not followed by a space character, the message is invalid and a "System error" is shown. We will keep constructing messages with random blocks until eventually, it is accepted by OAM. We then know, that the decryption of our chosen block, by mere chance contains a space character in the first byte:
|---------------|---------------|---------------|---------------|
< valid msg > <random data >< last 2 blocks of orig. msg >
After this step, the attack is simple: We simply use the message we constructed as a prefix to the blocks we want to test for a valid padding. If the padding is incorrect, the de-padding step fails, resulting in an error message. If the padding is correct, the OAM would properly de-pad the message, parse the valid message at the beginning, and respond with no error message.
|---------------|---------------|---------------|---------------|---------------|---------------|
< valid msg > <random block >< last 2 blocks of orig. msg >< some text ><pad valid?>
Putting it all together
Padding oracle allows us to decrypt any messages. As all the encrypted messages (encquery, encreply, OAMAuthnCookie) are encrypted with the same key, we can decrypt any of these messages.
What is less commonly known, is that a padding oracle attack can also be used to encrypt messages. So if we construct a valid authentication cookie and encrypt it with our padding oracle attack, we can pass it off as valid to the web server. In fact, there is nothing that stops us from doing so: Since the validate value in the encrypted strings is a simple hash rather than an HMAC, we can simply calculate it without needing any secrets.
SEC Consult developed a proof of concept exploit script but we will not publish it at this time.