Showing posts with label CSRF. Show all posts
Showing posts with label CSRF. Show all posts

Friday, February 22, 2008

Exploiting CSRF Protected XSS

XSS vulnerabilities which are protected by CSRF protections, are usually considered unexploitable due to the fact that we have no way of predicting the CSRF token.

However, these protections do nothing more than check that the user is first "logged in" and that the CSRF token they sent is tied to their session; nowhere in this chain of events is there a condition which states that an attacker must be forcing the victim to use their own session identifier (cookie).

If we are able to force the victim to send a request which contains the attacker's cookie, CSRF token and XSS payload, then we will pass the CSRF protection checks and have script execution.

A General Case



So how would we go about this? As I mentioned in my "Exploiting Logged Out XSS Vulnerabilities" post, Flash (until 9,0,115,0 and not in IE) allows us to spoof the Cookie header for a single request, however this suffers from the same problem that we cannot completely over-write cookies; only add an additional Cookie header.

This is indeed a possible attack vector though; if we first make sure the user is "logged out" (and also has no value for the login cookie) either by simply waiting, using a CSRF attack to log the user out (and hoping the website also clears it's cookies), or exhausting the browser's cookie limit, we can then add our own Cookie, CSRF token and XSS payload to the request using similar Flash code e.g.

class Attack {
static function main(mc) {
var req:LoadVars = new LoadVars();
req.addRequestHeader("Cookie", "PHPSESSID=our_valid_session_id");
req.x = "y";
req.send("http://site.com/page.php?csrf_token=our_csrf_token&variable=",
"_self", "POST");
// Note: The method must be POST, and must contain
// POST data, otherwise headers don't get attached
}
}


Then the application will receive a request with our (the attacker's) session id, a valid CSRF token and our XSS payload from the victim's browser.

Of course, the problem with this is that if the user is actually logged out (which we forced, due to our inability to simply over-write the cookie or stop it being sent) and the browser no longer has the victim's cookies, the only attacks we have from this point are the other attacks mentioned in my "Exploiting Logged Out XSS Vulnerabilities" post. And while this is not ideal, it does at least give us something other than an unexploitable XSS.

Cookie tricks


Again, with this technique we can also set a cookie for the specific path, either by having an XSS on a related subdomain or by abusing a cros-site cooking bug, and then the user will still have their original cookie intact and we can simply remove our own cookie from the user's browser once our XSS has fired.

Abusing RequestRodeo


Another case where the user would be logged out is the case where we can somehow get the cookies stripped from the user's request.

The technique I presented at 24c3 talked about abusing one such piece of software which stripped cookies; the RequestRodeo Firefox extension which was created by Martin Johns and Justus Winter which does a good job of protecting against a lot of CSRF attacks by stripping cookies from requests originating from 3rd party sites (i.e. a request going to site.com which was induced by evil.com will not have any cookies attached to it). Which is just what we need.

Of course, this is a nice place to note that this is of course a niche piece of software that doesn't really provide a valid avenue for exploitation in almost any scenario, but as I explained in my post "Understanding Cookie Security" we can also delete all a users' cookies by exhausting the browser's global limit on on the amount of cookies it will store.

Anyway, given that RequestRodeo all cookies (including the ones we are attempting to send via Flash), we still face the problem that we need to be sending a valid session identifier for which we need to be sending a valid CSRF token. We do not face this problem when we remove the user's cookies, and can use the Flash approach outlined above, but we can also use this approach, which has the added benifit of still working on everyone (not just those who are unpatched).

Anyway, one interesting feature of PHP and other frameworks is that they accept session identifiers through the URL. This has of course led to easily exploitable Session Fixation attacks; however in PHP at least, if a cookie is being sent, then the value in the URL is completely ignored.

In our case, no cookie is being sent, since it is either being stripped by RequestRodeo, or has been deleted by us, so we can simply supply our session identifier through the URL, attach our CSRF token and XSS payload and we're done, except in this case the browser still has the user's cookies, and our XSS functions like normal.

The result of this attack is the same as the above if we have deleted the cookies from the user's browser, however if we have stripped the cookies with RequestRodeo or some similar tool/technique/etc, then we have the further benifit of the user still being logged in when our XSS fires.

Other Cookie Tricks


As I wrote in my post "Understanding Cookie Security", if we have an XSS which is largely unexploitable (except via pure Session Fixation) since it is on a subdomain with no accounts, we can use it to set almost arbitrary cookies for other subdomains.

This gives us a perfect avenue, since we can set the path to only over-write cookies for our single CSRF Protected XSS page, and send the appropriate CSRF token and XSS payload for.

Self-Only CSRF Protected Persistent XSS


One case which is much simpler to exploit than the general case though, is where there are CSRF protections on the form where you submit the actual XSS, but the XSS is a persistent XSS for the user, in that it is rendered on another page (which is itself not CSRF protected, since it is used to display rather than edit data)

CAPTCHAs As CSRF Protections


CAPTCHAs are not designed to be CSRF protections, and in certain cases are bypassable.

There are essentially two (not completely broken) types of CAPTCHA systems I have seen in widespread use, one where the plaintext is simply stored in the server-side session and the captcha is included in a form like this:
<img src="captcha.php" />
The other is when a form has a hidden input tag which contains a value which is also inside the image URL, like so:
<input type="hidden" name="captcha_id" value="1234567890" />
<img src="captcha.php?id=1234567890" />


The first system is trivially bypassed for CSRF & CSRF Protected XSS attacks by simply inserting the CAPTCHA onto a page, or inside an iframe (to strip/spoof referers), and asking the user to solve it.

The second can often be trivially bypassed for CSRF & CSRF Protected XSS attacks since the id is usually not user-dependant and the CAPTCHA does not keep track of what id it sent the user. Therefore the attacker can simply retrieve the appropriate CAPTCHA, solve it, and put the answer along with the corresponding captcha id in the csrf or csrf protected xss attack.

Conclusion


So essentially if we can somehow trick the application into using an attacker's session identifier, either by altering the cookie (e.g. via subdomain tricks, Flash, injecting into Set-Cookie headers, or whatever other trick we can come up with), or by suppressing or deleting the cookie and passing the identifier through another means such as the URL, then all CSRF protected XSSs are exploitable.

However if we cannot, then we can still exploit some scenarios such as self-only CSRF protected persistent XSS if the logout/login functionality is not CSRF-protected (which very few are). And we can also bypass the semi-CSRF protection of CAPTCHAs in several cases.

CSRF-ing File Upload Fields

It seems I'm destined to have everything I sit on for a while patched or found and disclosed by someone else, *sigh*, I guess that's the way things go though.

Oh well, pdp has an interesting post over at gnucitizen.org about how to perform CSRF attacks against File upload fields using Flash: http://www.gnucitizen.org/blog/cross-site-file-upload-attacks/

Since there would be no point publishing this later, here is the method I came up with a while ago to CSRF File upload fields

<form method="post" action="http://kuza55.awardspace.com/files.php" enctype="multipart/form-data">
<textarea name='file"; filename="filename.ext
Content-Type: text/plain; '>Arbitrary File
Contents</textarea>
<input type="submit" value='Send "File"' />
</form>


It relies on a bug in Firefox/IE/Safari where the filenames are not escaped before being put into the POST body to set the filename parameter and content-type header.

Note: http://kuza55.awardspace.com/files.php is probably vulnerable to a tonne of things; I'm not too worried as it's on free hosting.

Sunday, January 21, 2007

Insecure OpenID 'Features'

Note: I wrote this a while ago, and I haven't gone over it completely, but I thought it would be worth posting about.

I read about OpenID a while ago, when my friend asked me what I thought of it from a security perspective, and from what I could tell from the documentation, except for DNS issues it wasn't a bad decentralized authentication protocol. And I didn't do any further research into it until I came a blog entry describing how it worked in practice http://www.readwriteweb.com/archives/openid_vs_bigco.php).

In that article/flash 'demo' I saw that as with any protocol, developers can come up with great 'features' which damage the security of the protocol. And this isn't one OpenID provider deciding to add an insecure feature, either, it is one that is common throughout the 3 OpenID providers mentioned in the article, which I assume are the most popular ones being used (why else would a blogger mention them?).

Now, what is this feature I feel should not exist? It is the feature to set a site to be able to accept your credentials without you having to enter your OpenID password, and since your OpenID provider does not provide these details to the host, they do

Of course, you still need to be logged into your OpenID provider, but since you're meant to be using this login for several sites, its not too much of a stretch to believe that you're going to be logged in all the time you're online - which is quite a large time frame. And if we consider that most sites will these days tell other users when a person is online, or allow you to reveal that fact yourself via posting comments, photos, etc, its not too difficult.

But enough about the feature itself; what does it mean to us? This means that an attacker can log you into any site you decided to trust via CSRF attacks because the site cannot tell if you've entered a password. Now this might not seem important, but it is very important for both large and targeted attacks because the user no longer needs to be logged into the service you want to attack, but merely logged into the central service.

Even worse, this fact is completely misrepresented to users. The questions that are posed essentially revolve around whether you trust the site that wants to verify your identity, and whether or not your OpenID provider should always verify your identity to this source, so even if a user is generally cautious about these kind of things, if something like an email provider started using this a user would be more likely to trust the site, and cause security issues. Essentially the more critical the information you are accessing the more likely to trust the site you are trying to access, and so the more critical sites are the ones which will have issues with this; lets just hope they don't use OpenID though, because leaving DNS security, essentially up to your users, is as about the worst idea anyone could come up with, but that's another rant altogether.

For reference, the 3 OpenID providers I tested were:

http://www.myopenid.com/ where users are asked if they want to allow the site they want to log into to be able to verify their identity, and the insecure answer is "Allow Forever"

http://www.claimid.com/ where users are asked if they want to log into a site, and the insecure answer is "Login and Trust"

http://www.videntity.org/ where users are asked if they trust the remote site with their identity, and the insecure answer is "Yes, and don't ask me Again"

Another insecure 'feature' is the lack of need to enter a password to register for a site. Out of those 3 OpenID vendors, only http://www.claimid.com/ asked users for a password when registering for a site, the other two had only CSRF protections. This is admittedly not particularly serious because you still need an XSS (or similar) flaw in the OpenID provider's site before you can take advantage of the design idea, but it is rather worrying that people designing secure systems don't seem to want to implement defence in depth.

On Stefan Esser's CSRF Protection Idea

A while ago I read a post entitled CSRF protections are not doomed by XSS by Stefan Esser which proposed an interesting method of using domain boundaries to stop an XSS hole in the main domain being used to extract form tokens and circumvent CSRF protections, and it would even go so far as being able to stop an XSS vuln in one form being able to circumvent the CSRF protections of another.

And even if it is more difficult to implement than simple token protections, it is still feasible if you use a wildcard DNS entry, and have a check on each form which checks if the $_SERVER['HTTP_HOST'] is the appropriate one, and if not then redirect to the appropriate one.

And I was even going to implement an example, until I realised one simple flaw; its all still hosted on the same server, and the only thing separating it is the HTTP Host header which can easily be forged via XMLHttpRequest or FlashRequest, so this protection can easily be beaten.

Which got me thinking; what would you need to add for it to work as intended? Well, you could put all the forms on separate servers, but that doesn't seem at all practical, the only really viable solution is to add more authentication mechanisms which are readable only by the specific subdomain and no others.

And thats really the biggest issue - you have to create an extra session key for every single form you have on your site, and set a cookie for them, and they all have to be set at login, and can never be regenerated because XMLHttpRequest and (possibly) FlashRequest can read response headers and extract the cookies being set on subdomains.

So while it is still of course possible to implement this, it seems completely impractical to create yet another session id which the server has to keep track of for every single form.

But if anyone has actually implemented something similar to this in any environment I'd really love to hear about it.