CVE-2026-10920
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/webshare/share_service_impl.cc |
modified | |
TEST_Fchrome/browser/webshare/share_service_unittest.cc |
modified | |
BindLambdaForTestingchrome/browser/webshare/share_service_unittest.cc |
modified |
Files Changed
chrome/browser/webshare/share_service_impl.ccchrome/browser/webshare/share_service_unittest.cc
Patch
From 3f267f3cd2d398153685b93e619d360bba561d5f Mon Sep 17 00:00:00 2001 From: Dan Murphy <[email protected]> Date: Mon, 04 May 2026 17:04:04 -0700 Subject: [PATCH] [WebShare] Add URL scheme validation in browser process A compromised renderer could bypass Web Share API validations by directly sending Mojo messages to the browser process, supplying a `file://` URL pointing to a sensitive local file. This CL adds independent validation in `ShareServiceImpl::Share` to ensure the `share_url` uses a permitted scheme (`http` or `https`) and explicitly rejects others like `file://` by reporting a bad message and terminating the renderer. Bug: 498977444 Change-Id: Ibc957bd0b41e33a9033a1b4e84f78907f7ecf613 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7813645 Auto-Submit: Daniel Murphy <[email protected]> Commit-Queue: Dibyajyoti Pal <[email protected]> Commit-Queue: Daniel Murphy <[email protected]> Reviewed-by: Dibyajyoti Pal <[email protected]> Cr-Commit-Position: refs/heads/main@{#1625029} --- diff --git a/chrome/browser/webshare/share_service_impl.cc b/chrome/browser/webshare/share_service_impl.cc index 805e2ba..158d69f6 100644 --- a/chrome/browser/webshare/share_service_impl.cc +++ b/chrome/browser/webshare/share_service_impl.cc @@ -184,6 +184,13 @@ return; } + if (!share_url.is_empty() && !share_url.SchemeIsHTTPOrHTTPS()) { + std::move(callback).Run(blink::mojom::ShareError::PERMISSION_DENIED); + ReportBadMessageAndDeleteThis( + "Web Share URL scheme must be http or https."); + return; + } + content::WebContents* const web_contents = content::WebContents::FromRenderFrameHost(&render_frame_host()); if (!web_contents) { diff --git a/chrome/browser/webshare/share_service_unittest.cc b/chrome/browser/webshare/share_service_unittest.cc index fa6b254..338ef6b7 100644 --- a/chrome/browser/webshare/share_service_unittest.cc +++ b/chrome/browser/webshare/share_service_unittest.cc @@ -20,6 +20,7 @@ #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" +#include "content/public/test/test_renderer_host.h" #include "storage/browser/blob/blob_data_builder.h" #include "storage/browser/blob/blob_impl.h" #include "storage/browser/blob/blob_storage_context.h" @@ -163,6 +164,7 @@ #if BUILDFLAG(IS_WIN) webshare::ScopedShareOperationFakeComponents scoped_fake_components_; #endif + protected: mojo::Remote<blink::mojom::ShareService> share_service_remote_; }; @@ -214,6 +216,28 @@ EXPECT_EQ(ShareError::OK, ShareGeneratedFileData(".webm", "video/webm")); } +TEST_F(ShareServiceUnitTest, ShareInvalidURLScheme) { + const std::string kTitle = "Title"; + const std::string kText = "Text"; + const GURL kUrl = GURL("file:///etc/passwd"); + std::vector<blink::mojom::SharedFilePtr> files; + + base::RunLoop run_loop; + share_service_remote_.set_disconnect_handler(run_loop.QuitClosure()); + + bool callback_called = false; + share_service_remote_->Share( + kTitle, kText, kUrl, std::move(files), + base::BindLambdaForTesting([&callback_called](ShareError error) { + callback_called = true; + EXPECT_EQ(error, ShareError::PERMISSION_DENIED); + })); + + run_loop.Run(); + EXPECT_TRUE(callback_called); + EXPECT_FALSE(share_service_remote_.is_connected()); +} + TEST_F(ShareServiceUnitTest, PortableDocumentFormat) { EXPECT_EQ(ShareError::OK, ShareGeneratedFileData(".pdf", "application/pdf")); }
Regression Test / PoC
diff --git a/chrome/browser/webshare/share_service_unittest.cc b/chrome/browser/webshare/share_service_unittest.cc
index fa6b254..338ef6b7 100644
--- a/chrome/browser/webshare/share_service_unittest.cc
+++ b/chrome/browser/webshare/share_service_unittest.cc
@@ -20,6 +20,7 @@
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_renderer_host.h"
#include "storage/browser/blob/blob_data_builder.h"
#include "storage/browser/blob/blob_impl.h"
#include "storage/browser/blob/blob_storage_context.h"
@@ -163,6 +164,7 @@
#if BUILDFLAG(IS_WIN)
webshare::ScopedShareOperationFakeComponents scoped_fake_components_;
#endif
+ protected:
mojo::Remote<blink::mojom::ShareService> share_service_remote_;
};
@@ -214,6 +216,28 @@
EXPECT_EQ(ShareError::OK, ShareGeneratedFileData(".webm", "video/webm"));
}
+TEST_F(ShareServiceUnitTest, ShareInvalidURLScheme) {
+ const std::string kTitle = "Title";
+ const std::string kText = "Text";
+ const GURL kUrl = GURL("file:///etc/passwd");
+ std::vector<blink::mojom::SharedFilePtr> files;
+
+ base::RunLoop run_loop;
+ share_service_remote_.set_disconnect_handler(run_loop.QuitClosure());
+
+ bool callback_called = false;
+ share_service_remote_->Share(
+ kTitle, kText, kUrl, std::move(files),
+ base::BindLambdaForTesting([&callback_called](ShareError error) {
+ callback_called = true;
+ EXPECT_EQ(error, ShareError::PERMISSION_DENIED);
+ }));
+
+ run_loop.Run();
+ EXPECT_TRUE(callback_called);
+ EXPECT_FALSE(share_service_remote_.is_connected());
+}
+
TEST_F(ShareServiceUnitTest, PortableDocumentFormat) {
EXPECT_EQ(ShareError::OK, ShareGeneratedFileData(".pdf", "application/pdf"));
}
Original Bug Report
Potential Arbitrary Local File Exfiltration via Web Share API on macOS
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the security team.
Overview: A compromised renderer process can bypass Web Share API validations by directly sending Mojo messages to the browser process. Because the browser process lacks its own URL scheme validation, an attacker can supply a file:// URL pointing to a sensitive local file. On macOS, this file is passed to the system share sheet with a spoofed title, potentially tricking the user into exfiltrating the file’s contents.
Affected files:
chrome/browser/webshare/share_service_impl.cccontent/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.mmchrome/browser/webshare/mac/sharing_service_operation.mm
Estimated timestamp from git blame: 2025-08-04
Summary
A vulnerability in the Web Share API implementation allows a compromised renderer process to bypass security checks and initiate the sharing of arbitrary local files. By directly invoking the blink::mojom::ShareService::Share Mojo interface, an attacker can avoid renderer-side URL scheme and user activation checks. The browser process (ShareServiceImpl::Share) fails to independently validate the URL scheme, allowing a file:// URL to be passed to the platform’s native sharing UI. On macOS 13.0+, an attacker can spoof the title in the share sheet, tricking the user into sending the local file to a target application like Mail or AirDrop.
Potential Attack Steps
Note: These are potential steps based on code analysis; a working proof-of-concept has not been executed.
- Renderer Compromise: An attacker gains arbitrary code execution within the sandboxed renderer process.
- Direct Mojo Invocation: The attacker binds directly to the
blink::mojom::ShareServiceand sends aSharemessage. This bypasses the renderer-side checks inNavigatorShare::shareandNavigatorShare::CanShareInternal, which would normally require transient user activation and restrict URLs to HTTP/HTTPS. - Payload Construction: The attacker provides a deceptive
title(e.g., “Cute Kitten Image”), an emptyfilesarray, and setsshare_urlto a sensitive local file path using thefile://scheme (e.g.,file:///Users/victim/.ssh/id_rsa). - Browser Process Bypass: The browser process receives the message in
ShareServiceImpl::Share(chrome/browser/webshare/share_service_impl.cc). Because thefilesarray is empty, it skips Safe Browsing and MIME type checks. Crucially, the browser process does not validate theshare_urlscheme or check for transient user activation, trusting the renderer implicitly. - macOS UI Spoofing: The request is routed to macOS UI components. In
RenderWidgetHostNSViewBridge::ShowSharingServicePicker(content/app_shim_remote_cocoa/render_widget_host_ns_view_bridge.mm), thefile://GURL is converted to a nativeNSURL. It is then wrapped in anNSPreviewRepresentingActivityItemalongside the attacker’s deceptivetitle. - Exfiltration: The macOS system share sheet appears without warning, displaying the spoofed title. If the user selects a sharing target (e.g., Mail), the macOS sharing system reads the local file specified by the
NSURLand attaches its contents, achieving local file exfiltration and a sandbox escape.
Suggested Fix
The browser process must not rely solely on the renderer for security validation.
- URL Scheme Validation: Add validation in
ShareServiceImpl::Share(chrome/browser/webshare/share_service_impl.cc) to ensureshare_urluses a permitted scheme (e.g.,http:orhttps:) and explicitly rejectfile://,chrome://, etc. - User Activation Check: Enforce that a transient user activation is present when handling the
ShareMojo request in the browser process.
Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33
Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.