Closed Bug 1694698 (CVE-2021-24000) Opened 1 year ago Closed 1 year ago

requestPointerLock Race Condition Allow Set pointerLock to Any window.open URL (with MouseEvent still pass to script origin)

Categories

(Core :: DOM: Core & HTML, defect)

defect

Tracking

()

VERIFIED FIXED
88 Branch
Tracking Status
firefox-esr78 --- unaffected
firefox86 --- wontfix
firefox87 --- wontfix
firefox88 --- fixed

People

(Reporter: sourc7, Assigned: edgar)

Details

(Keywords: csectype-spoof, sec-moderate, Whiteboard: [reporter-external] [client-bounty-form] [verif?][adv-main88+])

Attachments

(7 files, 1 obsolete file)

When using setTimeout to adjust strict timing to call requestPointerLock() and window.open(URL) surprisingly the pointerLock will applied to the new window URL.

After pointer is set to locked state, the MouseEvent still pass to website that trigger the pointerlock, thus allow the script origin to listen user MouseEvent movement data (including mousemove, mousedown, mouseup, click, and wheel) while user on another tab.

Interestingly I found when change pointerLock element to e.g. <input type="file"> or <select><options> and etc. (I'm still explore to find interesting HTML element), the mouse click event will redirected to the element, so it also possible to trick user that the dialog was initiated by the current tab page.

In the attached testcase I'm using <input type="file"> element to demonstrate to trick the user that upload file dialog was initiated by the current tab page.

Version Tested:

  • Firefox Nightly 88.0a1 (2021-02-24) (64-bit)
  • Firefox Release 86.0 (64-bit)

Steps to Reproduce:

  1. Visit attached pointerlock+inputfile.html
  2. Click "Visit VT to Upload File" button
  3. VirusTotal page is opened on new tab with pointer set to lock state (If VirusTotal tab immediately closed click again until success) (works most of the time on Windows 10, but on Linux may need to click for 2-5 times)
  4. Click "Choose file" button on VirusTotal page or click any on the page
  5. File upload dialog is open (as initiated by VirusTotal page)
  6. Select any file then the file is submitted to script origin page
Flags: sec-bounty?

Hereby I attach PoC testcase using <select><options> HTML element.

After click anywhere on the page the select options will appear on the Expedia page (so it looks pretty convincing that the dropdown showed by the Expedia page). When the user selects from the dropdown, the script origin will be able to read the selected value.

Group: firefox-core-security → dom-core-security
Type: task → defect
Component: Security → DOM: Core & HTML
Product: Firefox → Core
Flags: needinfo?(dveditz)
Attached file pointerlock+space.html

Hereby I attach my early testcase for bisection purpose, it works for older versions of Firefox (work from Firefox 82).

To use the testcase, just allow the pop-up blocker then hold down "space" until the pointer is successfully locked on the target website.

When I bisect this using my early testcase pointerlock+space.html (that work for older versions of Firefox).

Mozregression says it is a regression of "Bug 1662587 - Make pointer lock fission compatible":

2021-02-28T20:57:28.488000: INFO : Narrowed integration regression window from [f8254837, 9863c168] (3 builds) to [f8254837, 45eb5295] (2 builds) (~1 steps left)
2021-02-28T20:57:28.494000: DEBUG : Starting merge handling...
2021-02-28T20:57:28.495000: DEBUG : Using url: https://hg.mozilla.org/integration/autoland/json-pushes?changeset=45eb5295b99bb22925db3355781b35cd5c1ce653&full=1
2021-02-28T20:57:28.495000: DEBUG : redo: attempt 1/3
2021-02-28T20:57:28.495000: DEBUG : redo: retry: calling _default_get with args: ('https://hg.mozilla.org/integration/autoland/json-pushes?changeset=45eb5295b99bb22925db3355781b35cd5c1ce653&full=1',), kwargs: {}, attempt #1
2021-02-28T20:57:28.497000: DEBUG : urllib3.connectionpool: Resetting dropped connection: hg.mozilla.org
2021-02-28T20:57:30.544000: DEBUG : urllib3.connectionpool: https://hg.mozilla.org:443 "GET /integration/autoland/json-pushes?changeset=45eb5295b99bb22925db3355781b35cd5c1ce653&full=1 HTTP/1.1" 200 None
2021-02-28T20:57:30.602000: DEBUG : Found commit message:
Bug 1662587 - Make pointer lock fission compatible; r=smaug

Now requesting/releasing pointer lock in content process will send IPC to let
parent process know which content process request a lock, so parent process
could dispatch mouse event to the right content process. And if there is already
a content proess had a lock, parent process will reject lock request from other
content proesses.

Differential Revision: https://phabricator.services.mozilla.com/D90313

2021-02-28T20:57:30.602000: DEBUG : Did not find a branch, checking all integration branches
2021-02-28T20:57:30.603000: INFO : The bisection is done.
Flags: needinfo?(echen)

Thanks to the mozregression tool! it's very great to speeding up finding a regression!

The upgraded testcase pointerlock+inputfile.html and pointerlock+selectoption.html which only use one-click pointer lock (without disable pop-up blocker and without keyboard interaction) according to mozregression it's possible because of commit "Bug 1684837 - Adjust pointer lock request handling":

2021-03-01T02:25:56.902000: INFO : Narrowed integration regression window from [a8ec4b49, 40e9698f] (3 builds) to [a13bf50d, 40e9698f] (2 builds) (~1 steps left)
2021-03-01T02:25:56.915000: DEBUG : Starting merge handling...
2021-03-01T02:25:56.916000: DEBUG : Using url: https://hg.mozilla.org/integration/autoland/json-pushes?changeset=40e9698f5ca3e4f256ad1f05c8245c2b2858cf14&full=1
2021-03-01T02:25:56.916000: DEBUG : redo: attempt 1/3
2021-03-01T02:25:56.916000: DEBUG : redo: retry: calling _default_get with args: ('https://hg.mozilla.org/integration/autoland/json-pushes?changeset=40e9698f5ca3e4f256ad1f05c8245c2b2858cf14&full=1',), kwargs: {}, attempt #1
2021-03-01T02:25:56.918000: DEBUG : urllib3.connectionpool: Resetting dropped connection: hg.mozilla.org
2021-03-01T02:25:58.633000: DEBUG : urllib3.connectionpool: https://hg.mozilla.org:443 "GET /integration/autoland/json-pushes?changeset=40e9698f5ca3e4f256ad1f05c8245c2b2858cf14&full=1 HTTP/1.1" 200 None
2021-03-01T02:25:58.683000: DEBUG : Found commit message:
Bug 1684837 - Adjust pointer lock request handling; r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D100960

2021-03-01T02:25:58.683000: DEBUG : Did not find a branch, checking all integration branches
2021-03-01T02:25:58.685000: INFO : The bisection is done.
Assignee: nobody → echen
Flags: needinfo?(echen)

When the browser switch to (In reply to Irvan Kurniawan (:sourc7) from comment #0)

When using setTimeout to adjust strict timing to call requestPointerLock() and window.open(URL) surprisingly the pointerLock will applied to the new window URL.

I guess you said "the pointerlock is applied to the new window" is because you see the pointer lock notification on the opened window? but the notification in URL is the opener one, so the pointerLock is actually applied to the opener, not the new window.

After pointer is set to locked state, the MouseEvent still pass to website that trigger the pointerlock, thus allow the script origin to listen user MouseEvent movement data (including mousemove, mousedown, mouseup, click, and wheel) while user on another tab.

When the focus is moved out of the pointer locked window, the pointer locked should be released, but it seems there is a small timing that pointer lock isn't yet released on the pointer locked window, and user click mouse on the newly focused window, the mouse event is dispatched to pointer locked window.

Irvan, do you still see mouse event dispatched to the opener window after the new window is full loaded? I think this only happens in a small time frame right after focus is switched to the new window.

Flags: needinfo?(susah.yak)

(In reply to Edgar Chen [:edgar] from comment #7)

I guess you said "the pointerlock is applied to the new window" is because you see the pointer lock notification on the opened window? but the notification in URL is the opener one, so the pointerLock is actually applied to the opener, not the new window.

Right, the pointerLock is applied to the opener, but the tab still focus on the opened window.

When the focus is moved out of the pointer locked window, the pointer locked should be released, but it seems there is a small timing that pointer lock isn't yet released on the pointer locked window, and user click mouse on the newly focused window, the mouse event is dispatched to pointer locked window.

Irvan, do you still see mouse event dispatched to the opener window after the new window is full loaded? I think this only happens in a small time frame right after focus is switched to the new window.

Yes, the mouse event still redirected to the opener window, even after the new window is fully loaded (as on the demonstration video). The opener window is also still able to listen the MouseEvent movement data.

Did the pointerlock is released after new window is loaded on your end? Try to access the testcase using http:// or https:// protocol, because I was experience the same issue, when I access the testcase using file:// protocol the pointerlock is unexpectedly released.

Flags: needinfo?(susah.yak)

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

Did the pointerlock is released after new window is loaded on your end? Try to access the testcase using http:// or https:// protocol, because I was experience the same issue, when I access the testcase using file:// protocol the pointerlock is unexpectedly released.

Yeah, the pointerlock is release when the new window is loading on my side (I access the testcase via https://).
Another question: Does the switching tab make pointer lock get released? Thanks!

Flags: needinfo?(susah.yak)

(In reply to Edgar Chen [:edgar] from comment #9)

Yeah, the pointerlock is release when the new window is loading on my side (I access the testcase via https://).

I'm currently able to reproduce this using Arch Linux and Windows 10, the pointerlock is still locked on the new window (as on demonstration video). What OS do you use? Can you try to reproduce this on fresh Firefox profile or other OS?

Another question: Does the switching tab make pointer lock get released? Thanks!

As the mouse is on the locked state, switching tab using mouse is not possible. The user can release the pointerlock using keyboard shortcut.

Flags: needinfo?(susah.yak)

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

Can you try to reproduce this on fresh Firefox profile or other OS?

I could reproduce this on a fresh profile, I guess the timing is different with a fresh profile and make it easy to hit this. Thanks!

Severity: -- → S2

(In reply to Edgar Chen [:edgar] from comment #11)

I could reproduce this on a fresh profile, I guess the timing is different with a fresh profile and make it easy to hit this. Thanks!

Thanks Edgar for the update, I'm glad you able to reproduce the issue.

I guess your previous profile had Fission enabled, because I had same problem when Fission was enabled it caused pointerlock to be released after the page loaded.

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

I guess your previous profile had Fission enabled, because I had same problem when Fission was enabled it caused pointerlock to be released after the page loaded.

Thanks for this information! I think you are right, I disabled the fission on my daily used Nightly, I could also reproduce it.

Flags: needinfo?(dveditz)
Attachment #9209061 - Attachment description: Bug 1694698 - Reject pointer lock request if we already know the focus has moved away; → Bug 1694698 - Reject pointer lock request if the active tab is changed;
Group: dom-core-security → core-security-release
Status: UNCONFIRMED → RESOLVED
Closed: 1 year ago
Resolution: --- → FIXED
Target Milestone: --- → 88 Branch

I am no longer able to reproduce this (using both testcases) with Firefox Nightly 88.0a1 (2021-03-19) (64-bit) on Arch Linux and Windows 10. I verified this as fixed.

Status: RESOLVED → VERIFIED
Flags: sec-bounty? → sec-bounty+

Does this affect ESR78, and if so, do we need to consider it for backport? If yes, it'll need a rebased patch as well.

Flags: needinfo?(echen)

No, this doesn't affect ESR78.

Flags: needinfo?(echen)
Whiteboard: [reporter-external] [client-bounty-form] [verif?] → [reporter-external] [client-bounty-form] [verif?][adv-main88+]
Alias: CVE-2021-24000
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.