Previously I covered the basics of CSRF and demo'ed two iframe based brute force scripts. The posts can be found here:
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:
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 CodeI started off with some basic XHR code. Shreeraj Shah covers a simple XHR CSRF example here:
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 RequestsJust 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 ActI 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
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
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?!?
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:
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.
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!
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.
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! :)