Feed Sign in with OpenID OpenID

Simon Willison’s Weblog

Understanding the Greasemonkey vulnerability

If you have any version of Greasemonkey installed prior to 0.3.5, which was released a few hours ago, or if you are running any of the 0.4 alphas, you need to go and upgrade right now. All versions of Greasemonkey aside from 0.3.5 contain a nasty security hole, which could enable malicious web sites to read any file from your hard drive without you knowing.

Unfortunately, 0.3.5 disables all of the GM_ API functions, without which many of the more interesting user scripts out there simple won’t work. This is a temporary measure—the GM_ functions should return in a later release, once the security problem with them has been resolved.

I’m going to explain how the vulnerability works, because it illustrates a number of interesting concepts in both web application security and JavaScript.

Same-origin policy

JavaScript has always enforced a same-origin policy for scripts loaded over the internet. This originally applied to scripting between frames (and iframes): a script loaded from a certain domain is only allowed to access the DOM of other pages loaded from that same domain. The same restriction has been extended to XMLHttpRequest—you are only allowed to make an XMLHttpRequest call back to the domain from which the script was originally loaded.

This policy exists to prevent cross-domain attacks. Say for example you work for a company with an intranet hidden behind the firewall, full of interesting proprietary information. Without the same-origin policy, malicious sites that you visit on the public internet would be able to read information from your intranet, using your browser as the middle-man.

GM_xmlhttpRequest

The GM_xmlhttpRequest API function does not have this restriction—it can load data from any domain. This enables a whole host of interesting user scripts—the most famous of which is probably Book Burro, which shows comparison prices from different online stores for the item you are currently looking at on Amazon, Barnes and Noble and more.

Malicious user scripts could use this feature to steal information from your private intranet, but malicious user scripts could also do all manner of other nasty things—stealing your Hotmail password for example. This is why you should never install a user script from an untrusted source without first reviewing the code.

Restricting API functions to user scripts only

To keep things safe then, it is essential that the GM_ family of API functions can only ever be used by user scripts, not by code running on pages that you have visited. By installing a user script you have declared it trustworthy—but visiting a web page does not carry that contract.

The way the flawed versions of Greasemonkey do that now is simple: the GM_ functions are added to the JavaScript global object (which is the window object), the user scripts for the page are “injected” using dynamically created <script> elements, they run, then the GM_ functions are removed from the global object to prevent scripts on the page from accessing them. This works because Greasemonkey injection and execution happens just before the onload event is fired—which is when most well behaved scripts kick in.

Object.watch()

Here’s the clever part: JavaScript 1.5 defines a method of the Object class (which is inherited by all other JavaScript objects) called watch. Watch is extremely powerful: it lets you register some code to be executed when a property on some other object is assigned. This is the key to the Greasemonkey vulnerability—by watching the window object for the point at which Greasemonkey adds the API functions, a malicious script can use those functions at the moment they are attached.

The file:// protocol

Here’s the final piece of the puzzle: the file:// protocol in Firefox allows you to view files and directory listings in your browser. Unfortunately, it also allows the GM_xmlhttpRequest function to do the same. It’s not at all hard for a malicious script to use the function to load in files at a known location—or even load in directory listings (as HTML), parse them and use them to find all kinds of things scattered around your hard drive.

Solving the problem

The principle problem then is the requirement for “safe” Greasemonkey API functions—that is, functions that can be used by the user scripts but not by code running on a website. Aaron is looking in to this right now—it looks like the solution will require a minor change to be made to many existing scripts, but the trade-off in terms of security is more than worth it. The GM_xmlhttpRequest function will also be modified to disallow file:// URLs.

Until then, 0.3.5 is the only safe version of Greasemonkey.

This is Understanding the Greasemonkey vulnerability by Simon Willison, posted on 20th July 2005.

Tagged , ,

View blog reactions

Next: Exciting developments with Django

Previous: Introducing Django

18 comments

  1. Thank you, thank you, thank you for describing what exactly is going on here. I was getting pretty frustrated with all the OMG-Firefox-flaw coverage out there. Its good to know there's a way to put a smiley on the monkey in my browser in the near future.

    Adam Keys - 20th July 2005 04:42 - #

  2. I suggest you get the Mozilla security group to carefully review the fix you come up with. Trying to do this sort of cross-domain restriction from scratch is very hard. For example: what happens if you pass a javascript: URL? And that sort of thing.

    Gerv - 20th July 2005 08:26 - #

  3. Wouldn't it be much simpler to restrict all access to the filesystem (the file:// protocol) of the Firefox Browser? I think, a browser should only have fileaccess to its installation- and data-directories! Everything else should be retrieved with the http:// protocol....

    B. Ohr - 20th July 2005 09:02 - #

  4. B Ohr: You can't remove the file:// protocol from the browser completely as it's not only a useful feature in principal, but also heavily used by many people.

    Both IE and Firefox have functionality to save pages to disc with their resources in an accompanying directory. Some people use that for personal archiving. Additionally, many people (novices and pros alike) use file:// in website development when not running their own local webserver.

    ~

    Very interesting to read the explaination of the problem too. Thanks very much for that Simon. I does of course serve as a another 'must learn more about scripting' slap, but that's probably for the best!

    Ben - 20th July 2005 09:33 - #

  5. Nice explanation. This is certainly a problem, but to be exploited one would have to "Install User Script..." on a new malicious script, right? There's no way for anyone to hijack one that's already been installed. Shouldn't the warning be "Don't install any more user scripts until a new version is released" instead of disabling GreaseMonkey entirely until a new release can be created? This seems a little drastic.

    Dave Mabe - 20th July 2005 16:23 - #

  6. Dave: No, you don't have to install a malicious script. The problem is that scripts in web pages that you visit can hijack Greasemonkey and make use of the Greasemonkey API functions. Just by visiting a malicious web site you could have data stolen from your computer.

    Simon Willison - 20th July 2005 16:48 - #

  7. What do the Mozilla Security Group and Firefox's file:// protocol have to do with any of this? It's an extension running in chrome scope, and as such having full access to the system (as the currently logged on user).

    Jan! - 20th July 2005 16:53 - #

  8. "a script loaded from a certain domain is only allowed to access the DOM of other pages loaded from that same domain"

    ...

    "The GM_xmlhttpRequest API function does not have this restriction"

    Actually, if I understand correctly, XMLHttpRequest inately prohibits cross-domain DOM access at any security level. This is why responseXML does not work on GM_xmlhttpRequests. Strangely, as Greasemonkey proved, the responseText can be accessed from priveledged code.

    Lenny Domnitser - 20th July 2005 17:29 - #

  9. You didn't have to use object.watch to hook the GM API. You could also watch for DOM mutation (caused by script injection) and ref window.GM_APIs at that point.

    The one just posted to the list doesn't cause DOM mutation upon injection. Well, unless your script does.

    Jeremy Dunck - 20th July 2005 20:26 - #

  10. Sorry, let me clarify that last statement. I mean to say that there is an alternate to object.watch in all versions prior to 0.3.5. However, the XPI Aaron just posted to the list does not ever add the GM API functions to the content scope. Neither does it evaluate the script by injecting it into the content.

    Jeremy Dunck - 20th July 2005 20:50 - #

  11. Object.watch() is defined in JavaScript 1.2

    Sjoerd Visscher - 20th July 2005 23:32 - #

  12. Since Opera manages without GM_xmlhttpRequest , wouldn't it be best to simply disable GM_xmlhttpRequest in Firefox and Deer Park? (I've been using some of the scripts very successfully in Opera :) .) Maybe an XPI to effect that could be produced? BTW, that analyst for the Forrester Research report cautioning of Greasemonkey vulnerabilities must now be gleeful! Does anyone know who he is or anything about him?

    ckjnigel - 21st July 2005 23:41 - #

  13. Opera has some powerful functions of it own (and they are not available for the page), but they don't concern cross-site scripting AFAIK. I don't think a 'Book Burro' is possible with Opera.

    Opera 8 *does* *not* allow http content to access or link to the local filesystem. This causes some people problems, especially on intranets that link directly to UNC paths. And with some web based games that allow the user to install a large pack of images locally, to limit bandwidth comsumption. But the added fall-back security might be worth it...

    Rijk - 22nd July 2005 11:18 - #

  14. Is it correct that this vulnerability is a problem with GM_xmlhttpRequest and that any versions of greasemonkey that do not support the GM APIs should also be safe? I've got some ancient 0.2.x versions lying around that were before the GM APIs.

    Tim - 22nd July 2005 19:20 - #

  15. Tim, that's correct, but if you don't need GM APIs, you should go with 0.3.5, because that has fixes for FF 1.0.3 and 1.0.4, among various other things. Sadly, 0.3.5 is broken by 1.0.5 (and probably the just-released 1.0.6).

    Jeremy Dunck - 22nd July 2005 21:51 - #

  16. I tried to run a user scrit and got an error on my java script console "GM_xmlhttpRequest is not a function" Is this because of the current security updates to Graesemonkey. I have greasemonkey 0.3.5 installed currently.

    Kris - 23rd July 2005 21:35 - #

  17. Kris, quoting the article just above your comment:

    All versions of Greasemonkey aside from 0.3.5 contain a nasty security hole, which could enable malicious web sites to read any file from your hard drive without you knowing. #

    Unfortunately, 0.3.5 disables all of the GM_ API functions, without which many of the more interesting user scripts out there simple won't work

    Jeremy Dunck - 24th July 2005 05:50 - #

  18. Just wanted to get on the thank you bandwagon. Thanks for some actual details on this issue.

    Smith Ellis - 2nd August 2005 13:39 - #

Comments are closed.

Previously hosted at http://simon.incutio.com/archive/2005/07/20/vulnerability

A django site