Request Method Can Matter

August 15, 2012 by · Comments Off on Request Method Can Matter
Filed under: Development, Security 

One of the nice features of ASP.Net is that many of the server controls populate their values based upon the request method.  Lets look at a quick example.   If the developer has created a text box on the web form, called txtUserName, then on a post back the Text property will be populated from the proper request collection based on the request method.  So if the form was sent via a GET request, then txtUserName.Text is populated from Request.QueryString[“txtUserName”].  If the form was sent via a POST request, then txtUserName.Text is populated from Request.Form[“txtUserName”].  I know, master pages and other nuances may change the actual name of the client id, but hopefully you get the point. 

GET REQUEST

txtUserName.Text = Request.QueryString[“txtUserName”]

POST REQUEST

txtUserName.Text = Request.Form[“txtUserName”]

Although this is very convenient for the developer, there are some concerns on certain functionality that should be considered. Think about a login form.  One of the security rules we always want to follow is to never send sensitive information in the URL.  With that in mind, the application should not allow the login form to be submitted using a GET request because the user name and password would be passed via the query string.  By default, most login forms will accept both GET and POST requests because of how the framework and server controls work.  Why would someone use a get request?  Automation?  Easy login, think for example if I craft the proper URL and bookmark the login page so it auto logs me in to the site.  Although not very common we, as developers, have to protect our users from abusing this type of flaw.  In no way am I saying not to use the server controls.. they are great controls. The point is to be aware of the pitfalls in some situations.

The good news!!  We just need to check the request method and only accept POST requests.  If we receive a GET request, just reject it.  Yes, a user can still submit the GET request, but it won’t authenticate them and defeats the purpose of the user.  Lets take a moment to look at some code that uses the default functionality.

Below is a VERY simple method that demonstrates how both GET and POST requests act.  Although it is not anything more than Response.Write calls, it is sufficient to demonstrate the point.

protected void Page_Load(object sender, EventArgs e)
{
       if (Page.IsPostBack)
       {
            // Encode the Value from the TextBox
            Response.Write(HttpUtility.HtmlEncode(txtUserName.Text));
            Response.Write(":");
            // Encode the value from the TextBox
            Response.Write(HttpUtility.HtmlEncode(txtPassword.Text));
        }
}

Here is a POST Request:

POST http://localhost:60452/Default.aspx HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Referer: http://localhost:60452/Default.aspx
Accept-Language: en-US
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: localhost:60452
Content-Length: 316
Connection: Keep-Alive
Pragma: no-cache
Cookie: .ASPXANONYMOUS=YQOZB3SHzQEkAAAANDE
4NDczYzctZjVmNi00ZWEzLWJkYTMtZDZjYmZhN
zY0Y2MylOoTQwCvDTUzA6LJuOO33witVabGXV
4RoXUeyg52RDY1; 
ASP.NET_SessionId=jkne3su2dwemw0lu1kqbjrdq

__VIEWSTATE=%2FwEPDwUJNjk1NzE5OTc4ZGRjhRm%2FtVkqPqFadKC
IAA0lQHoBH0FsR4xVM%2FiTGn7Sew%3D%3D&__EVENTVALIDATION=%2
FwEWBALk1bPACQKMrfuqAgKOsPfSCQKP5u6DCONM%2B3R6i2D%2
FIRsWvIhZp5wqldnzoa%2BjoUVRrng5kifu&ctl00%24
MainContent%24txtUserName=jjardine&ctl00%24MainContent%24
txtPassword=password&ctl00%24MainContent%24cmdSubmit=

The application will write out “jjardine:password” to the browser.  This makes sense since those two values were passed.  Now lets take a look at a GET Request and see the same request:

http://localhost:60452/Default.aspx?__VIEWSTATE=%2FwEPDwUJNjk1N
zE5OTc4ZGRjhRm%2FtVkqPqFadKCIAA0lQHoBH0FsR4xVM%2FiTGn7Sew
%3D%3D&__EVENTVALIDATION=%2FwEWBALk1bPACQKMrfuqAgKOsP
fSCQKP5u6DCONM%2B3R6i2D%2FIRsWvIhZp5wqldnzoa%2BjoUVRrng
5kifu&ctl00%24MainContent%24txtUserName=jjardine
&ctl00%24MainContent%24txtPassword=password&
ctl00%24MainContent%24cmdSubmit=

Again, this output will write out “jjardine:password” to the browser.  The big difference here is that we are sending sensitive information in the URL which, as mentioned above, is a big no-no.  We can’t stop a user from submitting a request like this.  However, we can decide to not process it which should be enough to get someone to stop doing it. 

It is important to note that any form that has sensitive information and uses the server controls like this can be vulnerable to this issue.  There are some mitigations that can be put in place. 

Check the Request Method

It is very easy to check the request method before processing an event.  The below code shows how to implement this feature:

protected void Page_Load(object sender, EventArgs e)
{
    if (Page.IsPostBack)
    {
        if (Request.RequestType == "POST")
        {
            // Encode the Value from the TextBox
            Response.Write(HttpUtility.HtmlEncode(txtUserName.Text));
            Response.Write(":");
            // Encode the value from the TextBox
            Response.Write(HttpUtility.HtmlEncode(txtPassword.Text));
        }
    }
}

Now, if the request is not a POST, it will not process this functionality.  Again, this is a very simplistic example.

Implement CSRF Protection

Implementing CSRF protection is beyond the scope of this post, but the idea is that there is something unique about this request per the user session.  As we saw in the GET request example, there are more parameters than just the user name and password.  However, in that example, these fields are all static.  There is no randomness.  CSRF protection adds randomness to the request so even if the user was able to send a get request, their next session attempt would no longer work because of this missing random value.

Large Data

So large data doesn’t sound like a lot here, but it is a mitigation based on the construction of the page.  If the viewstate and other parameters become really long, then they will be too large to put in the URL (remember this is usually limited on length).  If that is the case, the user will not be able to send all the parameters required and will be blocked.  This is usually not the case on login pages as there is usually very little data that is sent.  The viewstate is usually not that big so make sure you are aware of those limits.

 

Why do we care?

Although this may not really seem like that big of an issue, it does pose a risk to an application.  Due to compliance reasons related to storing sensitive information in log files, doing our part in protecting users data (especially authentication data), and the fact that this WILL show up on penetration testing reports, this is something that should be investigated.  As you can see, it is not difficult to resolve this issue, especially for the login screen. 

In addition to the login screen, if other forms are set up to support both GETS and POSTS, it could make CSRF attacks easier as well.  Although we can do a CSRF attack with a POST request, a GET can be deployed in more ways.  This risk is often overlooked, but is an easy win for developers to implement.   Happy Coding!!

ViewStateMAC: Seriously, Enable It!

February 1, 2012 by · Comments Off on ViewStateMAC: Seriously, Enable It!
Filed under: Development, Security 

I have been doing a lot of research lately around event validation and view state.  I have always been interested in how Event Validation worked under the covers and if it could be tampered with.  I will attempt to explain that it is, in fact, possible to tamper with the Event Validation field in a similar manner that view state can be tampered.  I know, the title of the post reads “ViewStateMAC”, don’t worry I will get to that.  But first, it is important to discuss a little about how Event Validation works to understand why ViewStateMAC is important.

__EVENTVALIDATION – Basics

Event Validation is a feature that is built into ASP.Net web forms.  It is enabled by default and it serves the purpose to ensure that only valid data is received for  controls that register valid events or data.  As a quick example, think for a moment about a drop down list.  Each value that is programmatically added to the control will be registered with Event Validation.  Code Example 1 demonstrates loading values into a drop down list (data binding is also very common).  Each of these values will now exist in the Event Validation feature.  When a user attempts to submit a form, or post back, the application will verify the value submitted for that control is a valid value.  If it is not, a not so pretty exception is generated.

Example 1
private void FillDDL2()
{
  ddlList.Items.Add(new ListItem("1", "1"));
  ddlList.Items.Add(new ListItem("2", "2"));
  ddlList.Items.Add(new ListItem("3", "3"));
  ddlList.Items.Add(new ListItem("4", "4"));
  ddlList.Items.Add(new ListItem("5", "5"));
}

__EVENTVALIDATION – Hash

Event Validation is primarily based on storing a hash value for each of the values it needs to check for validity.  More specifically, there is a specific routine that is run to create an integer based hash from the control’s unique id and the specified value (the routine is beyond the scope of this post).  Every value that gets stored in Event Validation has a corresponding integer hash value.  These hash values are stored in an array list which gets serialized into the string that we see in the __EVENTVALIDATION hidden form field on the web page. 

__EVENTVALIDATION – Page Response

When a page is requested by the user it goes through an entire lifecycle of events.  To understand how Event Validation really works, lets first take a look at how the field is generated.  Before the page is rendered, each control or event that is registered for event validation will have its value hashed (see previous section) and added to the array list.  As mentioned before, this can be values for a list control, valid events for the page or controls (for example, button click events), and even View State.  This array is serialized and stored in the __EVENTVALIDATION hidden field.

__EVENTVALIDATION – Post Back Request

The request is where Event Validation takes action.  When a user submits a post back to the server, event validation will validate the values that have been registered.  So in the Drop Down List example in "Example 1” Event Validation is there to make sure that only the values 1-5 are submitted for ddlList.  It does this by taking the value that was sent (Request.Form[“ddlList”]) and re-generates the numeric hash.  The hash is then compared to the list and if it exists, the value is allowed.  If it doesn’t exist in the de-serialized Event Validation list, then an exception is thrown and the page cannot continue processing.

__EVENTVALIDATION – Manipulation

De-serializing the Event Validation value is pretty easy.  This is very similar to how it is done for View State.  After writing my own tool to tamper with the Event Validation, I found the ViewState Viewer (http://labs.neohapsis.com/2009/08/03/viewstateviewer-a-gui-tool-for-deserializingreserializing-viewstate/) plug-in for Fiddler.  The ViewState Viewer plugs right into Fiddler with no issues.  Don’t let the name mislead you, this tool works great with the Event Validation data as well.  When you paste in your Event Validation string and click the “Decode” button, it generates a nice XML snippet of the contents.  The screen shot below demonstrates the Event Validation value and its decoded value from a test page I created.

Once you have the information decoded, you can now add in your own integers to the System.Collections.ArrayList.  Look closely and you might see that the last integer –439972587 is not aligned with the rest of the items.  This is because that is a custom value that I added to the event validation.  These numbers don’t look like they really mean anything, but to Event Validation, they mean everything.  If we can determine how to create our own numbers, we can manipulate what the server will see as valid data.  Once you have made your modifications, click the “Encode” button and the value in the top box will refresh with the new __EVENTVALIDATION value.  It may be possible to attempt brute forcing the data you want (if you can’t create the exact hash code) by just padding a bunch of integers into the list and submitting your modified data.  This is definitely hit or miss, could be time consuming, and would probably generate a lot of errors for someone to notice.  We are monitoring our error logs right?

__EVENTVALIDATION – Thoughts

Maybe just me, but I always thought that if I had event validation enabled, as a developer, I didn’t have to validate the data from drop down lists that was submitted.  I thought that this type of data was protected because event validation enforced these constraints.  This is obviously not the case and honestly brings up a very important topic for another day; “We shouldn’t be relying solely on configuration for protection”.  Although this post uses the drop down lists as an example, this has a much greater effect.   What about buttons that are made visible based on your role.  If event validation can be tampered with, now those buttons that didn’t exist, can have their events added to the acceptable list.  If you are not checking that the user has the proper role within that event, you may be in big trouble. 

So what types of attacks are possible?

  • Parameter tampering
  • Authorization bypass
  • Cross Site Scripting
  • Maybe More…

ViewStateMAC – Finally!!

Ok, it is not all bad news and finally we come to our friend (or worst enemy if it is disabled) ViewStateMAC.  Keep in mind that ViewStateMAC is enabled by default, so if this is disabled, it was done explicitly.  ViewStateMAC adds a message authentication code to ViewState obviously, judging by its name.   Basically, it adds a hash to the view state so that an attacker cannot tamper with its data.  So if we go back to the drop down list, if a developer uses code like Example 2 to access the selected item, then you have to be able to tamper the view state, otherwise the first item in the original item will get selected.  But if you use code like Example 3 to access that data, you could add your value to the EventValidation and get it accepted by the application.   Or can you?

Example 2
protected void cmdSubmit_Click(object sender, EventArgs e)
{
  Response.Write(ddlList.SelectedItem.Value);
}

Example 3
protected void cmdSubmit_Click(object sender, EventArgs e)
{
  Response.Write(Request.Form["ddlList"].ToString());
}

Actually, ViewStateMAC does more than sign ViewState, it also signs the Event Validation value.  I was not able to identify any documentation on MSDN that indicates this feature, but apparently __PREVIOUSPAGE may get signed with this as well.  I have run extensive tests and can confirm that ViewStateMAC is critical to signing Event Validation.  So in general, If ViewStateMAC is enabled, it protects both ViewState and Event Validation from being tampered with.  That is pretty important and I am not sure why it is not listed on MSDN.  Unfortunately, disable it and it creates a much greater security risk than initially thought because it effects more than ViewState. 

ViewStateMAC – Not a Replacement for Input Validation

In no way should a developer rely solely on ViewStateMAC, Event Validation and ViewState as their means of input validation.  The application should be developed as if these features do not even exist.  Drop down lists should be validated that only valid values were submitted.  Control events should be verified that they are allowed.   Why not use these features?  I am not saying that you should not use these features, but they should be in addition to your own input validation.  What if ViewStateMAC were to get disabled during testing, or for some other unknown reason?  Even if Event Validation is still enabled, it is not looking good.  Unless you have ViewState being encrypted, which would help block tampering with the view state, an attacker could still manipulate the event validation code. 

Conclusion

The details provided here have been high level with not much actual detail in actually manipulating the Event Validation field.  The post would be too long to include all the details here.  Hopefully there is enough information to make developer’s aware of the severity of ViewStateMAC and how Event Validation actually works.  From a penetration tester’s view, if you visit a .net webform application with ViewStateMAC disabled, this should be researched more to help accurately identify risk for the application.  Devs, please, please, please do not disable this feature.  If you are on a web farm, make sure the same machine key is set for each machine and this should be supported. Remember, these features are helpers, but not complete solutions.   You are responsible for performing proper input validation to ensure that the data you expect is what you accept.

The information provided is for informational and educational purposes only.  The information is provided as-is with no claim to be error free.  Use this information at your own risk.

« Previous Page