Closed Bug 1684986 (CVE-2021-23966) Opened 4 years ago Closed 4 years ago

Firefox Lite history.replaceState same-origin check failure

Categories

(Emerging Markets Graveyard :: Security: Firefox Lite, defect)

defect

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: dveditz, Assigned: st3fan)

References

Details

(Keywords: csectype-spoof, reporter-external, sec-high, Whiteboard: [reporter-external] [client-bounty-form] [verif?])

Attachments

(1 file)

+++ This bug was initially created as a clone of Bug #1681103 +++

bug 1681103 comment 3 and following describes a separate issue that I'm moving to this bug.

testcase: attachment 9194803 [details]
result: attachment 9194804 [details]

There seem to be two failures to follow the specified behavior:

  1. relative URL is resolved wrt to baseURI (minor, annoying)
  2. there is no same-origin check (horrifyingly insecure)

I don't have a Firefox Lite to experiment with so I don't know if the same-origin check is always skipped or if it's only skipped in the case of relative URLs, but either way this POC demonstrates exactly why that restriction is in the standard!

I'm surprised either of these would be performed by the FF Lite browser front-end. I would have expected replaceState and pushState would be executed by the underlying platform WebView and the security checks should be at that level.

Irvan: what device do you have and android version? Does the default browser, or other WebView-based browsers have the same problem on your device? In bug 1581103 comment 0 you say the original problem doesn't reproduce in other browsers so it's likely you already tried this, but I'm very surprised at the thought of rolling our own replaceState() implementation.

Flags: needinfo?(susah.yak)

From Irvan in bug 1681103 comment 6:

The root cause of perfectspoof.html at comment 3 is when JS execute window.history.replaceState("", "", location.href), the WebView onPageFinished is pointing to the base href https://accounts.google.com/signin/v2/identifier, surprisingly it pass the url to WebViewClient doUpdateVisitedHistory(WebView view, String url, boolean isReload)

On Firefox Lite it also update the address bar when doUpdateVisitedHistory is called by passing String url parameter to update the address bar. Because of that condition Firefox Lite updated the address bar to https://accounts.google.com/signin/v2/identifier.

We can view the code at app/src/webkit/java/org/mozilla/focus/webkit/FocusWebViewClient.java I confirmed by changing the code from viewClient.onURLChanged(url) to viewClient.onURLChanged(view.getUrl()) it solve the perfectspoof.html bug.

Interesting: cloning a bug keeps the keywords but not the flags. restoring sec-bounty? because this is definitely worth consideration separately from bug 1681103

Flags: sec-bounty?

(In reply to Daniel Veditz [:dveditz] from comment #0)

I don't have a Firefox Lite to experiment with so I don't know if the same-origin check is always skipped or if it's only skipped in the case of relative URLs, but either way this POC demonstrates exactly why that restriction is in the standard!

Thanks Daniel for moving this into separate bug, you can also try this on Android Emulator, but I encourage to try this on real device, because on Android emulator I experienced the Play Store stuck when Android WebView to latest version, I have to clear Play Store data to make it work.

Currently I think the bug is only affect the address bar, because on Firefox Lite it update the address bar when it listen WebViewClient doUpdateVisitedHistory(WebView view, String url, boolean isReload) Firefox lite takes the String url to update the address bar.

There seem to be two failures to follow the specified behavior:

  1. relative URL is resolved wrt to baseURI (minor, annoying)
  2. there is no same-origin check (horrifyingly insecure)

I didn't know whether it able to bypass SOP, but it worth to investigate.

I'm surprised either of these would be performed by the FF Lite browser front-end. I would have expected replaceState and pushState would be executed by the underlying platform WebView and the security checks should be at that level.

Yes I also surprised when JS execute window.history.replaceState("", "", location.href), the WebView onPageFinished is pointing to the base href https://accounts.google.com/signin/v2/identifier then pass this to doUpdateVisitedHistory(WebView view, String url, boolean isReload).

Irvan: what device do you have and android version? Does the default browser, or other WebView-based browsers have the same problem on
your device?

I'm currently using Xiaomi 9T on Android 10 with WebView version 87.0.4280.101 (Updated on Dec 7, 2020). I also able to reproduce this on Android Emulator API 29.

Flags: needinfo?(susah.yak)

(In reply to Daniel Veditz [:dveditz] from comment #0)

or other WebView-based browsers have the same problem on your device?

Currently I can't reproduce this on another WebView based browser, I think other WebView-based browser doesn't take String url from WebViewClient doUpdateVisitedHistory to update the address bar.

The hotfix patch as mentioned on comment 1 is to refactor Firefox Lite to not update the address bar using String url from WebView doUpdateVisitedHistory.

I hope we can push the fix as there are a lot user using Firefox Lite (according to Google Play Store).

(In reply to my comment #3)

I didn't know whether it able to bypass SOP, but it worth to investigate.

Sorry on comment 3, I was think same-origin in context of UXSS.

(In reply to Daniel Veditz [:dveditz] from comment #0)

There seem to be two failures to follow the specified behavior:

  1. relative URL is resolved wrt to baseURI (minor, annoying)
  2. there is no same-origin check (horrifyingly insecure)

Edit: Yes, it able to bypass same-origin check on the Firefox Lite address bar using history.replaceState.

On the Stack Overflow there are answer to update the WebViewClient address bar using String url from WebView doUpdateVisitedHistory. Therefore, all WebView-based applications that update the address bar using that method are affected to this spoof.

Herewith I attach the patch to address the Firefox Lite history.replaceState same-origin bypass. When doUpdateVisitedHistory is called, it will take view.getURL() to update the address bar using URL of the current page.

(So i.e. https://www.attacker.com called history.replaceState("https://www.google.com") it's no longer possible)

After testing this patch for a day, I confirmed the address bar is still working as it should, so I consider it safe to push the current patch.

As far I know from various sources including Mozilla Blog L10n Report: December 2020 Edition and mozilla-tw/FirefoxLite repository status, the development of Firefox Lite has been dropped since September 25, 2020.

With this severe full address bar spoof with secure lock (only by visiting website), is it still possible Dan for Mozilla team to merge the hotfix patch then update this to Google Play Store?

(In reply to Irvan Kurniawan (:sourc7) from comment #8)

As far I know from various sources including Mozilla Blog L10n Report: December 2020 Edition and mozilla-tw/FirefoxLite repository status, the development of Firefox Lite has been dropped since September 25, 2020.

With this severe full address bar spoof with secure lock (only by visiting website), is it still possible Dan for Mozilla team to merge the hotfix patch then update this to Google Play Store?

setting needinfo for this.

Flags: needinfo?(dveditz)

(In reply to Irvan Kurniawan (:sourc7) from comment #8)

With this severe full address bar spoof with secure lock (only by visiting website), is it still possible Dan for Mozilla team to merge the hotfix patch then update this to Google Play Store?

I honestly do not know if we are able to do that.

David: There was supposed to be clarity about the status of Firefox Lite last week. This bug is the Holy Grail for phishers and we either need to ship a fix ASAP or publicly pull the plug on Firefox Lite and tell people to migrate to another browser.

Flags: needinfo?(dveditz) → needinfo?(dbolter)

Thanks for the escalation Dan. Per email thread I don't think we'll get to the larger strategic decision on FF LIte before we need to fix this. Stefan has offered to drive here, thanks!

Assignee: nobody → sarentz
Flags: needinfo?(dbolter)

(In reply to Daniel Veditz [:dveditz] from comment #10)

(In reply to Irvan Kurniawan (:sourc7) from comment #8)

With this severe full address bar spoof with secure lock (only by visiting website), is it still possible Dan for Mozilla team to merge the hotfix patch then update this to Google Play Store?

I honestly do not know if we are able to do that.

David: There was supposed to be clarity about the status of Firefox Lite last week. This bug is the Holy Grail for phishers and we either need to ship a fix ASAP or publicly pull the plug on Firefox Lite and tell people to migrate to another browser.

We will be actively working on getting this fix out soon.

Irvan, thank you very much for both the report and the patch. Please allow us some time to include this into an update. Consider this work in progress as of today.

(In reply to Stefan Arentz | :st3fan | ⏰ EST | he/him from comment #12)

We will be actively working on getting this fix out soon.

Irvan, thank you very much for both the report and the patch. Please allow us some time to include this into an update. Consider this work in progress as of today.

Thanks Stefan for the update,

I'm glad to be able to provide bug analysis and patches for Mozilla. Debugging on Android project is much easier, thanks to the Android Studio IDE.

Yes, I agree it will take some time to include the update, within that time we can test the patch, I hope it will be reliable (i.e. doesn't create new issue to user) and completely solves the address bar spoofing issue.

Irvan, we just finished testing and can confirm both the bug and patch. We're working on a plan now to roll this out.

One thing that we were wondering is whether this is a WebView bug or not. It seems odd that the push state for a website from a different origin is kept or allowed to be in the push state. Do you have any insight on that?

Flags: needinfo?(susah.yak)

Asking :sebastian to verify the patch. We know it works, but I think we would like to understand the underlying problem better to be more confident that this is the only place and the only product.

The easiest way to reproduce this is to load https://bugzilla.mozilla.org/attachment.cgi?id=9194803 into Lite. It will show a Google Sign-In page, with a google URL, while the page is really on bugzilla. (I can help)

Flags: needinfo?(s.kaspari)

(In reply to Stefan Arentz | :st3fan | ⏰ EST | he/him from comment #14)

One thing that we were wondering is whether this is a WebView bug or not. It seems odd that the push state for a website from a different origin is kept or allowed to be in the push state. Do you have any insight on that?

My insight is on comment 1 (quoted by Daniel from bug 1681103 comment 6), in summary as below:

Currently Firefox Lite is listen to WebViewClient doUpdateVisitedHistory(WebView view, String url, boolean isReload), then take the String url from the method to update the address bar.

The problem is when JS execute window.history.replaceState("", "", location.href), the WebView onPageFinished is also pointing to the base href (on perfectspoof.html is set base href to https://accounts.google.com/signin/v2/identifier).

Surprisingly the onPageFinished also pass the url to doUpdateVisitedHistory(WebView view, String url, boolean isReload), therefore the address bar is now changed (spoofed) to the base href.

Flags: needinfo?(susah.yak)

(In reply to my comment #16)

Surprisingly the onPageFinished also pass the url to doUpdateVisitedHistory(WebView view, String url, boolean isReload), therefore the address bar is now changed (spoofed) to the base href.

After further testing, it turns out that onPageFinished doesn't have to point to a spoofed URL, so I can say onPageFinished doesn't necessarily affect the url variable to the doUpdateVisitedHistory method.

(In reply to Stefan Arentz | :st3fan | ⏰ EST | he/him from comment #14)

It seems odd that the push state for a website from a different origin is kept or allowed to be in the push state. Do you have any insight on that?

Oh I got your point Stefan,

From my analysis WebViewClient doesn't allow push state or replace state from different origin, we can try from DevTools after insert JS command window.history.replaceState("", "", "https://different-origin.example,"), it will return an error "Failed to execute 'replaceState' on 'History': A history state object with URL...".

I found we can also do the spoof by calling location.href = `${location.href}#test` with url fragment.

I'm not sure whether it's a bug on WebViewClient that it shouldn't happen, or the WebViewClient doUpdateVisitedHistory is working as intended (on the API docs parameter url: The url being visited) (in this case passing the base href as the String url), then FF Lite misuse the doUpdateVisitedHistory callback to update the address bar.

The way how WebViewClient doUpdateVisitedHistory is works is at https://chromium.googlesource.com/chromium/src.git/+/refs/heads/master/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java on didFinishNavigation method.

Alias: CVE-2021-23966

We are on track to ship this fix very soon.

(In reply to Stefan Arentz | :st3fan | ⏰ EST | he/him from comment #18)

We are on track to ship this fix very soon.

Thanks Stefan for the progress!

I found another address bar spoof w/ secure lock using back button, reported on bug 1688979, I hope we can also address this.

We shipped an update with this patch included. The security advisory is at https://github.com/mozilla-mobile/FirefoxLite/security/advisories/GHSA-vm4v-cxqj-jjgm

Flags: needinfo?(s.kaspari)

(In reply to Stefan Arentz | :st3fan | ⏰ EST | he/him from comment #20)

We shipped an update with this patch included. The security advisory is at https://github.com/mozilla-mobile/FirefoxLite/security/advisories/GHSA-vm4v-cxqj-jjgm

Thank you Stefan and other Mozilla Team for pushing the patch to the Google Play Store!

I confirmed the vulnerability has been fixed on Firefox Lite 2.6.0 (20651) (Release date Jan 28, 2021).

Closing this bug since we shipped a fix.

Status: NEW → RESOLVED
Closed: 4 years ago
Resolution: --- → FIXED
Flags: sec-bounty? → sec-bounty+
Group: mobile-core-security → core-security-release
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: