Saturday, April 25, 2009

More on Blacklisting and XSS

Following up on my last post, another scenario where blacklisting of angle brackets doesn't work to stop XSS is where untrusted data is output into an existing section of script. Consider a JSP application that takes a URL parameter and outputs it within opening and closing <script> tags. If encoding is not being done, which it often isn't, then an XSS attack would be possible. An attacker would simply close the previous executable line of script with a semicolon and immediately follow that with his malicious script.

An example of how this might occur is shown below. A JSP defines a JavaScript function called "gotoPreferences()", which causes the browser to re-navigate to a URL ("prefURL"). Note that prefURL is constructed dynamically by incorporating untrusted data -- the "category" parameter.

<script type="JavaScript">
function gotoPreferences()
{
var prefURL="https://www.server.com/prefs.jsp?category=" + <%= request.getParameter("category") %> + ";"
location.href=prefURL;
}
</script>

To exploit XSS, an attacker might set the value of "category" to:

"";location.href="http://www.evilsite.com"


The resulting line in the HTML would then be:

var prefURL="https://www.server.com/prefs.jsp?category=" + "";location.href="http://www.evilsite.com";

When the function was called, the victim would be navigated to the attacker's site instead of the expected URL.

Thursday, April 23, 2009

Blacklisting and XSS Failures

Looking at a financial application, I was somewhat surprised to see blacklisting of angle brackets used as the main countermeasure against cross-site scripting. Stripping angle brackets or throwing an error every time you see one in a request is not sufficient protection against XSS. There were a couple of ways to exploit XSS in this application despite the rigorous rejection of angle brackets.

One attack vector was due to a page that accepted a parameter containing a relative URL to another page in the application. It ended up being a tailor-made way for someone to avoid the blacklisting altogether. When a request was received, this special page caused a forward (not a redirect) to the specified URL. The code that rejected angle brackets was hit on the request from the client only, and not when the forwarding was done. Therefore, "<" and ">" could be slipped in by double URL-encoding them in the initial request as follows: %253C and %253E (%25 is a URL-encoded percent sign).

The other XSS attack vector was via JavaScript event handlers. This technique is available when user-supplied data is output to the attribute list of a page element. This often happens for an <input> tag, where the HTML form has multiple text inputs. The user enters the data, but one or two values may be invalid, so the application returns the same page and prepopulates the values that were entered. An attacker can employ XSS by closing off the previous attribute with a double-quote (assuming double-quotes were used) and injecting an event handler such as onMouseOver, onFocus, or onClick.

An example injection is: x%22%20onFocus%3d%22alert('xss')%22%20%22. The resulting HTML might look like this:

<input type="text" name="val5" value="x" onFocus="alert('xss')" "">

Notice there were no angle brackets used in this attack, so blacklisting those characters did not provide any protection.

Friday, April 10, 2009

Just Say No to Forced Password Changes

Don't force web application users to change their passwords. Instead, require strong passwords from the outset. I feel sorry for users when I see strong password requirements with forced password change after a certain time. The aggravation and inconvenience for users is not worth the trouble. In fact, for web applications, forcing passwords changes may actually increase the chance that passwords will be compromised. The reason is in how a brute force attack is done: a malevolent person with a valid username systematically tries every possible password combination hoping to get a hit. Depending on password complexity, it could take decades or longer.

Let's say the minimum password length for a web application is 8 characters, and a certain user has chosen an initial password of "muiylmo9". Now assume a slow brute force attack on that user's account is launched, where a password of "aaaaaaaa" is tried, then "aaaaaaab", then "aaaaaaac", and so on. Some time after this attack begins, the user is forced to change his password and he chooses "aciylmo9". The result? The user's password is now more likely to compromised earlier on in this attack. The user's account would have been more secure if the password had never changed. This might be a simplistic scenario, but I think it demonstrates the dubious nature of it all.

Forced password changes make more sense when passwords are stored in a file of some sort (e.g., Apache HTTP Server or Windows) that could be stolen and brute-forced via rainbow tables in offline mode. If a password is cracked in that scenario, the account may still be safe if the user has changed his password.