Closed
Bug 853709
(CVE-2013-1670)
Opened 12 years ago
Closed 12 years ago
Its possible to call a content level constructor as if from a chrome/privileged page
Categories
(Core :: XPConnect, defect)
Tracking
()
People
(Reporter: codycrews00, Assigned: bholley)
References
Details
(Keywords: sec-high, Whiteboard: [adv-main21+][adv-esr1706+][adv-main35-])
Attachments
(3 files)
(deleted),
text/html
|
Details | |
(deleted),
patch
|
mrbkap
:
review+
akeybl
:
approval-mozilla-beta+
akeybl
:
approval-mozilla-esr17+
akeybl
:
approval-mozilla-b2g18+
abillings
:
sec-approval+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
mrbkap
:
review+
|
Details | Diff | Splinter Review |
User Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:19.0) Gecko/20100101 Firefox/19.0
Build ID: 20130307023931
Steps to reproduce:
I used the mozContact constructor to gain access to a chrome level array and then added a content level constructor to it and defined a setter to do the calling for me.
Actual results:
The content level constructor behaves as if called from a privileged page as shown from the error, this works with most content level constructors(that are function objects) but using the File constructor was just the easiest way to show this off. I had discussed this issue in passing in an email with Bobby Holly but hadn't had an easy way to show it off.
Expected results:
Chrome level Objects exposed to content should be careful about any properties that are 'rw' as defined in __exposedProps__, I think it may be best to avoid it at all if possible. Arrays from chrome and the normal COWs(this works with them too) are going to have to be rethought. This happens I believe because Function Objects inherit their principals from the nearest Object in the scope of their execution.
Reporter | ||
Updated•12 years ago
|
Attachment #728007 -
Attachment mime type: text/plain → text/html
Updated•12 years ago
|
Component: Untriaged → XPConnect
Product: Firefox → Core
Assignee | ||
Comment 1•12 years ago
|
||
Testcase works in a Desktop Nightly. Nice job, Cody.
Assignee | ||
Comment 2•12 years ago
|
||
Looks this there's a couple of things going on here. I'll look at it again when I'm less tired.
Reporter | ||
Comment 3•12 years ago
|
||
Yeah there's definitely a couple of things to this. Without the ability to define setters/getters on COW's or arrays the Function objects will continue to act as if called from content. The 'this' keyword always becomes the object of and I believe the beginning of the lexical scope when dealing with setters and getters even when inherited through the prototype chain. Also obtaining a reference to the created object would pose another problem and yet could probably be done, but at present just with the File constructor and using error messages it would be possible to map a users local Filesystem giving information about dlls and other files which could be used to bypass ASLR etc. I usually like to fully work out what all can be done with something before filing but that alone seemed serious enough for this bug to be filed ASAP.
Assignee | ||
Comment 4•12 years ago
|
||
Cody, this is extremely clever.
For those who don't want to pore over the testcase, here's what it does:
* It creates a mozContact, which is implemented as a COW-wrapped chrome object, with an array marked 'rw'.
* It puts the File constructor at index 0 in the array.
* It then defines a setter at index 1 whose setter function is index 0 of the array, i.e. the File constructor.
* It does arr[1] = 'c:\some\file\path\'. This trips the setter, which ends up invoking the File constructor with the associated string as its only argument.
The key point here is that the File constructor never gets any sort of Xray waiver around it, and so the chrome caller invokes it with Xray vision, thus never entering the compartment and never dropping from chrome principal to page principal. So the IsCallerChrome() check in nsDOMMultipartFile::InitFile succeeds. Yikes.
Now, the real question is how to fix this in the general case. Our security model assumes that it's safe to call an Xrayed function because it never enters the content compartment, meaning that there's no opportunity for the target to confuse it. But here we have the content entering the chrome compartment instead, and setting things up so that the chrome code shoots itself in the foot. :-(
There are number of mitigation techniques we can apply here:
* Stop defining (or even supporting?) rw properties on COWs.
* Don't allow accessors to be defined on COWs.
But in general, I think we fundamentally need to move away from letting content muck with privileged objects at all.
Updated•12 years ago
|
Assignee: nobody → bobbyholley+bmo
Flags: sec-bounty? → sec-bounty+
Updated•12 years ago
|
Attachment #729246 -
Attachment is private: true
Reporter | ||
Comment 5•12 years ago
|
||
This one was one that I was particularly interested in, yet there's still a few more things that I feel could lead to issues, though nothing quite like this. As to ways to mitigate this I'm sort of at a loss. I mean Bobby Holly has presented the viable options and its just about how much code relies on COW's with 'rw' properties. I'm pretty tired tonight but tomorrow I'll try to dig through mozilla internal code that those fixes might affect, addons will be a different story though.
Comment 6•12 years ago
|
||
So the fundamental issue here is that chrome is ending up with an Xray for a function when it doesn't expect one, right?
Disallowing accessors on COWs seems like a good step. I doubt anyone setting a property 'w' expects that to mean "content can redefine it as an accessor property".
But past that, I agree; we need to move away from letting content mess directly with privileged objects. The question is how we get there...
Comment 7•12 years ago
|
||
(In reply to Boris Zbarsky (:bz) from comment #7)
> Disallowing accessors on COWs seems like a good step. I doubt anyone
> setting a property 'w' expects that to mean "content can redefine it as an
> accessor property".
For some reason, treating accessor properties specially rubs me the wrong way. I did it for some XPCNativeWrapper holes back in the day, but it never felt right. I guess my question is: what is the fundamental difference between accessor properties and calling functions?
> But past that, I agree; we need to move away from letting content mess
> directly with privileged objects. The question is how we get there...
What do you mean with "mess with?" As long as we want to implement privileged APIs in chrome JS, we're going to have interaction between content and chrome (at the very least in the form of function parameters).
Can we make functions passed to chrome be non-waiver non-Xray wrappers (so the only Xray functions called by chrome would be functions explicitly gotten off of existing Xray wrapper objects which have guaranteed behavior)?
Comment 8•12 years ago
|
||
> what is the fundamental difference between accessor properties and calling functions?
The fact that the latter is syntactically obvious to the chrome code, and thus easier to avoid, imo.
> What do you mean with "mess with?"
"Put them into a state the chrome code operating on those objects does not expect them to be in".
> As long as we want to implement privileged APIs in chrome JS, we're going to have
> interaction between content and chrome (at the very least in the form of function
> parameters).
To some extent. For example, in the JS-implemented WebIDL most function parameters are ensured to be sane (in the sense of not having any unexpected behavior) by the binding layer, with the obvious exceptions being explicit "object" and "any" arguments and callbacks.
> Can we make functions passed to chrome be non-waiver non-Xray wrappers
So what I proposed to Bobby earlier today is that _calling_ a DOM interface object Xray should simply enter the content compartment for the call, just like an Xray for a scripted function would. These are Xrays because we want to be able to get at their properties, not because we want to call them in the chrome compartment, imo.
Assignee | ||
Comment 9•12 years ago
|
||
(In reply to Blake Kaplan (:mrbkap) from comment #8)
> Can we make functions passed to chrome be non-waiver non-Xray wrappers (so
> the only Xray functions called by chrome would be functions explicitly
> gotten off of existing Xray wrapper objects which have guaranteed behavior)?
This is effectively a third state for Xrayable objects, which I'm not really wild about implementing. I'd much prefer to just remove the ability for content to inject functions into the chrome scope.
(In reply to Boris Zbarsky (:bz) from comment #9)
> > As long as we want to implement privileged APIs in chrome JS, we're going to have
> > interaction between content and chrome (at the very least in the form of function
> > parameters).
>
> To some extent. For example, in the JS-implemented WebIDL most function
> parameters are ensured to be sane (in the sense of not having any unexpected
> behavior) by the binding layer, with the obvious exceptions being explicit
> "object" and "any" arguments and callbacks.
I agree with bz here that it's significantly harder to mount an attack like this with a C++ layer in between. And I think we can get there. The stuff in mozilla-central should just be moved to WebIDL, and the jetpack folks are moving towards an API that does everything via makeObjectPropsNormal-style function wrappers.
> So what I proposed to Bobby earlier today is that _calling_ a DOM interface
> object Xray should simply enter the content compartment for the call
As mentioned, I disagree here. I think it significantly muddles the Xray semantics.
> just like an Xray for a scripted function would.
There isn't really such a thing. It's true that we currently default to transparent for non-waived non-Xrayable objects, but I consider that to be a legacy bug that I'd like to move away from, if possible. IMO any raw JS objects used by chrome should be wrapped in a waiver.
Comment 10•12 years ago
|
||
This is a hard problem and has been causing headaches for a while, importing pure JS objects from chrome to content. While I always thought about providing the least limiting high level API to JS developers is the best, I kind of gave up on it.
Based on the talks I had in the past few weeks with pure JS developers, my impression is that they tend to prefer a limiting low level API a lot more, than dealing with weird, non standard JavaScript wrappers with __exposedProps__ magics, or that twin object version we came up with the last DOM work week. The trick we use in makeObjectPropsNormal seems to be quite safe way for exporting functions (only) to content. And if we only expose that trick to JS developers they seems to be happy to do everything else in JS (for states they can simply use closures, for simple object they can just use structure clone, etc.). This whole approach seems to be very safe (there is only a very thin layer between chrome and content to guard), and very flexible (the high level utility functions for exposing various more complex objects in some way is implemented in standard JS unlike __exposedProps__ or makeObjectPropsNormal), plus it is a lot easier to explain to JS people who are the main users of this API. That's just my two cents.
Assignee | ||
Updated•12 years ago
|
Flags: in-testsuite?
Assignee | ||
Comment 11•12 years ago
|
||
Attachment #731234 -
Flags: review?(mrbkap)
Assignee | ||
Comment 12•12 years ago
|
||
Attachment #731235 -
Flags: review?(mrbkap)
Assignee | ||
Comment 13•12 years ago
|
||
Assignee | ||
Comment 14•12 years ago
|
||
Comment on attachment 731234 [details] [diff] [review]
Deny accessor definitions in SecurityWrapper. v1
Pre-emptively flagging for sec-approval in case we decide we want to take this before the merge.
[Security approval request comment]
How easily could an exploit be constructed based on the patch?
Somewhat. Cody's exploit uses a number of tricks, and this fixes one of them. In general, defining a getter or setter on a COW wouldn't seem to be a huge problem, because we'd always enter the content compartment. So they'd have to think of the Xray trick here.
Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?
It's pretty clear that we don't want accessors to be defined on security wrappers. But I managed to make this patch general to all security wrappers and avoid talking about COWs at all.
Which older supported branches are affected by this flaw?
All.
Do you have backports for the affected branches? If not, how different, hard to create, and risky will they be?
This patch should work everywhere we have bug 800915, which should be all supported branches at this point.
How likely is this patch to cause regressions; how much testing does it need?
It should probably be fine. The only regression would be if some COW (maybe in an extension, though it's unlikely) was depending on having content define accessor properties. We now explicitly throw a very descriptive error message in that case (that's the whole point of the patch). That's probably unlikely.
Attachment #731234 -
Flags: sec-approval?
Updated•12 years ago
|
Attachment #731234 -
Flags: sec-approval? → sec-approval+
Updated•12 years ago
|
status-b2g18:
--- → affected
status-firefox19:
--- → affected
status-firefox20:
--- → affected
status-firefox21:
--- → affected
status-firefox22:
--- → affected
status-firefox-esr17:
--- → affected
tracking-firefox22:
--- → +
Updated•12 years ago
|
Attachment #731234 -
Flags: review?(mrbkap) → review+
Assignee | ||
Comment 15•12 years ago
|
||
Assignee | ||
Comment 16•12 years ago
|
||
Comment on attachment 731234 [details] [diff] [review]
Deny accessor definitions in SecurityWrapper. v1
[Approval Request Comment]
Bug caused by (feature/regressing bug #): longstanding issue, exploitable via the contact API
User impact if declined: security bugs
Testing completed (on m-c, etc.): just pushed to m-i
Risk to taking this patch (and alternatives if risky): Not very risky. No real alternatives.
String or IDL/UUID changes made by this patch: There's a change to js.msg, but I don't believe that affects l10n (correct me if I'm wrong).
Attachment #731234 -
Flags: approval-mozilla-esr17?
Attachment #731234 -
Flags: approval-mozilla-beta?
Attachment #731234 -
Flags: approval-mozilla-b2g18?
Assignee | ||
Comment 17•12 years ago
|
||
Comment on attachment 731234 [details] [diff] [review]
Deny accessor definitions in SecurityWrapper. v1
I guess I meant aurora here, which will be beta in a few days. :-)
Attachment #731234 -
Flags: approval-mozilla-beta? → approval-mozilla-aurora?
Comment 18•12 years ago
|
||
Status: NEW → RESOLVED
Closed: 12 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla22
Comment 19•12 years ago
|
||
Comment on attachment 731235 [details] [diff] [review]
Tests. v1
Review of attachment 731235 [details] [diff] [review]:
-----------------------------------------------------------------
::: js/xpconnect/tests/unit/test_bug853709.js
@@ +30,5 @@
> +
> + checkDefineThrows(contentSB, 'chromeObj', 'a', {get: function() { return 2; }});
> + checkDefineThrows(contentSB, 'chromeObj', 'a', {configurable: true, get: function() { return 2; }});
> + checkDefineThrows(contentSB, 'chromeObj', 'b', {configurable: true, get: function() { return 2; }, set: function() {}});
> + checkDefineThrows(contentSB, 'chromeArr', '1', {configurable: true, get: function() { return 2; }});
Also add a test for setter-only properties?
Attachment #731235 -
Flags: review?(mrbkap) → review+
Updated•12 years ago
|
Updated•12 years ago
|
Attachment #731234 -
Flags: approval-mozilla-esr17?
Attachment #731234 -
Flags: approval-mozilla-esr17+
Attachment #731234 -
Flags: approval-mozilla-beta+
Attachment #731234 -
Flags: approval-mozilla-b2g18?
Attachment #731234 -
Flags: approval-mozilla-b2g18+
Attachment #731234 -
Flags: approval-mozilla-aurora?
Comment 20•12 years ago
|
||
https://hg.mozilla.org/releases/mozilla-beta/rev/b4db16c3fc30
This is going to need some love for b2g18/esr17 uplift.
status-b2g18-v1.0.0:
--- → wontfix
status-b2g18-v1.0.1:
--- → affected
Comment 21•12 years ago
|
||
Looks like beta is going to need some love too. Backed out for OSX and Windows build bustage.
https://hg.mozilla.org/releases/mozilla-beta/rev/8ce4ff5714bd
https://tbpl.mozilla.org/php/getParsedLog.php?id=21399661&tree=Mozilla-Beta
In file included from ../../../../js/src/jswrapper.cpp:15:
../../../../js/src/jswrapper.h:190:18: error: 'defineProperty' marked 'override' but does not override any member functions
virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
^
../../../../js/src/jswrapper.cpp:892:20: note: in instantiation of template class 'js::SecurityWrapper<js::Wrapper>' requested here
template class js::SecurityWrapper<Wrapper>;
^
In file included from ../../../../js/src/jswrapper.cpp:15:
../../../../js/src/jswrapper.h:190:18: warning: 'js::SecurityWrapper<js::Wrapper>::defineProperty' hides overloaded virtual function [-Woverloaded-virtual]
virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
^
../../../../js/src/jswrapper.h:90:18: note: hidden overloaded virtual function 'js::Wrapper::defineProperty' declared here
virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
^
../../../../js/src/jswrapper.h:190:18: error: 'defineProperty' marked 'override' but does not override any member functions
virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
^
../../../../js/src/jswrapper.cpp:893:20: note: in instantiation of template class 'js::SecurityWrapper<js::CrossCompartmentWrapper>' requested here
template class js::SecurityWrapper<CrossCompartmentWrapper>;
^
In file included from ../../../../js/src/jswrapper.cpp:15:
../../../../js/src/jswrapper.h:190:18: warning: 'js::SecurityWrapper<js::CrossCompartmentWrapper>::defineProperty' hides overloaded virtual function [-Woverloaded-virtual]
virtual bool defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
^
../../../../js/src/jswrapper.h:138:18: note: hidden overloaded virtual function 'js::CrossCompartmentWrapper::defineProperty' declared here
virtual bool defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
^
2 warnings and 2 errors generated.
make[6]: *** [jswrapper.o] Error 1
Assignee | ||
Comment 22•12 years ago
|
||
You just need to change the HandleObject to JSObject* and the HandleId to jsid (in both the header and the cpp file).
https://hg.mozilla.org/releases/mozilla-beta/rev/4ad0a5f5f017
Comment 23•12 years ago
|
||
Updated•12 years ago
|
Alias: CVE-2013-1670
Whiteboard: [adv-main21+][adv-esr1706+]
Comment 24•12 years ago
|
||
Reproduced on FF17.0.5esr
Confirmed fixed on FF17.0.6esr candidate build 1
Confirmed fixed on FF21 candidate build 2
Confirmed fixed on FF22 2013-05-09
Confirmed fixed on FF23 2013-05-09
Status: RESOLVED → VERIFIED
Updated•11 years ago
|
Attachment #729246 -
Attachment description: Bug Bounty Awarded $3000 → Bug Bounty Awarded $3000 [paid] 5/1/13
Assignee | ||
Comment 25•10 years ago
|
||
Assignee | ||
Updated•10 years ago
|
Flags: in-testsuite? → in-testsuite+
status-firefox35:
--- → fixed
Updated•10 years ago
|
Group: core-security
Updated•10 years ago
|
Whiteboard: [adv-main21+][adv-esr1706+] → [adv-main21+][adv-esr1706+][adv-main35-]
You need to log in
before you can comment on or make changes to this bug.
Description
•