Lenny Domnitser’s

⇙ Jump to content


This is a static archive of the domnit.org blog,
which Lenny Domnitser wrote between 2006 and 2009.

UnsafeWindow Considered … Unsafe

Greasemonkey, software that lets users reprogram web pages normally out of their control, has changed the nature of Web browsing. It puts much power on the client side, and carries corresponding security risk. Like with all software, one must beware malicious user scripts, but because user scripts are typically written one-off, but run in a trusted environment, well-intentioned scripts can be rather dangerous.

There are specific poor practices I will describe, which I am prompted to do by a case study, Google Account Multi-Login. The script, which does just the sort of thing Greasemonkey is ideal for, lets people with several Gmail accounts switch between them instantly.

(Here I had warned not to install the script. However, I emailed with the script author while I was writing this, and we decided to hold off on publication until he posted a safe version. Anything from before 13 January 2008 is not safe.) If you have an old version of the script, here’s a scary proof-of-concept attack. Now go uninstall the old one and freshly install the new one.

The combination of 2 sloppy practices allow any website you visit to steal your Google logins. Since most services will send a password reminder when requested, if your Gmail is compromised—what’s a nice word for “you’re fucked”?

Here are the first few lines of the script:

// ==UserScript==
// @name           Google Account Multi-Login
// @namespace      http://eveningnewbs.googlepages.com
// @description    Replaces "Sign out" link on Google pages with a select box of accounts.
// @include        http*://*.google.com*
// @include        http*://google.com*
// @exclude        http*://mail.google.com/*ui=1*
// ==/UserScript==

// Load persistent user data
if (!GM_getValue)
    {alert("You need the newest version of Greasemonkey to run this script. Please upgrade."); return;}
unsafeWindow.gmAUTOLOGIN = GM_getValue("autologin");
unsafeWindow.gmUSERNAMES = GM_getValue("usernames", "").split(",");
unsafeWindow.gmPASSWORDS = GM_getValue("passwords", "").split(",");

And these are the 3 lines that do the damage:

// @include        http*://*.google.com*
unsafeWindow.gmUSERNAMES = GM_getValue("usernames", "").split(",");
unsafeWindow.gmPASSWORDS = GM_getValue("passwords", "").split(",");

As you may guess, the object called “unsafeWindow” is part of the problem. Normally, Greasemonkey scripts run in a special “sandbox,” isolated from untrusted web pages, but unsafeWindow is a way to access the untrusted context. By setting these properties on unsafeWindow, the web page can read the login information.

The include line is not actual code—it is metadata that tells Greasemonkey which pages to run the script on. The asterisks match any string of characters. With relaxed include rules, the logins are not just written to the web page context of Google pages, but any page that matches http*://google.com*. See how my proof-of-concept attack works? http://domnit.org/misc/be-careful-with-greasemonkey.google.com.html matches.

You can avoid problems by minding your includes and using alternatives to unsafeWindow. Scripters can learn more about Greasemonkey at the Greasespot wiki, and absolutely must read Mark Pilgrim’s Greasemonkey Talmud, Avoid Common Pitfalls in Greasemonkey.

Thanks to Jarett for his fast reply and fix. The upside of picking on him in this post is that now I recommend his script.