Wednesday 22 September 2010

Padding oracle attack on ASP.net a.k.a POET vs. ASP.net

I've been spending a bit of time at work looking at this...and a load more at home last night. It turns out once you get past all the jargon and attempts at security through obscurity it's fairly simple. First off - watch the following video - but keep in mind these things:
  1. This has nothing to do with any sort of Oracle database - the oracle in this case is referring to something that you are able to use to extract useful information from a server. 
  2. This is not restricted to DotNetNuke - it applies to pretty much all ASP.net applications.
  3. Don't get distracted by the bit after the DotNetNuke session token has been created because at this point it's game over and the rest is just a show.
  4. Don't get distracted by them setting custom errors to false - that setting is important but simply setting it to remoteOnly or whatever which is normal good practice is not enough to protect you!
  5. Don't blame me for "helping the bad guys" - all that damage has already been done as the exploit tool is out there. What's important now is that the good guys understand the risk and mitigations.


The attack is made possible by two vulnerabilities (okay one vulnerability and a with-hind-sight poor design choice) and a common design pattern. This is easiest understood in reverse order as you can then see where it's going:
  1. If you have someone's machine key you can forge session tokens, decrypt view states, and a myriad of other nasty bits of trickery.
  2. It is a common design pattern to include the machine key in the web config this makes sense for many applications especially in server farms however it does mean that if someone can steal the web config they get the crown jewels. By default DotNetNuke has the machine key in the web.config.
  3. The next vulnerability unfortunate design decision is in the way ASP.net handles resource requests for javascript files and the like in the WebResources.xsd handler. It completely relies on an encrypted token to identify the file to download - i.e. absolute trust in the encryption of the content sent from the client. In theory this is safe however if you can find a way to forge a token you can download any file in the application including the web.config file (so it may not just be your web.config at risk). See where this is going...? The final question then is - how do you forge the token for the resource request in order to exploit this?
  4.  This is where the padding oracle comes in. For an excellent clear explanation of padding oracle exploits see: http://www.gdssecurity.com/l/b/2010/09/14/automated-padding-oracle-attacks-with-padbuster/
    This is where the serious bug lies. ASP.net on receiving an encrypted resource request attempts to decrypt it to see what file you're after.
  5. Three things can happen at this point:

    1. The token decrypts to a valid file and returns it (200) - we're all happy as...
    2. The token can decrypt successfully but be garbage and therefore not find the resource and return a 404
    3. Or, and this is where it goes wrong, if it fails to decrypt the string due to padding error it throws an encryption exception and returns a 500 server error. Because we get a 404 if we got the padding right and a 500 otherwise we are able to decrypt and encrypt arbitrary data. My understanding is that this does not in itself compromise the keys (and takes a lot of requests - so watch your logs).
So in summary:
  1. There's a vulnerability in the resource handling that allows a padding oracle exploit so encrypted content sent to the client can be decrypted and encrypted remotely by sending a lot of requests and looking at the error codes returned. This may make your site vulnerable in itself.
  2. The implementation of the WebResources.xsd means that if you are able to encrypt your own strings you can fetch files you shouldn't have access to such as the web.config. This means any "secrets" you have in there like user names, connection strings, passwords, and machine keys could be compromised.
  3. If hackers have access to those "secrets" they may be able to use them to perform further attacks as demonstrated against DotNetNuke.

The advised mitigation
The following mitigations have been proposed on Scott Gu and Troy Hunt's blogs. I have added some discussion around why they work:
  1. "Change custom errors to return the same page and error code for 404 and 500 errors"This means that in the code discussed above although the code throws a 500 the exploit would not be able to tell the difference from the 404s. This is the very course equivalent of the try catch I suggest below as the likely patch MS will apply. This of course could lead to a negative user experience however there's an equivalent established precedent: It's good practice not to leak valid user names if a user name or password is wrong - we tell them "The user name or password you entered is incorrect". So we take the same approach - redirect to a page that says: "An error has occurred or the page you requested could not be found. Please search the site for the page you are looking for using the search box below or contact a member of our support staff for further assistance.". Both paths must return the same page and error code. Of course it follows from this that an IIS 404 error is not a problem - say a .jpg that isn't found. However SomeRandomPAgeThatAintThere.aspx must return a 500 internal server error, or an internal server error must return a 404 file not found until the patch is available. 
  2. "Add a random delay to your error page"This makes it harder to use timing to determine which failure reason occurred. As discussed below a better solution is to sleep a sufficient amount of time to make all errors take the same amount of time.
  3. "If you don't use the resource fetching capability - remove it" (this won't often be an option). Someone could still forge tokens and viewstate and the like however they wouldn't be able to steal the web.config. Hmm, would this allow them to create a forms authentication session token with an arbitrary user id? Possibly but it would be a much more difficult and expensive exploit.
For the MS FAQ see:

Now I've written this I hesitate on hitting publish...
It's surprising this hasn't been patched yet because when I looked at that bit of framework code with a colleague yesterday adding a try{ decrypt ) catch { throw 404 } instead of just allowing the exception to be thrown as a 500 should be an extremely low impact change to make that ought to close the door somewhat on the exploit - but then there's the issue of timing. 
Depending at what point the request fails it will take a different amount of time - adding a random sleep makes it far more difficult to determine which code path has been executed on a given request. This could be averaged out over a number of requests however it raises the bar in terms of the number of requests and complexity. The best solution to this would be to pad the time taken for all error messages to a round number of seconds - i.e. displaying any error always takes 3s or if more round to the nearest second.
On balance given the bad guys have the exploit code due to the hackers reckless disclosure (without any heads up to Microsoft to allow them to set the ball in motion with respect to fixes). I feel the greater good is served by web developers understanding the whole story and therefore properly understanding the proposed mitigations.

Happy to receive any comments or corrections.

Cheers
Andy

Acknowledgement
I found Troy Hunt's analysis useful in understanding this:
http://www.troyhunt.com/2010/09/fear-uncertainty-and-and-padding-oracle.html
Also the posts on Scott Gu's blog.

Here's some DotnetNuke specific info and a useful bit of history on the way the vulnerability was revealed:
http://www.dotnetnuke.com/Community/Blogs/tabid/825/EntryId/2799/Oracle-Padding-Vulnerability-in-ASP-NET.aspx

And some more general recommendations:
http://pentonizer.com/general-programming/aspnet-poet-vulnerability-what-else-can-i-do/

1 comment: