On Web Extensions shortcomings and their impact on add-on security

Recently, I reported a security issue in the new Firefox Screenshots feature (fixed in Firefox 56). This issue is remarkable for a number of reasons. First of all, the vulnerable code was running within the Web Extensions sandbox, meaning that it didn’t have full privileges like regular Firefox code. This code was also well-designed, with security aspects taken into consideration. In fact, what I found were multiple minor flaws, each of them pretty harmless. And yet, in combination these flaws were sufficient for Mozilla to assign security impact “high” to my bug report (only barely, but still). Finally, I think that these flaws only existed due to shortcomings of the Web Extensions platform, something that should be a concern given that most extensions based on it are not well-designed.

The Firefox Screenshots feature was introduced in Firefox 55 and allows users to easily take a screenshot of a web page or some part of it and upload it to a web service. All uploaded screenshots are public but you have to know the URL. Technically, this feature is really a browser extension that is integrated into Firefox. And when I looked at this extension, I immediately noticed a potential weakness: when you click its toolbar button, the extension needs to show you some user interface to select a website part and actually take the screenshot. And it will inject that user interface into the webpage. So a malicious webpage could in theory manipulate that user interface.

For years, I have been arguing that injecting trusted content (such as an extension’s user interface) into untrusted websites is a bad idea and should be avoided at any cost. However, Google Chrome won the extensions war and Firefox extensions are now just as limited as Chrome extensions always were. If a toolbar button with pop-up or developer tools panel won’t do for your extension, then there is no way around injecting extension’s user interface into the webpage. Sucks being an extension developer these days.

Of course, there are measures which can be taken to limit the potential damage, and Firefox Screenshots takes them. The extension injects only <iframe> elements into the page with an extension page loaded into the frame. The browser’s same-origin policy prevents the website from accessing anything within the frame. All it can do is manipulate the <iframe> element itself. For example, it could remove this element and effectively prevent Firefox users from taking screenshots on this website. Not great but not a big deal either.

Firefox Screenshots uses a slightly untypical approach however. The page loaded into the frame is always blank and the content is being determined by the content script that creates it:

let element = document.createElement("iframe");
element.src = browser.extension.getURL("blank.html");
element.onload = () => {
  element.contentDocument.documentElement.innerHTML = ...;
  // event handlers attached here
};

I’m moderately certain that this approach wouldn’t work in Chrome, the content script wouldn’t have the privileges to access frame contents there. But the real issue is a different one: the load event handler doesn’t verify what frame it injects the user interface into. The website can load about:blank into this frame and it will also trigger the extension’s load event handler. This way the extension can be tricked into injecting its user interface into a frame that the website can access and manipulate.

What would a malicious website do with this? It could generate fake events and select an area for the screenshot without any further user interaction. Interestingly, actually taking the screenshot wasn’t possible because the corresponding event handler checked event.isTrusted and wouldn’t react to fake events. But taking the screenshot merely requires the user to click a particular button. By making that button very large and transparent one can make sure that the user will trigger that screenshot no matter where they click (clickjacking).

At this point I decided to file a Firefox bug, well aware that it was unlikely to reach even “moderate” as security impact. After all, what’s the worst thing that can happen? A website that tricks users into screenshotting some obscenity? Paul Theriault’s reply in my newly filed bug went into that exact direction but this sentence made me think further:

worst case would be the ability to streak the users screenshot Uris but I don’t think that is possible through this bug.

Right, the website cannot read out the location of user’s other screenshots. But can’t it figure out the location of the screenshot it just took? The extension tells you that the screenshot location is copied to the clipboard. Yet Web Extension APIs don’t allow “proper” clipboard access, you have to use ugly tricks involving document.execCommand. And these ugly tricks won’t work in the extension’s background page right now, meaning that you are forced to use some untrusted context for them. And sure enough: Firefox Screenshots was running the “copy to clipboard” code in the context of the website, meaning that the website could intercept the value copied or manipulate it.

So what we have now: a malicious website can detect that a user tries to make a screenshot of it and hijack that process so that an arbitrary screenshot is taken with merely one more click anywhere on the page. And it can read out the location of this screenshot which lets it access it given that all screenshots are public. Users are easy to trick into performing both required actions (click and “Screenshots” toolbar icon and then click on the website) via social engineering. And the potential damage?

Websites are not supposed to know how exactly they are rendered, APIs like drawWindow() are reserved for privileged code. This is required to avoid reopening CSS History Leak for example. If websites could reliably distinguish visited and unvisited links, they could tell which websites the user visited in the past. But there is a far worse issue here: Firefox Screenshots can create screenshots of third-party frames within the webpage as well. So a website could for example load https://mail.google.com/ into a frame (not really, this particular website forbids framing), trick the user into starting screenshot creation, then screenshot that frame and read out the resulting screenshot. Oops, that’s your Google user name and all your emails in the screenshot leaked to a malicious website!

That’s three distinct issues: not ensuring that the frame to receive extension’s user interface is really trusted, not rejecting fake events in all event handlers consistently and performing copying to clipboard within an untrusted context. Nobody would treat any of these issues with priority when looking at them in isolation. I bet that similar issues will pop up in numerous extensions. If Mozilla is serious about enabling extensions and preventing security issues, adding integration points for extension’s user interface that don’t force them into untrusted contexts should be a priority. Also, the current state of clipboard manipulation is a huge footgun. At the very least, copying to clipboard should work on the background page. Proper APIs for clipboard manipulation would be better however.

Comments

There are currently no comments on this article.