Bug 1320039 (CVE-2016-9902)

Pocket extension unnecessarily exposes its messaging interface to web pages

VERIFIED FIXED in Firefox 50

Status

()

Firefox
Pocket
VERIFIED FIXED
5 months ago
29 days ago

People

(Reporter: Wladimir Palant, Assigned: Gijs)

Tracking

({csectype-priv-escalation, sec-moderate})

Trunk
Firefox 53
csectype-priv-escalation, sec-moderate
Points:
---
Bug Flags:
sec-bounty +

Firefox Tracking Flags

(firefox50+ verified, firefox51+ verified, firefox52+ verified, firefox53+ verified, firefox-esr4550+ verified)

Details

(Whiteboard: [adv-main50.1+][adv-esr45.6+])

Attachments

(3 attachments, 2 obsolete attachments)

(Reporter)

Description

5 months ago
Created attachment 8814034 [details]
Proof of concept webpage

Note: I am reporting this issue here rather than to Pocket because Pocket is an integral part of Firefox and cannot be disabled or removed by regular means.

The bubble displayed by the Pocket toolbar button is actually an <iframe> loading from either about:pocket-signup or about:pocket-saved. In order to communicate with that frame the Pocket extension registers event listeners on browser.xul's document, accepting untrusted events. The event listeners don't do anything to verify the origin of the event so that events bubbling up from the browser's content area will be processed as well.

Steps to reproduce (I tested with Firefox 53.0a1 nightly 2016-11-23 on macOS 10.12):

1. Make sure that E10S is disabled (with E10S events from content won't bubble up to the browser).
2. Click the Pocket button to make sure that the extension is initialized. It doesn't matter whether anything happens after that, you don't need to sign up with Pocket.
3. Now open the attached webpage in Firefox and click the "Open about:preferences with Sync tab selected".

This button will open about:preferences#sync page, something that web pages normally cannot do. Pop-up blocker doesn't affect this, so websites can open any number of pop-ups at any time this way. Security checks don't apply either so opening arbitrary file:///, chrome:// or about: URLs is possible.

Note that this only abuses "openTabWithUrl" message that Pocket listens to. There is a number of other message listeners which might also have abuse potential - e.g. "deleteItem" message allows deleting items from Pocket. Note also that the website can detect when its attack was successful because the Pocket extension will remove the event target from the page.

Also, this issue is completely avoidable. There is only one Pocket frame in the browser document, the event listeners could be registered directly on that iframe element rather than on the document - this would make sure that no events from other sources will be received.
(Assignee)

Comment 1

5 months ago
(In reply to Wladimir Palant from comment #0)
> Created attachment 8814034 [details]
> Proof of concept webpage
> 
> Note: I am reporting this issue here rather than to Pocket because Pocket is
> an integral part of Firefox and cannot be disabled or removed by regular
> means.

Eh, fairly sure that you can just right click the pocket button, click 'remove from toolbar', and that should stop all of this (if not immediately, certainly on the next restart). Also, most of this code was still written by pocket...

Shane, can you make sure we CC whoever needs to be CC'd from the pocket side on this?

> The bubble displayed by the Pocket toolbar button is actually an <iframe>
> loading from either about:pocket-signup or about:pocket-saved. In order to
> communicate with that frame the Pocket extension registers event listeners
> on browser.xul's document, accepting untrusted events. The event listeners
> don't do anything to verify the origin of the event so that events bubbling
> up from the browser's content area will be processed as well.

Ugggh.
Flags: needinfo?(mixedpuppy)
(Reporter)

Comment 2

5 months ago
Well, if you never click the Pocket button it will never initialize and my PoC won't work. But if you click it, and be it out of curiosity - removing it from the toolbar will no longer help you, for the current session you will be vulnerable.
(Assignee)

Comment 3

5 months ago
Created attachment 8814048 [details] [diff] [review]
Patch v0.1

This time, actual origin principal checks against about:pocket-signup/saved *and* restricting the message listeners to the iframe. Mike, can you check that this is sufficient and that I haven't broken anything (looking briefly, it seems OK to me, but...).
Attachment #8814048 - Flags: review?(mozilla)
(Assignee)

Comment 4

5 months ago
(In reply to :Gijs Kruitbosch from comment #3)
> Created attachment 8814048 [details] [diff] [review]
> Patch v0.1
> 
> This time, actual origin principal checks against about:pocket-signup/saved
> *and* restricting the message listeners to the iframe. Mike, can you check
> that this is sufficient and that I haven't broken anything (looking briefly,
> it seems OK to me, but...).

I'm assuming I don't need to touch messages.js because it only runs *inside* the iframes for messages/events going in the other direction. That said, I wonder if you could frame those documents using their chrome URIs and then manipulate them that way :| .
(Reporter)

Comment 5

5 months ago
(In reply to :Gijs Kruitbosch from comment #4)
> That said, I
> wonder if you could frame those documents using their chrome URIs and then
> manipulate them that way :| .

Given bug 1320057 it's probably a good thing that they are accessed via about: URIs that drop all privileges.

Note that changing removeMessageListener() is pointless - it is dead code not being called anywhere and it wouldn't work anyway (it removes the wrong function, not the closure added in addMessageListener). There is also quite a bit of unnecessary complexity in the surrounding code but that's irrelevant for this security issue.
(Assignee)

Comment 6

5 months ago
Created attachment 8814074 [details] [diff] [review]
Patch v0.2

We might as well make it impossible for it to open URLs it couldn't otherwise open itself in its own iframe, for defense in depth purposes.
Assignee: nobody → gijskruitbosch+bugs
Attachment #8814048 - Attachment is obsolete: true
Status: NEW → ASSIGNED
Attachment #8814048 - Flags: review?(mozilla)
(Assignee)

Comment 7

5 months ago
(In reply to Wladimir Palant from comment #5)
> (In reply to :Gijs Kruitbosch from comment #4)
> > That said, I
> > wonder if you could frame those documents using their chrome URIs and then
> > manipulate them that way :| .
> 
> Given bug 1320057 it's probably a good thing that they are accessed via
> about: URIs that drop all privileges.

They are in the iframe we use for the actual UI. But the pages have chrome:// URLs, e.g. chrome://pocket/content/panels/saved.html . They're contentaccessible too, presumably in order for the about: pages to be able to load all the other gunk. That still doesn't let you link to them, fortunately, and AIUI this vulnerability doesn't let you open URLs into arbitrary subframes - "just" in new tabs, which at least avoids then being able to spoof the messages back to these pages (like, say, the suggested tags) which you could then exploit to go other places in the now-system-privileged page you just opened. :|
(Assignee)

Comment 8

5 months ago
(though also, I think/hope that we don't allow system-privileged frames inside content-privileged documents)
(Assignee)

Comment 9

5 months ago
Created attachment 8814076 [details] [diff] [review]
Patch v0.3

If it's unused, let's just take it out, then.
Attachment #8814074 - Attachment is obsolete: true
Attachment #8814076 - Flags: review?(trev.moz)
Attachment #8814076 - Flags: review?(mozilla)
(Reporter)

Comment 10

5 months ago
(In reply to :Gijs Kruitbosch from comment #8)
> (though also, I think/hope that we don't allow system-privileged frames
> inside content-privileged documents)

I'm pretty sure that this is possible as long as a system-privileged script sets location.href.
(Reporter)

Comment 11

5 months ago
Comment on attachment 8814076 [details] [diff] [review]
Patch v0.3

Review of attachment 8814076 [details] [diff] [review]:
-----------------------------------------------------------------

::: browser/extensions/pocket/content/main.js
@@ +364,5 @@
>          // Open a new tab with a given url and activate if
>          var _openTabWithUrlMessageId = "openTabWithUrl";
> +        pktUIMessaging.addMessageListener(iframe, _openTabWithUrlMessageId, function(panelId, data, contentPrincipal) {
> +            try {
> +              urlSecurityCheck(data.url, contentPrincipal, Services.scriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);

Given that this is technically an extension and that the code is probably upstreamed to Pocket, is it a good idea to rely on implementation details like the global urlSecurityCheck function? Besides, just calling scriptSecurityManager directly wouldn't be significantly longer:

  var secMan = Services.scriptSecurityManager;
  secMan.checkLoadURIStrWithPrincipal(data.url, contentPrincipal, secMan.DISALLOW_INHERIT_PRINCIPAL);
(Assignee)

Comment 12

5 months ago
(In reply to Wladimir Palant from comment #10)
> (In reply to :Gijs Kruitbosch from comment #8)
> > (though also, I think/hope that we don't allow system-privileged frames
> > inside content-privileged documents)
> 
> I'm pretty sure that this is possible as long as a system-privileged script
> sets location.href.

Hrmpf. Will file an orthogonal bug about that.
Flags: needinfo?(mixedpuppy)
(Assignee)

Comment 13

5 months ago
(In reply to Wladimir Palant from comment #11)
> Comment on attachment 8814076 [details] [diff] [review]
> Patch v0.3
> 
> Review of attachment 8814076 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> ::: browser/extensions/pocket/content/main.js
> @@ +364,5 @@
> >          // Open a new tab with a given url and activate if
> >          var _openTabWithUrlMessageId = "openTabWithUrl";
> > +        pktUIMessaging.addMessageListener(iframe, _openTabWithUrlMessageId, function(panelId, data, contentPrincipal) {
> > +            try {
> > +              urlSecurityCheck(data.url, contentPrincipal, Services.scriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
> 
> Given that this is technically an extension and that the code is probably
> upstreamed to Pocket, is it a good idea to rely on implementation details
> like the global urlSecurityCheck function?

It also relies on openUILink / openLinkIn, I think? Which is in the same file. So I don't think that relying on this as well is fine.
(Assignee)

Comment 14

5 months ago
(In reply to :Gijs Kruitbosch from comment #13)
> It also relies on openUILink / openLinkIn, I think? Which is in the same
> file. So I don't think that relying on this as well is fine.

Eh, too many negations. But yeah, I think this is fine, tbh.

Updated

5 months ago
Attachment #8814076 - Flags: review+
(Reporter)

Comment 15

5 months ago
(In reply to :Gijs Kruitbosch from comment #13)
> It also relies on openUILink / openLinkIn, I think? Which is in the same
> file. So I don't think that relying on this as well is fine.

Not actually the same thing - it's in the same file now because it moved there. This is documented API (https://developer.mozilla.org/de/docs/Codeschnipsel/Tabbed_browser#Opening_a_URL_in_the_correct_window.2Ftab) and has been stable for a long time. Quite a few extensions rely on it already which is probably why it is still around in that form. urlSecurityCheck() is neither old nor documented.
It's documented to the extent that someone wrote a code snippet that uses it, but that says nothing about it being supported, or its use being good practice. In fact, the en-US version of that page was marked as obsolete because it suggested a lot of bad practices.

I don't think this is worth worrying about. checkLoadURIStrWithPrincipal is no more of a public API than urlSecurityCheck, and it would be much easier to make changes to the latter (which is already used by many add-ons) without breaking compatibility than it would the former.
(Reporter)

Comment 17

5 months ago
Comment on attachment 8814076 [details] [diff] [review]
Patch v0.3

Review of attachment 8814076 [details] [diff] [review]:
-----------------------------------------------------------------

Well, if everybody agrees that this function is safe to use from an extension...
Attachment #8814076 - Flags: review?(trev.moz) → review+

Comment 18

5 months ago
Comment on attachment 8814076 [details] [diff] [review]
Patch v0.3

Other folks have reviewed.
Attachment #8814076 - Flags: review?(mozilla)
Flags: sec-bounty?
Keywords: csectype-priv-escalation, sec-moderate
(Assignee)

Comment 19

5 months ago
https://hg.mozilla.org/integration/mozilla-inbound/rev/12c2619098c3
(Assignee)

Comment 20

5 months ago
This was merged yesterday but somehow the merge didn't comment here:

https://hg.mozilla.org/mozilla-central/rev/12c2619098c3
Status: ASSIGNED → RESOLVED
Last Resolved: 5 months ago
Resolution: --- → FIXED
Target Milestone: --- → Firefox 53
(Assignee)

Updated

5 months ago
status-firefox50: --- → affected
status-firefox51: --- → affected
status-firefox52: --- → affected
status-firefox53: affected → fixed
(Assignee)

Updated

5 months ago
status-firefox-esr45: --- → affected
This and bug 1320057 for 50.1?
Flags: needinfo?(rkothari)
(Assignee)

Updated

5 months ago
Flags: qe-verify?
(Assignee)

Comment 22

5 months ago
Comment on attachment 8814076 [details] [diff] [review]
Patch v0.3

[Approval Request Comment]
If this is not a sec:{high,crit} bug, please state case for ESR consideration: sec-moderate, obvious html injection issue that might be remotely exploitable, trivial patch. I would be more sanguine about not having this on ESR if we weren't about to roll it into 50.1 giving adversaries 4 months to figure out how to exploit this together with 1320057 and other addon bugs into something higher severity (before it'll ship as part of 52esr)
User impact if declined: see above
Fix Landed on Version: 53
Risk to taking this patch (and alternatives if risky): pretty low. The changes are straightforward. We should make sure they get tested on release/beta/aurora, of course.

Approval Request Comment
[Is this code covered by automated tests?]: Some basic automated tests cover stuff this code changes, but not in any great deapth, sadly.
[Has the fix been verified in Nightly?]: not yet...
[Needs manual test from QE? If yes, steps to reproduce]:

from comment #1:

> Steps to reproduce (I tested with Firefox 53.0a1 nightly 2016-11-23 on macOS
> 10.12):
> 
> 1. Make sure that E10S is disabled (with E10S events from content won't
> bubble up to the browser).
> 2. Click the Pocket button to make sure that the extension is initialized.
> It doesn't matter whether anything happens after that, you don't need to
> sign up with Pocket.
> 3. Now open the attached webpage in Firefox and click the "Open
> about:preferences with Sync tab selected".
> 
> This button will open about:preferences#sync page, something that web pages
> normally cannot do.

In builds without the patch, the page will open. In builds with the patch, it shouldn't open.

The patch changes how some of the pocket internals function. The basic verification steps would amount to checking:

a) you can still successfully save pages in pocket
b) you can still tag them as you like
c) you can still remove pages once saved in pocket, as you would before.

[List of other uplifts needed for the feature/fix]: bug 1320057 is not necessary for this fix to work, but because both affect pocket and how easy it is to run code in its code, I would suggest both need uplift.
[Is the change risky?]: not very
[Why is the change risky/not risky?]: well, the worst that could happen is that we break pocket's tagging system. Manual tests by me suggests this patch doesn't do that. It's also pretty straightforward.
Attachment #8814076 - Flags: approval-mozilla-release?
Attachment #8814076 - Flags: approval-mozilla-esr45?
Attachment #8814076 - Flags: approval-mozilla-beta?
Attachment #8814076 - Flags: approval-mozilla-aurora?
Comment on attachment 8814076 [details] [diff] [review]
Patch v0.3

Fix a sec-moderate related to pocket. Beta51+ and Auror52+. Should be in 51 beta 6.
Attachment #8814076 - Flags: approval-mozilla-beta?
Attachment #8814076 - Flags: approval-mozilla-beta+
Attachment #8814076 - Flags: approval-mozilla-aurora?
Attachment #8814076 - Flags: approval-mozilla-aurora+
https://hg.mozilla.org/releases/mozilla-aurora/rev/c1681c600fd1
status-firefox52: affected → fixed
tracking-firefox50: --- → ?
tracking-firefox51: --- → ?
tracking-firefox52: --- → ?
tracking-firefox53: --- → ?
tracking-firefox-esr45: --- → ?
https://hg.mozilla.org/releases/mozilla-beta/rev/51a5f11b6d66
status-firefox51: affected → fixed

Updated

5 months ago
tracking-firefox50: ? → +
tracking-firefox51: ? → +
tracking-firefox52: ? → +
tracking-firefox53: ? → +
tracking-firefox-esr45: ? → 51+
Flags: needinfo?(rkothari)

Comment 26

5 months ago
Comment on attachment 8814076 [details] [diff] [review]
Patch v0.3

This meets the 50.1.0 bar, approving for esr45.6 as well.
Attachment #8814076 - Flags: approval-mozilla-release?
Attachment #8814076 - Flags: approval-mozilla-release+
Attachment #8814076 - Flags: approval-mozilla-esr45?
Attachment #8814076 - Flags: approval-mozilla-esr45+
I don't think Pocket shipped with ESR45 (I don't see it in the extensions code)
status-firefox-esr45: affected → unaffected
tracking-firefox-esr45: 51+ → -
Attachment #8814076 - Flags: approval-mozilla-esr45+ → approval-mozilla-esr45-
Created attachment 8816258 [details] [diff] [review]
esr45 patch
ESR45 is affected after all.
status-firefox-esr45: unaffected → affected
tracking-firefox-esr45: - → ?

Comment 30

5 months ago
uplift
https://hg.mozilla.org/releases/mozilla-release/rev/7e87ed92ad46911d4668e60ea02055c196ad75e5
https://hg.mozilla.org/releases/mozilla-esr45/rev/21c615b650488bac52afd15e55bd201ce53ac75e
status-firefox50: affected → fixed
status-firefox-esr45: affected → fixed
We verified this along with some spot checks on the affected areas on the following builds:
Fx 50.1.0 tinderbox, 45.5.2 ESR tinderbox, 51b6, 52.0a2 (2016-12-05), 53.0a1 (2016-12-06).
More details here: https://public.etherpad-mozilla.org/p/pocket-security-testing
No issues were found, marking as verified fixed.
Status: RESOLVED → VERIFIED
status-firefox50: fixed → verified
status-firefox51: fixed → verified
status-firefox52: fixed → verified
status-firefox53: fixed → verified
status-firefox-esr45: fixed → verified
Flags: qe-verify?
This needs to be added to the 50.1 Firefox advisory and ESR 45.5.2 (or 45.6 since I don't think there is a 45.5.2 ESR release) after it ships in ESR.
Flags: needinfo?(dveditz)
Flags: needinfo?(abillings)
Ok. My mistake, there is a 45.6 release so this can be added once that ships.
Flags: needinfo?(abillings)
Whiteboard: [adv-main50.1+][adv-esr45.6+]
Alias: CVE-2016-9902
Flags: needinfo?(dveditz)
Group: firefox-core-security → core-security-release
tracking-firefox-esr45: ? → 50+
Flags: sec-bounty? → sec-bounty+
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.