Update: Security researchers Sirdarckcat and Gareth were kind enough to share the code for a pure CSS based CSRF token finder here . This is stealthier than my PoC below, which used a combination of both JS and CSS. So, it will still work even if you disable javascript and you are not safe anymore
. To make this PoC more responsive to the client, you need to use multiple CSS stylesheets using the import command. The only problem I see with this pure CSS based approach is there will be network latency involved with large key spaces because your large CSS stylesheet will need to be downloaded by your browser.
I was thinking about the problem of Cross Site Request Forgery and current mitigation strategies used in the Industry. In many of the real world applications I have tested so far, I see the use of random tokens appended as part of url. If the request fails to provide any token or provide a token with incorrect value, then the request is rejected. This prevents CSRF or any cross domain unauthorized function execution.
Uptil now, it was considered infeasible for an attacker to discover your CSRF token using Brute Force Attacks on the server.
The reasons being:
- It generates lot of noise on the network and is slow. So most probably an IDS or Web App Firewall will pick up the malicious behavior and block your ip. For example, a Base16 CSRF token of length 5 characters (starting with a character) will generate approximately 393,216 requests.
- Many applications are programmed to invalidate your session after it detects more than a certain number of requests with invalid token values. E.g. 30.
I am going to change this belief by showing you a technique to quicky find csrf tokens without generating alerts. This technique is a client side attack, so there is almost no network traffic generated and hence, your server and IDS/Web App Firewalls won’t notice it at all. This attack is based on the popular CSS History Hack found by Jeremiah Grossman 3 years ago.
In this exploit, we discover the csrf token by brute forcing the various set of urls in browser history. We will try to embed different csrf token values as part of url and check if the user has visited that url. If yes, there is a good chance that the user is either using the same CSRF token in the current active session or might have used that token in a previous session. Once we have a list of all such tokens, we can just try our csrf attack on the server using that small list. Currently this attack is feasible for tokens with length of 5 characters or shorter. I tried it on a base16 string of length 5 and was able to brute force the entire key space in less than 2 minutes.
Some of the prerequisites for this attack to work are either
- CSRF token remains the same for a particular user session. e.g. csrf token=hash(session_id) OR
- CSRF token submitted in older forms for the same session is accepted. Many times, this is the case as it enhances user experience and allows using forward and back browser buttons.
Proof of Concept is available here.
Before running the PoC, you need to change the url and csrftoken paramater values.
For testing using the defaults, you need to first visit one of the following urls, e.g.
- http://securethoughts.com/?param1=val1&csrftoken=b59fe [change b59fe to any 5-digit base 16 string starting with a character, i.e.greater than a0000]
- http://tinyurl.com/l2lwgd [which is 301 redirect to previous url].
Note: http://www.securethoughts.com and http://securethoughts.com are treated differently while storing in browser history.
A sample run will look like this –
For making this attack unfeasible,
- Server-Side Solution (for developers):
- Make your CSRF tokens long enough (8 or more chars) to be unfeasible for a CLIENT SIDE attack. The ever-increasing processing power will make this attack feasible for longer tokens as well.
- Store your CSRF token as part of hidden form field, rather than putting in url.
- Use a different random token for every form submission and not accept any obsolete token, even for the same session.
- Client-Side Solution (for your customers/users):
- Use a browser plugin such as SafeHistory, which defends against visited-link-based tracking techniques.
- Use the private browsing mode in your browser.
And last, but not the least, XSS obliterates all the CSRF protections possible. So, get rid of XSS first.
I would like to thank Jeremiah for providing his insightful feedback on this post.
Tags: Brute Force, Client Side Attack, Cross Site Request Forgery, CSS History Hack, Jeremiah Grossman
![[del.icio.us]](http://securethoughts.com/wp-content/plugins/bookmarkify/delicious.png)
![[Digg]](http://securethoughts.com/wp-content/plugins/bookmarkify/digg.png)
![[Facebook]](http://securethoughts.com/wp-content/plugins/bookmarkify/facebook.png)
![[Google]](http://securethoughts.com/wp-content/plugins/bookmarkify/google.png)
![[LinkedIn]](http://securethoughts.com/wp-content/plugins/bookmarkify/linkedin.png)
![[Reddit]](http://securethoughts.com/wp-content/plugins/bookmarkify/reddit.png)
![[StumbleUpon]](http://securethoughts.com/wp-content/plugins/bookmarkify/stumbleupon.png)
![[Technorati]](http://securethoughts.com/wp-content/plugins/bookmarkify/technorati.png)
![[Twitter]](http://securethoughts.com/wp-content/plugins/bookmarkify/twitter.png)
![[Yahoo!]](http://securethoughts.com/wp-content/plugins/bookmarkify/yahoo.png)
This is way cool! A very innovative combination of existing techniques!
Unfortunately, from a mitigation point of view, I only see the option of increasing the complexity of the tokens as feasible. Using POST requests for all actions is problematic from an other point of view (bookmarkeability for example) and requiring all users to use private browsing also doesn’t work well from an usability point of view (even though you can detect private browsing AFAIK, using the same CSS history hack – if your browser claims that you never visited an URL you’ve just requested, your in private browsing mode).
Best regards.
@Cd-MaN
CSRF attacks really only hurt you when they perform actions that modify server side data. Coincidentally, it is best practice to only use POST requests for such actions.
Rarely do you want to allow a user to bookmark (or copy and paste) a link that performs an action specifically for this reason.
An example of a link that should be bookmarkable is something like a search result page, a blog entry, or some other static content that is not performing an action.For example, a cross site request for a Google search result page has no effect on anything and therefor does no need to be a POST request and does not need a token.
On the other hand, a cross site request to the Google Accounts page to change your password should most certainly only accept POST requests and absolutely requires a token to prevent IFrame POSTs.
Placing the CSRF token in the data of a POST request never exposes the data to the user, never allows them to copy and paste it, and as a benefit, also keeps the URL clean.
I think that is the ultimate solution to this problem – and its already build in to every browser!
Very nice! Was just talking about an approach like this the other day. IMHO CSRF tokens in URL are a bad move for a lot of the same reasons as session ID in URL, it’s just that people are still getting their brains around the problem. If you’re at BH or DefCon this year, we should talk, or drop me a mail, some other approaches and a tool we’re about to release that should be useful here.
@Cd-MaN, not sure that complexity alone is the best answer, it would help, but I think per request tokens, as POST params rather than in URL, combined with referrer checks, are probably about as good as it gets at the moment as far as mitigation.
[...] http://securethoughts.com/2009/07/hacking-csrf-tokens-using-css-history-hack/ No comments Digg this [...]
Nice blog post. However I think it the requirements to be vulnerable are too specifics. And from my personal experience I don’t remember coming across an application that would both have:
1. tokens passed through GET
2. tokens that never change (which kills the purpose of using tokens anyway).
@Byran, @Benji – thanks for your feedback.
@Benji, requirement 2 is incomplete. i also said that system might allow use of previous token values or next token might be predictable.
This is not a theoretical attack, in fact there are many sites that would affected by this. I just want to raise some awareness. To give a real world example is Bestbuy.com. It uses a 5 digit numeric _requestid token when you add something to cart.
@Byran, using nonces in POST or hidden form field is the right secure approach. We cannot generalize anything as ultimate, as we are a user/customer of that vulnerable site, we have no option but to use something like SafeHistory, etc.
Hi Shawn, yes i will be at both blackhat and defcon. lets chat there, are you using twitter by any chance …
@Benji Per session rather than per request tokens are a lot more common than you’d think. Keep in mind that for high-traffic sites it can get pretty computationally expensive to do it the “right” way, and realize that even if tokens are per-request they also need to be mapped to a user session, which gets even more resource intensive (think sites handling thousands of requests per second).
As far as token in URI, it’s definitely out there, it’s not quite as common at this point but it’s out there.
As earlier, you have to do at least three things, and do them all well – per-request POST-only tokens, mapped to session, and referrer. If you do 1/3, or 2/3, you still fail.
The Internet is hard. Like, really, really hard.
@Inferno, I added you on that Tweeter thingie.
thanks Shawn for summarizing and tweeting
[...] This post was Twitted by haxplorer [...]
Head not working: can I just stick a short-lived token into the session instead of the url?
@bob – please note that this attack executes on the victim machine. So, no, you should not put the short-lived token into the session as sessionid is automatically sent as part in every browser request (provided victim is authenticated to vulnerable site). so, the right way to do it is use a nonce in POST or as part of a hidden form field.
I’ll have to respectfully disagree with most of the comments here and say that adding XSRF tokens in URLs can actually be better than adding them in forms, if done the right way. This is the topic of my BlackHat talk next week (http://www.blackhat.com/html/bh-usa-09/bh-usa-09-speakers.html#Sullivan), I’d be very happy to see you there and get some debate going.
Inferno, you say “And last, but not the least, XSS obliterates all the CSRF protections possible”. Could you develop this idea a little please ? If you use an XSS vulnerability to inject a javascript code somewhere, I still don’t see how you can find a nonce in POST (if it is in GET, you can find it by looking at the document.location).
@bryan – your talk looks very interesting. look forward to seeing you at Blackhat. are you on twitter ..?.
on your comment, i would say the best protection currently is using long cryptographic nonces for every request, and it can go in GET, POST or FORM field. It might be not feasible for every system because of the overhead and other requirements. i would hold off more questions until your talk next week
thanks for posting.
@TheShade – i want to give you the best real world example of this – Samy worm. Please see point 7 at http://namb.la/popular/tech.html. The token to send in POST has to be known by the browser and XSS can easily parse that.
[...] Researcher raids browser history for webmail login tokens Source: TheRegister & SecureThoughts [...]
@BryanSul Nathan and I are interested in your talk and interested in the debate as well. =)
[...] put together a couple things and came up with a fairly scaring attack on CRSF tokens. Hacking CSRF Tokens using CSS History Hack | SecureThoughts.com Tags: ( hacking crsf [...]
IMHO a data-changing action shouldn’t ever be allowed without valid lead-up to that action. E.g. submitting a new password for an account shouldn’t occur if the previous request wasn’t a legitimate request for the ‘change password form’ page, which itself wouldn’t have been served if the request prior to that wasn’t a legitimate request for the Account Management page. That “business process” of sorts is tracked in the session, not via data passed to and from the client, resulting in a reality-check of the interaction between the User and web application leading up to the data-changing request. This imposes the necessity to make a series of requests to perform a data-changing action, the web application can then impose extra checks such as ensuring that the timing between those requests reflects a real human user (i.e. response time + “think time”). That said, it would still be possible to scrape an initial anti-CSRF token from the web app (say thru an XSS vulnerability or sniffing) and then conduct the series of requests necessary to lead up to and utilise the data-changing request…Yet, using CAPTCHA for the anti-CSRF token is a great means of dealing with the inherent XSS problems regarding anti-CSRF tokens (as long as the CAPTCHA image can’t be re-served to the attacker), plus ensures that a real-human is making the request. Using CAPTCHA this way sortof* makes the “business process” checks more overkill than essential and also can be problematic in terms of legitimate automated use of the web app (e.g. business process monitoring).
*Since CAPTCHA can in some cases be bypassed via automation (neural networks), that business process reality-check provides an extra measure of scrutiny and makes it very hard/time-expensive to brute-force CAPTCHA.
Use of BACK/FORWARD browser buttons can be elegantly dealt with by sending the User to the non-data-changing-start of a business process from which he/she proceeds, rather than ending the session outright.
My view is that session-wide anti-CSRF tokens (“stale” tokens) are the bare-minimum solution. The best solution so far is an RSA-style token on any data-changing requests (benefit here is the web app never served the token out so it cant be scraped/sniffed). And an effective middle-ground is using “fresh” tokens served via CAPTCHA and entered via a form that gets POSTed.
Sorry to spam the crap out of this blog entry.
Thinking about Bryan’s post. I’m not sure I can see how tokens in URL can be inherently better than POSTs, but I will agree they aren’t worse, if they’re nonces, and if they’re not leaked in referer via included offsite content.
I think something we can all agree on is that the path to doing this the right way is reasonably well understood, but from the attacker perspective there are many, many cases where it’s not what’s been implemented. I like unified approaches in APIs where some kind of tokenization is pervasive unless the dev explicitly calls for a request not to be tokenized (i.e. for SEO and usability or something).
[...] This post was Twitted by timfaas [...]
@ShawnM: There are a few benefits to URL tokens: they actually do a great job protecting against XSS (reflected and local), open-redirect phishing and browser history theft as well as XSRF; and better still you can implement them on existing apps without having to make any code changes. There are definitely a number of disadvantages as well (search engines can’t index sites protected this way) and the defense is far from perfect (referer leakage as you mentioned, among other problems) but these problems can be mitigated to certain degrees. I’m releasing a PoC defense library for ASP.NET with my talk next week, please download it and let me know where it falls down. I don’t think this is the end-all solution to XSS and XSRF but I do think it can work well in certain situations.
Btw also very much looking forward to your and Nathan’s talk too
@Inferno: Nope, I’m not a big tweeter
If I have something to say I usually just drop it on the SDL blog.
[...] Hacking CSRF Tokens using CSS History Hack – “In this exploit, we discover the csrf token by brute forcing the various set of urls in browser history. We will try to embed different csrf token values as part of url and check if the user has visited that url. If yes, there is a good chance that the user is either using the same CSRF token in the current active session or might have used that token in a previous session. Once we have a list of all such tokens, we can just try our csrf attack on the server using that small list.” [...]
[...] n’est que plus récemment que ces failles ont trouvés une utilitée autre que celle de ravir les publicitaires: le bruteforcing de token. Combiné cette [...]