Published: 23 March 2020 at 14:03 UTC
Updated: 08 September 2020 at 10:00 UTC
Every once a while, you'll find what looks like a healthy XSS vulnerability, only to bash your head against a limited charset that prevents exploitation. One common restriction is a website that doesn't let you use parenthesis: (). There are two classic solutions for this particular challenge, but both have flaws:
location=name relies on an external website, and fails in Safari as it clears window.name on x-origin navigations.
onerror=alert;throw 1 is increasingly thwarted by WAFs blocking the keyword 'throw'.
Is there a third, better way? Terjanq posted an interesting limited-charset challenge on sla.ckers, and I took a break from my latest research epic (publication date TBC, no prizes for guessing why) to find the answer.
I was working with the restricted charset of
a-z A-Z';+.:=. You can call functions using
toString/valueOf but you can't pass arguments to the function, which makes it quite limiting. It also doesn't seem to be possible to extract parts of a string; you can only get the first character an object's toString value. For example the object
new RegExp returns a value of
/(?:)/ and you can get the first character by using
String.prototype.charAt. This works because
charAt gets called without any arguments and this returns the first character:
x = new RegExp;
x+''//returns a single /
Unfortunately you can't use
valueOf with string literals, so you can't even get the first character never mind any characters in-between on a string literal. What I needed to find was a gadget that would let me generate characters that I didn't have access to. I used the Hackability Inspector to enumerate the
new operator could enable me to generate a bunch of new characters. Using the inspector again I came up with a script that used the new operator on all enumerated objects on
window, and in the console I found one constructor that generated parenthesis: the DOMMatrix constructor! This constructor would generate an object that had a toString value that looked like a function call with parenthesis. For example calling the DOMMatrix constructor would generate the following:
> matrix(1, 0, 0, 1, 0, 0)
In effect I could generate a string that contained parenthesis that called a function called matrix with the arguments
1,0,0,1,0,0 but there were two problems: 1) I needed to control the function called and 2) I need to control the arguments sent to the function. I could get round the first problem by simply assigning a function to the matrix variable.
For the second problem I inspected the matrix object returned by the DOMMatrix constructor and noticed it had a property called "a" which would enable me to control the first argument sent to the function:
Putting it all together here's how to abuse DOMMatrix to call alert(1337):
You might be wondering how to execute arbitrary code. The argument sent to the matrix function has to be numeric but this isn't a problem since you could use String.fromCharCode for example to generate characters sent to a sink such as location. This is possible by overwriting the matrix variable with
String.fromCharCode and using the
In the code above you have to use
location twice, once to generate the characters and a second time to evaluate those characters. The
void 1 at the end returns undefined and prevents the browser from writing the second location assignment as HTML.
There is another constructor that can be used in a similar way, but we strongly suspect our blog posts are getting copy+pasted into WAF blacklists so we're keeping it to ourselves for now.
If you liked this post we have some similar posts on this topic and we also released an Academy lab that inspired the sla.ckers discussion and this blog post, so if you want to experiment with these techniques please check out our lab!
<a href="http://subdomain1.portswigger-labs.net/safari_window_name_bypass/read.html" target="alert(1337)">PoC</a>
Enjoy & stay safe!