Closed Bug 201132 (liu1) Opened 22 years ago Closed 22 years ago

Same origin violation using events

Categories

(Core :: Security, defect)

defect
Not set
critical

Tracking

()

RESOLVED FIXED
mozilla1.4beta

People

(Reporter: security-bugs, Assigned: security-bugs)

References

()

Details

Attachments

(4 files, 6 obsolete files)

193 bytes, text/html
Details
728 bytes, text/html
Details
5.42 KB, patch
security-bugs
: review+
brendan
: superreview+
Details | Diff | Splinter Review
2.98 KB, patch
security-bugs
: review+
brendan
: superreview+
Details | Diff | Splinter Review
from liudieyuinchina@yahoo.com.cn - the following testcase appears to be able to read properties (including document.cookie) from a third-party webpage - a same-origin violation.
Attached file The exploit
Whoops, disregard the last one
Attachment #119758 - Attachment is obsolete: true
OK, I give up. The exploit doesn't work when run directly from the attachment above, but you can try it at http://liudieyuinchina.vip.sina.com/NETSCAPECONFIDENTIAL-WS1-030408002400/MOZedgeLink.htm I have reproduced this - it works. Heikki, jst, any clue what's going on here? Looks like he's setting a timeout, changing the page location, and then calling an event handler from the timeout, which reads the newly loaded page.
Severity: normal → critical
Status: NEW → ASSIGNED
Keywords: nsbeta1
OS: Windows 2000 → All
Hardware: PC → All
Target Milestone: --- → mozilla1.4beta
Adding John to CC since this might be events code...
Btw, I was unable to see this originally because my popup controls prevented window.open, so maybe that can protect some people in some cases...
For me the problem seems that MyLink is saved from the old window and then used in the new window to access it. 25 MyLink=w.document.links[0]; then 15 MyLink.onclick(); The event handler is executed in the context of the target window, without a check that it is called from a page in another domain. Probably there is additionally a race condition also.
Attached patch Plug this security hole. (obsolete) — Splinter Review
This code makes sure that event handlers are compiled using the right principal, even when event handlers on elements on documents that are no longer displayed in a window are compiled. This also eliminates the per-context strong reference to the security manager in favor of a global one, that'll cut off 4 bytes off of the size of a context, and also simplify the code a bit. If needed, we can take that change out for landing on a branch or something, but this is what I want to have checked in on the trunk.
Attachment #120013 - Flags: superreview?(brendan)
Attachment #120013 - Flags: review?(mstoltz)
I think you want NS_IF_ADDREF in nsJSContext::GetSecurityManager().
Comment on attachment 120013 [details] [diff] [review] Plug this security hole. General remarks: - This is all because nsEventListenerManager.cpp defers compilation until an event is actually being handled on the object, saving the string value of the attribute for deferred compilation, right? - In that case, using the target's object principal instead of the context's (subject) principal is clearly the right fix -- pls. comment in nsJSContext::CompileEventHandler saying so. - But could it not be the wrong thing if some evil content could set the onclick property of a victim window's document.links[0], or similar? Do we restrict access to all event targets using same-origin checks? I hope we do. This patch, while it clearly fixes the bug at hand, adds a degree of "auto-magic" to nsJSContext::CompileEventHandler that may break the "inverse" case, if such a case is possible. Such a case (evil onclick setter on victim target, not good onclick setter on dangling-therefore-potentially-evil-in-light-of-this-bug target) should be ruled out with extreme prejudice! ;-) >@@ -930,25 +932,37 @@ nsJSContext::CompileEventHandler(void *a > const nsAString& aBody, > PRBool aShared, void** aHandler) > { >+ JSObject *target = (JSObject*)aTarget; > JSPrincipals *jsprin = nsnull; > >- nsCOMPtr<nsIScriptGlobalObject> global; >- GetGlobalObject(getter_AddRefs(global)); >- if (global) { >- // XXXbe why the two-step QI? speed up via a new GetGlobalObjectData func? >- nsCOMPtr<nsIScriptObjectPrincipal> globalData = do_QueryInterface(global); >- if (globalData) { >- nsCOMPtr<nsIPrincipal> prin; >- if (NS_FAILED(globalData->GetPrincipal(getter_AddRefs(prin)))) >- return NS_ERROR_FAILURE; >- prin->GetJSPrincipals(&jsprin); >+ if (target && sSecurityManager) { >+ nsCOMPtr<nsIPrincipal> prin; >+ >+ nsresult rv = >+ sSecurityManager->GetObjectPrincipal(mContext, target, >+ getter_AddRefs(prin)); >+ NS_ENSURE_SUCCESS(rv, rv); >+ >+ prin->GetJSPrincipals(&jsprin); >+ } else { >+ nsCOMPtr<nsIScriptGlobalObject> global; >+ GetGlobalObject(getter_AddRefs(global)); >+ if (global) { >+ // XXXbe why the two-step QI? speed up via a new >+ // GetGlobalObjectData func? >+ nsCOMPtr<nsIScriptObjectPrincipal> globalData = do_QueryInterface(global); >+ if (globalData) { >+ nsCOMPtr<nsIPrincipal> prin; >+ if (NS_FAILED(globalData->GetPrincipal(getter_AddRefs(prin)))) >+ return NS_ERROR_FAILURE; >+ prin->GetJSPrincipals(&jsprin); >+ } > } > } Why not move down and fuse the prin->GetJSPrincipals(&jsprin) calls (and therefore move up the nsCOMPtr<nsIPrincipal> prin decl)? You'd need to null-test prin in case !globalData, but perhaps we should instead insist that we always get a non-null prin here -- otherwise we're compiling without principals. > NS_IMETHODIMP > nsJSContext::GetSecurityManager(nsIScriptSecurityManager **aInstancePtr) > { >- if (!mSecurityManager) { >- nsresult rv = NS_OK; >- >- mSecurityManager = do_GetService(kScriptSecurityManagerContractID, &rv); >- NS_ENSURE_SUCCESS(rv, rv); >- } >- >- *aInstancePtr = mSecurityManager; >+ *aInstancePtr = sSecurityManager; > NS_ADDREF(*aInstancePtr); Heikki's got a point, but you've changed the semantics a bit: before, we would fail if do_GetService failed -- we would not return a null *aInstancePtr and NS_OK. Do you want to fail if !sSecurityManager? /be
Yes, I think we do want to fail if !sSecurityManager; that seems safer. jst, can you help me test Brendan's first point about the reverse case?
I can't think of any way to set an event handler on an element in a document from a different host, so I think the reverse case is OK, but I'd like to hear from jst.
Attachment #120013 - Attachment is obsolete: true
Attachment #120096 - Flags: superreview?(brendan)
Attachment #120096 - Flags: review?(heikki)
Attachment #120096 - Flags: review?(heikki) → review+
Alias: liu1
Comment on attachment 120096 [details] [diff] [review] Patch v2 - addresses review comments >@@ -930,25 +932,37 @@ Use cvs diff -p -u -- the -p provides winning function or method name context. > const nsAString& aBody, > PRBool aShared, void** aHandler) > { >+ JSObject *target = (JSObject*)aTarget; > JSPrincipals *jsprin = nsnull; >+ nsCOMPtr<nsIPrincipal> prin; > >- nsCOMPtr<nsIScriptGlobalObject> global; >- GetGlobalObject(getter_AddRefs(global)); >- if (global) { >- // XXXbe why the two-step QI? speed up via a new GetGlobalObjectData func? >- nsCOMPtr<nsIScriptObjectPrincipal> globalData = do_QueryInterface(global); >- if (globalData) { >- nsCOMPtr<nsIPrincipal> prin; >- if (NS_FAILED(globalData->GetPrincipal(getter_AddRefs(prin)))) >- return NS_ERROR_FAILURE; >- prin->GetJSPrincipals(&jsprin); >+ if (target && sSecurityManager) { Question: if target && !sSecurityManager, should we really use the subject prin? Or should we fail hard inside an if (target) { ... } block here, right away, if (!sSecurityManager)? >+ // If we're doing lazy compilation of a handler defined as an attribute, >+ // we want to use the principal of the target object, *not* the principal >+ // of the context, which may be different. >+ nsresult rv = >+ sSecurityManager->GetObjectPrincipal(mContext, target, >+ getter_AddRefs(prin)); >+ NS_ENSURE_SUCCESS(rv, rv); >+ } else { >+ nsCOMPtr<nsIScriptGlobalObject> global; >+ GetGlobalObject(getter_AddRefs(global)); >+ if (global) { >+ // XXXbe why the two-step QI? speed up via a new >+ // GetGlobalObjectData func? >+ nsCOMPtr<nsIScriptObjectPrincipal> globalData = do_QueryInterface(global); >+ if (globalData) { >+ globalData->GetPrincipal(getter_AddRefs(prin)); >+ } > } >- } >+ } >+ if (!prin) >+ return NS_ERROR_FAILURE; This if is not needed here in the (target && sSecurityManager) case, because of the NS_ENSURE_SUCCESS(rv, rv) -- I say make the else clause that gets the subject prin fail hard if (!globalData), so you can assert that prin is non-null here (don't bother asserting -- just dereference null). /be
Comment on attachment 120096 [details] [diff] [review] Patch v2 - addresses review comments >+ NS_ENSURE_SUCCESS(rv, rv); >+ } else { >+ nsCOMPtr<nsIScriptGlobalObject> global; >+ GetGlobalObject(getter_AddRefs(global)); >+ if (global) { >+ // XXXbe why the two-step QI? speed up via a new >+ // GetGlobalObjectData func? >+ nsCOMPtr<nsIScriptObjectPrincipal> globalData = do_QueryInterface(global); >+ if (globalData) { >+ globalData->GetPrincipal(getter_AddRefs(prin)); >+ } > } >- } Have to fail if (!global), too, with the change I proposed to ensure a non-nul prin. /be
Flags: blocking1.4b?
jst said he's getting rid of the (target && !sSecurityManager) clause that gets the subject principal.
This should fix all of the above comments, this makes us never use the principal from the global object in the context where the event handler is compiled, we now always use the target. This makes a target and a script security manager required to compile an event handler, w/ the current code, we'd crash in the JS engine if we didn't get a target, and we should never end up in this code in the first place if we can't create a script security manager.
Attachment #120096 - Attachment is obsolete: true
Comment on attachment 120152 [details] [diff] [review] Same thing, but always get the principal from the target, bail if no target. r=mstoltz.
Attachment #120152 - Flags: superreview?(brendan)
Attachment #120152 - Flags: review+
Attachment #120152 - Flags: superreview?(brendan) → superreview+
Comment on attachment 120152 [details] [diff] [review] Same thing, but always get the principal from the target, bail if no target. Not so fast, if you please. I'll mark sr=brendan@mozilla.org on substantially different, new patches. /be
Attachment #120152 - Flags: superreview+ → superreview?(brendan)
Comment on attachment 120152 [details] [diff] [review] Same thing, but always get the principal from the target, bail if no target. See bug 195010, look for the diff -p identifier for nsXULElement::CompileEventHandler. I am trying to debloat (both code and dynamic memory) the XUL content sink from having to create global objects for each XUL document, just to be able to precompile event handlers and scripts. It is legal to pass a null "target" scope object into JS_CompileUCFunctionForPrincipals. It's also legal to pass a null name, btw -- the two are independent parameters. You might want the JS compiler to prebind the function it compiles to a scope that you know you'll use when invoking the function; or not. You might want to name the function internally (for decompilation, pretty-printing) but not find the function object value to that name in any scope when precompiling (i.e., when passing a null target obj param); or not. jst, mstoltz: can we allow a null target parameter here and use a null principal? The XUL code and the JS engine will arrange to clone any function objects that are precompiled against a null, or the wrong, scope object. The caps code should use the static (parent) link to find the right principals for the clone, preferring those principals to any precompiled into the cloned function object's prototype object. /be
Attachment #120152 - Flags: superreview?(brendan) → superreview-
I wrote > but not find the function object value to that name in any scope when > precompiling s/find/bind/ I agree we should not use the subject principals in nsJSContext::CompileEventHandler under any circumstances. I'm proposing that if a null target is passed in, that method should use null principals when calling JS_CompileUCFunctionForPrincipals. Null principals should not give any powers to scripts having them. /be
Unfortunately, that's not the case - null principal is treated as equivalent to the system principal. While I think that's a bad state of affairs (I have an action item to make all system scripts require the system principal), it would be a big, somewhat risky change that I don't want to attempt as part of this fix. So, for now, no object should have a null principal.
Ok, but I don't see a practical difference right now between nsJSContext::CompileEventHandler using a null principal for null target (when XUL is precompiling) and requiring a non-null target from which to get a non-null principal. All precompiled XUL JS comes from chrome: URLs, which get the system principal through the pile of bloated nsXULPDGlobalObject/nsXULPrototypeDocument code I'm trying to eliminate in bug 195010 and sequelae. If null means the same as system principal, let's save that code and runtime data. But remember: all precompiled functions are auto-cloned before being invoked against real chrome windows and other, window-contained event targets. See http://lxr.mozilla.org/mozilla/source/dom/src/base/nsJSEnvironment.cpp#1090 and http://lxr.mozilla.org/mozilla/source/content/xul/content/src/nsXULElement.cpp#1848 (notice the difference between aTarget and scopeObject in the latter, especially when you apply my patch for bug 195010, which I'm hoping to land today). See http://lxr.mozilla.org/mozilla/source/caps/src/nsScriptSecurityManager.cpp#1820 for where the caps code does the right thing with cloned function objects. /be
Ok, this should allow for that, and I assume you're correct in that JS_CompileUCFunctionForPrincipals() is safe for a null object, though I did try that in the debugger, and the JS engine ended up trying to access members on the object, and crashed. I'm assuming that then only happens in circumstances where it would never get a null object...
Comment on attachment 120240 [details] [diff] [review] Allow for null targets, use null JSPrincipals r=mstoltz on the latest patch.
Attachment #120240 - Flags: review+
Comment on attachment 120240 [details] [diff] [review] Allow for null targets, use null JSPrincipals jst: make sure you have an up-to-date jsapi.c. This looks ok to me, although nsJSContext::GetSecurityManager now seems to go out of its way to set its out param before failing, which isn't strictly necessary (but which doesn't hurt). /be
Attachment #120240 - Flags: superreview+
Question about comment 23: >All precompiled XUL JS comes from chrome: URLs, which get the system principal >through the pile of bloated nsXULPDGlobalObject/nsXULPrototypeDocument code I'm >trying to eliminate in bug 195010 and sequelae. If null means the same as >system principal, let's save that code and runtime data. Is this precompiled JS: a=new Script("alert('a')");a.compile();a.exec() Can it cause security problems because it is remote?
georgi: by precompiled XUL JS I mean the scripts and event handlers compiled by code in content/xul/content/src/nsXULElement.cpp. Using the Script object from JS precompiles differently, and uses the principals of the calling script. See code at and after http://lxr.mozilla.org/mozilla/source/js/src/jsscript.c#191. /be
Fix checked in. Marking FIXED. Feel free to reopen if this needs fixin' on branches or whatever.
Status: ASSIGNED → RESOLVED
Closed: 22 years ago
Resolution: --- → FIXED
Oh, and yeah, Brendan, I had an old jsapi.c when I tested (null target)...
Blocks: 201889
We backed this out because of smoketest blocker bug 201889. I have a tree with the patch, trying to look into it now.
Status: RESOLVED → REOPENED
Resolution: FIXED → ---
So I tried adding back some code to use the principal of the global object when target is null in nsJSContext::CompileEventHandler and that seems to fix the problems (I test by trying to open the JS Console). It's obviously not the right fix but it might give others some clue as to what is going on. I need to go home now, will check back from there.
This fixes the regression that was caused by the above patch. This patch makes caps treat a cloned Function object whose script has no principals as brutally shared chrome.
Attachment #120152 - Attachment is obsolete: true
Per feedback from Brendan, this makes caps always get the principals for a cloned function object from the functions scope since the principals that are compiled into the function are never reliable, no matter what they are.
Attachment #120508 - Attachment is obsolete: true
Comment on attachment 120518 [details] [diff] [review] Same as above with some assertions and comment changes. sr=brendan@mozilla.org with the comment tweak we spoke about on IRC. /be
Attachment #120518 - Flags: superreview+
Comment on attachment 120518 [details] [diff] [review] Same as above with some assertions and comment changes. This change looks OK to me - looks like the security-critical cases will behave correctly. r=mstoltz.
Attachment #120518 - Flags: review+
Checked in both patches. FIXED.
Status: REOPENED → RESOLVED
Closed: 22 years ago22 years ago
Resolution: --- → FIXED
Group: security
Fix checked in on the 1.3 branch.
Flags: blocking1.4b?
Attachment #120013 - Flags: superreview?(brendan)
Attachment #120013 - Flags: superreview-
Attachment #120013 - Flags: review?(mstoltz)
Attachment #120013 - Flags: review-
Comment on attachment 120096 [details] [diff] [review] Patch v2 - addresses review comments removing obsolete review request
Attachment #120096 - Flags: superreview?(brendan)
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: