Reflected XSS

Reflected XSS

TL;DR Logout early, logout often.

Having found a number of reflected XSS issues in third party products in the last couple of weeks, including accidentally stumbling on one in the popular WordPress plugin wp-supercache (you’ve upgraded to the latest wp-supercache already, right?), I’ve had some interesting discussions with software vendors.

Donncha, an experienced WordPress plugin developer, just got on with fixing the issue in wp-supercache, but some other vendors suggested that because their users use browsers with protection that perhaps they don’t need to fix reflected XSS issues.

I hope here to explain the current situation with reflected XSS defences in browsers, and explain why it is still necessary to fix reflected XSS vulnerabilities.

What is reflected XSS (Cross Site Scripting)

Reflected XSS is one of the easiest web security issues to understand.

Someone supplies or persuades your browser to follow or open a link thus making a request, that contains some JavaScript. Usually the JavaScript is in the URL, but it can occur in anything the attacker can control.

This JavaScript is then returned (reflected) in the content of the web server’s response by a fault in the coding of the web page (or web server), allowing an attacker to run their own JavaScript in that page letting them steal credentials or data.

In practice they don’t usually send you a link saying:

http://www.example.com/“><script>SomeNastyJavaScript();</script>

They typically send you a plausible looking link which is then redirected to the dodgy link, much as you might redirect someone from “http://www.example.com” to “http://example.com”, or be redirected when using a URL shortener (think about that a bit). Sometimes the redirecting server tries to figure out what will work best against your browser and redirects to different places for different browsers, or the redirector is part of a hacking toolkit for which reflected XSS is just one method it can choose to bring you a bad web experience. Those one line URL emails you knew weren’t really from your friends who use Yahoo! Mail, that appear to redirect you to weight-loss sites, are just such links, don’t click them.

A currently live example courtesy of Watt at www.xssposed.com

http://www.dailymail.co.uk/registration/loginError.html?username=%22%20onmouseover=alert%28String.fromCharCode%2869,120,97,109,112,108,101,32,88,83,83%29%29%20%22

Cut and pasting this link into Firefox and trying to enter the email field will result in a pop-up.

Daily Mail XSS exploit Firefox

Chrome will sanitize the pop-up, but the original exploit by Watt shows that CSS changes are still applied even if the JavaScript in the onmouseover event is detected and removed. This could be used to fool people even if the XSS itself has been blocked.

http://www.dailymail.co.uk/registration/loginError.html?username=%22%20style=background:url(https://www.xssposed.org/images/design/xssposed-logo.png);left:-350px;top:-300px;height:1000px;width:1000px;position:absolute%20onmouseover=alert(String.fromCharCode(88,83,83,80,79,83,69,68))%20%22

Sanitized XSS vulnerability

As you can see the site is almost unreadable in Chrome.

We’ll leave the issue of the Daily Mail login page being served over HTTP for Troy Hunt to explain to them.

In the Daily Mail example Watt opens a dialog, but it could equally attempt to grab the username, and password when entered, although there are some restrictions on the code that can be “injected” into this page, we are getting into the ingenuity of would-be attackers, and away from the intended behaviour of the webpage.

Staying Safe

The simplest step most people can take is to logout when they have finished with a web application, especially things like banks or brokers. This typically means you’ll be presented with an unexpected login screen for your webmail rather than having your webmail contacts spammed. Also don’t login at unexpected login screens, as some will redirect you back to the original URL with the attack intact after login is completed. If you have a password manager like 1Password, LastPass, or even the browser’s own, logging in again should not be a chore, so like voting; logout early, logout often. This approach is not foolproof, but it stops a whole bunch of real world attacks.

In the Daily Mail example the login screen is the affected web page, but being aware of the issue, and not login via unexpected login pages, or via links in emails or from untrusted websites is a sensible precaution.

Another approach would be to manage sensitive applications from a web browser that isn’t your default browser. If you have say Chrome and Firefox installed, you could have Chrome as the default web browser you use for surfing, and reserve Firefox for online banking. Not as secure as separate machines or rebooting from read only media, but here I am concerned with reflected XSS issues in the application, which means avoiding opening malicious links in a browser that has access to sensitive information.

Browser Defences

Internet Explorer since IE8, and current WebKit browsers such as Google Chrome, and Safari, implement filters for “obvious” reflected XSS attacks. These are largely based around spotting weird punctuation in the URL, and finding the same weird punctation in the returned web page, and guessing that the former caused the later.

The Firefox Plugin “No-Script” has a similar feature, but Mozilla has been somewhat reluctant to release a native filter for Firefox. This is probably in part because Mozilla’s security people are pushing “Content Security Policy” as the “right” solution to many XSS issues. But mostly it is due to this filtering being difficult to do correctly, and concerns about false confidence (it won’t stop all reflected XSS attacks, it won’t stop other types of XSS attacks, and false positives – it could break legitimate web sites).

Both Microsoft and Webkit managed to introduce significant security issues in the original deployment of their browser based filters.

The Webkit filter has also been exploited as a means of stripping out content to get a valid attack left when it passed through other filters (think removing “<script>” from  “<scr<script>ipt>” but fiddlier)

Even simply changing the structure of a web page may change the execution of JavaScript within the page, and lead to unwanted behaviour.

Browser XSS filters will always be insufficient, because it is not possible for the browser to know in advance what the fault in the server code is. So whilst such filters may mitigate many issues, and they certainly raise the bar, they are not a panacea.

A common case where bypass is more likely to arise is where the injection point is not in the HTML but is being embedded directly into some existing JavaScript on the server. This typically means you don’t need telltale “<script>” or “javascript:” strings in the URL, and even no or limited punctuation in the URL maybe required to exploit the vulnerability (always a joy to discover such a vulnerability as all that punctuation still makes my head hurt, otherwise I’d have become a LISP programmer).

Server Defences

The first is “X-XSS-Protection” “mode” flag. You can tell browsers that implement XSS filtering to block a page rather than trying to fix it, if it detects such an attack. This seems a more sensible behaviour to me, as long as you’ve got your testing sorted and aren’t going to break your user’s web experience with it triggering in error.

Web Application Firewalls might offer some protection, but my predecessor at Surevine was skeptical:

Again such tools can catch the same low hanging fruit the browsers catch with limited risks (do you really want to allow URLs with “<script>” in? Well sometimes you do, but probably not often), again the difficult cases get through.

There are some other “solutions” based around “nonces” (number used once), where we attempt to ensure each page load is part of a logical sequence, thus preventing some random page loaded in the middle of a session from working at all, but these tend to sacrifice behaviour we have come to expect from web applications, like the ability to go “back”, or to share a page with a colleague via the URL. One of the web applications I tested earlier this month, managed to defeat a number of XSS issue which were present by using just such a token, unfortunately it had similar vulnerabilities elsewhere in the site which were not part of work flows protected by the token.

Another obvious defence is to avoid problematic modules and software. The bug in wp_supercache that motivated this article could be avoided by using other ways to accelerate a WordPress site that don’t introduce large complex plugins. Content Delivery Networks such as CloudFlare and MaxCDN will typically allow a site to be scaled up aggressively. Reverse proxy software such as Varnish or Squid, or even Apache’s or Nginx’s own proxy feature, all provide well tested ways to deliver cached responses fast. That said it is often desirable to have the origin website work quickly in a CDN, and if there wasn’t utility in products such as wp_supercache I wouldn’t have had it installed on my test box, and wouldn’t have stumbled on the issue.

Ultimately we probably should be using tools that rely less on humans getting the details right consistently. Certainly in the case of the WordPress Settings API, WordPress plugin developers are expected to do a lot of work to handle relatively common cases of administrative input such as creating a tabbed form with checkboxes and the input of numbers within given ranges. They will inevitably make mistakes, and we’ve arranged it such that a broad range of these mistakes potentially allow an attacker to take over administrative control of the website of anyone who uses the vulnerable versions of that plugin and visits a web page controlled by them. Down this route lies madness.

Background Reading

Mozilla feature page for XSS_Filter: https://wiki.mozilla.org/Security/Features/XSS_Filter

Content Security Policy: http://en.wikipedia.org/wiki/Content_Security_Policy

Problems with XSS protection in browsers:

https://bugzilla.mozilla.org/show_bug.cgi?id=528661#c3

http://homakov.blogspot.co.uk/2013/02/hacking-facebook-with-oauth2-and-chrome.html