Closed Bug 344495 Opened 18 years ago Closed 17 years ago

XSS against certain sites that are using scripts that access properties of top, parent or opener

Categories

(Core :: Security, defect, P2)

defect

Tracking

()

VERIFIED FIXED
mozilla1.9alpha8

People

(Reporter: moz_bug_r_a4, Assigned: mrbkap)

References

Details

(Keywords: arch, Whiteboard: [sg:high] patch makes it sg:critical :-()

Attachments

(2 files, 6 obsolete files)

Attached file testcase 1
This tries to get cookies for login.live.com.
Attached file testcase 2
This tries to get cookies for Gmail.
Attached patch Terrible patch (obsolete) — Splinter Review
This might be the ugliest patch ever, but it's really more of a POC than anything. As far as I know, this patch fixes these bugs without breaking anything badly enough (e.g., I could start the browser and go to google maps and gmail). The idea is to make native frames participate in decisions about whether or not property accesses are allowed. Because of the way that XPConnect hooks into the JS engine, we actually need to skip the top frame just before we call the native function, and when we're wrapping the results.
Assignee: dveditz → mrbkap
Status: NEW → ASSIGNED
Attachment #229320 - Flags: review?(brendan)
Comment on attachment 229320 [details] [diff] [review]
Terrible patch

Missing jsinterp.h diff?

/be
Attached patch With jsinterp.h (obsolete) — Splinter Review
Since I don't have a thunderbird build to try out, I'm getting mscott to try this patch for me.
Attachment #229320 - Attachment is obsolete: true
Attachment #229327 - Flags: review?(brendan)
Attachment #229320 - Flags: review?(brendan)
Comment on attachment 229327 [details] [diff] [review]
With jsinterp.h

Redundant Set-Skippable before goto done.

Also wondering how wide the API needs to be.

/be
Comment on attachment 229327 [details] [diff] [review]
With jsinterp.h

Another thought: make JS_FrameIterator check JSFRAME_SKIPPABLE (rename to JSFRAME_DONT_ITERATE?).

/be
Attached patch Updated to comments (obsolete) — Splinter Review
This patch addresses brendan's comments. It also fixes a bug where calls from chrome through XPCNativeWrapper were getting denied because the XPCNativeWrapper frame was hiding the chrome frame. I fixed this by making that stack frame not-iterable through the call, which should be OK, since, as far as caps is concerned, we're simply mirroring the principals of whatever we're calling.
Attachment #229327 - Attachment is obsolete: true
Attachment #229682 - Flags: review?(brendan)
Attachment #229327 - Flags: review?(brendan)
Comment on attachment 229682 [details] [diff] [review]
Updated to comments


>+++ js/src/xpconnect/src/XPCNativeWrapper.cpp	18 Jul 2006 17:38:43 -0000
>@@ -376,23 +376,29 @@ XPC_NW_FunctionWrapper(JSContext *cx, JS
>   JSObject *methodToCallObj = ::JS_GetParent(cx, funObj);
>   XPCWrappedNative *wrappedNative =
>     XPCNativeWrapper::GetWrappedNative(cx, obj);
> 
>   if (!::JS_ObjectIsFunction(cx, methodToCallObj) || !wrappedNative) {
>     return ThrowException(NS_ERROR_UNEXPECTED, cx);
>   }
> 
>+  JSStackFrame *fp = cx->fp;
>+  JSBool iterable = JS_GetFrameIterable(cx, fp);
>+  JS_SetFrameIterable(cx, fp, JS_FALSE);
>+
>   jsval v;
>   if (!::JS_CallFunctionValue(cx, wrappedNative->GetFlatJSObject(),
>                               OBJECT_TO_JSVAL(methodToCallObj), argc, argv,
>                               &v)) {
>+    JS_SetFrameIterable(cx, fp, iterable);
>     return JS_FALSE;
>   }
> 
>+  JS_SetFrameIterable(cx, fp, iterable);

Here, unlike the XPCWrappedNative::CallMethod case, I would prefer JSBool ok = ::JS_CallFunctionValue(...) followed by a single unconditional JS_SetFrameIterable call.

Nice patch, r=me with that.

/be
Attachment #229682 - Flags: review?(brendan) → review+
Attached patch With that too (obsolete) — Splinter Review
I missed a spot in the previous patch: we needed to not iterate around the call to ccx.CanCallNow(), since that could create a tearoff, which would need to wrap something.
Attachment #229682 - Attachment is obsolete: true
Attachment #229692 - Flags: superreview?(bzbarsky)
Attachment #229692 - Flags: review?(brendan)
OS: Windows XP → All
Priority: -- → P2
Hardware: PC → All
Whiteboard: [patch]
Target Milestone: --- → mozilla1.8.1beta2
Dan, this bug's patch adds a needed access-check to watchpoint invocation, which might help stop that firebug exploit.

/be
Comment on attachment 229692 [details] [diff] [review]
With that too

r=me, thanks.

/be
Attachment #229692 - Flags: review?(brendan) → review+
I've applied the latest patch and tested.

It is still exploitable by using location.href setter instead of
location.assign to load a javascript: url on the target site.  Also, eval can
be used to exploit on the trunk, 2.0 and 1.5.0.5.  Sorry, I should have
attached various test cases (at least the one that uses eval).
Attached file testcase 3
This uses location.href setter.
Attached file testcase 4
This uses location.href setter with Components.lookupMethod.
Attached file testcase 5
This uses eval.
Attached patch Patch to test (obsolete) — Splinter Review
moz_bug_r_a4, could you test this patch? Feel free to use new Script, etc.
Attachment #229692 - Attachment is obsolete: true
Attachment #229692 - Flags: superreview?(bzbarsky)
(In reply to comment #17)
That patch stops all testcases.  And, I've tried to use new Script and new
Function somehow, but nothing has worked.
It seems that that patch also fixes the privilege escalation bugs.  With that
patch, all testcases in bug 344494, bug 344751 and 345305 fail to exploit.
Flags: blocking1.9a1+
Flags: blocking1.8.1?
Flags: blocking1.8.0.6?
Whiteboard: [patch] → [sg:high][patch]
Blocks: 344494
Blocks: 344751
Blocks: 345305
Flags: blocking1.8.1? → blocking1.8.1+
Attached patch Updated again (obsolete) — Splinter Review
I had to go back to checking explicitly for JS_GetFrameSkippable in caps to preserve JS_FrameIterator API compatibility (and thus changed the name back again). I also added a large comment in xpcwrappednative.cpp explaining what most of this craziness is about.
Attachment #229913 - Attachment is obsolete: true
Attachment #231014 - Flags: superreview?(bzbarsky)
Attachment #231014 - Flags: review?(brendan)
Attachment #231014 - Flags: review?(jst)
Comment on attachment 231014 [details] [diff] [review]
Updated again

r=jst
Attachment #231014 - Flags: review?(jst) → review+
Flags: blocking1.8.0.6? → blocking1.8.0.6+
Whiteboard: [sg:high][patch] → [sg:high][patch] needs review brendan
Comment on attachment 231014 [details] [diff] [review]
Updated again

>Index: js/src/jsobj.c

>+        JS_ASSERT(down || !caller);
>+
>+        if (down && caller && down != caller) {

Given the JS_ASSERT, testing |down && caller| is equivalent to testing 

>+            if (!principals ||
>+                !caller->script->principals ||
>+                !principals->subsume(caller->script->principals, principals))

Should we error out if principals->subsume != caller->script->principals->subsume ?

So what does this block of code really check?  What's |caller|?  What's |down| when we're done with the loop?  Some comments would be very nice here.  For example, how do I tell whether the subsume call is getting the args passed in the right order?  (This last part is not helped by the fact that the declaration of |subsume| doesn't say what the args are, so I'd have to look at existing impls to figure out what should be passed to it.  Adding comments about this would be nice too.)

> obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval 

>+    if (!JS_ValueToId(cx, id, &propid) ||
>+        !OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &v, &attrs)) {

Whose access to do what is this checking?  Please document?

>Index: js/src/jsdbgapi.h

>+extern JS_PUBLIC_API(JSBool)
>+JS_GetFrameSkippable(JSContext *cx, JSStackFrame *fp);
>+
>+extern JS_PUBLIC_API(void)
>+JS_SetFrameSkippable(JSContext *cx, JSStackFrame *fp, JSBool skippable);

What do these do?  I realize there is a policy of not documenting things in the JS engine headers, but did you at least put docs for these on the wiki?

In particular, is this just a hook for jseng consumers, who can make "skippable" mean whatever they want, or does jseng actually do something interesting with frames set as skippable?  If the latter, document what, at least generally?

>Index: js/src/xpconnect/src/xpcwrappednative.cpp
> XPCWrappedNative::CallMethod(XPCCallContext& ccx,

>+    JSStackFrame *fp = ccx.GetJSContext()->fp;
>+    PRBool skippable = JS_GetFrameSkippable(ccx, fp);
>+    JS_SetFrameSkippable(ccx, fp, JS_TRUE);
>+
>     nsresult rv = ccx.CanCallNow();
>+
>+    JS_SetFrameSkippable(ccx, fp, skippable);

This needs some documentation love.  Explain why this is being done, point to the code that relies on this happening, whatever it takes to make this magic make sense.

If the big comment below covers this, just say so here, but it's non-obvious why we need to be skippable just around CanCallNow().  Why reset the old state?  Early returns or something?  If so, document.  If not, document what the reason is.

>+    //    of the function itself has no bearing on what the principals that is

s/principals/principal/

>+    // -- In general, callers of GetSubjectPrincipal, when called from
>+    //    C++ code don't care about the currently running XPConnect function.

Missing comma after "code"?

>+    //    Instead, they really want to see who called the XPConnect function.
>+    //    This means that we trust XPConnect functions more than other
>+    //    functions (more than JS engine functions, even).

Doesn't it rather mean that the principal of the XPConnect function is the same as the object principal for security check purposes and that if we're in this code we're calling into C++ so the function that's going to run is C++-implemented, generally speaking?  I suppose this last could mean we trust it, but the real issue is that its principal is silly, right?

>+    // Note that we'd need to set this frame as skippable around this security
>+    // check (and the CanCallNow check above) and while wrapping return
>+    // values, since during this time, this function isn't actually running,

Hmm.. Which one is "this frame" here?  Is it the frame that called into XPConnect?  Or is it a frame that XPConnect has pushed on the frame stack?  I'm hoping its the latter, given what we're doing here; perhaps we should just document that?

>Index: js/src/xpconnect/src/XPCNativeWrapper.cpp
>+  JSStackFrame *fp = cx->fp;
>+  JSBool skippable = JS_GetFrameSkippable(cx, fp);
>+  JS_SetFrameSkippable(cx, fp, JS_TRUE);

Please document why this is the right thing to do.

sr=bzbarsky with the expanded documentation.
Attachment #231014 - Flags: superreview?(bzbarsky) → superreview+
(In reply to comment #22)
> Given the JS_ASSERT, testing |down && caller| is equivalent to testing 

Did you mean to say "testing |caller|"?

> Should we error out if principals->subsume !=
> caller->script->principals->subsume ?

I don't think so: subsumes was created exactly to avoid doing this sort of opaque-pointer checking when we get two objects from two different worlds.

> > obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval 
> Whose access to do what is this checking?  Please document?

This was originally brendan's idea, and in trying to document it, I've lost the motivation (despite the fact that it fixes the first testcase without the rest of the patch). If I add a watchpoint to an object, and the function is from my security context, and then some code from another context runs, using my object for whatever purpose, then that code shouldn't need access to the original watch function to modify that object. I'll take it out unless brendan defends it.
(In reply to comment #23)
Sounds like some other bug seemed to require checking from obj_watch_handler, but we fixed that bug. ;-)

I still think we should strive to walk the stack and compute the greatest lower bound of trust labels active.  I don't particularly trust certain kinds of native methods more than others.  But I need to unjetlag to review this patch carefully.

/be
The proposed patch causes a regression.  With that patch applied, when
document.open is called, the document's principal is set to the document.open
function's principal instead of the caller's principal.  (document.write does
not regress -- when document.write on a document that has finished loading is
called, the document's principal is set to the caller's principal.)

Please see bug 346659.  The proposed patch does *not* fix bug 346659, and the
regression makes the problem far worse.
Depends on: 346659
(In reply to comment #25)
> The proposed patch causes a regression.  With that patch applied, when
> document.open is called, the document's principal is set to the document.open
> function's principal instead of the caller's principal.

Isn't the regression there simply that we push document.open.call's principals onto the principal stack now (and since document.open is allAccess, that's a Bad Thing (TM))? That's easy to fix, although I don't think that this patch will necessarily fix that bug.
(In reply to comment #26)

Yeah, there is another critical regression, which I didn't notice, regarding
Function.prototype.(call|apply).  This allows content to escalate privilege.

  <marquee id="m">
  h = location.__lookupSetter__("href");
  m.init.call.call(h, location, "javascript:alert(Components.stack)");
This works only with the proposed patch applied.
(In reply to comment #27)
> Yeah, there is another critical regression, which I didn't notice, regarding
> Function.prototype.(call|apply).  This allows content to escalate privilege.

Yeah, I realized this yesterday. This patch won't fly in the long run, it'll be whack-a-mole fixing the regressions caused by it. The discussion in bug 346659 will hopefully end up in a better patch for this bug and similar.
We'd like this on the branch if we can to get bug 344494 fixed as well.
Comment on attachment 231014 [details] [diff] [review]
Updated again

We need a breather, then a sane model on which to implement a non-whack-a-mole fix.  So this bug should not block beta 2.

/be
Attachment #231014 - Flags: review?(brendan) → review-
The patches in this bug cause more harm than good. I have a new strategy that I'll try to put in action tomorrow.
Target Milestone: mozilla1.8.1beta2 → mozilla1.8.1
Given the last two comments it doesn't look like this could possibly make 1.8.0.7, especially not safely.
Flags: blocking1.8.0.8?
Flags: blocking1.8.0.7-
Flags: blocking1.8.0.7+
Is this something that we still want for 181?
Pushing to 1.8.1.1
Flags: blocking1.8.1.1?
Flags: blocking1.8.1-
Flags: blocking1.8.1+
Restoring lost blocking flag
Flags: blocking1.8.0.9?
Brendan and I might have found a new alternative that isn't so buggy... Stay tuned!
Flags: blocking1.9?
(In reply to comment #38)
> Brendan and I might have found a new alternative that isn't so buggy... Stay
> tuned!

Still tuned, did the alternative pan out?
Flags: wanted1.8.1.x+
Flags: wanted1.8.0.x+
Flags: blocking1.8.1.1?
Flags: blocking1.8.1.1-
Flags: blocking1.8.0.9?
Flags: blocking1.8.0.9-
Flags: blocking1.8.1.2+
Flags: blocking1.8.0.10+
Flags: wanted1.8.1.x+
Flags: wanted1.8.0.x+
Not baked yet, moving to the next release
Flags: wanted1.8.1.x+
Flags: wanted1.8.0.x+
Flags: blocking1.8.1.2+
Flags: blocking1.8.0.10+
Depends on: xow
Flags: blocking1.9a1+
Flags: blocking1.9?
Flags: blocking1.9+
any updates on what we should do on this one?
Whiteboard: [sg:high][patch] needs review brendan → [sg:high] patch makes it sg:critical :-(
Attachment #231014 - Attachment is obsolete: true
Keywords: arch
Blake, any word on the alternative?
Targeting to B1 per conversation with Blake.
Target Milestone: mozilla1.8.1 → mozilla1.9beta1
Depends on: 387390
This bug as filed is fixed by XOWs. The remaining issues will be covered by other bugs.
Status: ASSIGNED → RESOLVED
Closed: 17 years ago
Resolution: --- → FIXED
Blocks: 372075
Blocks: 363597
Blocks: 386695
This is verified fixed in Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9pre) Gecko/2008050704 Minefield/3.0pre. 
Status: RESOLVED → VERIFIED
Flags: in-testsuite?
Group: core-security
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: