A very serious vulnerability in ASP.NET was revealed this past month that allows attackers to completely compromise ASP.NET Forms Authentication, among other things. When things like this happen, as developers it's important to see what lessons can be learned in order to improve the defensibility of our software.
This vulnerability illustrates a couple of important lessons that all developers should take note of:
- Doing cryptography correctly is challenging
- Don't store sensitive information on the client
They've fixed the first issue with an out of band patch which means Microsoft took this vulnerability as a very real and serious threat to ASP.NET. If you're not convinced as to the seriousness of this attack, watch this YouTube video of POET (Padding Oracle Exploit Tool) vs. DotNetNuke (which uses Forms Authentication) here.
Doing Cryptography Correctly Is Challenging
As developers, we must be masters of many different domains and it's impossible to stay on top of all the different technologies, platforms, frameworks, and security issues that apply to each domain. Case in point: Cryptography and this Padding Oracle Vulnerability. As developers, how many know what a side-channel attack is, as well as how to protect against it?
This is why we rely on Frameworks - we stand on the shoulders of others who were able to sit down, focus on a problem and come up with a solution that is safe, stable, reusable way of a doing a common task. This helps us save time on development and focus more specifically on the function requirements. The other benefit of using a widely propagated Framework is that in the event of a foible or huge screw-up, there will be a fix. Using obscure libraries doesn't actually provide more protection; instead it just means that the problems that do exist may be taken advantage by attackers without it ever coming to light — though for some, ignorance is bliss.
A good example of this is authentication. Home grown web authentication mechanisms are typically fraught with mistakes that often lead to the compromise of web applications. There are many things that can be done wrong in web authentication. With ASP.NET Forms Authentication the common task authenticating users is abstracted into some reusable objects, controls, datastore, and configuration settings. Now, in theory, developers don't need to think about authentication very much and can instead rely on someone else who took the time to sit down and to worry about the intricacies of doing this correctly. That doesn't mean developers shouldn't understand Forms Authentication because there are still many ways to incorrectly configure and use Forms Authentication that can leave sites vulnerable to attack. Using Frameworks is a good thing since it's impractical for every developer to keep re-inventing the authentication 'wheel' every time they build a web site that needs to authenticate users. However, the benefit of having someone else implement a feature such as authentication can also be to the detriment of the system if it's done incorrectly. ASP.NET Forms Authentication has handled this job pretty well for many years and Microsoft should be commended for this, even though it's not been without any mistakes. The latest misstep isn't limited to Forms Authentication in ASP.NET (JavaServer Faces, Ruby on Rails, and OWASP's Java ESAPI), this is just one of many place where this vulnerability manifests itself.
ASP.NET encrypts information and stores it in several places on the client - in the URL, in a cookie or in a hidden form field when using features such as WebResources, ViewState, Forms Authentication, Anonymous Identification, and the Role Provider. ASP.NET typically uses AES (or 3DES depending on configuration) to encrypt this data and it uses a common key called the Machine Key. AES and 3DES are block ciphers, or symmetric key ciphers, that perform a transform on a fixed number of bytes of input (the plaintext) and then outputs encrypted data (or ciphertext) that is the same number of bytes as the input. Since the block size is fixed (64 or 128 bits) and the plaintext text size can vary, the transform needs to be able to encrypt data longer than the initial block size. This is accomplished using a ?mode of operation' (Cipher-block chaining or CBC is what ASP.NET uses) combined with a padding scheme (PKCS5 Padding) that is applied before the transformation (so that the input size matches the block size).
Because of the way these block ciphers are designed, if an attacker can change the encrypted data and send it back up to the server and get a SUCCESS or FAILED message as to if the padding is correct (hence Padding Oracle), they can actually use this information to decrypt the data. It turns out they can also encrypt new data as well using a different technique.
If a crypto system leaks just one bit of information about the encryption/decryption process — that's all that may be needed for an attacker to beat the crypto system.
What's interesting about this vulnerability is that it can easily occur through very legitimate use of the .NET Crypto API — meaning if you aren't aware of this sort of vulnerability and you allow an untrusted client to bounce data that's been encrypted with a block cipher against a service of some sort and the service returns error messages, then this vulnerability exists.
One solution you may arrive at is to just use one generic custom error page that doesn't reveal the .NET cryptographic exception, but the attacker just needs to INFER a padding error occurred, they don't require the actual padding error message. So the HTTP status may also give this away?so what if the web site just returns a 404 status code or 200 status code and an error page? An attacker can just use a timing attack to determine if it was a padding error in the decryption process — this is why one of the recommended workaround from Microsoft was to put a random timer loop in the error page to throw off these timing attacks used to detect if a Padding Oracle existed.
To cryptographers, this was an obvious mistake — verifying a MAC before decrypting the data or verifying a digital signature of a hash of the encrypted data would have provided the protections needed to prevent the tampering of the data and thus would have silenced the Padding Oracle. This is exactly what Microsoft has done in their patch, so the vulnerability has been fixed. The other important thing to consider is that encryption algorithms have a limited shelf-life as well — it's always only a matter of time before they can be circumvented. I would prefer to see ASP.NET put a large cryptographic pseudo-randomly generated ID in a cookie or URL and keep the actual sensitive data related to Authentication and roles on the server. As it turns out, ASP.NET does apply a MAC in almost every case — for ViewState, Forms Authentication Tickets, Anonymous Identification, and Role Cookies. However the MAC is used to check the authenticity AFTER decryption which is too late to protect against this vulnerability — it doesn't silence the Padding Oracle. As for the WebResource that has encrypted data in the URL Query String, no MAC is used at all.
This is why it's important to never store sensitive information on the client to begin with — because doing cryptography correctly is not a trivial task.