Bypassing Chrome’s Anti-XSS filter

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

Injecting HTML tags in vulnerable page

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
Attempting to inject JavaScript

Attempting (and failing) to inject JavaScript

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
Ommiting the closing script tag

Ommiting the closing script tag

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>
Bypassing Chrome's XSS filter

At last… success!

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).

This entry was posted in Breaking stuff. Bookmark the permalink.

29 Responses to Bypassing Chrome’s Anti-XSS filter

  1. skeptic_fx says:

    Good one . Opens some new door to XSS in general.

  2. foo says:

    interestingly noscript had no problems blocking the offending split script.

    [NoScript XSS] Sanitized suspicious request. Original URL [http://securitee.org/files/chrome_xss.php?a=%3Cscript%3Ealert(1);/*&b=*/%3C/script%3E] requested from [chrome://browser/content/browser.xul]. Sanitized URL: [http://securitee.org/files/chrome_xss.php?a=%20script%3EALERT%201%20%3B%2F*&b=*%2F%20%2Fscript%3E#9551845861820314641].

    how it did follow the script through the multiple parameters however..

  3. Adam Barth says:

    The reason we can’t mitigate this vulnerability is that you’re able to inject content into the page in two locations. We’re working to reduce the false negative rate of the filter. You can follow our work by looking at the dependency tree of this bug:

    https://bugs.webkit.org/show_bug.cgi?id=66579

    Thanks for the feedback.

    • nikifor says:

      Hi Adam,

      Thanks for your response. Would any issues arise if you would just concatenate all GET variables from the URL and search for JavaScript in there? It seems to me that you would keep on detecting the attacks you do today, plus the one I mention in this post. Is there a false-positive somewhere in there that I am not thinking of?

      Kind Regards
      Nick

      • Sebastian says:

        Hey Nick,

        concatenating alone would not be sufficient enough I think. The HTML could include the parameters in another sequence than the sequence of the GET parameters. So you somehow need take the internal ordering into account. Which is very difficult if you do not know the internals of a web page.

        Another way to confuse the detection mechanism would be to simply introduce bogus GET parameters to prevent the mechanism from finding valid JavaScript.

        Best Regards,
        Sebastian

  4. Pingback: Wie man den Cross-Site-Scripting-Filter von Googles Chrome umgeht « Web-Sicherheit

  5. Luis Santana says:

    Honestly I don’t think the problem is with Chrome’s anti XSS filter as this is supposed to just be a security blanket for stupid developers. At the end of the day Chrome is just doing what it can to help with the most common XSS styled attacks to protect its users and it falls on the hands of the developer of the vulnerable web app to ensure their code is not vulnerable in the first place.

    If Chrome can fix this bypass that’s great, if not, hopefully it keeps pressure on web application developers to ensure their code is secure instead of thinking, “It’s cool, Chrome will stop the attack”.

    Just my 2 cents

  6. t3st says:

    I agree to the idea that a developer should be responsible for vulnerabilities on a website. We cannot depend on browsers to protect us from web attacks since there still are a million ways to bypass filters. If the developer can code securely, all bypass techniques are useless.

    However, your article and finding are nice ;D

    Best Regard,

  7. Cory says:

    A good read! Another method might be to use a textarea.
    e.g.
    Leaving this tag unclosed will load all following HTML/JS into a hidden textbox.

    • nikifor says:

      Hi Cory,

      Thanks for your comment. The problem with the textarea is that it is HTML and not JavaScript code, thus it won’t work. You need a way to make the JavaScript engine ignore the various tags between your two parameters, while staying in the JavaScript environment – which is where multi-line JS comments come in hand.

  8. Wh1t3Rabbit says:

    So … there is another attack possibility, rather than having to have 2 *separate* parameters to inject into a page – it’s conceivable to have the same parameter echo twice, and have an attack crafted that would effective still do the same thing?

    For example:
    http://securitee.org/files/chrome_xss.php?a=*/alert(1);/*
    &b=foo

    I don’t have a sandbox to test – but I suspect it can be done?

    /Wh1t3Rabbit

    • Sebastian says:

      Hey Wh1t3Rabbit,

      here is a way how it can be done within one parameter:

      */alert(1);/*

      I tested this and it works.

      Best Regards,
      Sebastian

      • Sebastian says:

        well, you posted the same…but the blog removes the “script” tags. So the alert has to be followed by a closing and an opening script tag…

  9. Rocky says:

    Actually this is how initial filters of XSS in .NET framework were compromised.

    But, good job!

  10. Pingback: XSS Browser Filter Mitigation « Stubbornly Me

  11. Pingback: Top web hacking techniques « -: Infosec Notes :-

  12. Pingback: Vote Now! Top Ten Web Hacking Techniques of 2011 | WhiteHat Security Blog

  13. Pingback: Top Web Hacking Techniques of 2011 | MYH3R3

  14. Gilbert says:

    Hey! This is really good and well though out. I don’t believe it works when sites only allow the user to input one variable instead of two though. Any ideas?

    • nikifor says:

      Gilbert,

      No, this method, and its variations, require that you control at least two output variables.

      Best regards
      Nick

  15. Gilbert says:

    I see. Thanks for the reply!

  16. Sebu says:

    sorry pal, but there’s tons of filter bypasses for chrome. The filter is not even maintained and known to not protect ;)

    • nikifor says:

      Sebu,

      As you may or may not know, I am a PhD student in Computer Science so, with your permission, I will approach your comment, scientifically.

      1) You say “there are ton of filter bypasses”. Where are the references towards this “ton” of resources? Should I take your word for it or can it be shown to me, objectively, that you are right? Again, as you may or may not know, my original blog post (without the two updates) was voted one of the top 10 Web hacking techniques of 2011. Strange that I would be voted for such a thing if there was a “ton” of bypasses and the filter was “known, not to protect”.

      2) You say that the filter is “not even maintained”. Again, if you would have taken the time to read through the whole blog post, you would have found out that, twice over the last year, I published a way around the filter and twice the bypass was fixed (this last one is my third one). Again, what a strange behaviour for a filter “not even maintained”.

      3) Your writing style is pseudo-friendly (even though we are not friends) and marginally derogatory, which is something that I do not enjoy, especially given the lack of any real content in your comment.

      In short, your comment is rejected. If you think you are right, please revise and resubmit.

      Kind regards
      Nick Nikiforakis

  17. Thank you for this very interesting article.

    Although your latest “hack” still works in the latest version of chrome, it failed on Internet Explorer 9.0.8112. This doesn’t mean IE is more secure than Chrome, but shows XSS can be, at least, mitigated if enough thought is put into it.

    Nevertheless, I, like others posted here before, share the belief that it’s not the browser’s but the web developer’s job to prevent XSS attacks.

  18. This is really good and well though out.I agree to the idea that a developer should be responsible for vulnerabilities on a website.

  19. kurtisebear says:

    Great post, I think these days it is getting harder and harder to get round filters on both website and browser level. So information like this can only breed new methods.

  20. Vincent says:

    Hi Nick,

    Great article. I have a question concerning the last bypassing technique. Is it possible to combine the last updated one (void..etc.) with another bypass technique like putting “> in front of the opening script tag? Or will the javascipt engine get confused?

    Thanks!

  21. Abeon says:

    Google updated their XSS filter. This doesn’t work anymore :/

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>