Background
Google Gears is a well-known RIA infrastructure, used extensively by Google in various services such as Google Docs and Google Reader as well as in non-Google services such as MySpace, Zoho Writer and WordPress.
Gears is a browser extension that allows developers to create richer and more responsive web-applications. One of its key features is the ability to create web-applications that can run both online and offline transparently.
Some of the capabilities Gears introduces are:
- A local server, to cache and serve application resources (HTML, JavaScript, images, etc.) without needing to contact a server
- A database, to store and access data from within the browser
- A worker thread pool, to make web applications more responsive by performing expensive operations in the background
- The HttpRequest API, which implements a subset of the W3C XmlHttpRequest specification
- A Geolocation API that enables a web application to obtain a user's geographical position
(The descriptions above are taken from the Google Gears documentation)
In my opinion, one of the nicest things in Gears is the way it is utilized. This is done by inserting JavaScript calls to Gears' API within the HTML code of the web-application. Therefore, unlike some of its alternatives, Gears can be integrated into existing web-applications easily and fluently.
For a full explanation and usage examples of Google Gears, you are invited to enter the Getting Started section in the Google Gears website.
Like other RIA infrastructures, Google Gears offer developers cross-origin communication capabilities. These capabilities are very important to developers, as they make it much easier to implement mashups and other desirable features.
Security-wise, however, cross-origin communication has some downsides. A poor or careless implementation might allow attackers to break-out of the same-origin policy and mount large scale user-impersonation attacks. The ramifications of such a flaw can be disastrous.
A few months ago, I discovered that the cross-origin communication security model of Google Gears wasn't solid enough, and that under some circumstances it could be bypassed pretty easily.
After coordinating a fix with Google, I can now reveal the details.
Gears' Cross-Origin communication implementation
Let's assume that we are web-developers and that we have a web page located at http://Some.Site/ that needs to gather information from a user-authenticated session at http://Another.Site/.
This can be done by using Google Gears' WorkerPool API. All you have to do is load a Google Gears "worker" (JavaScript code with access to Google Gears capabilities such as Local Server, Http communication and Database) using the createWorkerFromUrl(scriptUrl) method.
Google Gears "workers" that are intended to be loaded from a remote origin must begin with a call to allowCrossOrigin(). This serves as a security measure against unauthorized remote loading of "workers".
If a worker was created from a different origin, all methods on google.gears.factory
will fail in that worker until allowCrossOrigin()
is called.
This prevents cross-site scripting attacks where the attacker could load a worker URL from another domain, then send malicious messages to that worker (e.g. "delete-all-data").
Workers that call allowCrossOrigin()
should check messageObject.origin
and ignore messages from unexpected origins.
Here's an excerpt from Google Gears' documentation:
If a worker was created from a different origin, all methods on google.gears.factory
will fail in that worker until allowCrossOrigin()
is called.
This prevents cross-site scripting attacks where the attacker could load a worker URL from another domain, then send malicious messages to that worker (e.g. "delete-all-data").
Workers that call allowCrossOrigin()
should check messageObject.origin
and ignore messages from unexpected origins.
The Problem
At first sight, this protection seems to be solid.
However, after playing around with the infrastructure, I found that the Google Gears workers' loader has a rather promiscuous policy: it disregards the headers of the Gears worker files it loads!
That fact opens an aperture for malicious attacks. It significantly broadens the options an attacker has for planting malicious Gears worker code in a target website. For example, it is possible to upload files with an image suffix that actually contain Gears Worker code instead. Later on, such a file might be loaded from the context of other domains by a Google Gears Worker loader, despite the fact that it is served as an image file by the web-server!
It follows that the security of websites that contain users' content (forums, web-mails, social networks, office-like services, etc.) might be circumvented and damaged due to this behavior. During my research, I verified that various well-known services are indeed susceptible to the attack described in this summary.
Furthermore, the fact that the Gears worker code doesn't contain concrete "dangerous" characters might actually make it harder for websites to defend against Google Gears-based cross-origin access attacks such as the one described below.
An example of Google Gears worker code:
var wp = google.gears.workerPool;
wp.allowCrossOrigin();
wp.onmessage = function(a, b, message) {
var request = google.gears.factory.create('beta.httprequest');
request.open('GET', 'http://TARGET.SITE/SENSITIVE_PAGE.htm');
request.onreadystatechange = function() {
if (request.readyState == 4) {
wp.sendMessage("The response was: " +
request.responseText, message.sender);
}
};
request.send();
}
The script above grabs information from http://TARGET.SITE and then leaks it back to its remote caller using Google Gears' built in messaging API.
Flow of Attack
- Attacker creates a text file that contains (malicious) Google Gears commands (Accessing the DB, using the HttpRequest module, etc.).
- Attacker finds a way to put the text content into a target domain (http://TARGET.SITE/Upload/innocent.jpg, for example). The Gears "worker" code does not contain suspicious characters (<,>, etc...), it is therefore less likely to be filtered by http://TARGET.SITE's server-side logic.
- Attacker creates http://ATTACKER.SITE/attack.html which contains some Google Gears code that loads and executes http://TARGET.SITE/Upload/innocent.jpg.
- The code embedded in innocent.jpg (in this example) runs in the context of http://TARGET.SITE. It therefore has permissions to access Google Gears client-side objects such as the DB, the local server data or web resources (with the victim's credentials) using the HttpRequest module built into Google Gears.
- All information collected in the previous phase can easily be leaked back to http://ATTACKER.SITE using Google Gears' standard messaging mechanism.
Note:
While http://ATTACKER.SITE has to be approved for using Google-Gears, http://TARGET.SITE can be any site that hosts user-created content, even if it doesn't use Google-Gears at all.
The Fix
Following my reporting to Google of the aforementioned flaw and attack, a patched version of Google Gears was released. The fix is based on a special Google-Gears Content-Type header value (application/x-gears-worker) that must be sent by the web-server when it serves Google-Gears worker code files. Without that value the loading of such worker files is denied.
While this looks like a great solution, it suffers from a slight backward-compatibility issue. Web-developers who rely on Google Gears should be aware that the fix might require some changes, such as creating a special rule in the web-server for serving Google-Gears worker code files.
For more information about the new security restriction described above, please visit the Google-Gears cross-origin workers documentation.
Acknowledgments:
I would like to thank the Google Gears security team for their quick responses and the efficient way in which they handled this security issue.