Bug 757376 (CVE-2012-1955)

History navigation error with late location.hash changes

VERIFIED FIXED in Firefox 14

Status

()

Core
Document Navigation
VERIFIED FIXED
5 years ago
2 years ago

People

(Reporter: Mariusz Mlynski, Assigned: Justin Lebar (not reading bugmail))

Tracking

(Depends on: 1 bug, {sec-high})

Trunk
mozilla16
sec-high
Points:
---
Dependency tree / graph
Bug Flags:
sec-bounty +

Firefox Tracking Flags

(firefox12 affected, firefox13 affected, firefox14+ verified, firefox15+ verified, firefox16+ verified, firefox-esr1014+ verified)

Details

(Whiteboard: [advisory-tracking+])

Attachments

(3 attachments, 3 obsolete attachments)

(Reporter)

Description

5 years ago
This is basically another flavor of bug 687745, the impact is identical.

It is possible to confuse the URL navigation in the browser, such that the user input on a victim site is redirected to a location on the attacker's server. The bug exists in the history navigation, which incorrectly handles calls to history.forward and history.back that are immediately followed by a write to location.hash. Suppose the history object is in the following state:

[0]: 'http://attacker/' (active)
[1]: 'http://victim/'

Calling history.forward on the attacker's website will redirect a user to the victim website. If location.hash is set before the attacker's script is terminated, the history object will be in the following state after opening http://victim/:

[0]: 'http://attacker/'
[1]: 'http://attacker/#hash' (active)

There's a discrepancy between the displayed page/URL bar and the address stored in history. Calling history.back() will set baseURI back to http://attacker/ (thus all user input will be posted there), but since that's only a hash change in the "confused" history object, the page will not be reloaded and will still display the content of the victim's website. The Larry badge for HTTPS websites will also continue to show the victim's domain name.

Although, as Daniel noted in bug 687745 comment 21, as a spoof it's on par with a typical phishing attack (though with a convincing Larry badge), the attacker may delay the call to history.back until the user is busy filling out the forms. The URL shift is completely seamless and will not reset any input. I've implemented this in the PoC as a 7 second delay between the loading of Mozilla Sync login page and navigating back to change the baseURI.

Verified to work with the latest Nightly (15.0a1 2012-05-21) and the release build (12.0).
(Reporter)

Comment 1

5 years ago
Created attachment 625947 [details]
proof of concept

Comment 2

5 years ago
This sounds really familiar.  In particular, sounds a lot like bug 737307.  Justin, do you have time to look into this?
(Assignee)

Updated

5 years ago
Attachment #625947 - Attachment mime type: application/octet-stream → application/zip
(Assignee)

Comment 3

5 years ago
> Justin, do you have time to look into this?

I'll make time.

I can't reproduce this bug, though.  I briefly see the login page appear, and then we're back at redirect.htm#h, which is blank.  After a few seconds we go to redirect.htm, which is also blank.  The URL bar matches the content shown.

Mariusz, if you can't make the testcase work by posting HTML files directly to bugzilla, can you host the testcase at some hard-to-find URL on your own server?
(Reporter)

Updated

5 years ago
Attachment #625947 - Attachment mime type: application/zip → application/java-archive
(Reporter)

Comment 4

5 years ago
(In reply to Justin Lebar [:jlebar] from comment #3)
> Mariusz, if you can't make the testcase work by posting HTML files directly
> to bugzilla, can you host the testcase at some hard-to-find URL on your own
> server?

Sure, I'll hack something up later.
(Reporter)

Updated

5 years ago
Attachment #625947 - Attachment is obsolete: true
(Reporter)

Comment 5

5 years ago
Created attachment 626233 [details]
just an auxiliary file I'll use
(Reporter)

Updated

5 years ago
Attachment #626233 - Attachment mime type: text/plain → text/html
(Reporter)

Comment 6

5 years ago
(In reply to Justin Lebar [:jlebar] from comment #3)
> I can't reproduce this bug, though.  I briefly see the login page appear,
> and then we're back at redirect.htm#h, which is blank.  After a few seconds
> we go to redirect.htm, which is also blank.  The URL bar matches the content
> shown.

OK, I know what's going on. This is due to my PoC being too optimistic about dom.min_background_timeout_value. I have this set to 0 locally, but the default config is different. I'll provide fixed PoCs (and a video) in a few hours.
(Assignee)

Comment 7

5 years ago
Okay, I can reproduce this with dom.min_background_timeout set to 0.  It doesn't work if I load from http://localhost, but it does if I load from my local IP.
(Reporter)

Comment 8

5 years ago
In case there are any more problems, I've broken the test down into individual steps. The buttons do the following:

step 1: open the victim site in the redirected window and navigate back.
step 2: call window.forward() and write a hash to confuse the history object.
step 3: call window.back() to change the baseURI.

Videos (use HD and fullscreen):

https://www.youtube.com/watch?v=FnTqwi1YgkQ -- a step-by-step reproduction of the issue in the latest Nightly (clean profile).
https://www.youtube.com/watch?v=shbgu9NSUPA -- this video shows how this bug can be used to hijack script fetches using relative paths (based on https://bugzilla.mozilla.org/attachment.cgi?id=626233)
(Reporter)

Comment 9

5 years ago
Created attachment 626357 [details]
updated PoC's
(Assignee)

Updated

5 years ago
Attachment #626357 - Attachment mime type: application/octet-stream → application/zip
Status: UNCONFIRMED → NEW
Ever confirmed: true
Keywords: sec-high
(Assignee)

Updated

5 years ago
Blocks: 757939
(Assignee)

Comment 11

5 years ago
So I think this is extremely similar to bug 737307.  It appears that, like in bug 737307, we're loading into a SHEntry when we should be creating a new one.

The testcase does

  1) window.location = http://youtube.com
  2) Go back as soon as reading opener.location.href throws.
  <Pause>
  3) history.forward()
  4) location.hash = "#h"

I think (2) is the key step here.  It appears that we create a SHEntry for youtube without ever loading the page.  Not entirely sure how that works yet, but I presume the back() cancels the load.  

So then when we do |history.forward(); location.hash=#h;|, the change to location.hash creates a new session history entry, and then the history.forward() loads into the new entry.

Without the specific timing in (2), youtube would presumably be bfcached, which would break the testcase.  (Not that bfcache would mitigate the attack, of course, because bfcache doesn't always work, but it would make it harder to reproduce.)

Notice that this gets around the fix to bug 737307 because the two navigations have the same load type (they're both history loads).
(Assignee)

Comment 12

5 years ago
Created attachment 626855 [details] [diff] [review]
Straw-man patch.

This works to fix the security problem here, although it's not the right fix.

What happens with this patch is:

  0) At the beginning, our session history is
       A: Attack page  <-- current
       B: Youtube

  1) We do history.forward.  Now we start loading youtube into shentry B.

  2) We do location.hash = "#h".  This rewrites session history so that it's now
       A: Attack page
       C: Attack page #h.  <-- current

  3) Youtube finishes loading into shentry B.  But B is not in the SHistory chain!

The net effect is, Youtube loads fine, and the URL bar is fine.  But our
SHistory is inconsistent; if you go back then forward, you end up at attack#h
(SHEntry C).

We have four options here:

  I)   Cancel the youtube load when you modify the hash.
       Result: SHistory A, B.
  II)  Overwrite/remove the SHEntry for #h when youtube loads.
       Result: SHistory A, C.
  III) Insert the youtube SHEntry before #h.
       Result: SHistory A, B, C.
  IV)  Insert the youtube SHEntry after #h.
       Result: SHistory A, C, B.

We established in bug 737307 that we can't do (I) when step (1) above is
location.replace instead of history.forward, so we probably shouldn't do (I) in
this case either.

(III) is good because it makes SHistory respect the order the page did things
in.  But A and C share a document, so it's super-weird for them to be non-adjacent
in SHistory.  (In fact, I don't think HTML5 allows this at all.)

(IV) is not great because the shistory doesn't match the order the page did things in.

Which leaves us with (II).  This isn't great, but it's similar to how
navigating before onload doesn't create a new SHEntry, so maybe it's OK.
(Assignee)

Comment 13

5 years ago
>  I)   Cancel the youtube load when you modify the hash.
>       Result: SHistory A, B.
>  II)  Overwrite/remove the SHEntry for #h when youtube loads.
>       Result: SHistory A, C.

The results here are backwards; the first one results in A, C, and the second one results in A, B.

I was surprised to discover that Chrome does (I).  If you replace history.forward() with location.replace, then Chrome does (IV).

Safari matches Chrome in both cases.

Opera does (IV) with or without location.replace.

IE does (II) with or without location.replace.

So...that's about as unenlightening as we could have hoped for.  :)
(Assignee)

Comment 14

5 years ago
bz, any idea how I can tell whether a network load is pending?  I can look at GetCurrentDocChannel(), but I don't think that tells me whether the network load has called back into InternalLoad yet...

Modulo getting the right condition for network-load-is-pending, it seems straightforward to make any hashchanges (and pushstates) which happen during network-load-is-pending use replacement.  Just switch the load flags to LOAD_NORMAL_REPLACE.

The downside is that this would not match any other browser's behavior; the result would be C, B.  Also, it would mean that pushState effectively becomes replaceState during network-load-is-pending.

Comment 15

5 years ago
You could compare GetCurrentDocChannel() to mDocumentRequest (or GetDocumentChannel() if you want to stick to the getters), perhaps?

We really need to spec this stuff.  :(
(Assignee)

Comment 16

5 years ago
> You could compare GetCurrentDocChannel() to mDocumentRequest (or GetDocumentChannel() if you want to 
> stick to the getters), perhaps?

Unfortunately at the critical moment GetCurrentDocChannel() still points to the old doc, while mDocumentRequest points to the new doc.  (This actually makes sense, since location.hash='#h' operates on the current doc.)

I can add yet another magic boolean, if nothing else.

Comment 17

5 years ago
> Unfortunately at the critical moment GetCurrentDocChannel() still points to the old doc,
> while mDocumentRequest points to the new doc.

Right.  That's the "we started a load, but that load has not become the current document we're showing yet" condition: when GetCurrentDocChannel() and mDocumentRequest are different.
(Assignee)

Comment 18

5 years ago
Created attachment 628459 [details] [diff] [review]
Patch v1

I'm not too happy about this; I'm afraid we're just dancing around the real problem here.

I'm not sure if I should leave the AutoRestore of mLSHE in there.  The whole point of this patch is that we do replacement loads so we don't need that.  But it may turn future security bugs into merely weird-navigation bugs.

If we do leave the AutoRestore in, I'm not sure if we should be restoring mLSHE via something like AddToSessionHistory.  It's not clear what the right behavior is because it's not clear when it would ever have an effect!
Attachment #628459 - Flags: review?(bzbarsky)

Comment 19

5 years ago
Comment on attachment 628459 [details] [diff] [review]
Patch v1

You shouldn't need the static_cast, but other than that this looks good.

Perhaps leave the AutoRestore in but also assert at the end of the block that mLSHE has not actually changed?
Attachment #628459 - Flags: review?(bzbarsky) → review+
(Assignee)

Comment 20

5 years ago
Hm, maybe these AutoRestore(mLSHE)'s are wrong.

Suppose we're loading a document, but JustStartedNetworkLoad() is false, because we're past that point.  Suppose we call pushState, or change the hash.  Shouldn't mLSHE change?  I have no idea if it matters -- maybe mLSHE is never read past that point.  But changing mLSHE in that case certainly seems right...
(Assignee)

Comment 21

5 years ago
Created attachment 629768 [details] [diff] [review]
Bug 757376.

Removing auto-restore of mLSHE, per last comment.
Attachment #629768 - Flags: review?(bzbarsky)

Comment 22

5 years ago
Comment on attachment 629768 [details] [diff] [review]
Bug 757376.

r=me
Attachment #629768 - Flags: review?(bzbarsky) → review+
Assignee: nobody → justin.lebar+bug
(Assignee)

Updated

5 years ago
Attachment #628459 - Attachment is obsolete: true
(Assignee)

Updated

5 years ago
Attachment #626855 - Attachment is obsolete: true
(Assignee)

Comment 23

5 years ago
https://hg.mozilla.org/integration/mozilla-inbound/rev/1b34d689a6ba

I admit I'm setting these "affected" flags without testing the relevant versions.  I strongly suspect they're affected, however.

Experience suggests we should let this bake for a few days before considering it for branches.
status-firefox-esr10: --- → affected
status-firefox12: --- → affected
status-firefox13: --- → affected
status-firefox14: --- → affected
status-firefox15: --- → affected
Target Milestone: --- → mozilla16
(Assignee)

Updated

5 years ago
status-firefox16: --- → affected
tracking-firefox-esr10: --- → ?
tracking-firefox14: --- → ?
tracking-firefox15: --- → ?
tracking-firefox16: --- → ?
https://hg.mozilla.org/mozilla-central/rev/1b34d689a6ba
Status: NEW → RESOLVED
Last Resolved: 5 years ago
status-firefox16: affected → fixed
Resolution: --- → FIXED
Tracking FF14 and up since this is a recently and externally reported sg:high issue.
tracking-firefox14: ? → +
tracking-firefox15: ? → +
tracking-firefox16: ? → +
(Assignee)

Comment 26

5 years ago
Comment on attachment 629768 [details] [diff] [review]
Bug 757376.

[Approval Request Comment]
Security bug, no string changes.  This is a docshell change, so it is risky as always.

Rather than waiting before asking for approval for branches, can I get approval to land, say, Friday June 9, assuming we don't hear about any regressions before then?  I could land on Aurora and then Beta/ESR some time later, if we wanted to further minimize risk.
Attachment #629768 - Flags: approval-mozilla-esr10?
Attachment #629768 - Flags: approval-mozilla-beta?
Attachment #629768 - Flags: approval-mozilla-aurora?
Verified both tests in attached PoC no longer work in 6/7 nightly trunk build. Compared against shipped Firefox 13.
Status: RESOLVED → VERIFIED
Comment on attachment 629768 [details] [diff] [review]
Bug 757376.

[Triage Comment]
I think it makes more sense to land this on both Aurora and Beta at the same time, given the risk here. That'll get this fix into our second FF14 beta (please land today). I'd rather have more feedback sooner than protect our beta users from this risk.
Attachment #629768 - Flags: approval-mozilla-esr10?
Attachment #629768 - Flags: approval-mozilla-esr10+
Attachment #629768 - Flags: approval-mozilla-beta?
Attachment #629768 - Flags: approval-mozilla-beta+
Attachment #629768 - Flags: approval-mozilla-aurora?
Attachment #629768 - Flags: approval-mozilla-aurora+
(Assignee)

Comment 29

5 years ago
(In reply to Alex Keybl [:akeybl] from comment #28)
> I think it makes more sense to land this on both Aurora and Beta at the same
> time, given the risk here. That'll get this fix into our second FF14 beta
> (please land today). I'd rather have more feedback sooner than protect our
> beta users from this risk.

That sounds quite reasonable to me.  I'll land asap.
Comment on attachment 629768 [details] [diff] [review]
Bug 757376.

Actually - I take that back. To ensure that we don't cause a regression in this next FN beta, let's start with Aurora. We'll land for the next FF15 beta.
Attachment #629768 - Flags: approval-mozilla-esr10?
Attachment #629768 - Flags: approval-mozilla-esr10+
Attachment #629768 - Flags: approval-mozilla-beta?
Attachment #629768 - Flags: approval-mozilla-beta+
(Assignee)

Comment 31

5 years ago
Fixed in Aurora 15 and ESR 10:

https://hg.mozilla.org/releases/mozilla-aurora/rev/851b070766ad
https://hg.mozilla.org/releases/mozilla-esr10/rev/e1f98eb2476e

I'll land on Beta 14 on Wednesday.
status-firefox-esr10: affected → fixed
status-firefox15: affected → fixed
(Assignee)

Comment 32

5 years ago
I didn't notice that my approval flags had been rescinded; I already landed on ESR.  I guess I'll wait to land on beta until I get a+ again.
Comment on attachment 629768 [details] [diff] [review]
Bug 757376.

You're good to go for landing on Beta.
Attachment #629768 - Flags: approval-mozilla-beta? → approval-mozilla-beta+
(Assignee)

Comment 34

5 years ago
Can you please mark approval-esr-10, since I already landed based on the old a+?
(Assignee)

Comment 35

5 years ago
Landed in Beta 14: https://hg.mozilla.org/releases/mozilla-beta/rev/c6680c9f29fe
tracking-firefox-esr10: ? → 14+
Attachment #629768 - Flags: approval-mozilla-esr10? → approval-mozilla-esr10+
status-firefox14: affected → fixed
Whiteboard: [advisory-tracking+]
Alias: CVE-2012-1955
I'm a bit confused in the expected behaviour here. Can someone please clarify so I can verify this fixed?

BZ testcase with Firefox 13.0.1:
1. Load 'bz' testcase and click the 'click' button
2. A window opens stating "please wait while redirecting"
3. Redirects to index.htm#h then back to index.htm
4. The secondary window closes

YT testcase with Firefox 13.0.1:
1. Load 'yt' testcase and click the 'click' button
2. A window opens with three 'step' buttons
3. Clicking 'step 1' appears to do nothing
4. Clicking 'step 2' loads youtube.com in the main window
5. Clicking 'step 3' puts index.htm in the location bar of the main window but youtube is still loaded

Thanks in advance for your help.
Whiteboard: [advisory-tracking+] → [advisory-tracking+][qa?]
(Reporter)

Comment 37

5 years ago
(In reply to Anthony Hughes, Mozilla QA (irc: ashughes) from comment #36)
> BZ testcase with Firefox 13.0.1:
> 1. Load 'bz' testcase and click the 'click' button
> 2. A window opens stating "please wait while redirecting"
> 3. Redirects to index.htm#h then back to index.htm
> 4. The secondary window closes

After a few seconds, an alert dialog should appear (see the second video in comment 8).

> YT testcase with Firefox 13.0.1:
> 1. Load 'yt' testcase and click the 'click' button
> 2. A window opens with three 'step' buttons
> 3. Clicking 'step 1' appears to do nothing
> 4. Clicking 'step 2' loads youtube.com in the main window
> 5. Clicking 'step 3' puts index.htm in the location bar of the main window
> but youtube is still loaded

The location bar/loaded content mismatch you see in point 5 is the expected (ie. bad) behavior.
(Reporter)

Updated

5 years ago
OS: Windows 7 → All
Hardware: x86_64 → All
(In reply to Mariusz Mlynski from comment #37)
> After a few seconds, an alert dialog should appear (see the second video in
> comment 8).
I'm not seeing an alert dialog in 13.0.1. I expect to see one given that it's status-firefox13:affected.

> > YT testcase with Firefox 13.0.1:
> The location bar/loaded content mismatch you see in point 5 is the expected
> (ie. bad) behavior.
At least I can reproduce this, so I'll be able to verify the fix on this case. However, is this case alone enough to call this bug verified fixed?
(Reporter)

Updated

5 years ago
Attachment #626357 - Attachment mime type: application/zip → application/java-archive
(Reporter)

Comment 39

5 years ago
(In reply to Anthony Hughes, Mozilla QA (irc: ashughes) from comment #38)
> (In reply to Mariusz Mlynski from comment #37)
> > After a few seconds, an alert dialog should appear (see the second video in
> > comment 8).
> I'm not seeing an alert dialog in 13.0.1. I expect to see one given that
> it's status-firefox13:affected.

Ah, do you run it from the file system? It won't work from file: because mozilla.org is not allowed to load local files, and baseURI ends up being whatever the location of the testcase is. Try to upload the /bz/ directory to some remote HTTP server and run it from there.

> > > YT testcase with Firefox 13.0.1:
> > The location bar/loaded content mismatch you see in point 5 is the expected
> > (ie. bad) behavior.
> At least I can reproduce this, so I'll be able to verify the fix on this
> case. However, is this case alone enough to call this bug verified fixed?

Both test cases rely on the same mechanism, if one fails then the other won't work either.
Before I go to all the trouble of setting up a webserver so I can upload the testcase to test this, are you already set up to test this? If so, can you assist with the verification of this fix? The immediate need is verifying this against the latest Firefox 14 Beta and the latest mozilla-esr10 nightly.

Alternatively, if you can set this up on a remote server temporarily so I can test it, that would help.

Thanks
(Assignee)

Comment 41

5 years ago
> Before I go to all the trouble of setting up a webserver so I can upload the testcase to test this,

You don't have a people.mozilla.org account?
No, I personally do not. I'm willing to take the time to get done what's needed for me to test this. I just think it would be better, for the sake of getting this verified before we release, for someone that's already set up to test this to do it in parallel.
(Reporter)

Comment 45

5 years ago
If you don't see any alert dialog, it's good.
Okay, thanks, marking my two comments which reference your server IP private. Please keep the test server live until I've verified on Aurora, Beta, and ESR.
Verified fixed for Firefox 14, 15, 16 and 10.0.6esr. Mariusz, you can take down your test server if you want.
status-firefox-esr10: fixed → verified
status-firefox14: fixed → verified
status-firefox15: fixed → verified
status-firefox16: fixed → verified
Whiteboard: [advisory-tracking+][qa?] → [advisory-tracking+]
FWIW, all MoCo employees with an ssh pubkey in LDAP (criteria you satisfy since you have commit access) have access to people.m.o - just |ssh ahughes@people.mozilla.org|.
(Assignee)

Comment 49

5 years ago
When will we be able to un-protect this bug?  We already have opened bug 687745, which is basically the same but with a jankier testcase and no link to the fixing cset.
Group: core-security
rforbes-bugspam-for-setting-that-bounty-flag-20130719
Flags: sec-bounty+

Updated

2 years ago
Depends on: 1184681
You need to log in before you can comment on or make changes to this bug.