Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUnsafe navigation in Navigation
DescriptionUnsafe navigation in Navigation
ComponentNavigation
Bug ClassLogic Error
Tracker476898368
Fix commitd2e7bad529aa (chromium/src) +1/-6
CISA KEVNot listed
CreditedPovcfe of Tencent Security Xuanwu Lab
Disclosed2026-03-10

Changed Functions

FunctionChangeNotes
if
ios/web/navigation/crw_wk_navigation_handler.mm
modified

Files Changed

  • ios/web/navigation/crw_wk_navigation_handler.mm
From d2e7bad529aa30eab1a68da76ef9ab87ffafe132 Mon Sep 17 00:00:00 2001
From: Olivier Robin <[email protected]>
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 <[email protected]>
Commit-Queue: Mike Dougherty <[email protected]>
Reviewed-by: Mike Dougherty <[email protected]>
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,
Loading diff…

Original Bug Report

reported by [email protected]

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 the NavigationContext.
  • 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 and shouldAllowAppSpecificURLNavigationAction sees PAGE_TRANSITION_RELOAD and 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-test on first visit, and
  • Issuing a 302 redirect to chrome://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 as chrome://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

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

View on issue tracker