Sunday 17 February 2013

Insecure Postback based authentication flaw in ASP.NET

Introduction


POSTBACK in ASP.NET is an event that occurs whenever an action is performed by a control in the ASP.NET page. For example clicking a button on a ASP.NET form/page, causes data in that form or page to be “posted back” to the server.  This is an event and can be tracked or detected using the “IsPostBack” property of the ASP.NET page. This property simply allows the developer to check if the page is “called” or “refreshed” as a result of a control event OR it is accessed for the first time. Button clicks, dropbox selection changes, or textbox changes, are all examples of control events in ASP.NET. It returns “true” in the case of a control event  and “false” in case it is accessed via link.


Often developers use this property to be able detect first time loading of an ASP.NET form, to initialize key variables like connection strings, or populate ASP.NET controls with field values.


// Only if the form is accessed without raising a control event like a button click or selecting from a dropbox

If (!IsPostBack)  

// Initialize variables, controls 

}

What happens if the developer mixes authentication check code into the above logic?

Take a look at the code snippet below, it checks for authenticated request within the if condition, which checks for the IsPostBack property. This would mean that the authentication check implemented within the if condition of IsPostBack propery, will be “called” or “executed” only if the form is “accessed” the first time via a link, and would not happen if someone invoked a control event.


How does the flaw manifest?

A flaw of this kind manifests from the assumption that developers make while coding, that a form will be accessed by an authenticated user first time by clicking on a link. Developers assume that once the form is presented or accessed, only then forms can be submitted and/or control events can be invoked.

Just think about it, what will happen if instead of requesting for a page/form a user tries to directly submit a form?

If an attacker happens to send any POST request for an internal action, for instance, to create a new employee, along with parameters and the control event to invoke, The execution flow will not enter the “IsPostBack” if condition path above, and therefore will not execute or run the authentication check. This is the root cause of the vulnerability. A simple POST request would easily bypass authentication check implemented above.

2 out 5 ASP.Net applications I have audited seem to have this flaw in a few sections dealing with form submissions.

How do we exploit this flaw?

In our scenario an application allows only admin users to create new employees.  The typical exploit involves, sending a POST request simulating a form submission that creates an employee, along with all the parameter names, and control event name like button.

Let’s see how it is done.

Exploit Steps

Try accessing the internal page, CreateEmployees.aspx without login. 


We are instantly redirected to a “Not Authenticated” page, this is expected.

We do not stop here! Lets send a HTTP POST request loaded with parameters directly to the form!

Now to understand the create employee feature, lets login and create an employee.

Access the create employee form after login.


Create a new employee record.


Observe the HTTP POST request patent via Web Proxy tool. You can see that along with textbox fields, control event name i.e name of the button is also sent.


As shown a new employee record is created.


Now lets see if we can create an employee without authentication.

Access the login page


Change the login request to a HTTP POST request with appropriate parameters to the createemployees.aspx page.



Notice we are attempting to create an employee “HackEmp”.

This will trigger a PostBack event, and will render the “IsPostBack” property to true. Notice the code writtin in the Page_Load event of the form. The Page_Load event is always called every the page is requested from the browser.


Since the HTTP POST request, would cause the IsPostBack property to be rendered to true, it would effectively bypass authentication check present in it. 

This would mean that the employee would get added into the database!

Lets us now login to the application as a legitimate admin user, and check the View Employees section to see if out employee got added.


And lo! And behold! We created an employee without having to login to the application.
From this we can conclude that combining the authentication check, alongwith the IsPostBack  property resulted in a loophole in the authentication mechanism for the web application. Even though the form is not accessible, it is still possible to invoke the corresponding control event, like a button click code.

Recommended Fix

Since this flaw resulted from the assumption made by developers to check for authentication alongwith the IsPostBack property check, the obvious recommendation is to treat the two as independent conditional checks. In other words the IsPostBack Property check, used to initialize variables when the form is loaded for the first time,  should be in an independent if condition within Page_Load Method. The Authentication check, used to verify if the request is from an authenticated user, must be another independent user.

The fix should ensure that the authentication check must always be called under the Page_Load event of ASP.NET page.


References


Page.IsPostBack Property - 
http://msdn.microsoft.com/en-us/library/system.web.ui.page.ispostback%28v=vs.71%29.aspx

Versions Affected
NET Framework 1.1 and above




J2EE Container Managed Authentication Security Flaw


Introduction

Web applications also often rely on the web servers for authentication. The container managed authentication is a well-known concept in J2EE, which kicks-off just with a simple configuration of “security-constraints” in the deployment descriptor i.e. web.xml file.

The container is responsible to block unauthenticated access to the internal resources of the application, as configured in the “web.xml” file. But we have discovered that a user can still perform any internal operation of the application without authentication by exploiting a severe flaw of the J2EE based web containers.

Let’s understand the flaw.

Before we dive into the flaw, let’s look at how does the container managed thing work in J2EE applications.

A figure given below explains it in detail.


 

Here, whenever a user requests for an Internal page of the application, the user gets redirected to a login page and only after authentication the requested page is served to the user.

So, what is the catch here? Isn’t it normal to behave that way?
It is perfect to behave that way, but have you ever wondered how does the server redirect the user to the page requested before authentication? Don’t you think the server might be maintaining the copy of the previous request somewhere? We have already shown it to you in the figure above that it does it in the session object.

Bingo! That’s exactly the catch :)

In Tomcat, the container managed authentication is handled by
 “org.apache.catalina.authenticator.AuthenticatorBase” class and it subclasses depending on the type of authentication being configured.
It has 5 subclasses:
  • BasicAuthenticator
  • DigestAuthenticator
  • FormAuthenticator
  • NonLoginAuthenticator
  • SSLAuthenticator
      Out of which “FormAuthenticator” is our interest as it comes into action for Form based authentication, which is widely seen in web applications.

It all starts with the “invoke” method implemented in the “AuthenticatorBase” class. The “security-constraints” configuration of web.xml is considered as a valve by the web container. And every valve has to have an “invoke” method as specified by the “ValveBase” class, super class of “AuthenticatorBase” class.
So, whenever a request is received by the server, and if the container managed authentication is enabled (i.e. “security-constraints” is set) one of the subclasses of “AuthenticatorBase” based on the authentication type is initialized and its “invoke” method is called.
The “invoke” method then calls the “authenticate” method to authenticate the request.

The definition of “authenticate” method of “FormAuthenticator” class is shown below.

Observe that it checks if the incoming request is a login request i.e. whether the request is posted to the URL – “j_security_check”. If no, it saves the request using the “saveRequest” method and displays the login page to the user. That means the user must be authenticated to the site before accessing any of its pages. However, we will see shortly how it can be bypassed.

public boolean authenticate(Request request, Response response, LoginConfig config)throws IOException {
boolean loginAction = requestURI.startsWith(contextPath) && requestURI.endsWith(Constants.FORM_ACTION);
// No -- Save this request and redirect to the form login page
        if (!loginAction) {
               session = request.getSessionInternal(true);
               if (log.isDebugEnabled())
        log.debug("Save request in session '" + session.getIdInternal() +"'");
try {
        saveRequest(request, session);
} catch (IOException ioe) {
        …
}
forwardToLoginPage(request, response, config);
       return (false);

The definition of “saveRequest” method is shown below. Observe that it retrieves all the information of the request and saves it in session. It initializes an object of the class – “org.apache.catalina.authenticator.SavedRequest” for storing the request information.

Note an important point here that in case of a POST request, even the body of the request containing the request parameters is also saved.

protected void saveRequest(Request request, Session session) throws IOException {

// Create and populate a SavedRequest object for this request
            SavedRequest saved = new SavedRequest();
               ...
             if ("POST".equalsIgnoreCase(request.getMethod())) {
                 ByteChunk body = new ByteChunk();           body.setLimit(request.getConnector().getMaxSavePostSize());
                 byte[] buffer = new byte[4096];
                 int bytesRead;
                 InputStream is = request.getInputStream();
                 while ( (bytesRead = is.read(buffer) ) >= 0) {
                     body.append(buffer, 0, bytesRead);
                 }
                 saved.setContentType(request.getContentType());
                 saved.setBody(body);
             }
             saved.setMethod(request.getMethod());
             saved.setQueryString(request.getQueryString());
             saved.setRequestURI(request.getRequestURI());
             // Stash the SavedRequest in our session for later use
             session.setNote(Constants.FORM_REQUEST_NOTE, saved);

Once the user is authenticated the user is then redirected to the previously request saved URL.
When this time the request comes to the server, it checks if the saved URL matches the requested URL using a “matchRequest” method and in case of a match it restores and processes the previously stored request from the session.

How does this flaw manifest?

We already asked you to note an interesting fact before, and that is in case the pre-authentication request is POST, even the request body is stored at the server.
So, here in this case, if an attacker happens to send any POST request for an internal action, for instance, to add a new user in the system, from a victim user’s browser. When the victim user comes and logs into the application using the same browser the internal operation will get carried out without the knowledge of the user.

So, that’s the flaw, the server stored the request information sent before authentication along with all the request parameters, which can be easily exploited using technique shown in the section below.

Exploit Steps

Consider a dummy social networking application called – “Facehook” that has uses container managed authenticated. Observe that it has configured “security-constraint” in “web.xml” and all the pages within the directory - “site” has been declared as protected. That means container’s authentication process will come into play whenever a user tries to access any of those pages. 



Let’s see how we can exploit it.

Create a HTML page – that can send a request to an internal page of the application – i.e. “site/Updation.jsp”. This page is used to allow users to edit their profile information. Craft the page such a way that it can send the profile change request whenever the page loads using a javascript, without the knowledge of the user accessing it. A sample code is shown in the figure below.


The malicious page designed above is displayed on the browser as shown below.



The above HTML page can then we used to victimize users of “Facehook” applications and edit their profiles, without their knowledge.

Let’s see how this would work.


If a victim user opens this page, the profile change request will go to the server. A snapshot of the captured request is shown in the figure below. 


This being an internal request, the server redirects the user to the login page of the application.


Since, our page had misled the user in believing that in order to obtain promotional benefits from “Facehook” application, he must login to the application. The user continues to log into the application, as shown below.


The captured login request is shown in the figure below. Observe that the request is being posted to “j_security_check” that invokes the containers authentication process.



Once, the user is authenticated, the server redirects the user to the profile update page, to which the initial request was sent by the malicious page, without the knowledge of the user.



With this redirection request, the profile change request gets processed for the user and users account thus gets hacked, as shown in the figure below.


Here, the victim user did not intend to send any profile change information, he had just logged-in to the application to receive the new promotional benefits from the “Facehook” application, as indicated by the malicious page. But the user got tricked, because of the container managed authentication flaw.

The server had saved the previous profile change request sent by the malicious page and when the user logged-in to the site, it executed that request. Similarly all the users of applications having container managed authentication can be tricked into performing internal operations without their knowledge. 

Similarity with CSRF

Though the way this container managed flaw can be exploited might be similar to Cross-Site request forgery but the concept is different. Unlike CSRF in this case we don’t need any pre-logged in session of the user, the only requirement is that the user must login after the malicious unintended request has been sent to the server using the same browser. It can also be carried out my local attackers who are situated locally in the same premise as the victim, and who have got access to victim’s browsers. They can forge a request from the victim’s browser, and keep the login page open on the victim’s browser. When the victim logs in, the malicious request will get executed under his login privilege.

Recommended Fix

The solution is to ensure that all actions that result in change or addition of data, transactions etc, within application, should be accompanied with a random token.  All HTTP requests that cause change in data should carry a random and unique token, that should verified by a servlet filter or the processing servlet before making any changes to the application.
This will prevent any attacker from forging a HTTP request and duping legitmate users into executing such a request.
Always use POST request to modify or delete data on the server
Use an token along with every POST HTTP request
  •       Token should unique for each user session
  •       Token should be random making it difficult to guess
  •        Should be always validated at the server

References
Code References
Affected Servers
JBOSS, Tomcat, Glassfish