Closed Bug 118657 Opened 23 years ago Closed 21 years ago

Bypassing same-origin by brute-force find()

Categories

(Core :: Security, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla1.5alpha

People

(Reporter: security-bugs, Assigned: sfraser_bugs)

Details

(Whiteboard: [sg:mustfix])

Attachments

(6 files, 3 obsolete files)

With the help of window.find(), frames and brute forcing it is
possible to read the source of arbitrary documents - especially these
on intranet servers.
A disadvantage of this is because of the bruteforcing it is *really* slow,
but anyway it is a bug.
Examine the attached file for demonstration against www.mozilla.org

Suggest disallowing window.find() across frames from scripts.
fun fun fun. Looks like we'll have to change the find function (implemented in
docshell I think) so that it doesn't iterate into subframes from different
hosts, if the find was initiated by an unprivileged script. That could be
complicated.

Probably not a huge risk right now, because the time necessary to actually read
a whole page by this method is prohibitive; would-be victims will probably kill
their browser before the script finishes. This could be used more simply, for
example to see if a user is on a specific page by searching for key words, but
since the target page must be loaded in a frame this presents difficulties as
well. Essentially, while each find() call reveals a bit of information about a
page, it's a small amount of information only.
Status: NEW → ASSIGNED
Target Milestone: --- → mozilla1.0.1
The place to look is in nsWebBrowserFind::FindNext -- all the
iterate-through-frames code is there.

The check would probably go somewhere near the GetNext call at line 168 of
nsWebBrowserFind.cpp, and then again around the GetNext call at line 213.
Target Milestone: mozilla1.0.1 → mozilla1.2alpha
Since this is a pretty impractical attack, can't we open the bug?
Actually, this is fairly serious. Suppose there is a site blah.com where some
URL, say https://blah.com/login, gives a page containing the user's name, e.g.,
"Hello Robert O'Callahan!". Then you can reverse engineer the name using an
average of 13x(length of name) find's. (E.g. search for "Hello, A" ... "Hello,
Z" then "Hello Ra" ... "Hello, Rz", etc.) That's not at all unrealistic.
Whiteboard: [sg:mustfix]
Over to akkana. I think we should make it so that find() does not recurse into
subframes containing pages from different hosts when called from a script. When
find() is called from the menu, of course, it should behave as normal.
Assignee: mstoltz → akkana
Status: ASSIGNED → NEW
Target Milestone: mozilla1.2alpha → mozilla1.3alpha
Akkana, have you had a chance to look at this bug?
Sorry, no, but I can look at it now.  Any hints as to how to tell when we're
being called from a script?
Kin of Simon, can you pick up the investigation on this one?
Taking.
Assignee: akkana → sfraser
Simon, have you had a chance to look at this bug? Please set a target milestone
(preferably in 1.4).
Will take a look.
Status: NEW → ASSIGNED
OS: Windows 2000 → All
Hardware: PC → All
Target Milestone: mozilla1.3alpha → mozilla1.4alpha
I think the real bug here is that we can call a [noscript] method on
nsIDOMWindowInternal from JS. Is this just because it happens to have the same
name as the Find method on nsIDOMJSWindow?

Maybe we should rename the nsIDOMWindowInternal method.
Never mind. The nsIDOMJSWindow::Find impl grabs params from JS anyway, by design.
I can't get the testcase to work in current builds. At the end, the alert just
contains "<html>" so it looks like nothing was found.
Attached patch Untested patchSplinter Review
Here's what I think a patch would look like, but since I'm unable to reproduce
the problem, I'm not sure it works.
I also can't reproduce this now.
Shall check modifications.
Target Milestone: mozilla1.4alpha → mozilla1.5alpha
Per Mitch's request I tested this again.
Can't reproduce it anymore.
Checked for modifications few weeks ago, couldn't find an exploit.
Shall check for more modifications.
Simon, you mentioned that find() no longer iterates into frames when called from
js. Is this because of a bug that will be fixed, or is this by design? If the
former, we need a permanent fix for 1.4b.
There are a couple of issues going on here. First, the testcase doesn't work
correctly because of an issue with the current search frame. I'll attach a
patch, and a better testcase.
The find init code uses the focussed frame as the default current search frame
(which is what you want for XUL). This patch makes the global window code set
the current search frame to the window on which .find() was called. This makes
the testcase work better (i.e. we are more vulnerable), but is the correct
thing to do.
Attachment #63892 - Attachment is obsolete: true
Attachment #63894 - Attachment is obsolete: true
Attachment #120758 - Attachment is obsolete: true
Attachment #120729 - Flags: superreview?(jst)
Attachment #120729 - Flags: review?(mstoltz)
Comment on attachment 117545 [details] [diff] [review]
Untested patch

I've now tested this patch, using the testcases attached, and verified that it
correctly prevents access to frames from a different host.

The reason the testcase failed to work is that the Find code failed a same
origin check in nsRnage code, which is used internally by the Find
implmenetation.
Attachment #117545 - Flags: superreview?(jst)
Attachment #117545 - Flags: review?(mstoltz)
Comment on attachment 117545 [details] [diff] [review]
Untested patch

- In nsWebBrowserFind::FindNext():

+    // Get JSContext from stack
+    nsCOMPtr<nsIJSContextStack> stack =
do_GetService("@mozilla.org/js/xpc/ContextStack;1");
+    NS_ENSURE_TRUE(stack, NS_ERROR_FAILURE);
+
+    JSContext *cx = nsnull;
+    rv = stack->Peek(&cx);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (!cx)
+      return NS_ERROR_FAILURE;
+

A null context on the JS context stack just means that there's no JS running at
the moment, so you shouldn't return an error just because there's no JS
running. In stead, you should let the caller do the find, since there's nothing
to protect against if there's no JS on the stack.

And you can even remove the above code, you can pass null as the context to the
script security manager and it will do the above for you, no need to duplicate
that code here. Oh, and if you do that, no need to #include
"nsIJSContextStack.h"

sr=jst with that change.
Attachment #117545 - Flags: superreview?(jst) → superreview+
Comment on attachment 120729 [details] [diff] [review]
Patch to nsGlobalWindow  to set the current search frame

+  nsCOMPtr<nsIWebBrowserFindInFrames> framesFinder(do_QueryInterface(finder));
+  framesFinder->SetRootSearchFrame(this);   // paranoia
+  framesFinder->SetCurrentSearchFrame(this);

Should you null-check |framesFinder|, or do we not care?

sr=jst
Attachment #120729 - Flags: superreview?(jst) → superreview+
Comment on attachment 120729 [details] [diff] [review]
Patch to nsGlobalWindow  to set the current search frame

Patch 2 ooks good, r=mstoltz.
Attachment #120729 - Flags: review?(mstoltz) → review+
Comment on attachment 117545 [details] [diff] [review]
Untested patch

+    rv = secMan->CheckSameOrigin(cx, docURI);
+    NS_ENSURE_SUCCESS(rv, rv);

NS_ENSURE_SUCCESS does an assertion when it fails, doesn't it? As it's
completely legitimate for this check to return failure (meaning different
origins) we should not assert in this  case, but just return.

Ditto on jst's suggestions. With those changes, r=mstoltz.
Attachment #117545 - Flags: review?(mstoltz) → review+
NS_ENSURE_SUCCESS does a warning if it fails, and you're right Mitch, we don't
need that for this security check. if (NS_FAILED(rv)) return rv; is all we need.
Checked in with comments addressed. Thanks all!
Status: ASSIGNED → RESOLVED
Closed: 21 years ago
Resolution: --- → FIXED
Bugs published on the Known-vulnerabilities page long ago, removing confidential
flag.
Group: security
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: