Medium CVSS 4.3 firefox Spoofing 🔧 Commit mapped

Overview

Medium
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionThe z-order of the browser windows could be manipulated to hide the fullscreen notification. This could potentially be leveraged to perform a spoofing attack.
ComponentWidget
Bug ClassSpoofing
Tracker1940162
Fix commit98ab4c1b27a1 (firefox) +4/-3
CWECWE-1021
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
CISA KEVNot listed
CreditedIrvan Kurniawan
Disclosed2025-02-04

Background

`nsWindow::Show`
The Windows widget entry point that maps a native HWND on-screen and positions it in the window z-order via ::SetWindowPos.
`::SetWindowPos`
The Win32 API that changes a window’s size, position, and z-order relative to a supplied insert-after window handle.
`SWP_NOOWNERZORDER`
A ::SetWindowPos flag that suppresses the default behavior of also repositioning the target window’s owner in the z-order.
Fullscreen notification
The Firefox-drawn overlay warning the user that a page has entered fullscreen mode, which must remain visible above content to prevent spoofing.

Root Cause Analysis

When nsWindow::Show displays a non-PopupLevel::Top popup, it calls ::SetWindowPos twice: once to place the popup (mWnd) relative to its owner, and once to place the owner relative to the popup. Neither call originally passed SWP_NOOWNERZORDER, so Windows was free to additionally reorder the owner’s own owner (an ancestor top-level window) as a side effect of positioning these popups. Because the fullscreen notification is realized as an owned window layered above the browser content, this implicit owner-chain z-order shuffling could push a window above the notification, hiding it. The invariant violated is that showing a child popup must not alter the relative z-order of unrelated windows in the owner chain.

The fix adds SWP_NOOWNERZORDER to both the base flags and the second ::SetWindowPos call, so only the explicitly named windows move and the owner-chain ordering — and thus the notification’s stacking — is preserved.

Key insight
The single mistake was omitting SWP_NOOWNERZORDER on popup-positioning ::SetWindowPos calls, which let Windows silently reorder owner-chain windows; adding the flag confines each call’s z-order effect to exactly the two windows named, so the fullscreen notification can no longer be reordered beneath other windows.

Attack Path

  1. Enter fullscreen A malicious page requests fullscreen, causing Firefox to display the fullscreen notification as an owned window layered above the content.
  2. Trigger popup display The page induces creation and showing of a popup window (e.g. a select dropdown or other non-top popup) so that nsWindow::Show runs its ::SetWindowPos sequence.
  3. Exploit implicit owner z-ordering Because the calls lack SWP_NOOWNERZORDER, Windows reshuffles the owner chain, moving another window above the fullscreen notification.
  4. Hide the notification With the notification obscured, the fullscreen state is no longer signaled to the user.
  5. Spoof the UI The attacker renders fake browser chrome or address-bar content in the now-unannounced fullscreen view to deceive the user.

Impact Assessment

An attacker gains the ability to suppress the fullscreen notification and present a spoofed, trusted-looking UI within the content process’s rendered page, undermining the user’s ability to recognize fullscreen mode. Exploitation requires the user to enter fullscreen and the page to drive popup display so the vulnerable ::SetWindowPos path executes; it is limited to the Windows widget backend. The result is a moderate-severity spoofing risk rather than memory corruption or code execution.

Changed Functions

FunctionChangeNotes
if
widget/windows/nsWindow.cpp
modified

Files Changed

  • widget/windows/nsWindow.cpp

Audit Directions

  • Missing `SWP_NOOWNERZORDER`
    Review every ::SetWindowPos call in the Windows widget code for cases involving owned windows where implicit owner-chain z-order changes are undesired.
  • Security-overlay stacking
    Audit any code that renders trust-critical overlays (fullscreen, permission, and pointer-lock notifications) as owned windows to confirm nothing can reorder them beneath other windows.
  • Z-order side effects
    Look for window-management APIs (BringWindowToTop, SetWindowPos, owner/owned relationships) whose documented default side effects on relative ordering are not explicitly constrained by flags.
diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp
index 2d0d45afed5..ba71007965a 100644
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -1688,7 +1688,7 @@ void nsWindow::Show(bool aState) {
           // layer. Remember not to set the SWP_NOZORDER
           // flag as that might allow the taskbar to overlap
           // the popup.
-          flags |= SWP_NOACTIVATE;
+          flags |= SWP_NOACTIVATE | SWP_NOOWNERZORDER;
           HWND owner = ::GetWindow(mWnd, GW_OWNER);
           if (owner) {
             // PopupLevel::Top popups should be above all else.  All other
@@ -1696,8 +1696,9 @@ void nsWindow::Show(bool aState) {
             // changing the owner's z-level relative to other windows.
             if (mPopupLevel != PopupLevel::Top) {
               ::SetWindowPos(mWnd, owner, 0, 0, 0, 0, flags);
-              ::SetWindowPos(owner, mWnd, 0, 0, 0, 0,
-                             SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+              ::SetWindowPos(
+                  owner, mWnd, 0, 0, 0, 0,
+                  SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
             } else {
               ::SetWindowPos(mWnd, HWND_TOP, 0, 0, 0, 0, flags);
             }
Loading diff…