Skip to content

Reverse DOM XSS

by on May 4, 2014

I came recently with an idea about how to exploit a DOM XSS vulnerability that it might be worth sharing here so others can use this trick, adapt it and defeat some poor filters with a little of JavaScript and some creativity.

During an engagement I found a piece of code similar to this one:

<a href="#" onclick="goToWebsite(this, 'url',  '/ShowInfo.php?id=[INJECTION]&category=CARS');">

The code behind goToWebsite was something like this:

function goToWebsite(e, param, base) { 
   window.location = base + param + "=" + actionURL.value;
}

It’s a very clear example of DOM XSS where we can control the flow of the page using the window.location element. But why it wasn’t a reflected XSS? Well, they have some filters in place. Double quotes ( ‘”‘ ), brackets ( ‘<‘ and ‘>’ ) and even semicolon ( ‘;’ ) where filtered so we couldn’t escape from the injection point, having to dig deeper into the goToWebsite function to find our way into executing arbitrary JavaScript.

Lucky for us we were still allowed to use some characters that were necessary for this trick, like parenthesis ( ‘(‘ and ‘)’ ), quotes ( ”’ ) and dots ( ‘.’ ).

For a DOM XSS attack we need to modify the address where the window.location is going to navigate and make it go to javascript: URL handler. But, in this case we couldn’t write directly as the injection is happening in the middle of a string, which contains some reference to a relative URL: “/ShowInfo.php?id=

How do we overcome this problem? Well, we cannot escape from the function call but as we can insert quotes and parenthesis we can modify the string that is received by the goToWebsite function like this:

<a href="#" onclick="goToWebsite(this, 'url',  
'/ShowInfo.php?id='.split('').reverse().join('').concat('&category=CARS');">

These function (split, reverse and join) will reverse the string ‘/ShowInfo.php?id=‘ to be ‘=di?php.ofnIwohS/‘. And we are using concat to make the code valid and as it’s at the end of the result string we don’t care about modifying it.

So now we need to insert our payload, the classic alert(1):

<a href="#" onclick="goToWebsite(this, 'url',
'/ShowInfo.php?id=//)1(trela:tpircsavaj'.split('').reverse().join('').concat('&category=CARS');">

Now, if we execute that code, the resulting string will be:

javascript:alert(1)//=di?php.ofnIwohS/&category=CARS

Perfe… wait! What’s this? Code is not being executed! We have managed to insert our javascript payload at the beginning of the string passed to window.location but the code is not being executed. A closer look at the generated code on the page revealed the mystery:

%2f%2f)1(trela%3Atpircsavaj'.split('').reverse().join('').concat('

Key characters as slash ( ‘/’ ) and colon ( ‘:’ ) were encoded, so our code wasn’t able to executed. Time to think how to bypass the encoding of these characters.

JavaScript has the ability to replace a character inside a string like: replace(‘old’, ‘new’) so I thought I could use String.fromCharCode to bypass the character limitations and make my code execute, replacing ‘/’ and ‘:’ by two other characters that are not URLEncoded like ‘~’ and ‘+’ but it was a problem: comma character was also URL encoded so I couldn’t use the replace function.

Time for the second trick! Apparently, in JavaScript, you can split a string by a character and then use another character to join the strings together, like this:

'abc-def'.split('-').join('!')

After being executed this will render abc!def

The best part? We are not using any forbidden characters! Just again our old friends quotes and parenthesis.

If we put everything together we have something like this in order to exploit this DOM XSS bug:

~~)1(trela+tpircsavaj'.split('').reverse().join('').split('~').join(String.fromCharCode(47)).split('+').join(String.fromCharCode(58))).concat('

That way I was able to execute JavaScript code in this particular scenario without using any forbidden char. I am pretty sure you will not find yourself in this exact situation in the future but hopefully you can use these two little tricks someday!

From → pentura

2 Comments
  1. Jason Ymous permalink

    > “I am pretty sure you will not find yourself in this exact situation in the future”
    Heh.
    Still a good read though 🙂

  2. mmetince permalink

    I’m still trying to understand why did you work on this case so much. Following payload will work like charm without any reverse str or other behavior that you used.

    PAYLOAD = FOO’.str(alert(1))).concat(‘

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: