Closed
Bug 311024
Opened 19 years ago
Closed 19 years ago
eval(code, window) allows XSS attacks
Categories
(Core :: Security, defect)
Core
Security
Tracking
()
VERIFIED
FIXED
mozilla1.8beta5
People
(Reporter: sync2d, Assigned: mrbkap)
References
Details
(Keywords: fixed1.8, verified1.7.13, Whiteboard: [sg:high] xss splitwindows?)
Attachments
(2 files, 1 obsolete file)
973 bytes,
text/html
|
Details | |
31.62 KB,
patch
|
brendan
:
review+
jst
:
superreview+
dveditz
:
approval-aviary1.0.8-
dveditz
:
approval1.7.13-
asa
:
approval1.8rc1+
|
Details | Diff | Splinter Review |
Several months ago, bug 296639 (split-window) fixed bug 296514 and bug 298315.
The problem is solved by linking closures' __parent__ to inner window object
instead of outer window object. SO they get right principal when executing.
However, this restriction can be circumvented by eval(code, window)
because "window" property refers to the outer window object.
testcase
Works on:
Mozilla/5.0 (Windows; U; Win98; en-US; rv:1.9a1) Gecko/20051003 Firefox/1.6a1
Updated•19 years ago
|
Status: UNCONFIRMED → NEW
Ever confirmed: true
Flags: blocking1.8b5+
Target Milestone: --- → mozilla1.8beta5
Comment 2•19 years ago
|
||
We probably need an innerObject hook in JSExtendedClass. jst, mrbkap: any other
ideas?
/be
Comment 3•19 years ago
|
||
(In reply to comment #2)
> We probably need an innerObject hook in JSExtendedClass. jst, mrbkap: any other
> ideas?
I have one, and this has been proposed before, bz will recall it: never let
anyone get object principals from an outer window object. Make them eat
not-a-principal or whatever it's called.
There was some reason, a while back in the split window death march, for
forwarding to the inner window when getting object principals. I call that a
security hole, and this bug is the proof.
/be
Comment 4•19 years ago
|
||
Testcase works on 1.0.7 as well. Is that the same bug, or simply the original
problem split-windows was trying to fix?
Flags: blocking1.7.13+
Flags: blocking-aviary1.0.8+
Whiteboard: [sg:high xss]
Comment 5•19 years ago
|
||
(In reply to comment #4)
> Testcase works on 1.0.7 as well. Is that the same bug, or simply the original
> problem split-windows was trying to fix?
They're the same problem. Comment 0 contains the key. In past releases, there
was only one window object, not one outer and one or more inners for each entry
in session history. So if an attacker could run a lexical closure in the scope
of the window after navigating away from the attacker's document, it would run
with the principals of the new document to which the window was navigated currently.
With split window objects, so long as the outer window supports geting the
object princpals from the current inner window, if an attacker can lexically
capture the outer window in a scope chain, the same exploit works.
The outer window must never return the principals for the current window, or for
any other window in session history. It should return not-a-principal, or until
we implement that special singleton, an about:blank codebase principal.
/be
Comment 6•19 years ago
|
||
Hoping jst can try again to neuter getting object principals from outer window
objects, and make it work for all cases this time.
/be
Assignee: dveditz → jst
Comment 7•19 years ago
|
||
> never let anyone get object principals from an outer window object.
We can't really do that, as far as I can see.
More precisely, say I have a window object in my code (outer one, since that's
the only way to really get an explicit window object). Say someone's trying to
access a property on it. We need to do a security check. What principals do
you suggest we use for it? At the moment we use those of the inner window,
which is what makes the most sense, really...
And this is a use case we really don't want to break, since this is the |var win
= window.open()| use case.
Comment 8•19 years ago
|
||
(In reply to comment #7)
> At the moment we use those of the inner window,
> which is what makes the most sense, really...
This "at the moment" could be read other than how you meant it, as "at the
moment, or moments, when the current inner window is the one to which the script
should have access."
The problem ie lexical environment capture of the outer window. In talking this
through with jst (who patiently re-reviewed the use-cases for me) and mrbkap, it
became clear that the only fix is to stop environment capture from entraining
the outer window. So I sketched a plan to add innerObject to JSExtendedClass,
and mrbkap is running with it. He is indefatigable!
/be
Assignee: jst → mrbkap
Comment 9•19 years ago
|
||
moving out to RC1.
Flags: blocking1.8rc1?
Flags: blocking1.8b5-
Flags: blocking1.8b5+
Comment 10•19 years ago
|
||
'with (window) eval(code)' should have the same effect, btw.
/be
Status: NEW → ASSIGNED
Flags: blocking1.8b5- → blocking1.8b5+
Assignee | ||
Comment 11•19 years ago
|
||
Brendan, is this something like what you had in mind? I'm not sure if the
jsscript.c changes are strictly necessary (or if we need to make
script_{compile,exec} look more like obj_eval wrt the principals changes; if
so, we might consider breaking the common stuff out into common functions).
Assignee | ||
Updated•19 years ago
|
Attachment #198625 -
Flags: review?(brendan)
Comment 12•19 years ago
|
||
Comment on attachment 198625 [details] [diff] [review]
patch v1
Drive-by review comments...
+/* NB: The outerObject and innerObjects hooks are assumed to be infallible. */
... but then you've got:
+JS_STATIC_DLL_CALLBACK(JSObject *)
+XPC_WN_InnerObject(JSContext *cx, JSObject *obj)
+{
+ XPCWrappedNative *wrapper =
+ XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj);
+ if(!wrapper)
+ {
+ Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
+
+ return nsnull;
+ }
etc etc.
So this, and the existing outerObject impl here should never return null.
Comment 13•19 years ago
|
||
Comment on attachment 198625 [details] [diff] [review]
patch v1
>+/* NB: The outerObject and innerObjects hooks are assumed to be infallible. */
> struct JSExtendedClass {
> JSClass base;
> JSEqualityOp equality;
> JSObjectOp outerObject;
>+ JSObjectOp innerObject;
> jsword reserved0;
Don't forget to remove one 0, from JSCLASS_NO_RESERVED_MEMBERS too.
>+ /* Ensure we compile this eval with the right object in the scope chain. */
>+ clasp = OBJ_GET_CLASS(cx, scopeobj);
>+ if (clasp->flags & JSCLASS_IS_EXTENDED) {
>+ JSExtendedClass *xclasp;
>+
>+ xclasp = (JSExtendedClass*)clasp;
>+ if (xclasp->innerObject) {
>+ scopeobj = xclasp->innerObject(cx, scopeobj);
>+ JS_ASSERT(scopeobj);
>+ }
>+ }
If it doesn't win in optimized code space (never mind cycles, they don't matter
with eval) to subroutine this for use here and from jsscript.c, at least
macroize in jsobj.h?
>+JS_STATIC_DLL_CALLBACK(JSObject *)
>+XPC_WN_InnerObject(JSContext *cx, JSObject *obj)
>+{
>+ XPCWrappedNative *wrapper =
>+ XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj);
>+ if(!wrapper)
>+ {
>+ Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
>+
>+ return nsnull;
>+ }
>+
>+ if(!wrapper->IsValid())
>+ {
>+ Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx);
>+
>+ return nsnull;
>+ }
Shouldn't these all return obj and not Throw (at most NS_WARNING)? As written,
besides botching the JS_ASSERTs you have in the open-coded scopeobj inner-izing
paragraphs, won't they lead to null deref crashes immediately after?
>+
>+ XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
>+ if(si && si->GetFlags().WantInnerObject())
>+ {
>+ JSObject *newThis;
>+ nsresult rv =
>+ si->GetCallback()->InnerObject(wrapper, cx, obj, &newThis);
>+
>+ if(NS_FAILED(rv))
>+ {
>+ Throw(rv, cx);
>+
>+ return nsnull;
Same here. This suggests deCOMTaminating InnerObject. Is that thinkable? We
really don't need an nsresult r.v. with a JSObject ** out param, just a
JSObject * r.v.
>@@ -842,21 +881,22 @@ JSExtendedClass XPC_WN_NoHelper_JSClass
> nsnull, // getObjectOps;
> nsnull, // checkAccess;
> nsnull, // call;
> nsnull, // construct;
> nsnull, // xdrObject;
> nsnull, // hasInstance;
> XPC_WN_Shared_Mark, // mark;
> nsnull // spare;
> },
> XPC_WN_Equality,
>- XPC_WN_OuterObject
>+ XPC_WN_OuterObject,
>+ XPC_WN_InnerObject
So you should use JSCLASS_NO_RESERVED_MEMBERS here.
>@@ -6107,20 +6117,50 @@ nsWindowSH::OuterObject(nsIXPConnectWrap
> // never be called when we have no outer. But just in case, return
> // null to prevent leaking an inner window to code in a different
> // window.
>
> *_retval = nsnull;
Ok, if outerObject can return null without Throwing, then it's infallible but
nullable. So innerObject should be the same. No Throw, null might be ok, but
caller has to defend.
>+NS_IMETHODIMP
>+nsWindowSH::InnerObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
>+ JSObject * obj, JSObject * *_retval)
>+{
>+ nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper);
>+
>+ if (win->IsInnerWindow()) {
>+ // Return the inner window.
>+
>+ *_retval = win->GetGlobalJSObject();
>+ } else {
>+ // Try to find the current inner window. If there isn't one, then we're
>+ // setting up the window (i.e., this is before we even have a document) so
>+ // we can just return the outer window.
>+
>+ nsGlobalWindow *inner = win->GetCurrentInnerWindowInternal();
>+ *_retval = inner
>+ ? inner->GetGlobalJSObject()
>+ : nsnull;
Does not agree with comment or with caller expectations, although nullability
implies defensive callers (but that's more code -- ideally, we'd remove
nullability for both innerObject and outerObject).
/be
Attachment #198625 -
Flags: review?(brendan) → review-
Updated•19 years ago
|
Flags: blocking1.8rc1?
Flags: blocking1.8rc1+
Flags: blocking1.8b5+
Assignee | ||
Comment 14•19 years ago
|
||
(In reply to comment #13)
> Shouldn't these all return obj and not Throw (at most NS_WARNING)? As written,
> besides botching the JS_ASSERTs you have in the open-coded scopeobj inner-izing
> paragraphs, won't they lead to null deref crashes immediately after?
I fixed this in response to comment 12, but this sort of scares me. Perhaps we
should not assume that these operations are infallible. If someone finds a bug
in XPConnect to fail at some point here, it would be exploitable here to grab
references to bad objects (or objects with bad scope chains).
> Same here. This suggests deCOMTaminating InnerObject. Is that thinkable? We
> really don't need an nsresult r.v. with a JSObject ** out param, just a
> JSObject * r.v.
nsIXPCScriptable is an idl interface. This sort of thing is just asking for its
own bug.
> Ok, if outerObject can return null without Throwing, then it's infallible but
> nullable. So innerObject should be the same. No Throw, null might be ok, but
> caller has to defend.
I turned this into an NS_ERROR here. This feels like another reason to not force
these hooks to be infallible.
> >+ nsGlobalWindow *inner = win->GetCurrentInnerWindowInternal();
> >+ *_retval = inner
> >+ ? inner->GetGlobalJSObject()
> >+ : nsnull;
>
> Does not agree with comment or with caller expectations, although nullability
> implies defensive callers (but that's more code -- ideally, we'd remove
> nullability for both innerObject and outerObject).
>
This one is OK. I felt that setting |obj| twice, so see the if (!*_retval)
clause after this if statement.
I'll attach a new patch tomorrow.
Assignee | ||
Comment 15•19 years ago
|
||
It turns out that we didn't treat outerObject as being infallible. innerObject
follows in outerObject's footsteps. This patch makes us throw in more cases.
Attachment #198625 -
Attachment is obsolete: true
Attachment #198850 -
Flags: review?(brendan)
Comment 16•19 years ago
|
||
Comment on attachment 198850 [details] [diff] [review]
patch v2
Nits only:
1. Invert if/else sense in nsWindowSH::OuterObject to avoid bogo-control-flow:
if (foo) {
bar
} else {
baz;
return UNEXPECTED;
}
return OK;
2. Use trailing _, not leading, for local names in OBJ_TO_INNER_OBJECT, to
avoid invading the C impl namespace.
/be
Attachment #198850 -
Flags: review?(brendan) → review+
Assignee | ||
Comment 17•19 years ago
|
||
Comment on attachment 198850 [details] [diff] [review]
patch v2
AIUI the C impl namespace reserves _<CAPITAL> and anything containing __, but
I'll yield.
Attachment #198850 -
Flags: superreview?(jst)
Comment 18•19 years ago
|
||
Comment on attachment 198850 [details] [diff] [review]
patch v2
+nsWindowSH::InnerObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
+ JSObject * obj, JSObject * *_retval)
+{
+ nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper);
+
+ if (win->IsInnerWindow()) {
+ // Return the inner window.
+
+ *_retval = win->GetGlobalJSObject();
That could just be *_retval = obj here.
sr=jst
Attachment #198850 -
Flags: superreview?(jst) → superreview+
Assignee | ||
Comment 19•19 years ago
|
||
Fix checked into trunk.
Status: ASSIGNED → RESOLVED
Closed: 19 years ago
Resolution: --- → FIXED
Assignee | ||
Comment 20•19 years ago
|
||
Comment on attachment 198850 [details] [diff] [review]
patch v2
This fixes a security hole. The fix only affects consumers of eval who
explicitly pass in a window as a scope object.
Attachment #198850 -
Flags: approval1.8rc1?
Updated•19 years ago
|
Depends on: splitwindows
Whiteboard: [sg:high xss] → [sg:high] xss (splitwindows?)
Updated•19 years ago
|
Attachment #198850 -
Flags: approval1.8rc1? → approval1.8rc1+
Updated•19 years ago
|
Updated•19 years ago
|
Flags: testcase+
Updated•19 years ago
|
Whiteboard: [sg:high] xss (splitwindows?) → [sg:high] xss
Comment 22•19 years ago
|
||
Comment on attachment 198850 [details] [diff] [review]
patch v2
aviary101/moz17 landing approval: a=dveditz for drivers. Please add the fixed1.7.13 and fixed-aviary1.0.8 keywords when landed.
Attachment #198850 -
Flags: approval1.7.13+
Attachment #198850 -
Flags: approval-aviary1.0.8+
Comment 23•19 years ago
|
||
Comment on attachment 198850 [details] [diff] [review]
patch v2
This patch isn't going to work for moz17/aviary101, is it? Not even with Blake's splitwindow alternative I don't think.
Attachment #198850 -
Flags: approval1.7.13?
Attachment #198850 -
Flags: approval1.7.13+
Attachment #198850 -
Flags: approval-aviary1.0.8?
Attachment #198850 -
Flags: approval-aviary1.0.8+
Updated•19 years ago
|
Whiteboard: [sg:high] xss → [sg:high] xss splitwindows?
Comment 24•19 years ago
|
||
mrbkap says his split-windows alternative does in fact fix this. QA-wanted.
Keywords: qawanted
Comment 25•19 years ago
|
||
Fixed on the aviary1.0/mozilla1.7 branches by the split-window alternative (bug 316589)
Comment 26•19 years ago
|
||
Comment on attachment 198850 [details] [diff] [review]
patch v2
Don't need this on old branches with the split-windows alternative fix.
Attachment #198850 -
Flags: approval1.7.13?
Attachment #198850 -
Flags: approval1.7.13-
Attachment #198850 -
Flags: approval-aviary1.0.8?
Attachment #198850 -
Flags: approval-aviary1.0.8-
Comment 27•19 years ago
|
||
verified with:
Windows:
Moz - Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.13) Gecko/20060215
Fx - Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.13) Gecko/20060215
Firefox/1.0.8
Macintosh:
Moz - Mozilla/5.0 (Macintosh; U;PPC Mac OS X Mach-O; en-US; rv:1.7.13)
Gecko/20060215 Firefox/1.0.8
Fx - Mozilla/5.0 (Macintosh; U;PPC Mac OS X Mach-O; en-US; rv:1.7.13)
Gecko/20060215 Firefox/1.0.8
Linux
Moz - Mozilla/5.0 (X11; U;Linux i686; en-US; rv:1.7.13) Gecko/20060215
Status: RESOLVED → VERIFIED
Updated•18 years ago
|
Group: security
Updated•18 years ago
|
Flags: in-testsuite+ → in-testsuite?
You need to log in
before you can comment on or make changes to this bug.
Description
•