Bug 522430 (CVE-2009-3986)

window.opener allows chrome access from unprivileged pages

RESOLVED FIXED in mozilla1.9.2

Status

()

Core
DOM
P2
critical
RESOLVED FIXED
8 years ago
8 years ago

People

(Reporter: David James, Assigned: mrbkap)

Tracking

({verified1.9.0.16, verified1.9.1})

Trunk
mozilla1.9.2
verified1.9.0.16, verified1.9.1
Points:
---
Bug Flags:
blocking1.9.2 +
blocking1.9.0.16 +
wanted1.9.0.x +

Firefox Tracking Flags

(status1.9.2 beta2-fixed, blocking1.9.1 .6+, status1.9.1 .6-fixed)

Details

(Whiteboard: [sg:moderate])

Attachments

(1 attachment, 1 obsolete attachment)

(Reporter)

Description

8 years ago
User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.4) Gecko/20091007 Firefox/3.5.4
Build Identifier: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.4) Gecko/20091007 Firefox/3.5.4

When new windows are opened in Firefox, the new window has a reference to the window that created it using window.opener. If window.opener is a ChromeWindow, then any webpage opened in this window will be able to access the functions inside the ChromeWindow.

Both Firefox itself and many extensions for Firefox often open windows from within chrome code. With this security hole, windows opened by chrome code are unsafe.

Reproducible: Always

Steps to Reproduce:
1. Select Help -> About Mozilla Firefox
2. Click on "Licensing Information"
3. A new window will open with licensing information. In this window, go to http://www.google.com
4. In the URL bar, type javascript:window.opener.URLBarSetURI({spec:"https://www.ebay.com/"},true)
5. In the URL bar, type javascript:window.opener.BrowserOffline.toggleOfflineStatus()
6. Browse to a web page that contains the above code in a script tag. Note that Firefox's security model does not prevent this code from running.
Actual Results:  
1. The URL bar of the opening window is set to "https://www.ebay.com/"
2. The browser is set to work offline
3. Web pages can access and run chrome code

Expected Results:  
The new window should not be able to access properties of the chrome window.

This bug likely effects not just Firefox itself, but also many extensions which open windows using window.open.
Blake: Weren't XOW supposed to protect us from this? (Note: The JS in step 4 doesn't actually work for me, but step 5 definitely does.)
Assignee: nobody → mrbkap
Status: UNCONFIRMED → NEW
blocking1.9.1: --- → .5+
status1.9.1: --- → wanted
Ever confirmed: true
Flags: wanted1.9.0.x+
Flags: blocking1.9.0.16+
Flags: blocking-firefox3.6?
OS: Windows XP → All
Hardware: x86 → All
Whiteboard: [sg:moderate]
Version: unspecified → Trunk
Once you've done this it's trivial to exploit to gain sg:crit access. Is this sg:moderate only because you have to do something "unusual" to get the browser to do this bad window.open?

(To exploit, call openURI with a chrome URI and then a javascript URI).

I thought we nulled out window.opener when a chrome-privileged page calls window.open without the "chrome" feature, but I can't find the code to actually do that.
It's "moderate" because we don't know of any Firefox dialogs that will open random pages in this way. There is quite a bit of mitigation value if an attacker has to tell the user
 1) open the about dialog
 2) click the license link
 3) now please come visit my page in that window
    (type the URL, paste it, use a bookmark)

It's more likely that some add-on somewhere is doing something like this although we don't know of any specific examples. It would be an sg:critical bug for users of that add-on. Does that make it sg:critical overall? Always? Only if the addon is really popular? I don't know, maybe.

I guess that's a long-winded "yes" to your question.
It's not 'any' window opened by 'any' chrome dialog. New windows opened from the bookmarks/history dialog don't do this, for instance. Is it limited to XUL <label class="text-link"> links?
(Reporter)

Comment 5

8 years ago
This bug doesn't affect 'any' windows opened from chrome, but it does impact common ways of opening windows from chrome.

Here are some more examples:

Reproduction Recipe A:
  1) Install LiveHTTPHeaders
  2) Go to Tools -> LiveHttpHeaders
  3) Select About -> Check for update
  4) In the new window, type the URL: "javascript:window.opener"

This shows that LiveHTTPHeaders opens a hole with this code:
  document.commandDispatcher.focusedWindow.open(url);

Reproduction Recipe B:
  1) Go to Tools -> Options -> Tabs and deselect "Open new windows in a new tab instead"
  2) Install YSlow
  3) Run YSlow on your favourite website
  4) Open the YSlow Panel and click an external link
  5) In the new window, type the URL: "javascript:window.opener"

This shows that YSlow opens a hole with the following code:
  window.open(url, " blank");
(Assignee)

Comment 6

8 years ago
(In reply to comment #1)
> Blake: Weren't XOW supposed to protect us from this? (Note: The JS in step 4
> doesn't actually work for me, but step 5 definitely does.)

Sort of -- the problem is that the object we're handing out is *so* wrong, XOWs don't even notice.
(Assignee)

Updated

8 years ago
Component: Security → DOM
Flags: blocking-firefox3.6?
Product: Firefox → Core
QA Contact: firefox → general
Target Milestone: --- → mozilla1.9.2
(Assignee)

Updated

8 years ago
Flags: blocking1.9.2?
(Assignee)

Comment 7

8 years ago
Created attachment 407407 [details] [diff] [review]
Proposed fix

Easy fix: don't return an opener if we're not chrome and the window that we're returning is a chrome window (even content windows with chrome privileges get wrapped and protected).
Attachment #407407 - Flags: superreview?(jonas)
Attachment #407407 - Flags: review?(jst)
(Assignee)

Comment 8

8 years ago
Created attachment 407408 [details] [diff] [review]
Assertion

I don't want to land this at the same time as attachment 407407 [details] [diff] [review] because I think this assertion + that fix make the problem here extremely obvious to interested onlookers. This assertion checks to make sure that we don't ever try to wrap a chrome window in a content scope. As a belt and braces, we could either throw in these cases, or wrap all [object ChromeWindow]s in SystemOnlyWrappers.
Attachment #407408 - Flags: review?(jst)

Updated

8 years ago
Attachment #407407 - Flags: review?(jst) → review+

Updated

8 years ago
Flags: blocking1.9.2? → blocking1.9.2+
Priority: -- → P2
Attachment #407407 - Flags: superreview?(jonas) → superreview+
Blake, can you land this for trunk and 1.9.2?
(Assignee)

Comment 10

8 years ago
http://hg.mozilla.org/mozilla-central/rev/2a7e710043b6 and
http://hg.mozilla.org/releases/mozilla-1.9.2/rev/d9ea184f0558 are your answers.
Status: NEW → RESOLVED
Last Resolved: 8 years ago
status1.9.2: --- → final-fixed
Resolution: --- → FIXED
(Assignee)

Updated

8 years ago
Attachment #407407 - Flags: approval1.9.1.6?
Comment on attachment 407407 [details] [diff] [review]
Proposed fix

Does the lack of a 1.9.0.16 approval request mean we need a different back-port? Or just an oversight?
(Assignee)

Comment 12

8 years ago
Comment on attachment 407407 [details] [diff] [review]
Proposed fix

In this case it means that I forgot to request approval.
Attachment #407407 - Flags: approval1.9.0.16?
Comment on attachment 407407 [details] [diff] [review]
Proposed fix

Approved for 1.9.1.6 and 1.9.0.16, a=dveditz for release-drivers
Attachment #407407 - Flags: approval1.9.1.6?
Attachment #407407 - Flags: approval1.9.1.6+
Attachment #407407 - Flags: approval1.9.0.16?
Attachment #407407 - Flags: approval1.9.0.16+
Keywords: checkin-needed
http://hg.mozilla.org/releases/mozilla-1.9.1/rev/6c34838b10ea
status1.9.1: wanted → .6-fixed
Fixed in CVS.
Keywords: fixed1.9.0.16
Keywords: checkin-needed
Verified fixed in 1.9.1.6 with Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6pre) Gecko/20091119 Shiretoko/3.5.6pre (.NET CLR 3.5.30729) using steps in comment 0. 

Used steps in comment 5 for 1.9.0.16 since the licensing link doesn't exist in 1.9.0. Verified fixed for 1.9.0.16 with Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.16pre) Gecko/2009111921 GranParadiso/3.0.16pre (.NET CLR 3.5.30729).
Keywords: fixed1.9.0.16 → verified1.9.0.16, verified1.9.1
Alias: CVE-2009-3986

Updated

8 years ago
Attachment #407408 - Flags: review?(jst) → review+
Group: core-security
(Assignee)

Comment 17

8 years ago
http://hg.mozilla.org/mozilla-central/rev/07e47cdf85ad
(Assignee)

Comment 18

8 years ago
I had to back that out because we hit it on tinderbox alive tests... http://hg.mozilla.org/mozilla-central/rev/61cd29e06840
Status: RESOLVED → REOPENED
Resolution: FIXED → ---
Status: REOPENED → RESOLVED
Last Resolved: 8 years ago8 years ago
Resolution: --- → FIXED

Updated

8 years ago
Blocks: 554410
(Assignee)

Comment 19

8 years ago
Comment on attachment 407408 [details] [diff] [review]
Assertion

Sadly, we can't do this. Given code like:
    var chromeWin = aWindow 
                        .QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIWebNavigation)
                        .QueryInterface(Ci.nsIDocShellTreeItem)
                        .rootTreeItem
                        .QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDOMWindow)
                        .QueryInterface(Ci.nsIDOMChromeWindow);

on a content window (through an XPCNativeWrapper, even), we start in a content scope (the window)... when GI to docshelltreeitem, we create a docshell JS object in the scope we think we're in (i.e. the content window), then when we get the DOMWindow from that, we wrap it in the current scope, which is still the content window... But it's not *really* in that scope, we just think it is.
Attachment #407408 - Attachment is obsolete: true
You need to log in before you can comment on or make changes to this bug.