Closed Bug 1737722 (CVE-2022-36319) Opened 1 year ago Closed 7 months ago

Redirect Mouse Input to Attacker Controlled Position while Visible Cursor Remain on Original Position

Categories

(Firefox :: Security, defect)

defect

Tracking

()

VERIFIED FIXED
104 Branch
Tracking Status
firefox-esr78 --- unaffected
firefox-esr91 103+ verified
firefox-esr102 103+ verified
firefox93 --- wontfix
firefox94 --- wontfix
firefox95 --- wontfix
firefox96 --- wontfix
firefox102 --- wontfix
firefox103 + verified
firefox104 + verified

People

(Reporter: sourc7, Assigned: tnikkel)

References

(Regressed 1 open bug, Regression)

Details

(Keywords: csectype-spoof, regression, sec-moderate, Whiteboard: [reporter-external] [client-bounty-form][adv-main103+][adv-esr91.12+][adv-esr102.1+])

Attachments

(13 files)

7.39 KB, application/x-zip-compressed
Details
185.07 KB, video/mp4
Details
319.91 KB, video/mp4
Details
7.66 KB, application/x-zip-compressed
Details
156.47 KB, video/mp4
Details
4.36 KB, application/zip
Details
168.23 KB, video/mp4
Details
1.14 KB, application/x-zip-compressed
Details
615.10 KB, video/mp4
Details
6.92 KB, text/plain
Details
48 bytes, text/x-phabricator-request
Details | Review
48 bytes, text/x-phabricator-request
Details | Review
201 bytes, text/plain
Details
Attached file launcher.zip

After using overflow-block: hidden combined with large transform: scale surprisingly the mouse input will be redirected to another position (including on Toolbar, Permission panel). I found the redirect click position can be controlled through scale number to click "Allow" on the permission panel while the visible cursor position still on the HTML page.

As the testcase using scale to redirect the mouse input, the mouse redirect position may different across OS (i.e. not hitting the permission panel), I have adjusted the scale number on the testcase to work (as on PoC video) on the following device (with Fission enabled):

Tested on:

  • Firefox 95.0a1 (2021-10-25) on Arch Linux (X11) (2560x1440)
  • Firefox 95.0a1 (2021-10-25) on Windows 10 (Laptop with 1920x1080 screen and VirtualBox VM with 2560x1440 guest screen)

Steps to reproduce:

  1. Extract attached launcher.zip
  2. Visit attached launcher-windows.bundle.html or launcher-kdex11.bundle.html
  3. Click "Launch" button
  4. Move cursor to "Download" image, the "Allow" button on permission panel will be highlighted
  5. While the "Allow" button highlighted
  6. Click the "Download" image
  7. Location permission is now allowed
Flags: sec-bounty?

It is also possible to redirect mouse input to "Add" WebExtension while cursor on "Download" image. The WebExtension install PoC is attached in launcher-webextension.zip.

Okay, it turns out the OS Display Scaling is the big factor, all above testcase is reproduced on OS scaling 125%, when I change the display scaling to 100% the entire behavior is different.

I've adjusted the scale to be able to reproduce on OS scaling 100%, also tested works on same device above. I see after changing resolution on guest machine (auto-resize guest display) the testcase still works the same.

See Also: → CVE-2021-43546

This is a totally different issue from bug 1737751. This is probably math errors in APZ with really big transforms causing mouse events to go outside the content area or such. Botond / Hiro, perhaps you know where stuff might be going wrong?

Flags: needinfo?(hikezoe.birchill)
Flags: needinfo?(botond)

Though I can't reproduce this issue on my Linux box with 200% scaled display (I don't even see the download image), it looks an our webrender hit testing issue. If Botond is hard to take time on this, I will jump in.

One thing I don't yet quite understand is, the permission popup window is not apz-aware one, how we pick it. Even if APZ picked a wrong target, I assume it's the newly opened browser window, then the browser window chooses the popup? That's a plausible scenario I can think of.

I wonder whether this issue also happens with the old non webrender backend.

Oops, I forgot saying, I did CC Timothy, he might have some insights on this.

Fission is required for me to reproduce. Webrender is also required for me to reproduce (if I use nightly 2021-06-01 where I can disable webrender).

Regression from bug 1675547. I'm guessing there is a scroll frame we activate in the dialog so that apz can target it? Whereas before that change it was not active, so apz could not target it. So the bug was likely present before that bug, and could manifest if a dialog had an already active scroll frame in it.

Webrender still seems required though, if I turn on fission, webrender off, and activate all scroll frames for nonwr on then I can't reproduce.

Confirmed that flipping apz.wr.activate_all_scroll_frames_when_fission to false fixes the problem.

Confirmed that setting apz.wr.activate_all_scroll_frames to true allows me to reproduce when fission is disabled.

Regressed by: 1675547
Has Regression Range: --- → yes
Keywords: regression

The scrollframe that gets activated is the body of the page in the test case. And indeed I can reproduce the bug without fission if I do something to make that scrollframe active first.

We untransform the mouse coords before passing them to the root presshell in the parent process here

https://searchfox.org/mozilla-central/rev/f8576fec48d866c5f988baaf1fa8d2f8cce2a82f/gfx/layers/apz/src/APZCTreeManager.cpp#1553

the transforms there are

transformToApzc
[7.8287222038397886e-08 0 0 0]
[0 7.8287222038397886e-08 0 0]
[0 0 1 0]
[883.99981689453125 74.155632019042969 0 1]

transformToGecko
[12773476 0 0 0]
[0 12773476 0 0]
[0 0 1 0]
[-11291751424 -947225216 0 1]

outTransform
[0.99999994039535522 0 0 0]
[0 0.99999994039535522 0 0]
[0 0 1 0]
[-1024 0 0 1]

So it's as if we have a 1024 pixel async scroll offset permanently. I haven't worked out in detail how we get these matrices but the large scales involved seem like they could generate these, with the -1024 coming about because of float inaccuracies?

When we build the display list for GetFrameForPoint to find the targeted frame I guess we walk into the placeholders for open menupopups (like we do for other out of flow content like fixed or abs pos) and so we can target things in the popup with an event that was sent to the main widget, and the root frame of it.

I haven't thought about how to fix this very much yet, but possible ideas so far:

  1. treat apzcs with scales too big or too small as if the transform is non-invertible, ie they are dead to events etc. We'd have to come up with some kind of assurance that the limits work to avoid this problem and also don't affect real content.
  2. do the required computations somehow to avoid the floating point inaccuracies. Is this always possible? Don't we eventually have to hit a limit?
  3. something else.

The old layers hit testing code did seem to manage to avoid this (although it might just be luck), so perhaps looking into that might be useful. It could also be a dead end as the webrender hit testing is quite different and necessarily (for fission) more precise.

Even if we fix this large/small scale issue wouldn't it be possible for a page to create a long-lasting async scroll offset to cause mouse events in the content area to target chrome? The page could for example ask for a smooth scroll and then immediately enter an infinite busy loop. Or the page could be designed in such a way that the user wants to scroll down the right amount while the page is already in an infinite busy loop.

Even outside of attacker scenarios, if there is an async scroll offset of 100 on a scrollable subframe, and the user clicks in the subframe, but within 100 pixels of the scrollport of the subframe, the click will get moved 100 pixels to be outside of the subframe scrollport. More generally, anytime that we untransform using the targeted apzc A1 but then when we dispatch the event and find the frame target T, and T is not a descendant of A1 we will send the event to the wrong place.

I can only reproduce the issue when WebRender is enabled, it turns out I also able to reproduce on Fission disabled.

By using overflow-y: hidden combined with transform scale or matrix it also works to move the mouse coordinate.

When using mozregression with prefs gfx.webrender.enabled: true then visit attached launcher-mozregression-20180502.zip testcase, I can reproduce the issue from FF build date 2018-05-02, after the popup launched, right-clicking the page will show the context menu away from the cursor position.

Attached file compare.txt

The first two matrices in comment 12 are actually inverses of each other, however the 883.99981689453125 in the first matrix can't be represented accurately enough by a float so that the -1024 doesn't show up in the product. It should be 883.999893529, note how it starts differing after 7 digits, ie float precision.

So that might lead to a solution: if the transform matrix of an apzc times it's inverse is too far from the identity then we don't allow it to produce a hit.

I looked at why layers isn't affected and I think it's just dumb luck: when testing if the apzc for the body element is a hit we determine that it is not (which is incorrect) and so we hit the html element and we avoid any big scales.

Attached the hit testing tree and hittest comparing layers and webrender.

Thanks Timothy for investigating!

(In reply to Timothy Nikkel (:tnikkel) from comment #15)

The first two matrices in comment 12 are actually inverses of each other, however the 883.99981689453125 in the first matrix can't be represented accurately enough by a float so that the -1024 doesn't show up in the product. It should be 883.999893529, note how it starts differing after 7 digits, ie float precision.

So that might lead to a solution: if the transform matrix of an apzc times it's inverse is too far from the identity then we don't allow it to produce a hit.

This approach sounds promising to me.

(In reply to Timothy Nikkel (:tnikkel) from comment #12)

Even if we fix this large/small scale issue wouldn't it be possible for a page to create a long-lasting async scroll offset to cause mouse events in the content area to target chrome? The page could for example ask for a smooth scroll and then immediately enter an infinite busy loop. Or the page could be designed in such a way that the user wants to scroll down the right amount while the page is already in an infinite busy loop.

Even outside of attacker scenarios, if there is an async scroll offset of 100 on a scrollable subframe, and the user clicks in the subframe, but within 100 pixels of the scrollport of the subframe, the click will get moved 100 pixels to be outside of the subframe scrollport. More generally, anytime that we untransform using the targeted apzc A1 but then when we dispatch the event and find the frame target T, and T is not a descendant of A1 we will send the event to the wrong place.

My understanding is that FlushRepaintsToClearScreenToGeckoTransform(), which is called when receiving a mouse-down among other scenarios, is intended to address this issue in the case where it arises from an actual async transform (rather than an inaccuracy introduced by multiplying a CSS transform by its inverse).

Set release status flags based on info from the regressing bug 1675547

Flags: needinfo?(botond)
Type: task → defect
Attached file Bug 1737722. r?botond
Assignee: nobody → tnikkel
Status: NEW → ASSIGNED

(In reply to Timothy Nikkel (:tnikkel) from comment #12)

When we build the display list for GetFrameForPoint to find the targeted frame I guess we walk into the placeholders for open menupopups (like we do for other out of flow content like fixed or abs pos) and so we can target things in the popup with an event that was sent to the main widget, and the root frame of it.

Just wanted to clarify this point (if not for anyone else at least for myself). We don't walk into popup out of flows when building a display list for hit testing.

https://searchfox.org/mozilla-central/rev/8bd5597baef9be63fc4689a69e550d607dc68444/layout/generic/nsIFrame.cpp#4131

In PresShell::EventHandler::ComputeRootFrameToHandleEvent we call ComputeRootFrameToHandleEventWithPopup

https://searchfox.org/mozilla-central/rev/8bd5597baef9be63fc4689a69e550d607dc68444/layout/base/PresShell.cpp#7725

which eventually iterates the list of open popups

https://searchfox.org/mozilla-central/rev/8bd5597baef9be63fc4689a69e550d607dc68444/layout/base/nsLayoutUtils.cpp#1765

to see if one of them is where the event should be handled.

Flags: needinfo?(tnikkel)
Flags: needinfo?(hikezoe.birchill)
Flags: sec-bounty?
Flags: sec-bounty+
Flags: qe-verify+

Is this ready for uplift requests? Note that ESR91 will need a bit of rebasing due to bug 1729118.

Group: firefox-core-security → core-security-release
Flags: needinfo?(tnikkel)

Comment on attachment 9275430 [details]
Bug 1737722. r?botond

Beta/Release Uplift Approval Request

  • User impact if declined: attacker could user move mouse input
  • Is this code covered by automated tests?: No
  • Has the fix been verified in Nightly?: Yes
  • Needs manual test from QE?: No
  • If yes, steps to reproduce:
  • List of other uplifts needed: None
  • Risk to taking this patch: Low
  • Why is the change risky/not risky? (and alternatives if risky): patch only has an effect with huge transforms that real content should never hit
  • String changes made/needed:
  • Is Android affected?: No

ESR Uplift Approval Request

  • If this is not a sec:{high,crit} bug, please state case for ESR consideration: sec mod, low risk
  • User impact if declined: attacker could user move mouse input
  • Fix Landed on Version: 104
  • Risk to taking this patch: Low
  • Why is the change risky/not risky? (and alternatives if risky): patch only has an effect with huge transforms that real content should never hit
Flags: needinfo?(tnikkel)
Attachment #9275430 - Flags: approval-mozilla-esr102?
Attachment #9275430 - Flags: approval-mozilla-beta?
QA Whiteboard: [qa-triaged]

A patch has been attached on this bug, which was already closed. Filing a separate bug will ensure better tracking. If this was not by mistake and further action is needed, please alert the appropriate party.

Comment on attachment 9275430 [details]
Bug 1737722. r?botond

Approved for 103.0 RC1 and ESR102.1, thanks.

Attachment #9275430 - Flags: approval-mozilla-esr102?
Attachment #9275430 - Flags: approval-mozilla-esr102+
Attachment #9275430 - Flags: approval-mozilla-beta?
Attachment #9275430 - Flags: approval-mozilla-beta+
Attachment #9285574 - Attachment description: Bug 1737722. ers91. r?botond → Bug 1737722. esr91. r?botond

Comment on attachment 9285574 [details]
Bug 1737722. esr91. r?botond

ESR Uplift Approval Request

  • If this is not a sec:{high,crit} bug, please state case for ESR consideration: sec mod, low risk
  • User impact if declined: attacker could user move mouse input
  • Fix Landed on Version: 104
  • Risk to taking this patch: Low
  • Why is the change risky/not risky? (and alternatives if risky): patch only has an effect with huge transforms that real content should never hit
Attachment #9285574 - Flags: approval-mozilla-esr91?
QA Whiteboard: [qa-triaged] → [qa-triaged][post-critsmash-triage]

Comment on attachment 9285574 [details]
Bug 1737722. esr91. r?botond

Approved for ESR91.12, thanks.

Attachment #9285574 - Flags: approval-mozilla-esr91? → approval-mozilla-esr91+

Hi Irvan! Can you please help us verifying this bug on the fixed builds?

Unfortunately, I am not able to reproduce the issue even though I've tried with the test cases from comment 0 and comment 13. I had fission enabled as well as webreneder on an affected Nightly build (2021-10-15), but the "Download" image was not displayed on my Win 10 x64 machine.

Flags: needinfo?(susah.yak)
Whiteboard: [reporter-external] [client-bounty-form] [verif?] → [reporter-external] [client-bounty-form][adv-main103+]
Whiteboard: [reporter-external] [client-bounty-form][adv-main103+] → [reporter-external] [client-bounty-form][adv-main103+][adv-esr91.12+]

(In reply to Ciprian Georgiu [:ciprian_georgiu], Release Desktop QA from comment #32)

Hi Irvan! Can you please help us verifying this bug on the fixed builds?

Unfortunately, I am not able to reproduce the issue even though I've tried with the test cases from comment 0 and comment 13. I had fission enabled as well as webreneder on an affected Nightly build (2021-10-15), but the "Download" image was not displayed on my Win 10 x64 machine.

Alright, I'm currently checking this now.

(In reply to Ciprian Georgiu [:ciprian_georgiu], Release Desktop QA from comment #32)

Hi Irvan! Can you please help us verifying this bug on the fixed builds?

Unfortunately, I am not able to reproduce the issue even though I've tried with the test cases from comment 0 and comment 13. I had fission enabled as well as webreneder on an affected Nightly build (2021-10-15), but the "Download" image was not displayed on my Win 10 x64 machine.

On Windows 11 the "Download" image is disappear after Bug 1757647 - Implement Windows 11 overlay scrollbars is applied, to make "Download" image re-appear we can toggle widget.windows.overlay-scrollbars.enabled from true to false.

On Linux the "Download" image is disappear after scrollbar is refactored on pushlog https://hg.mozilla.org/integration/autoland/pushloghtml?fromchange=d02c31423ae04546ab8dd3c2fa107a03fb66b629&tochange=ef94ce8ed301e54b5df8b578d7f1d5bdfead2c0d, unfortunately there are no prefs switch to undo the patch, but we can also test by right click into the page, after single tap to the page then right click the context menu will appear on different location if it still reproducible.

When verifying the bug on Windows 11 with resolution 1920x1080 on 125% scale using testcase on comment 0, I verified it has been fixed on following build:

  • Firefox 104.0a1 (2022-07-19) (64-bit)
  • Firefox 103.0 (64-bit)
  • Firefox 102.1.0esr (64-bit)
  • Firefox 91.12.0esr (64-bit) (webrender.enabled to true)

I also verified the bug is no longer reproducible on Linux platform using Firefox Nightly 104.0a1 (2022-07-19) (64-bit) by right click into the page.

The Firefox ESR102.1 download link that you refer is point to Firefox ESR102.0.1 version released on 11-Jul-2022 which still reproducible. I think it's typo which has to refer to ESR102.1.0 link which is fixed on that build.

Status: RESOLVED → VERIFIED
Flags: needinfo?(susah.yak)
Alias: CVE-2022-36319

Thank you very much, Irvan!

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

The Firefox ESR102.1 download link that you refer is point to Firefox ESR102.0.1 version released on 11-Jul-2022 which still reproducible. I think it's typo which has to refer to ESR102.1.0 link which is fixed on that build.

Indeed, I wanted to point out the download link from ESR102.1.

Whiteboard: [reporter-external] [client-bounty-form][adv-main103+][adv-esr91.12+] → [reporter-external] [client-bounty-form][adv-main103+][adv-esr91.12+][adv-main102.1+]
Whiteboard: [reporter-external] [client-bounty-form][adv-main103+][adv-esr91.12+][adv-main102.1+] → [reporter-external] [client-bounty-form][adv-main103+][adv-esr91.12+][adv-esr102.1+]
Regressions: 1792447
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.