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.
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.
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
- http://tomcat.apache.org/tomcat-4.0-doc/catalina/docs/api/org/apache/catalina/authenticator/AuthenticatorBase.html
- http://tomcat.apache.org/tomcat-5.5-doc/catalina/docs/api/org/apache/catalina/authenticator/FormAuthenticator.html
- http://tomcat.apache.org/tomcat-4.1-doc/catalina/docs/api/org/apache/catalina/authenticator/SavedRequest.html
Code References
- http://www.docjar.com/html/api/org/apache/catalina/authenticator/AuthenticatorBase.java.html
- http://www.docjar.com/html/api/org/apache/catalina/authenticator/FormAuthenticator.java.html
Affected Servers
JBOSS, Tomcat, Glassfish
No comments:
Post a Comment