Closed Bug 616659 (CVE-2011-0051) Opened 9 years ago Closed 9 years ago

Recursing the JavaScript eval function over itself eventually causes all dialogs with confirmation to evaluate to "true".

Categories

(Core :: DOM: Core & HTML, defect, critical)

defect
Not set
critical

Tracking

()

RESOLVED FIXED
Tracking Status
blocking2.0 --- betaN+
blocking1.9.2 --- .14+
status1.9.2 --- .14-fixed
blocking1.9.1 --- .17+
status1.9.1 --- .17-fixed

People

(Reporter: zrhoffman, Assigned: smaug)

Details

(Keywords: verified1.9.1, verified1.9.2, Whiteboard: [sg:critical] [qa-ntd-191])

Attachments

(2 files)

User-Agent:       Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-us) AppleWebKit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5
Build Identifier: http://www.mozilla.com/en-US/products/download.html?product=firefox-3.6.12&os=win&lang=en-US

Obviously, code like eval(a="eval(a)") causes the script to error out.  But adding a try/catch block like eval(a="try{eval(a)}catch(b){/*code here*/}") lets you catch the "eval'd-out" state and execute code in it.  Any dialog box opened in this state instead shows a textless dialog box with two textless buttons, and pressing these buttons does not close this box.  Closing the box with the corner "X" button causes input boxes (confirm, onbeforeunload, netscape.security.PrivilegeManager.enablePrivilege, etc.) to evaluate to "true".

Reproducible: Always

Steps to Reproduce:
The following code will open a dialog: eval(a="try{eval(a)}catch(b){c=confirm('You will not see this text')}")
Actual Results:  
In Windows 7, the only immediate way to close the aforementioned dialog is by pressing the "X" corner button.  If the user performs this action, Firefox will proceed as if the user pressed "OK", causing c == true.

Expected Results:  
In a normal case, this action is the equivalent of pressing "Cancel", meaning c == false.

See the attachment in the comments for an example that uses this vulnerability for arbitrary code execution.
Because this example uses netscape.security.PrivilegeManager.enablePrivilege, an on-the-Web version would be just as dangerous but would probably need to be placed inside a signed script JAR.  To avoid this complexity, I'm assuming that you know to run it locally.
Component: General → DOM: Core & HTML
Product: Firefox → Core
QA Contact: general → general
Hardware: x86 → All
blocking1.9.1: --- → ?
blocking1.9.2: --- → ?
blocking2.0: --- → ?
status1.9.1: --- → ?
status1.9.2: --- → ?
As before, run locally on Windows.  Clicking the button causes a textless dialog to pop up. "X"-ing out the dialog opens C:\WINDOWS\system32\calc.exe.
Status: UNCONFIRMED → NEW
blocking1.9.1: ? → .17+
blocking1.9.2: ? → .14+
Ever confirmed: true
Whiteboard: [sg:critical]
Awesome.

Assigning to Olli per jst.
Assignee: nobody → Olli.Pettay
On Mac we use "sheets" rather than dialogs -- there is no 'X' to dismiss. You end up unable to switch tabs or do much of anything and must force-quit (arguably better than letting the attack succeed). I expect the attack to succeed on Linux but have not tried it (of course the testcase must be modified).

I tried Minefield on windows and did not get the testcase to work. The dialog never came up and eventually I got a security exception about file:/// trying to get UniversalXPConnect privs. I don't think we changed anything with enablePrivilege so maybe we changed something in the JS engine or DOM that causes the attack to fail (the dialogs return false/cancel when they can't be created?). The attack definitely succeeds on Windows in both 3.5.x and 3.6.x
blocking2.0: ? → betaN+
On 3.6 linux the browser just enters to an endless loop 
and gives "JavaScript error: , line 0: too much recursion"
There are no dialogs.
So how do I get the "sheet" on OSX?
I run the test locally, and still just get endless loop or something.
No dialogs, no "sheets".
When I ran the testcase on OSX I saw just the very beginning of the "sheet" which was just a square a few pixels wide right below the location bar.  It was window-modal, though, in the sense that my menus were disabled and I couldn't switch to any other tab.
Ah, ok. I think I see something similar on OSX.
Attached patch fix?Splinter Review
I don't have Windows development setup, and can't reproduce the problem on Linux
and on OSX the problem is not critical if I understand this right.

But here is a patch which might help.
Based on the code and the error I get on Linux this could help.
Anyone willing to try? The fix is for netscape.security.PrivilegeManager.enablePrivilege() -like prompts.
Status: NEW → ASSIGNED
Comment on attachment 496170 [details] [diff] [review]
fix?

OK, this fixes the critical part of the bug.
Similar thing could be added to other dialogs.
Attachment #496170 - Flags: review?(jst)
(In reply to comment #4)

> I tried Minefield on windows and did not get the testcase to work. The dialog
> never came up and eventually I got a security exception about file:/// trying
> to get UniversalXPConnect privs. I don't think we changed anything with
> enablePrivilege so maybe we changed something in the JS engine or DOM that
> causes the attack to fail (the dialogs return false/cancel when they can't be
> created?). The attack definitely succeeds on Windows in both 3.5.x and 3.6.x
Prompt handling was rewritten for FF4.
So I could still clarify that the code where the bug is, has been removed in FF4.
What exactly is the patch fixing? eButtonPressed isn't checked by other code, and commonDialog.js's commonDialogOnload() should be initializing it already... Is this bug somehow causing a code path that avoids the initialization?
(In reply to comment #14)
> What exactly is the patch fixing? eButtonPressed isn't checked by other code,
> and commonDialog.js's commonDialogOnload() should be initializing it already...
> Is this bug somehow causing a code path that avoids the initialization?

By the time that we try to call into commonDialog.js, we're already basically out of C stack space to run JS in, so we probably throw trying to even call into that code. However, given what a mess all of the dialog/window code is, we probably lose the fact that we failed to run the JS somewhere and continue on as if we had.
Attachment #496170 - Flags: review?(jst) → review+
Attachment #496170 - Flags: approval1.9.2.14?
Attachment #496170 - Flags: approval1.9.1.17?
Ah, I guess that makes sense. Mildly surprising there isn't an error being returned back from OpenWindow(), we still use that code path so it would kinda be nice to figure out where the fail is being dropped.

But this workaround seems fine. You could fix all the prompts for free by having nsPromptService::DoDialog() just do aParamBlock->SetInt(eButtonPressed, 1). Unconditionally setting this should be fine. [IE, basically do the initialization commonDialog.js was doing.]
(In reply to comment #16)
> Ah, I guess that makes sense. Mildly surprising there isn't an error being
> returned back from OpenWindow(), we still use that code path so it would kinda
> be nice to figure out where the fail is being dropped.

This is total speculation, but I assume that we run JS while loading the XUL for the dialog's window. That would mean that the error actually gets thrown into a nested event loop run by nsWindowWatcher::OpenWindowJSInternal and gets lost there.
(In reply to comment #16)
> You could fix all the prompts for free by
> having nsPromptService::DoDialog() just do aParamBlock->SetInt(eButtonPressed,
> 1). Unconditionally setting this should be fine. [IE, basically do the
> initialization commonDialog.js was doing.]
Yeah, could probably do that.
Attached patch v2Splinter Review
Attachment #498119 - Flags: review?(dolske)
Attachment #498119 - Flags: approval1.9.2.14?
Attachment #498119 - Flags: approval1.9.1.17?
Attachment #496170 - Flags: approval1.9.2.14?
Attachment #496170 - Flags: approval1.9.1.17?
Dolske, review ping.
Waiting on review to approve.
Comment on attachment 498119 [details] [diff] [review]
v2

Would be good to add a "Default to 'Cancel'" comment.
Attachment #498119 - Flags: review+
Sure. I'll add that before pushing.
Attachment #498119 - Flags: review?(dolske) → review+
Comment on attachment 498119 [details] [diff] [review]
v2

Approved for 1.9.2.14 and 1.9.1.17
Attachment #498119 - Flags: approval1.9.2.14?
Attachment #498119 - Flags: approval1.9.2.14+
Attachment #498119 - Flags: approval1.9.1.17?
Attachment #498119 - Flags: approval1.9.1.17+
I don't think that I'd call this fixed. I verified the problem with 1.9.2.13 and then tried the nightly .14pre build on XP. The PoC's no longer launch calculator after the fix but the browser is still completely unusable. The user winds up with an Internet Security dialog with two blank buttons that cannot be closed. See http://img84.imageshack.us/img84/8548/screenshot20110104at153.png.

The only way out is to kill the entire Firefox process. Does a DoS count as fixed? :-)

Loading both of the PoC's on 1.9.1.16, as soon as the user clicks on the "clicky" button, the whole browser loses focus and cannot be closed (I can't see focus going anywhere but the main window cannot get it back). The only solution is to kill the process. No calculator is launched but the browser is unusable. Post-fix on 1.9.1.17pre, the behavior is exactly the same.

This appears to need some more work.
I was fixing the sg:critical part of the bug. The DoS part is something quite different.
So I can call this "verified" for 1.9.2 since calculator doesn't launch?

Pre-fix on 1.9.1, it never launches calculator and the pre and post-fix behavior is the same. I'm not sure what to verify there.
> So I can call this "verified" for 1.9.2 since calculator doesn't launch?

that sounds right once you get to a state like Dan had in comment 4 where he was able to recreate on 1.9.1 and 2.

and maybe open a new bug for the sg:dos part?
I haven't been able to duplicate Dan's state in comment 4. Dan, any comments or suggestions? Have you been able to duplicate it again?
Status: RESOLVED → UNCONFIRMED
Ever confirmed: false
Resolution: FIXED → ---
What exactly is left here for trunk?
The code where the sg:crit bug is, has been removed in the trunk.
Gavin, why did you change this to UNCONFIRMED? And removed also 1.9.1 and 1.9.2 status flags?
Changing back to fixed.
Status: UNCONFIRMED → RESOLVED
Closed: 9 years ago9 years ago
Resolution: --- → FIXED
Sorry, just a mistake (didn't reload hard enough).
Marking verified for 1.9.2. Since the problem doesn't manifest with the PoC on 1.9.1, I can't verify the fix there.
Keywords: verified1.9.2
Whiteboard: [sg:critical] → [sg:critical] [qa-ntd-191]
Attached file Testcase 3
Apparently, in 1.9.1 the browser's behavior varies depending on whether any dialogs have been shown during the current session but prior to the attack.  This example displays a dialog before continuing, which probably causes it to succeed for the entire 1.9.1 branch.
Using the new testcase 3, I've verified calculator launching pre-fix and being fixed in the current 1.9.1 nightly (Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.17pre) Gecko/20110118 Shiretoko/3.5.17pre ( .NET CLR 3.5.30729)).
Keywords: verified1.9.1
Alias: CVE-2011-0051
Group: core-security
Flags: sec-bounty+
You need to log in before you can comment on or make changes to this bug.