As a professional penetration tester, there are many features of an application that are similar across all languages. Unfortunately, just understanding general web concepts is not enough to fully test an application. This is due to the fact that each language, framework and implementation is different. They all have their own unique features that are outside the default web protocols. In this post, I am going to focus on ASP.Net WebForms, more specifically, how server controls handle GET and POST requests. I will not focus on server-side code, but rather on how the requests are sent and processed.
ASP.Net server controls are very popular with developers that use web forms. These controls are provided by the framework and cover items such as TextBox, DropDownList, HyperLink, Button, etc. From a developer standpoint, these are nice because they give a nice object oriented feel to web development. In addition, they automatically populate their properties based on the Request Method (GET or POST).
Microsoft has done a great job of creating a platform that allows developers to create robust applications without knowing how the framework is really implemented. Good for developers to get applications to market quickly. Great for penetration testers as developers may not understand the implications of how they implemented functionality.
So what is the issue? There are a few things we need to think about. One of the core tenants of security is to not send sensitive data via the URL. This information could be logged on the server or on proxies along the way. Let's look at an example, a simple log-in form. The user enters their user name and password and submits it for authentication. By default, this would be submitted using a POST request, as expected and desired. Obviously, the password is sensitive information, and the combination with the user name is really sensitive. What if the user/attacker decides to submit that same request, but as a GET, moving all the POST parameters into the querystring? Is it successful? If so, we could have a problem here.
In addition to the storing of sensitive information, CSRF also comes to mind. Sure, CSRF can be performed with POST requests, but we have way more options if we can send them as a GET. Phishing emails are much easier as just a link, vs a form to post.
It is pretty simple to test this in an ASP.Net application, and really any other application. I know I said this is focused on ASP.Net but depending on how a developer has implemented their code, it could be available in other applications as well.
The first step is to make the original POST request and capture the request. This is similar to how we would go about testing CSRF. Once we capture the request we need to check a few things.
Next, look to see if there are CSRF protections. This could be a big limiting factor and allow us to stop wasting any more time. If there are not any CSRF protections (Randomness to the parameters) then look at the size of the content being submitted. GET requests are much more limited on the amount of data that can be sent.
In ASP.Net, the __VIEWSTATE parameter is going to be the biggest killer here. WAIT!!! there may still be hope. If __VIEWSTATE is really large, check the version of .Net that the application uses. Most developers leave this in the headers. If it is Version 2.0 AND you do not see __EVENTVALIDATION in the parameter list, try submitting an empty view state parameter (__VIEWSTATE=). You can try this in version 4.0 as well, but if the developer has set ViewStateUserKey then the request will fail. Honestly, ViewStateUserKey in 4.0 is a CSRF mitigation and in this situation, could block this from working (outside of your current session).
If you are at the point where you can submit the GET request, make sure you have all the parameters that are required. Depending on the version of the .Net framework and configuration settings depend on what is really needed. Make sure you test this in the scenario you are focusing on. For example, if this is for CSRF, test it in another session. If it is just to see if the log-in supports GET requests, log out, and start a new session to make sure nothing is hanging around.
Lets take a look at a sample 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=
Looking at the sample POST we see that there are no random parameter values, like a NONCE. The application is using __EVENTVALIDATION, but that is ok. Event Validation and ViewState by them selves do not guarantee uniqueness and nothing prevents replay of these values.
The following shows the above POST converted to a GET (just the URL is shown):
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=
Notice that we have all of the same values as the original post. If this is a log-in screen, this shouldn’t work. However, in many cases it works just fine. We should look at log-in forms to ensure that they are blocking GET requests for the actual authentication process. I have posted information from a .Net development standpoint on http://www.jardinesoftware.net/2012/08/15/request-method-can-matter/.
Although this doesn’t seem like a major vulnerability, it is something we should be aware of and checking for during our tests. It is simple to check and usually a pretty simple fix by development teams. It helps protect companies from storing sensitive information in their logs as well as prohibiting users from storing log-in links like this as a bookmark to bypass having to log in every time they want to access the application.