Closed Bug 1120398 Opened 10 years ago Closed 3 years ago

Security: Addons with no contentaccessible resources can be enumerated via differing error results


(Core :: DOM: Security, defect, P5)

34 Branch
Windows 7





(Reporter: erandog, Unassigned)



(4 keywords, Whiteboard: [domsecurity-backlog][fingerprinting] [fp-triaged]verify both chrome: and resource: when fixed)


(2 files)

Attached file
User Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0
Build ID: 20141126041045

Steps to reproduce:

Create a malicious webpage with an IFRAME whose SRC attribute loads another URL from the malicious server.

This new URL causes the server to respond with a "302 Found" HTTP code, redirecting to a "resource" or "chrome" URL coresponding to an addon component.

Actual results:

If the addon is installed and enabled, an error page will load in the IFRAME and the IFRAME load event WILL NOT fire.

If it is not installed or disabled, a blank page will be shown and the IFRAME load event WILL fire.

So, detecting whether the IFRAME load event fires or not enables the malicious page to find out if the addon is installed and enabled.

Please see attached file for a proof of concept. It contains 3 files:
* firefox_addons.htm -> the malicious page
* redirect.php -> Used to generate redirections
* screen_captures.pdf -> Some screen captures

To test it, just uncompress the archive in a web server directory and load "firefox_addons.php" in your Firefox browser through HTTP or HTTPS

Expected results:

There should be no difference (at least, as far as the JavaScript code cand "know") in the response depending of the status of the addon.

Resources for not installed, disabled or nonexistent addons should be treated as if they were non-contentaccessible.
I have realized that XMLHttpRequest also can be used to check for addons as it has a similar problem to the one stated above.

Redirections to a not installed, disabled an nonexistent addon resource seems to not trigger an exception while redirections to an actual resource from an enabled addon does. 

The code, using the same "redirect.php" file from previous examples, could be:

// Extensions to check
var extensions = [
	{"name":"FireShot", "url":"chrome://fireshot/content/"},
	{"name":"FireBug", "url":"chrome://firebug/content/"},
	{"name":"Down Them All", "url":"chrome://dta/content/"},
	{"name":"Download Helper", "url":"chrome://dwhelper/content/"},	
	{"name":"Skype Click to Call", "url":"resource://skype_ff_extension-at-jetpack/"},
	{"name":"Mcafee VScore", "url":"chrome://vscore/content/"}

var report = "";

for (var i in extensions) {
	var extension=extensions[i];
	var xhr = new XMLHttpRequest();"GET", "redirect.php?url="+ extension.url, false);
	try {
		report += + "\n";

Component: Untriaged → Security
Product: Firefox → Core
bz, have any ideas where to start digging for this one?
Flags: needinfo?(bzbarsky)
reporter requested a security bounty in mail to security@
Flags: sec-bounty?
Keywords: privacy
So with a redirect, here's what happens.

1)  We create an nsIURI for the new URI.
2)  We create an nsIChannel for that nsIURI.
3)  We do a channel redirect notification.
4)  If the redirect is not canceled, we go ahead and do the new load.

If the chrome package is unknown, step 2 fails with NS_ERROR_FILE_NOT_FOUND, so we never reach step 3.  In the no-appcache case, that returns the error to nsHttpChannel::AsyncProcessRedirection which returns it directly to nsHttpChannel::ProcessResponse.  DoNotRender3xxBody(NS_ERROR_FILE_NOT_FOUND) seems to be false, so we go ahead and do ContinueProcessResponse.

ContinueProcessResponse has a special case to treat NS_ERROR_DOM_BAD_URI on a redirect as an error, but otherwise will go ahead and render the response body.  The description above talks about "a blank page will be shown", but presumably that's just because the 3xx response from the server didn't include any useful response body.  Otherwise, _that_ would have been shown.

In fact, you could just use script in that response body to communicate to the parent page whether it got loaded.

Anyway, in the case when the extension does exist, NewChannel succeeds, the redirect notification makes its way to the security manager, CheckLoadURIWithPrincipal is called, and if the resource is not whitelisted the redirect is canceled with NS_ERROR_DOM_BAD_URI.

So we have several options here:

1)  Change the HTTP code in necko to somehow special-case NS_ERROR_FILE_NOT_FOUND here.
2)  Change the chrome protocol handler to fail channel creation with NS_ERROR_DOM_BAD_URI
    instead of NS_ERROR_FILE_NOT_FOUND when there is no matching chrome package.  I expect
    this is one of the error cases in ConvertChromeURL; both the direct !baseURI one and
    the GetFlagsFromPackage one would probably need to be changed.
3)  Change the chrome protocol handler to allow creating such a channel but in an 
    already-canceled state so loading from it just fails in an async way.  This would
    allow us to get to the security manager check in the algorithm above.

My preference is #2 for a quick fix and perhaps #3 for a longer-term fix, because #2 introduces this weird coupling on the exact error code used.

> bz, have any ideas where to start digging for this one?

In the future, for HTTP redirect cases, maybe :mayhemer?   ;)
Ever confirmed: true
Flags: needinfo?(bzbarsky)
rating this low because it's not a security vulnerability per se, but could help an attacker know which victims to target with an add-on specific vulnerability. And of course it's a privacy/fingerprinting issue that folks trying to create anonymous browsing will be unhappy about.
Keywords: sec-low

I have realized that previuos comments deal with "chrome://" URIs.

Please include "resource://" URIs too, as they can be (mis)used in the same way. In the example code they were used to detect the "Skype Click to Call" addon.
It's still a "low" severity "security" problem, but raising the severity as a lame hack to try to indicate it's bad from a privacy POV.
Keywords: sec-lowsec-moderate
Whiteboard: user fingerprinting
Flags: sec-bounty? → sec-bounty+
Group: core-security → dom-core-security
needinfo? on myself to investigate what happens in the resource: case. At some point we'll find out that a particular alias is not registered and fail so returning the same error might get us the desired result.
Flags: needinfo?(dveditz)
Whiteboard: user fingerprinting → user fingerprinting; verify both chrome: and resource: when fixed
Flags: needinfo?(dveditz)
Flags: needinfo?(dveditz)
FWIW Jonas Lejon contacted us last week pointing to a PoC he had written ( (login with tor/tor112) checking for Tor Browser basically with

function ChkTorBtn() {
  var msg = "";
  try {
    var http = new XMLHttpRequest();'HEAD', "chrome://torbutton/locale/", false);
    msg = http.status;
  catch(err) {
    msg = err;
  return msg;

So, no redirect and fancy things involved.
That's not all that page is doing to detect Tor, but yeah, you don't need redirects to detect installed chrome: or resource:

resource: is especially problematic since they are current intentionally available to <img src=>. Any package that defines a resource: alias has effectively declared that "contentaccessible=yes". Perhaps we need the ability to hide some resource: declarations from web content since many times they are for internal use. The most consistent with what we have would be to require an explicit contentaccessible=yes, but that is guaranteed to break lots of add-ons. The opposite, making contentaccessible=no available, guarantees that almost no add-ons will be that private except for a handful of the most privacy-conscious ones.

Our best hope is for WebExtensions to get the defaults right.

Anyway, Georg is right that you don't need redirects. The following code will detect installed chrome and resource packages. For chrome it's sufficient to use chrome://<pkg>/content/ and for resource you can use resource://<alias>/

function ChromeExists(url) {
  var msg='';
    var xhr= new XMLHttpRequest();'GET',url,false);
    console.log("*** unexpected: no throw for "+url);
  } catch(e) {
  return /NS_ERROR_DOM_BAD_URI/.test(msg);
Flags: needinfo?(dveditz)
Keywords: testcase
Maybe the answer is to make the ChromeRegister GetFlagsFromPackage() return NS_ERROR_BAD_DOM_URI for unknown packages. would that be sufficient? what tests would we break?

For resource: it might be enough to change the return value in nsResProtocolHandler::GetSubstitutionInternal (we call this when the SubstitutingProtocolHandler has already failed to find a registered match).

But as I mentioned before, resource: is currently public and detectable through sub-resource checks, like an <img> tag with an onerror handler.
Depends on: 903959
Component: Security → DOM: Security
Whiteboard: user fingerprinting; verify both chrome: and resource: when fixed → [domsecurity-backlog] user fingerprinting; verify both chrome: and resource: when fixed
The <img src> thing for resource should generally be fixed by bug 863246 when the addon _is_ installed.

When the addon is _not_ installed, we should check what actually happens in various cases (<img src>, redirects, direct XHR).  I recommend having separate bugs on resource:// and chrome:// here because they may need different solutions and definitely need separate code changes.
Hey Dan, is this issue still valid in the world of WebExtensions, or can we close this out?
Flags: needinfo?(dveditz)
It's less important in the world of Web Extensions. It's hard to guess the GUID part of a potentially-installed extension's moz-extension:// namespace, but it still applies to Firefox chrome and system addons, and especially the resources of any "Firefox-based" product like Tor Browser. Firefox chrome might be a way to distinguish different versions if you're trying to spoof what you have (though there are probably feature-detection ways to do that) and system addons might reveal if you're part of an experimental cohort or something.
Flags: needinfo?(dveditz)
Priority: -- → P5
Whiteboard: [domsecurity-backlog] user fingerprinting; verify both chrome: and resource: when fixed → [fp-triaged] [domsecurity-backlog] user fingerprinting; verify both chrome: and resource: when fixed
Our understanding of this issue is that it would allow an attacker to detect that a user is using Tor Browser (by detecting Tor's default system extensions), possibly do version detection by poking at the chrome, and detect things like Test Pilot and other Mozilla-installed system extensions (hotfixes perhaps).

Based on this, anti-fingerprinting's perspective is that this is P5.
Whiteboard: [fp-triaged] [domsecurity-backlog] user fingerprinting; verify both chrome: and resource: when fixed → [fp-triaged] [domsecurity-backlog][fingerprinting] verify both chrome: and resource: when fixed
Summary: Security: Addons with no contentaccessible resources can be enumerated → Security: Addons with no contentaccessible resources can be enumerated via differing error results
Whiteboard: [fp-triaged] [domsecurity-backlog][fingerprinting] verify both chrome: and resource: when fixed → [domsecurity-backlog][fingerprinting] verify both chrome: and resource: when fixed
Whiteboard: [domsecurity-backlog][fingerprinting] verify both chrome: and resource: when fixed → [domsecurity-backlog][fingerprinting] [fp-triaged]verify both chrome: and resource: when fixed

sec-low because it can only be used for fingerprinting the browser.

Closed: 3 years ago
Keywords: sec-moderatesec-low
Resolution: --- → WONTFIX
Group: dom-core-security
You need to log in before you can comment on or make changes to this bug.