Common .NET ViewstateUserKey CSRF Issue

I’ve added the 2013BH tag to all posts related to my recent Blackhat EU talk – more posts are coming, and I’ll post the whole talk and finished whitepaper relatively soon. To understand this post, reviewing MVC .NET CSRF issues and Problems with triple submit cookies may be useful.

The most common advice for mitigating CSRF in .NET web applications is to set ViewStateUserKey to sessionID. This is an extremely common CSRF defense. At the time of this writing, something like this is present in the OWASP prevention cheat sheet as well as the Microsoft SDL. The following is a snippet from OWASP.

ViewState can be used as a CSRF defense, as it is difficult for an attacker to forge a valid ViewState. It is not impossible to forge a valid ViewState since it is feasible that parameter values could be obtained or guessed by the attacker. However, if the current session ID is added to the ViewState, it then makes each ViewState unique, and thus immune to CSRF.
To use the ViewStateUserKey property within the ViewState to protect against spoofed post backs. Add the following in the OnInit virtual method of the Page-derived class (This property must be set in the Page.Init event)
if (User.Identity.IsAuthenticated)
ViewStateUserKey = Session.SessionID; }

Unfortunately, this recommendation doesn’t always work for similar reasons to MVC. To clarify what the sessionID is: it is just a cookie, and it’s a cookie that isn’t always used for authentication. As already mentioned, most large scale sites tend to use custom authentication. Microsoft sites tend to use LiveID much more frequently than simple forms based auth. As should be obvious from the previous posts, if the sessionID isn’t used for authentication then this cookie can simply be overwritten by using an attacker cookie and an attacker ViewState. This attack is most useful with lateral escalation, meaning with one account on an application, you can CSRF other users of the application.

This is a super common problem in my experience. To illustrate this for Blackhat I wrote a sample app that’s not worth showing. It sets the ViewStateUserKey to the sessionID and uses ACS for authentication similar to how this tutorial describes (the only difference is this app uses Forms rather than MVC).

This pic shows the cookies sent immmediately after authenticating with ACS. Although ASP.NET_SessionId is automatically set, it has nothing to do with the authentication of the web application.

cookie

To understand how this attack works, perform the following steps on an ASP.net forms based application using ACS for auth.

  1. Create two users, user_victim and user_attacker where VIEWSTATE is used as a CSRF mitigation and ViewStateUserKey = SessionID.
  2. As user_attacker, capture the POST values. This will include several ASP.NET specific VIEWSTATE fields which are used to prevent CSRF. Also, capture the ASP.NET_SessionId cookie.
  3. As user_victim, replace the POST values with the values captured in request 2. This request will fail with a 500 (Validation of viewstate MAC failed), because ViewStateUserKey = SessionId. Otherwise, this could be used to exploit classic CSRF.
  4. However, if we cause the application to consume user_attacker’s ASP.NET_SessionId cookie rather than user_victim’s cookie, the request will go through.

In terms of exploitability, this is again equivalent to naïve double submit. An attacker needs the ability to write cookies (e.g. find XSS in a neighboring sub domain or be in the middle), but in many cases this is exploitable.

There are several ways to mitigate this. The most straightforward is to, after authentication, set the ViewStateUserKey to the cookies actually used to tie the user to a session. In the example above, ViewStateUserKey could be set to the FedAuth cookie. Unfortunately, this type of thing can be difficult to tie into the framework or detect with static analysis tools because these things have no way of knowing how exactly custom authentication works.

5 Responses to Common .NET ViewstateUserKey CSRF Issue

  1. Murali says:

    How about encrypting Session ID with a server secret key and using the encrypted value as the viewstateuserkey?

    • It shouldn’t matter. Assuming the key is constant between users and “session” is not used for auth, then $attacker could just replace $victim’s values as his own and the application has no way to distinguish.

  2. Pawel says:

    What should be done in a scenario with forms authentication? If attacker hijacks formsauth cookie, wouldn’t it be the same case like hijacking the ASP.NET_SessionId?

    • It should be ok if you set viewstateuserkey to sessionID in forms auth because in that case ASP.NET_SessionId is used for auth. It’s important to understand writing a cookie is a lot easier than reading a cookie. So if the victim uses forms auth, then yes, an attacker could still write over the sessionID cookie, but at that point the victim is no longer logged in and CSRF isn’t as relevant.

Leave a comment