I Know What Websites You Are Logged-In To (Login-Detection via CSRF)

Now that we know a lot about a visitor’s browser, we can mix and match several techniques – six, by my count — that http://maliciouswebsite/ can use to learn what other websites a visiting browser is logged in to – an online bank, social network, email provider, a local home router’s Web interface, and basically anything else.

[youtube]https://www.youtube.com/watch?v=wjGaspK3H_g[/youtube]

Each of the login detection techniques described below, while subtly different, relies on a website’s fundamental ability to tell a browser to send Web requests to arbitrary locations. When this ability is used maliciously, the attack is generally referred to as Cross-Site Request Forgery: the creation of a real, but forged, Web request — one the visitor certainly didn’t want to be made. For example, when a browser visits http://maliciouswebsite/, the Web page may include HTML that calls in images from http://thirdparty/. Like so:

<* img src=”http://thirdparty/image.png” />

What happens is the visiting browser automatically sends a “GET” request to http://thirdparty/ — something like:

GET /image.png HTTP/1.1

Host: http://thirdparty/

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1

Cookie: ASPSESSIONIDQSCQAQBD=FFLCANFPGPGIPHGJGGNELFLB

 

Pay close attention to the “Cookie” header that the browser automatically sends. Whenever a browser makes a Web request, it’ll automatically attach any cookies stored under that domain. In this case the domain is “thirdparty.” So if the browser is logged-in to http://thirdparty/ when visiting http://maliciouswebsite/, then the authenticated session cookies for “thirdparty” will be sent with the IMG GET request. This fundamental design in Web technology is what makes browser Login-Detection possible.

Here are six specific examples of cross-site request forgery:

 

1) Log-in Detection via Authenticated Images (Event Handler)

Let’s consider a scenario where the image http://thirdparty/image.png is only accessible if the browser is currently logged in to http://thirdparty/, as would be the case if it were a private file upload storage service. If the browser is NOT logged-in when http://maliciouswebsite/ instructs the browser to make the Web request, the image will obviously not be returned. If the browser is logged-in, the image will be returned normally.

Based upon this binary behavior, http://maliciouswebsite/ can detect the difference between a logged-in and not logged-in browser, and by attaching a small bit of Javascript to the IMG tag, can return that information to the malicious site:

 <* img src=”http://thirdparty/image.png” onload=”loggedin()” onerror=”notloggedin()” />

When encountering the above HTML code on http://maliciouswebsite/, the “loggedin” function assigned to the OnLoad event handler will execute if the browser is logged-in (image returned successfully). If the browser is not logged-in (image did not return), the “notloggedin” function assigned to the OnError event handler will execute. This Login-Detection technique can be extended to any website provided that an authenticated image URL can be identified ahead of time.

 

2) Log-in Detection via Authenticated Javascript (Event Handler)

Images are not the only Web page resource that can be used to perform Login Detection. It is also possible to use Javascript resources, as long as the content from http://thirdparty/ returns differently depending on the browser’s logged-in status.  The method is nearly identical to the IMG version above in which the OnLoad and OnError event handlers are attached to a SCRIPT tag as the means of detection.

<* script src=”http://thirdparty/javascript.js” onload=”loggedin()” onerror=”notloggedin()”> <* /script>

Obviously then if the browser is logged-in to http://thirdparty/, the Javascript should return successfully and fire the OnLoad event handler. If the browser is not logged-in, the Javascript will not return successfully and the OnError event handler will fire instead.

 

3) Log-in Detection via Authenticated Javascript (Object Detection)

There are some limitations with using event handlers on SCRIPT tags as described above. The first and foremost is that some browsers (Internet Explorer in particular) do not support OnError on SCRIPT tags. There isn’t much to be done about that. However, sometimes executable Javascript will return whether or not the browser is logged-in, just different Javascript. In this case, it is possible to perform object detection.

When the http://thirdparty/ SCRIPT tag loads the requested Javascript, there could be objects, variable names and values, and other pieces of the browser DOM that may be different depending on the log-in state. It is fairly straight-forward to identify and detect these differences.

<* script src=”http://thirdparty/javascript.js”> <* /script>

<* script>

if (typeof loggedInObject != ‘undefined’){

console.log (“Logged-In”);

}

<* /script>

 

As in the IMG example, employing this technique just takes some upfront research.

 

4) Log-in Detection via Authenticated Cascading Style Sheets (Object Detection)

As is often the case, not all websites have authenticated Javascript resources, but they may have authenticated Cascading Style Sheet (CSS) resources that can be used in similar ways. When CSS from http://thirdparty/ is called in, via a LINK tag, it is possible for the stylesheet to be different, or not return at all, depending on the browser’s logged-in state.

<* link rel=”stylesheet” type=”text/css” href=”http://thirdparty/stylesheet.css” />

Just as with Javascript object detection, it is possible for Javascript to detect the differences in the style sheet rules attached to the DOM.

 

5) Log-in Detection via IFRAMEs (iframe.contentWindow.length)

If none of the previous techniques work for a given website, IFRAMEs provide yet another option. First, pick a Web page URL on http://thirdparty/ that returns differently depending on the log-in state of the browser.  Obviously there are potentially quite a number of Web pages on http://thirdparty/ to choose from — account profile pages are a personal favorite. Place that URL into an IFRAME tag.

<* iframe id=”login” src=”http://thirdparty/profile/”><* /iframe>

For some reason, on certain URLs, the iframe.contentWindow.length property value will be different depending on the login-state of the browser. And for some reason, obtaining the value of this property cross-domain does not raise a same-origin policy security exception.

<* script>

if (iframe.contentWindow.length > 0) {

console.log (“Logged-In”);

}

<* /script>

In this example, if the value of “iframe.contentWindow.length” is greater than 0, then the browser is logged-in to http://thirdparty/. If the value is 0, then the browser is probably not logged-in. When using this technique from one website or Web page to the next, the lengths will often vary. So, as with the other approaches, some up front research is required.

 

6) Log-in Detection via IFRAMEs (XFO Detection)

The aforementioned IFRAME length property technique can work quite nicely, as long as http://thirdparty/ does not use something called “X-FRAME-OPTIONS (XFO).” XFO is an HTTP response header, mostly used to combat Clickjacking, that informs a supporting browser if the Web page should be allowed to render in a FRAME or IFRAME.

A header value of “X-Frame-Options: DENY” says that the browser should never allow the Web page to be framed by anyone. “X-Frame-Options: SAMEORIGIN” means only the hosting domain, such as “thirdparty,” is allowed to frame the page.  In either case, http://thirdparty/ Web pages are not allowed to be framed by http://maliciouswebsite/. For the purposes of Login Detection, it is also very helpful to be aware that IFRAMEs do not execute OnLoad event handlers when XFO protection is present.

There are scenarios where XFO is used; it is often not used across all [authenticated] Web pages on a website. This can actually assist in identifying whether or not a browser is logged in, by — for example —  calling in the following HTML code:

<* iframe id=”login” src=”http://thirdparty/profile/”><* /iframe>

A browser that is logged in to http://thirdparty/, the authenticated profile Web page may load normally in the IFRAME without XFO protection. If the browser is NOT logged-in, the Web page will redirect to a login page (http://thirdparty/login.html), which has XFO protection. Therefore, if http://maliciouswebsite/ can detect the presence or absence of XFO protection on the IFRAME, then Login Detection is possible.

In such a case, all http://maliciouswebsite/ needs to do is create an IFRAME, set an OnLoad function that will immediately remove the IFRAME from the DOM, and then use a setTimeout to check for its existence a few seconds later. If the IFRAME still exists when the timeout executes, this probably means an XFO was present which prevented the IFRAME removal (via the OnLoad), which in turn means the browser is not logged-in. If the IFRAME no longer exists, it probably means the profile Web page loaded with XFO protection, and the browser is logged-in.

Below is a quick example of XFO detection, without any Login Detection checks, on a few websites.

 

<* script src=”http://ajax.googleapis.com/ajax/libs/dojo/1.7.2/dojo/dojo.js”><* /script>

<* script>

var urls = [

'http://www.wikipedia.org/',

'http://ha.ckers.org/',

'http://www.google.com/',

'http://www.facebook.com/',

'https://github.com/',

'http://daringfireball.net/',

];

function detect() {

dojo.forEach(urls, function(url) {

var iframe = dojo.create(“iframe”, { src: url, id: url });

dojo.attr(iframe, “style”, {display: ‘none’});

dojo.connect(iframe, “onload”, function() {

dojo.destroy(iframe);

});

dojo.place(iframe, dojo.body());

setTimeout(function () {

var obj = dojo.byId(url);

if (obj) {

dojo.destroy(iframe);

var entry = dojo.create(“li”, null, dojo.body());

entry.innerHTML = “Yes: ” + url;

} else {

var entry = dojo.create(“li”, null, dojo.body());

entry.innerHTML = “No: ” + url;

}

}, 3000);

});

}

<* /script>

 

These are very simple techniques that allow a malicious site to determine your actual location or identity.  Other attacks can then reveal even more information; I’ll explain how in the next section.

 

I Know…

This entry was posted in Web Application Security on by .

About Jeremiah Grossman

Jeremiah Grossman is the Founder and interim CEO of WhiteHat Security, where he is responsible for Web security R&D and industry outreach. Over the last decade, Mr. Grossman has written dozens of articles, white papers, and is a published author. His work has been featured in the Wall Street Journal, Forbes, NY Times and hundreds of other media outlets around the world. As a well-known security expert and industry veteran, Mr. Grossman has been a guest speaker on six continents at hundreds of events including TED, BlackHat Briefings, RSA, SANS, and others. He has been invited to guest lecture at top universities such as UC Berkeley, Stanford, Harvard, UoW Madison, and UCLA. Mr. Grossman is also a co-founder of the Web Application Security Consortium (WASC) and previously named one of InfoWorld's Top 25 CTOs. He serves on the advisory board of two hot start-ups, Risk I/O and SD Elements, and is a Brazilian Jiu-Jitsu Black Belt. Before founding WhiteHat, Mr. Grossman was an information security officer at Yahoo!