CVE-2026-3930
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifios/web/navigation/crw_wk_navigation_handler.mm |
modified |
Files Changed
ios/web/navigation/crw_wk_navigation_handler.mm
Patch
From d2e7bad529aa30eab1a68da76ef9ab87ffafe132 Mon Sep 17 00:00:00 2001
From: Olivier Robin <olivierrobin@google.com>
Date: Fri, 23 Jan 2026 13:46:35 -0800
Subject: [PATCH] Prevent Reload navigations to app specific URLs
In conjunction with server redirection, reload allows any page to
navigate to chrome:// pages, which should be disallowed.
This fix was initially introduced to allow reloading from reader mode,
but is redundant with
crrev.com/c/7246254 so it is not needed anymore.
Fixed: 476898368
Change-Id: I5d33c0d6fe85ecf9fb67b78426ad7904fb03d074
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7511016
Auto-Submit: Olivier Robin <olivierrobin@chromium.org>
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Reviewed-by: Mike Dougherty <michaeldo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1573932}
---
diff --git a/ios/web/navigation/crw_wk_navigation_handler.mm b/ios/web/navigation/crw_wk_navigation_handler.mm
index cf31779d..7bb26fd 100644
--- a/ios/web/navigation/crw_wk_navigation_handler.mm
+++ b/ios/web/navigation/crw_wk_navigation_handler.mm
@@ -1368,7 +1368,7 @@
// running App specific pages in the same process as a web site from the
// internet. Allows navigation to app specific URL in the following cases:
// - last committed URL is app specific
-// - navigation not a new navigation (back-forward or reload)
+// - navigation not a new navigation (back-forward)
// - navigation is typed, generated or bookmark
// - navigation is performed in iframe and main frame is app-specific page
- (BOOL)shouldAllowAppSpecificURLNavigationAction:(WKNavigationAction*)action
@@ -1391,11 +1391,6 @@
return YES;
}
- if (pageTransition & ui::PAGE_TRANSITION_RELOAD) {
- // Allow reload navigations.
- return YES;
- }
-
// Allow navigating to chrome:// pages if the navigation happens due to
// - user typing the url in the omnibox,
// - user tapping on a suggestion in the omnibox,
Original Bug Report
Privileged `chrome://` navigation via reload-triggered server redirect in iOS Chrome
Report description
Privileged chrome:// navigation via reload-triggered server redirect in iOS Chrome
Bug location
Where do you want to report your vulnerability?
Chrome VRP – Report security issues affecting the Chrome browser. See program rules
Which URL (or repository) have you found the vulnerability in?
https://chromium.googlesource.com/chromium/src/
The problem
Please describe the technical details of the vulnerability
On iOS, Chrome embeds Chromium’s ios/web navigation stack on top of WKWebView. The navigation delegate class CRWWKNavigationHandler contains special handling for “app-specific” URLs, which in the iOS Chrome configuration include internal chrome:// WebUI pages such as chrome://flags/.
The decision for whether to allow an app-specific navigation is implemented in ios/web/navigation/crw_wk_navigation_handler.mm:
- (BOOL)shouldAllowAppSpecificURLNavigationAction:(WKNavigationAction*)action
transition:
(ui::PageTransition)pageTransition {
GURL requestURL = net::GURLWithNSURL(action.request.URL);
DCHECK(web::GetWebClient()->IsAppSpecificURL(requestURL));
web::NavigationItem* lastItem =
self.webStateImpl->GetNavigationManager()->GetLastCommittedItem();
if (lastItem &&
(web::GetWebClient()->IsAppSpecificURL(lastItem->GetVirtualURL()) ||
web::GetWebClient()->IsAppSpecificURL(lastItem->GetURL()))) {
// Last committed page is also app specific and navigation should be
// allowed.
return YES;
}
if (pageTransition & ui::PAGE_TRANSITION_FORWARD_BACK) {
// Allow back-forward navigations.
return YES;
}
if (pageTransition & ui::PAGE_TRANSITION_RELOAD) {
// Allow reload navigations.
return YES;
}
// ... user-typed / bookmark and iframe cases omitted ...
return NO;
}
For an app-specific URL (such as a chrome:// page), the function unconditionally allows the navigation when the page transition has the PAGE_TRANSITION_RELOAD bit set, even if the last committed page was a regular web origin rather than an internal WebUI.
This method is called from the main navigation policy decision in the same file:
- (void)webView:(WKWebView*)webView
decidePolicyForNavigationAction:(WKNavigationAction*)action
preferences:(WKWebpagePreferences*)preferences
decisionHandler:
(void (^)(WKNavigationActionPolicy,
WKWebpagePreferences*))handler {
// ...
GURL requestURL = net::GURLWithNSURL(action.request.URL);
// ...
ui::PageTransition transition =
[self pageTransitionFromNavigationType:action.navigationType];
if (isMainFrameNavigationAction) {
web::NavigationContextImpl* context =
[self contextForPendingMainFrameNavigationWithURL:requestURL];
if (context &&
(!context->IsRendererInitiated() ||
(context->GetPageTransition() & ui::PAGE_TRANSITION_FORWARD_BACK))) {
transition = context->GetPageTransition();
if (context->IsLoadingErrorPage()) {
decisionHandler(WKNavigationActionPolicyAllow);
return;
}
}
}
// ...
web::WebStatePolicyDecider::PolicyDecision policyDecision =
web::WebStatePolicyDecider::PolicyDecision::Allow();
if (web::GetWebClient()->IsAppSpecificURL(requestURL)) {
if (![self shouldAllowAppSpecificURLNavigationAction:action
transition:transition]) {
policyDecision = web::WebStatePolicyDecider::PolicyDecision::Cancel();
}
if (policyDecision.ShouldAllowNavigation()) {
[self.delegate navigationHandler:self createWebUIForURL:requestURL];
}
}
// ...
}
The page transition value used here is derived from WebKit’s navigation type and, for main-frame navigations with an associated NavigationContext, from the context’s GetPageTransition():
- (ui::PageTransition)pageTransitionFromNavigationType:
(WKNavigationType)navigationType {
switch (navigationType) {
case WKNavigationTypeLinkActivated:
return ui::PAGE_TRANSITION_LINK;
case WKNavigationTypeFormSubmitted:
case WKNavigationTypeFormResubmitted:
return ui::PAGE_TRANSITION_FORM_SUBMIT;
case WKNavigationTypeBackForward:
return ui::PAGE_TRANSITION_FORWARD_BACK;
case WKNavigationTypeReload:
return ui::PAGE_TRANSITION_RELOAD;
case WKNavigationTypeOther:
// ... heuristic for "Other" ...
}
}
Server-side redirects are handled in didReceiveServerRedirectForProvisionalNavigation:
- (void)webView:(WKWebView*)webView
didReceiveServerRedirectForProvisionalNavigation:(WKNavigation*)navigation {
// ...
GURL webViewURL = net::GURLWithNSURL(webView.URL);
web::NavigationContextImpl* context =
[self.navigationStates contextForNavigation:navigation];
if (!context) {
return;
}
// Redirecting to a data url is always unsafe.
if (webViewURL.SchemeIs(url::kDataScheme) ||
// Block redirects to JavaScript schemes.
webViewURL.SchemeIs(url::kJavaScriptScheme)) {
self.pendingNavigationInfo.unsafeRedirect = YES;
} else {
context->SetUrl(webViewURL);
}
// ...
}
Redirects to data: and javascript: are treated as unsafe, but redirects to other schemes (including chrome://) simply update the context URL and do not change the page transition type.
Additionally, NavigationManagerImpl::CreateNavigationItem in ios/web/navigation/navigation_manager_impl.mm prevents certain internal rewrites from turning a reload of a non-app-specific URL into an app-specific load, but it does not apply to explicit server redirects that directly target an app-specific URL:
std::unique_ptr<NavigationItemImpl> NavigationManagerImpl::CreateNavigationItem(
const GURL& url,
const web::Referrer& referrer,
ui::PageTransition transition,
web::NavigationInitiationType initiation_type,
web::HttpsUpgradeType https_upgrade_type,
const GURL& previous_url,
const std::vector<BrowserURLRewriter::URLRewriter>* additional_rewriters)
const {
GURL loaded_url(url);
// ... URL rewriter logic ...
// The URL should not be changed to app-specific URL if the load is
// renderer-initiated or a reload requested by non-app-specific URL.
if ((initiation_type == web::NavigationInitiationType::RENDERER_INITIATED ||
PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD)) &&
loaded_url != url && web::GetWebClient()->IsAppSpecificURL(loaded_url) &&
!web::GetWebClient()->IsAppSpecificURL(previous_url)) {
loaded_url = url;
}
// ...
}
Putting these pieces together:
- A main-frame reload is represented as
PAGE_TRANSITION_RELOAD, and this information is carried in theNavigationContext. - A server redirect that happens during that reload does not change the transition type, but updates the URL to the redirect target.
- When the redirect target is an app-specific URL (for example
chrome://flags/),IsAppSpecificURL(requestURL)is true andshouldAllowAppSpecificURLNavigationActionseesPAGE_TRANSITION_RELOADand allows the navigation even if the last committed page was a regular HTTP(S) site. - As a result, a regular web page can cause a navigation into a privileged internal
chrome://WebUI page as long as the user has triggered a reload.
The provided proof-of-concept server in web/reload_redirect/ios_nav_reload_test_server.py demonstrates this behavior by:
- Serving a normal HTML page at
/ios-nav-reload-teston first visit, and - Issuing a
302redirect tochrome://flags/on subsequent visits after setting a cookie to record that the client has already visited the page once.
When this server is accessed from iOS Chrome and the user manually reloads the test page, the browser successfully follows the redirect into chrome://flags/, confirming that a non-privileged origin can drive navigation into a privileged chrome:// page via a reload-triggered server redirect.
Impact analysis
Who can exploit it
- Any remote web origin that a user visits in iOS Chrome, as long as the site can induce the user to reload the page once (for example via a UI hint).
What they can do
- After the user reloads the attacker-controlled page, the site can cause iOS Chrome to leave the attacker origin and open a privileged
chrome://WebUI page such aschrome://flags/via a server-side redirect. - This lets untrusted web content reliably drive navigation into internal configuration UIs that are normally intended to be reached only from trusted entry points (e.g. browser UI, bookmarks, or existing WebUI pages).
The cause
What version of Chrome have you found the security issue in?
145.0.7632.1/stable
Is the security issue related to a crash?
No, it is not related to a crash.
Choose the type of vulnerability
Privilege Escalation
How would you like to be publicly acknowledged for your report?
Povcfe of Tencent Security Xuanwu Lab