Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Fullscreen
DescriptionInappropriate implementation in Fullscreen
ComponentFullscreen
Sub-componentBlink>WebShare, UI>Browser>FullScreen
Bug ClassLogic Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker40067914
Fix commit92bebd7aaa77 (chromium/src) +32/-2
CWECWE-290
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
CISA KEVNot listed
CreditedUmar Farooq
Reported2023-07-22 · bounty $5000 (per vendor advisory)
Fix landed2024-10-18 (public git)
Disclosed2025-01-14
Patch gap88 days — fix public in source before the advisory

Background

`navigator.share`
the Web Share API entry point that lets a page hand a title, text, or URL to the operating system’s native share UI.
Fullscreen
a browser mode, entered via element.requestFullscreen(), in which the web contents occupy the entire display and browser chrome is hidden.
`ForSecurityDropFullscreen`
a WebContents method that forces the tab out of fullscreen for security reasons and returns a base::ScopedClosureRunner whose destruction/reset lifts the accompanying fullscreen block.
`base::ScopedClosureRunner`
a RAII helper that runs a stored closure when it is reset or destroyed, used here to keep fullscreen suppressed for the lifetime of the share operation.

Root Cause Analysis

On Windows, ShareServiceImpl::Share launched the native Windows Share UI directly from within the page’s fullscreen context via webshare::ShareOperation, without first leaving fullscreen. The Windows Share experience is designed as a windowed flow, so when it appears over a fullscreen tab the user cannot easily see or dismiss it, and interacting with the surrounding page can pull focus unintuitively — a spoofing/confusion hazard because the trustworthy native UI is layered over attacker-controlled full-screen content. The invariant being violated is that security- and trust-sensitive native OS surfaces should not be presented while the web contents own the entire screen.

The fix calls web_contents->ForSecurityDropFullscreen() before starting the share and holds the returned base::ScopedClosureRunner (fullscreen_block) for the duration of the operation, resetting it only in the completion callback. This guarantees the tab is windowed while the Share UI is active, restoring the invariant.

Key insight
The core mistake was invoking a windowed native OS share surface while the page still controlled the full screen, letting fullscreen web content sit behind trusted UI; the fix drops fullscreen via ForSecurityDropFullscreen and keeps it suppressed through the share’s lifetime with a base::ScopedClosureRunner.

Attack Path

  1. Enter fullscreen A malicious page calls document.body.requestFullscreen() so the attacker-controlled content occupies the entire display and browser chrome is hidden.
  2. Trigger share The page invokes navigator.share(...), reaching ShareServiceImpl::Share on Windows without any fullscreen exit.
  3. Overlay native UI The Windows Share flow starts through webshare::ShareOperation and renders its windowed UI on top of the still-fullscreen tab.
  4. Confuse the user Because the tab remains fullscreen, the user cannot easily locate or dismiss the share dialog, and clicks can fall back into the attacker’s page, enabling UI confusion or misdirected interaction.

Impact Assessment

An attacker gains a way to present the trusted Windows native Share UI over full-screen, attacker-controlled web content in the browser (renderer-initiated, handled in the browser process), producing UI confusion or spoofing-friendly conditions rather than memory corruption. The precondition is that the victim is on Windows, is in fullscreen on the attacker’s page, and the page can call navigator.share (which requires a user gesture and a secure context).

Changed Functions

FunctionChangeNotes
IN_PROC_BROWSER_TEST_F
chrome/browser/webshare/share_service_browsertest.cc
modified
SafeBrowsingShareServiceBrowserTest
chrome/browser/webshare/share_service_browsertest.cc
modified

Files Changed

  • chrome/browser/webshare/share_service_browsertest.cc
  • chrome/browser/webshare/share_service_impl.cc

Audit Directions

  • Native UI launched from fullscreen
    Review other renderer-triggered flows that surface OS or browser-trusted dialogs (file pickers, permission prompts, print, payment) to confirm they drop fullscreen via ForSecurityDropFullscreen before display.
  • Platform-guarded branches
    Audit #if BUILDFLAG(IS_WIN) (and other per-OS) code paths in shared services, since security mitigations added on one platform’s branch are easy to omit on others.
  • Scoped-lifetime mitigations
    Check uses of base::ScopedClosureRunner for security blocks to ensure the runner is threaded through async callbacks and reset only after the protected operation completes, not dropped early.
From 92bebd7aaa776384c1a62b1814ce8de1ab90ea75 Mon Sep 17 00:00:00 2001
From: Hoch Hochkeppel <[email protected]>
Date: Fri, 18 Oct 2024 23:07:16 +0000
Subject: [PATCH] Exit fullscreen on Windows for navigator.share

The 'Share' experience on Windows is designed with a windowed
experience in mind, and results in unintuitive behaviors when invoked
from a fullscreen context. To mitigate any risks from these behaviors,
and to ensure the optimal user experience, this change ensures that
fullscreen is exited prior to starting the Windows portion of the
'Share' flow.

Bug: 40067914
Change-Id: I36af174544f738965e988e8d0fb4b29349cad8c5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5938489
Reviewed-by: Daniel Murphy <[email protected]>
Commit-Queue: Hoch Hochkeppel <[email protected]>
Reviewed-by: Amanda Baker <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1370877}
---

diff --git a/chrome/browser/webshare/share_service_browsertest.cc b/chrome/browser/webshare/share_service_browsertest.cc
index 1c4f45a..e6a131f 100644
--- a/chrome/browser/webshare/share_service_browsertest.cc
+++ b/chrome/browser/webshare/share_service_browsertest.cc
@@ -108,6 +108,27 @@
                                      WebShareMethod::kShare, kRepeats);
 }
 
+#if BUILDFLAG(IS_WIN)
+IN_PROC_BROWSER_TEST_F(ShareServiceBrowserTest, Fullscreen) {
+  base::HistogramTester histogram_tester;
+  ASSERT_TRUE(embedded_test_server()->Start());
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("/webshare/index.html")));
+  content::WebContents* const web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  ui_test_utils::FullscreenWaiter waiter(browser(), {.tab_fullscreen = true});
+  EXPECT_TRUE(
+      content::ExecJs(web_contents, "document.body.requestFullscreen();"));
+  waiter.Wait();
+  ASSERT_TRUE(web_contents->IsFullscreen());
+
+  EXPECT_EQ("share succeeded",
+            content::EvalJs(web_contents, "share_text('hello')"));
+  EXPECT_FALSE(web_contents->IsFullscreen());
+}
+#endif  // BUILDFLAG(IS_WIN)
+
 class SafeBrowsingShareServiceBrowserTest : public ShareServiceBrowserTest {
  public:
   SafeBrowsingShareServiceBrowserTest()
diff --git a/chrome/browser/webshare/share_service_impl.cc b/chrome/browser/webshare/share_service_impl.cc
index 83195cd7..f174b4bb 100644
--- a/chrome/browser/webshare/share_service_impl.cc
+++ b/chrome/browser/webshare/share_service_impl.cc
@@ -282,6 +282,12 @@
          blink::mojom::ShareError result) { std::move(callback).Run(result); },
       std::move(sharing_service_operation), std::move(callback)));
 #elif BUILDFLAG(IS_WIN)
+  // Drop fullscreen mode so the Share UI can be easily clicked away from,
+  // without clicking back into the web contents
+  base::ScopedClosureRunner fullscreen_block =
+      web_contents->ForSecurityDropFullscreen(
+          /*display_id=*/display::kInvalidDisplayId);
+
   auto share_operation = std::make_unique<webshare::ShareOperation>(
       title, text, share_url, web_contents);
   auto* const share_operation_ptr = share_operation.get();
@@ -289,10 +295,13 @@
       std::move(files),
       base::BindOnce(
           [](std::unique_ptr<webshare::ShareOperation> share_operation,
-             ShareCallback callback, blink::mojom::ShareError result) {
+             base::ScopedClosureRunner fullscreen_block, ShareCallback callback,
+             blink::mojom::ShareError result) {
+            fullscreen_block.RunAndReset();
             std::move(callback).Run(result);
           },
-          std::move(share_operation), std::move(callback)));
+          std::move(share_operation), std::move(fullscreen_block),
+          std::move(callback)));
 #else
   NOTREACHED_IN_MIGRATION();
   std::move(callback).Run(blink::mojom::ShareError::INTERNAL_ERROR);
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/webshare/share_service_browsertest.cc b/chrome/browser/webshare/share_service_browsertest.cc
index 1c4f45a..e6a131f 100644
--- a/chrome/browser/webshare/share_service_browsertest.cc
+++ b/chrome/browser/webshare/share_service_browsertest.cc
@@ -108,6 +108,27 @@
                                      WebShareMethod::kShare, kRepeats);
 }
 
+#if BUILDFLAG(IS_WIN)
+IN_PROC_BROWSER_TEST_F(ShareServiceBrowserTest, Fullscreen) {
+  base::HistogramTester histogram_tester;
+  ASSERT_TRUE(embedded_test_server()->Start());
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("/webshare/index.html")));
+  content::WebContents* const web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  ui_test_utils::FullscreenWaiter waiter(browser(), {.tab_fullscreen = true});
+  EXPECT_TRUE(
+      content::ExecJs(web_contents, "document.body.requestFullscreen();"));
+  waiter.Wait();
+  ASSERT_TRUE(web_contents->IsFullscreen());
+
+  EXPECT_EQ("share succeeded",
+            content::EvalJs(web_contents, "share_text('hello')"));
+  EXPECT_FALSE(web_contents->IsFullscreen());
+}
+#endif  // BUILDFLAG(IS_WIN)
+
 class SafeBrowsingShareServiceBrowserTest : public ShareServiceBrowserTest {
  public:
   SafeBrowsingShareServiceBrowserTest()
Loading diff…

Original Bug Report

reported by [email protected]

Security: [Esc] KeyPress Does Not Work in FullScreen While navigator.share Is Active

VULNERABILITY DETAILS

We Can Prevent [Esc] KeyPress in Full Screen Mode Using navigator.share

VERSION
Chrome Version: [115.0.5790.99] + [stable]
Operating System: [Windows 10]

REPRODUCTION CASE

  1. Open PocIndex.html
  2. Double Click [Click Me!] button
  3. Done

CREDIT INFORMATION
Reporter credit: Umar Farooq

View on issue tracker