XSS Session Hijacking Part I19 Apr 2016
This is not a blog on discovering XSS vulnerabilities. Rather, this is a blog on what to inject into an XSS vulnerability once you have found it.
Session hijacking attacks involve stealing a user’s session cookie, then using it to impersonate the user. This allows the attacker to gain access to restricted portions of the website, as well as escalate privileges by impersonating an administrator. Most session hijacking attacks make use of a tool known as a Cookie Stealer.
In this tutorial we will be develop three different cookie stealers of increasing complexity and effectiveness. We will also demonstrate how to use these cookie stealers to perform session hijacking attacks using a web page vulnerable to XSS.
Before we get started, let’s get our development environment set up. We start by creating a new project directory.
We then enter our new directory and create a new virtual environment within it:
Next we activate our virtual environment:
We then create a dependency file containing flask and flask-cors.
Finally, we install the dependencies enumerated within pip.req:
Building a Simple Cookie Stealer (no redirect)
Let’s build a simple cookie stealer that follows this model. In our project directory, create a new file named ‘no-redirect.py’.
Let’s then open up no-redirect.py in a text editor such as vim, and add the following lines of code to the top of the file to import Flask and Flask-CORS.
Next, we instantiate a new Flask object. Flask objects can be thought of as lightweight HTTP servers responsible for handling incoming HTTP requests.
Our cookie stealer needs to be able to accept and handle HTTP requests originating from domains other than its own. In other words, the target website needs to be able to send information to our server despite the fact that it is located at another domain.
This presents us with a problem, because Flask strictly enforces Same Origin Policy.
. . . same-origin policy is an important concept in the web application security model. Under the policy, a web browser permits scripts contained in a first web page to access data in a second web page, but only if both web pages have the same origin. An origin is defined as a combination of URI scheme, hostname, and port number. This policy prevents a malicious script on one page from obtaining access to sensitive data on another web page through that page’s Document Object Model. – Wikipedia
To disable Flask’s same origin policy using Flask-CORS, we just add the following line to our no-redirect.py file.
Our Flask object is instantiated and has been patched to accept cross origin requests. We now add a route handler for the main page:
The code shown above tells Flask that any HTTP requests to our cookie stealer should be handled by the function named index(). Our index() function first stores any cookies passed as request parameters, and writes them to ‘cookies.txt’. It then sends ‘index.html’ as an HTTP response, causing it to be displayed on the page.
Finally, we add the following line of code that to our script to launch our server.
The complete cookie stealing server is shown above.
How to use a cookie stealer
Now that we’ve built our cookie stealer, let’s try using it in an actual XSS attack. This video uses three virtual machines running on a VMWare virtual network as a lab environment.
OWASP Broken Web Applications Project (Target HTTP Server)
The OSASP BWA virtual machine is running Damn Vulnerable Web App, which we will be attacking. For more information on running virtual machines, check out this previous blog post. Kali can be downloaded here. I’ll be using my laptop running Fedora 22 as a victim in the attack.
Improving our Cookie Stealer with Redirection
As you can see from the video, our cookie stealer has a pretty glaring limitation – it’s loud. The target web page is rendered unusable after the XSS attack, and all users are instantly redirected to an obviously malicious attack site.
The way to fix this issue to redirect the user back to the infected web page instead of presenting them with a new html template. The code shown below does just that.
Notice that we’ve added another import statement at the top of the file:
This allows us to use Flask’s redirect function within our code. We’ve also modified our return statement. It now redirects users back to their referrer instead of rendering a new web page.
As you can see in the following video, the attack is significantly more subtle. The user is redirected to the cookie stealer as soon as they load the page, before being redirected nearly instantly back their referrer.
Our improved cookie stealer is not without some problems, however. For one thing, it forms an infinite redirect loop between the target web page and the cookie stealer. The client accesses the page, is redirected to the cookie stealer, is redirected back to the target page, where it is redirected back to the cookie stealer, and so on and so forth.
This is a serious issue for multiple reasons. We end up with lots of duplicate session cookies. This means that it will be hard to locate individual cookies within our ‘cookies.txt’ file. It also means that the file will get very large, very quickly. The second issue is that the redirect loop generates very loud network traffic. Check out the wireshark output below.
The constant redirection spread across multiple users amplifies the target webapp’s normal HTTP traffic, greatly increasing the chances of the attacker being detected. In a worse case scenario, where the site is already experiencing high volume, the additional traffic generated by the redirect loop could crash the server.
#Fixing our Improved Cookie Stealer
As you can see above, the script only redirects the user to the cookie stealer if the cookie has not been set. The cookie is set immediately before redirection.
This is about as good as it gets for redirect based cookie stealers. As you can see in the video, the attack is visually subtle and does not produce excessive network traffic. Our Python script does leave room for improvements, however. We could spoof the referrer in the HTTP header to prevent our cookie stealer’s IP address from showing up in log files on the target network. We could also use a 301 (permanent) redirect. Flask issues a 302, or temporary, redirect by default. Temporary redirects cause web browsers to continue to use the old url for future requests, rather than switch to a new one. This means that subsequent requests to the infected page will be made my first sending a request to our Cookie Stealer server. These improvements are left up to the reader as exercises.
AJAX - The Session Hijacker’s Holy Grail
So far we’ve focused on stealing sessions by redirecting users to a malicious web server that logs their session cookies. Although this approach is simple and effective, it is subject to the limitations that we previously discussed. The most important of these limitations is the fact that redirect based cookie stealers always cause abnormal web traffic to and from the target server. The solution to this is to use AJAX.
The two features in question are that you can:
- Make requests to the server without reloading the page
- Receive and work with data from the server
AJAX allows us to send a request from the victim’s web browser to our cookie stealer without refreshing the page and, most importanty, without sending additional HTTP traffic to the target server. The user loads the infected page and has their cookies stolen silently in the background. The infamous Sammy Worm incorporated this technique into its source code.
Building an AJAX cookie stealer
The code shown above simply creates a new XMLHttpRequest objects, initializes a POST request object, and uses the request object to send the user’s cookies to the attacker’s server. The script’s injectable form is shown below.
The Python script that acts as our cookie stealer’s server is smilar to the ones we created before. Its distinctive characteristics are that it accepts ‘POST’ requests and grabs the cookies from the JSON POST data as opposed to the query string.
In addition, the script keeps a set of stolen cookies to keep duplicates to a minimum. You can see the AJAX cookie stealer in action in the video below.
In this post we developed two modern cookie stealers, placing an emphasis on stealth. These cookie stealers provide a means to steal session cookies in order to expand the attacker’s access to the target website. Stealing sessions is ineffective in a number of situations however. These include websites that use http-only cookies, sessionless auth, or that require an additional passphrase to perform privileged operations.
You can check out and run all four demo scripts on Github here.