Its been a while since my last post so I decided to make it worthwhile :). I was recently checking a friend’s site for the classic Web application vulnerabilities, when I found a reflected XSS attack. While I was investigating the bug, I noticed that while the bug worked on Mozilla’s Firefox, it didn’t work on Google’s Chrome. As it turns out, Chrome uses an Anti-XSS filter, based on static analysis, which attempts to detect XSS. If it detects such an attempt, it filters out the injected code, and effectively stops the on-going attack.

In order to demonstrate this, I made a vulnerable page at http://securitee.tk/files/chrome_xss.php. This page simply reads two GET parameters, namely a and b, which it then prints out in the resulting page.

To show that injection is possible, I start by injecting some HTML which is indeed rendered as part of the HTML page.

http://securitee.tk/files/chrome_xss.php? a=<u>HTML_INJECTION</u>&b=bar

Now if you try to replace these tags by the standard alert function of JavaScript you will see that it doesn’t work.

http://securitee.tk/files/chrome_xss.php? a=<script>alert(1);</script>&b=bar

If you pay attention to the part that I have placed in the red box on the right of the screen, you will notice that Chrome detected my injected JavaScript and filtered out the alert function, leaving me with an empty script. The next thing I tried, was to omit the closing script tag and see how the browser would react to that:

http://securitee.tk/files/chrome_xss.php? a=<script>alert(1);&b=bar

In this case, Chrome didn’t remove my script (actually it tried to finish it by including a closing script tag of its own, right before the end of the body tag) but it didn’t work since all the normal text and HTML is now in the script environment. Given the fact that HTML is not valid JavaScript, the interpreter fails and still we don’t get the alert box. All that needs to be done is to somehow make the JavaScript engine ignore the HTML and text between our two controlled variables. This can be easily achieved by using JavaScript multi-line comment delimiters.

http://securitee.tk/files/chrome_xss.php? a=<script>alert(1);/*&b=*/</script>

And indeed, it worked!!! The multi-line comments mean nothing to the HTML but mean the world when they are placed in a script environment 🙂 In summary, all you need to bypass the XSS filter is to have at least two variables under your control, and break up your injected script, with the help of multi-line comments, to use both.

Till next time

Nick Nikiforakis

P.S. I have already told the Chrome folks about this, but their answer was that their filter is not meant to protect against this attack… I don’t know why… you can ask them 😉

Update – January 19th 2012

Recently I noticed that the attack explained above stopped working. The developers of Chrome still say that if you control two variables you can bypass the Anti-XSS but at the same time they agree that the bypass that I had originally demonstrated is no longer working. So here is a bypass, for their filtering of my original bypass 🙂 (tested in Linux Chrome 17.0.963.38 beta)

http://securitee.tk/files/chrome_xss.php? a=<script>/*//&b=*/alert(1);</script>

Now if you look at what’s happening here, all I am doing is adding a double-slash comment right at the end of the first controlled variable. This shouldn’t make any difference since we are already in comment-mode but apparently it does. If I would have to guess, I’d say that they have really hard-coded the multiline comment rule in their filters and any small deviation will still do the trick.

Update – October 25th 2012

Today one of the Master students who I supervise here at KU Leuven told me that my attack no longer works. After verifying that he was right, I tried out a modified bypass that I had discovered some months ago but “failed” to disclose. Works like a charm 🙂

(tested in Linux Chrome 23.0.1271.40 beta)

http://securitee.tk/files/chrome_xss.php? a=<script>void('&b=');alert(1);</script>

Instead of using comments to get rid of the intermediate HTML, I just make the whole thing a string and give it to the JavaScript function void, who couldn’t care less :). Using this, the script environment is ready to accept your arbitrary code, which in our case is the usual alert(1).