Chrome · WebUI
CVE-2026-87506
Logic Error in WebUI
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
IN_PROC_BROWSER_TEST_Fcontent/browser/webui/web_ui_security_browsertest.cc |
modified |
Files Changed
content/browser/renderer_host/render_frame_host_impl.cccontent/browser/webui/web_ui_impl.cccontent/browser/webui/web_ui_security_browsertest.cc
Patch
From d074777b13325b2108b4c0c2565c7c239cafa526 Mon Sep 17 00:00:00 2001
From: Fred Shih <ffred@chromium.org>
Date: Tue, 04 Aug 2026 13:21:57 -0700
Subject: [PATCH] Fix sandbox escape for chrome-untrusted://
The bug only mentioned startup, but I guess this issue persists
throughout the lifetime. This change ensures that chrome-untrusted://
does not get access to chrome:// and file:// URLs by default.
I hope this doesn't break anything...
Bug: 497551905
Change-Id: Ibb48b22505a5603cb235f76e6ff56a23bd6d7e25
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8178737
Commit-Queue: Fred Shih <ffred@chromium.org>
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1673628}
---
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 78ce44e..0ba81f0 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -13740,6 +13740,11 @@
web_ui_type_ = new_web_ui_type;
// WebUIs need the ability to request certain schemes.
+ if (!GetSiteInstance()->GetSiteInfo().site_url().SchemeIs(
+ kChromeUIUntrustedScheme)) {
+ web_ui_->AddRequestableScheme(kChromeUIScheme);
+ web_ui_->AddRequestableScheme(url::kFileScheme);
+ }
for (const auto& scheme : web_ui_->GetRequestableSchemes()) {
ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestScheme(
GetProcess()->GetDeprecatedID(), scheme);
diff --git a/content/browser/webui/web_ui_impl.cc b/content/browser/webui/web_ui_impl.cc
index aa277dde..0fd5d93 100644
--- a/content/browser/webui/web_ui_impl.cc
+++ b/content/browser/webui/web_ui_impl.cc
@@ -215,8 +215,7 @@
}
WebUIImpl::WebUIImpl(WebContents* web_contents)
- : requestable_schemes_({kChromeUIScheme, url::kFileScheme}),
- web_contents_(web_contents),
+ : web_contents_(web_contents),
web_contents_observer_(
std::make_unique<WebUIMainFrameObserver>(this, web_contents_)) {
DCHECK(web_contents_);
diff --git a/content/browser/webui/web_ui_security_browsertest.cc b/content/browser/webui/web_ui_security_browsertest.cc
index 87a9e46..f4ba4f57 100644
--- a/content/browser/webui/web_ui_security_browsertest.cc
+++ b/content/browser/webui/web_ui_security_browsertest.cc
@@ -58,7 +58,8 @@
ScopedWebUIControllerFactoryRegistration factory_registration_{&factory_};
};
-// Verify chrome-untrusted:// have no bindings.
+// Verify chrome-untrusted:// have no bindings and cannot request chrome or file
+// URLs.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, UntrustedNoBindings) {
auto* web_contents = shell()->web_contents();
WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
@@ -67,17 +68,19 @@
const GURL untrusted_url(GetChromeUntrustedUIURL("test-host/title1.html"));
EXPECT_TRUE(NavigateToURL(web_contents, untrusted_url));
+ auto process_id =
+ *shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID();
EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
- shell()
- ->web_contents()
- ->GetPrimaryMainFrame()
- ->GetProcess()
- ->GetDeprecatedID()));
+ process_id));
EXPECT_TRUE(shell()
->web_contents()
->GetPrimaryMainFrame()
->GetEnabledBindings()
.empty());
+ EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
+ process_id, GURL("file:///etc/passwd")));
+ EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
+ process_id, GURL("chrome://version")));
}
// Loads a WebUI which does not have any bindings.
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/content/browser/webui/web_ui_security_browsertest.cc b/content/browser/webui/web_ui_security_browsertest.cc
index 87a9e46..f4ba4f57 100644
--- a/content/browser/webui/web_ui_security_browsertest.cc
+++ b/content/browser/webui/web_ui_security_browsertest.cc
@@ -58,7 +58,8 @@
ScopedWebUIControllerFactoryRegistration factory_registration_{&factory_};
};
-// Verify chrome-untrusted:// have no bindings.
+// Verify chrome-untrusted:// have no bindings and cannot request chrome or file
+// URLs.
IN_PROC_BROWSER_TEST_F(WebUISecurityTest, UntrustedNoBindings) {
auto* web_contents = shell()->web_contents();
WebUIConfigMap::GetInstance().AddUntrustedWebUIConfig(
@@ -67,17 +68,19 @@
const GURL untrusted_url(GetChromeUntrustedUIURL("test-host/title1.html"));
EXPECT_TRUE(NavigateToURL(web_contents, untrusted_url));
+ auto process_id =
+ *shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->GetID();
EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
- shell()
- ->web_contents()
- ->GetPrimaryMainFrame()
- ->GetProcess()
- ->GetDeprecatedID()));
+ process_id));
EXPECT_TRUE(shell()
->web_contents()
->GetPrimaryMainFrame()
->GetEnabledBindings()
.empty());
+ EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
+ process_id, GURL("file:///etc/passwd")));
+ EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
+ process_id, GURL("chrome://version")));
}
// Loads a WebUI which does not have any bindings.
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page