Sep
22
2010

As I mentioned in my last post, I have a solution in the works to prevent the Padding Oracle exploit from working against the FormsAuthentication tickets & cookies.  The workaround adds a handful of security-related features that might be interesting to you.  I plan to expand on it to add other modules relating to web security.

The EnhancedSecurityModule adds server-side state to ASP.NET's FormsAuthentication subsystem. This drop-in module works with most ASP.NET applications, breaks the Padding Oracle exploit, and paves the way for new features such as kicking, banning, and restricting users to a single IP.  Check it out:

http://sws.codeplex.com/

For a high-level understanding of how it works, check out the project page.

How the EnhancedSecurityModule Works

The EnhancedSecurityModule attaches to the BeginRequest, EndRequest, and Error events of each request.  The FormsAuthenticationModule does it’s job somewhere in the middle during the AuthenticateRequest event.  My module compares the state of the request before FormsAuthentication kicks in to the state of the request just before the response is sent to the user. If it detects that a FormsAuthenticationCookie was issued in between those two points, the current request must have been the postback of the login form (or execution of the Login action) that resulted in a successful login. It intercepts the FormsAuthenticationCookie and FormsAuthenticationTicket on their way out to the user and records all information about both (along with the IP address of the client) in a record on the server. It assigns a randomly generated GUID to the record & computes a salted SHA-512 hash of the FormsAuthenticationTicket’s properties, concatenates them and stuffs them in the UserData field in the FormsAuthenticationTicket. It rebuilds the FormsAuthenticationCookie and replaces the outbound cookie with the new one.

At the beginning of each subsequent request, the StatefulFormsAuthenticationModule attempts to extract the FormsAuthenticationTicket from the FormsAuthenticationCookie.  If it finds one, it attempts to parse the UserData field, separating the SHA-512 hash from the GUID used as a key to the server-side store of information about the cookie and ticket.  If the hash matches, it attempts to retrieve all of the information about the cookie and ticket that the server knew to be factual when it issued them from the server-side store.  If it finds the information, it cross-validates just about every property in the FormsAuthenticationCookie, FormsAuthenticationTicket, and even the current request state (IP address) against the information stored on the server.  If everything matches, the module allows the FormsAuthenticationCookie to continue to exist.  The FormsAuthenticationModule's AuthenticateRequest event handler kicks in and establishes the identity for the request, the authorization subsystem operates on the assumption the identity is correct, and all is right with the world.

If anything breaks down in the validation process, the server attempts to destroy the FormsAuthenticationCookie on the client’s browser, removes the cookie from the Request’s Cookies collection, and forces the request to run anonymously. Even if the client maliciously refuses to destroy the cookie, it is removed from the processing pipeline at the beginning of each and every request.

Additionally, there are some posts out there claiming that users can download web.config via a padding oracle attack on WebResource.axd.  The module looks for CryptographicExceptions on requests dealing with .axd files, introduces a random, configurable delay, and returns an HTTP 200 response telling the attacker to go away.

I’m still doing analysis on the exploit tools that I’ve seen and will try to adapt the module to stand in the way of any attack vectors I’ve found.  If I’ve missed a spot, please let me know and/or submit patches.

About the Project

The StatefulFormsAuthenticationModule project is a derivative work of another FormsAuthentication-related project that I’ve been working on for the past few years, the .NET client for JA-SIG’s CAS authentication system (http://www.jasig.org/cas). I’ve been hacking away at FormsAuthentication since 2008.

When I first read about the Padding Oracle exploit, I began analyzing the DotNetCasClient code and realized that one of the features I added to it theoretically eliminated the FormsAuthenticationTicket attack vector (the ServiceTicketManager). Given the potential widespread impact of the problem, I decided to strip that aspect of the code away from the CAS module and create a free-standing module that protects FormsAuthentication users regardless of whether they are using CAS.  If you happen to be looking for a rock solid, cross-platform Single Sign-On solution, check it out.

Sep
21
2010

Last Friday, a vulnerability was disclosed at the Ekoparty Security Conference which exploits a bug in the .NET Framework's cryptographic classes.  Apparently, the bug compromises the security of the ViewState and FormsAuthentication subsystem in ASP.NET along with several other crypto-related features in the .NET Framework. 

What you’re about to read is based on what I know about FormsAuthentication and what I saw in a 4 minute YouTube video that will probably get yanked any day now due to the poor choice of background music.  If this video turns out to be BS, you can assume that my foot is in my mouth and move along.  Otherwise, pay attention.  I’ve got a pretty good solution in the works and I’ll post what I’ve got on CodePlex under the Apache v2.0 license in a day or so.

Apparently, tools have shown up in the wild that allow an attacker to break into web applications protected by FormsAuthentication, allowing the attacker to assume the identity of any known user on the system.  Due to the architecture of the FormsAuthentication subsystem, the attacker does not need to know the password of the user and might not even see a login screen.  Since FormsAuthentication operates at such a low level in ASP.NET, the same exploit would theoretically work for WebForms and MVC applications in ASP.NET.

Understanding FormsAuthentication and ASP.NET Authorization

When an anonymous user requests a resource on an ASP.NET application for which anonymous access is not allowed (i.e., by web.config's [location/]authorization/deny rules or MVC's [Authorize] attribute on an action), the user is redirected to a login page where they are asked to supply credentials.  If the user authenticates successfully, a FormsAuthenticationTicket is created on the server containing, among other things, the username of the user.  The FormsAuthenticationTicket is serialized, encrypted, encoded as a string, stuffed in a cookie (the FormsAuthenticationCookie) and stored on the user's browser.   The user is then redirected back to the originating page or action.

With each subsequent request, ASP.NET detects the presence of the FormsAuthenticationCookie.  It attempts to convert it back into a FormsAuthenticationTicket by decoding the string representation, decrypting it, and deserializing it.  If that process is successful, it then ensures that the user did not maliciously tamper with the FormsAuthenticationCookie's path or expiration properties by comparing them to the values embedded in the FormsAuthenticationTicket during the login step.  This is how FormsAuthenticationTickets are validated.  If the ticket validates, the User.Identity and Thread.CurrentPrincipal are set to a GenericPrincipal containing the username from the FormsAuthenticationTicket and the request is authenticated—the system believes that you are who you claim to be.  Now the various authorization mechanisms in ASP.NET (UrlAuthorization, MVC action authorization, and code access security) can determine what privileges you have.

This process is repeated for every single request: WebForm pages, MVC actions, web services, handlers, javascript files, images, stylesheets, etc. (Not 100% accurate for static content on IIS 5/6).

How the Padding Oracle Exploit works

I'm not a mathematician and I don't have a deep understanding of the inner workings of the crypto classes in .NET.  In fact, only a fraction of those classes are even written in managed code. The rest of them call into unmanaged libraries to get the job done. Like most users, when I needed to do encryption or decryption in .NET, I (re-)learned the API and treated it like a black box.  I knew it worked and I assumed it was secure.  Apparently, so did Microsoft.

The Padding Oracle exploit involves the side-channel release of information via the browser that allows an attacker to learn the web server's decryption key over a series of carefully crafted requests.   This makes it possible for the user to decrypt a FormsAuthenticationCookie and read a FormsAuthenticationTicket on the client side.  This is not supposed to be possible... but it's not the end of the world.  The real risk would be if a user figured out a way to modify their FormsAuthenticationTicket & FormsAuthenticationCookie or to create new ones from scratch and still get past the server’s validation. 

Enter CBC-R Encryption.   I don't know how it works either, but with code in the wild, it doesn’t really matter. The gist of it is that when you have a padding oracle like the one exposed by the bug, you can convert the decryption key you just discovered into an encryption key.

Padding Oracle + CBC-R defeats FormsAuthentication

Before the compromise of the cryptographic classes, it was safe to assume that a user would be unable to maliciously tamper with the FormsAuthenticationCookie's value without breaking the server's ability to decrypt the bytes.  After all, changing just one byte of an encrypted message makes decryption of that message impossible.  However, the exploit makes it possible to both decrypt and encrypt messages on the client side that are compatible with the web application's machine key.  Now all the attacker needs is a username with decent privileges and they are in.

To recap, an attacker:

  • Finds the decryption key by way of the padding oracle exploit
  • Creates a matching encryption key by using CBC-R
  • Creates a FormsAuthenticationTicket on their computer with any username, expiration date, cookie path and persistence setting they want.
  • Serializes, encrypts, and encodes the FormsAuthenticationTicket
  • Sticks the encoded string into a standard HTTP cookie
  • Passes the cookie to the server they stole the keys from.
  • The server decodes the string, decrypts the bytes, deserializes the FormsAuthenticationTicket, and authenticates any requests containing that cookie as the username in the ticket (assuming that user has access to the system).
  • The authorization subsystem determines what they can do on the system based solely on the username.

The attacker was never prompted to enter a password and your application is compromised.

The Second Weakest Link

The crypto bug is the biggest weak point, but its exploitation is made possible by the fact that the server doesn't keep track of the FormsAuthenticationTickets it generated.  The security of the entire authentication subsystem is based on the assumption that if your application encounters a FormsAuthenticationCookie that it can decrypt into a FormsAuthenticationTicket, it must have created them both and the user presenting them must be the user whose name appears in the FormsAuthenticationTicket.

I've got a simple fix for this problem in the works.  As I said earlier, I'll post it to CodePlex as soon as I can.  It doesn't cure the padding oracle problem, but it eliminates the ability to create or modify FormsAuthenticationTickets on the client and it paves the way for a few other cool features.

Written by scottt732
Sep
21
2010

I spent about a month getting this blog set up and the following year trying to decide what to write about. ;-)   It looks like it’s going to be .NET.  I’m Scott Holodak.  I work for Princeton University as a web developer and spent about 5 years doing the same at Rutgers University before that.  I know both institutions would be disappointed for this, but I’m about to blatantly plagiarize from Scott Hanselman and say “the opinions herein are my own personal opinions and do not represent my employer’s view in any way”.  I have a BS in Computer Science and Cognitive Science from Rutgers and I’ve been doing a ton of C# & database development dating back to the 2001 release of .NET 1.0 beta.  If you don’t know what that means, this blog is going to be chock full of incredibly boring & confusing nonsense.  If you do know what that means, I can’t make any promises.  Drumroll….