CVE-2026-106254
Overview
Files Changed
content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.javacontent/browser/android/navigation_handle_proxy.ccchrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
Patch
From 6b575a3f89b3395c0a52e537e5c25deeb2b22db2 Mon Sep 17 00:00:00 2001
From: Michael Thiessen <mthiesse@chromium.org>
Date: Wed, 09 Sep 2026 10:45:36 -0700
Subject: [PATCH] Update referrer on redirect for Java NavigationHandle
Bug: 557036053
Change-Id: Iae9949d2fde57476ed05d53c1433dc144866f5ed
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8382058
Reviewed-by: Calder Kitagawa <ckitagawa@chromium.org>
Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
Commit-Queue: Michael Thiessen <mthiesse@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1694711}
---
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
index a9d53f2..68b69f05 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManagerTest.java
@@ -84,6 +84,7 @@
import org.chromium.content_public.browser.NavigationHandle;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.common.ContentFeatures;
+import org.chromium.network.mojom.ReferrerPolicy;
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.base.PageTransition;
import org.chromium.ui.base.ViewUtils;
@@ -366,7 +367,11 @@
(Boolean shouldIgnore) -> {
result.set(shouldIgnore);
});
- navigationHandle.didRedirect(redirectUrl, true);
+ navigationHandle.didRedirect(
+ redirectUrl,
+ true,
+ /* referrerUrl= */ GURL.emptyGURL(),
+ /* referrerPolicy= */ ReferrerPolicy.DEFAULT);
mPanel.getOverlayPanelContent()
.getInterceptNavigationDelegateForTesting()
.shouldIgnoreNavigation(
diff --git a/content/browser/android/navigation_handle_proxy.cc b/content/browser/android/navigation_handle_proxy.cc
index 07c5d41..073dbf03 100644
--- a/content/browser/android/navigation_handle_proxy.cc
+++ b/content/browser/android/navigation_handle_proxy.cc
@@ -78,7 +78,10 @@
Java_NavigationHandle_didRedirect(
env, java_navigation_handle_,
url::GURLAndroid::FromNativeGURL(env, cpp_navigation_handle_->GetURL()),
- cpp_navigation_handle_->IsExternalProtocol());
+ cpp_navigation_handle_->IsExternalProtocol(),
+ url::GURLAndroid::FromNativeGURL(
+ env, cpp_navigation_handle_->GetReferrer().url),
+ static_cast<jint>(cpp_navigation_handle_->GetReferrer().policy));
}
void NavigationHandleProxy::DidFinish() {
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java b/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java
index 82cd317..cc813d3e 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java
@@ -210,10 +210,13 @@
*/
@CalledByNative
@VisibleForTesting
- public void didRedirect(GURL url, boolean isExternalProtocol) {
+ public void didRedirect(
+ GURL url, boolean isExternalProtocol, GURL referrerUrl, int referrerPolicy) {
mUrl = url;
mIsRedirect = true;
mIsExternalProtocol = isExternalProtocol;
+ mReferrerUrl = referrerUrl;
+ mReferrerPolicy = referrerPolicy;
takeNavigationStateSnapshot();
}
Original Bug Report
Server redirect leaves a stale full referrer in Android external-app intents
Report description
Server redirect leaves a stale full referrer in Android external-app intents
Bug location
Where do you want to report your vulnerability?
Chrome VRP – Report security issues affecting the Chrome browser. See program rules
The problem
Please describe the technical details of the vulnerability
Summary
Chrome for Android exports the start-time referrer of a navigation to an external application after a server redirect, even though Chromium’s authoritative navigation state has sanitized the referrer for the final destination. A same-origin first hop can therefore retain a sensitive source path and query and disclose it to an attacker-controlled Android activity reached through an external protocol.
The attached reproduction uses a synthetic URL token, a Python server, an ordinary Chrome binary, and an installable receiver application. It requires no browser patch, test target, unsafe flag, extension, or privileged Android permission.
Bisect
- Range: 969721 → 969722 — https://chromium.googlesource.com/chromium/src/+log/6c12a403d1e790d95b8a362d7026ad52a2755f62..9fca5b9a8afc8d7d60faf0c8faf71d8f33f910fd
- Introduced by: https://chromium.googlesource.com/chromium/src/+/9fca5b9a8afc8d7d60faf0c8faf71d8f33f910fd — “Add missing params from NavigationParams to NavigationHandle”
- Landed on: M100.0.4882.0
The introducing change copied the initial referrer into the Java navigation mirror. Redirect notifications continued to update only the target URL and external-protocol bit, leaving the initial referrer available to later Android external-navigation code.
Affected versions and validation
- Current runtime: Chromium ChromePublic 154.0.8028.0, revision
6cf6556e12ab04141b504a3b98c84bf075039e87, on Android 15/API 35 x86_64. - Released runtime: Google Chrome Stable 124.0.6367.219, package
com.android.chrome, version code636771938, on Android 15/API 35 x86_64. - Released Stable source: Chromium 152.0.7977.72, revision
026bb13a93d60e7adfefa2bbf58d6f57c2d335cc, contains the complete chain. - Official main source: Chromium 155.0.8042.0, revision
fc03f4856597274bcf07f89c76e592db5a682d96, also contains the complete chain. - Citation tree: Chromium 154.0.8012.0, revision
34a482b3cd98d6d599f43091babae366fb9c3d8e; every cited file matches that revision byte-for-byte. - Platforms: the demonstrated sink is Chrome for Android. Android WebView, desktop Chrome, ChromeOS, and iOS are not claimed.
Security boundary and prerequisites
- An attacker controls an Android application that handles the redirect destination.
- The victim visits a source URL containing sensitive path or query data and follows a normal link through a same-origin redirect endpoint.
- The first response redirects to the external destination, and the user confirms opening the receiving application if prompted.
- The source uses the default
strict-origin-when-cross-originpolicy or another policy that reduces the referrer for the final destination. - The receiver needs no sensitive Android permission; it reads its own launch intent.
- The user gesture authorizes opening the selected destination. It does not authorize disclosure of referrer data that the effective policy removed.
- App-link verification and Incognito amplification are not part of the demonstrated claim; the PoC uses a custom scheme.
Source chain / root cause
The following citations are from clean revision 34a482b3cd98d6d599f43091babae366fb9c3d8e.
-
content/browser/renderer_host/navigation_request.cc:3980-3990updates the authoritative URL and re-sanitizes the referrer for the redirected target:common_params_->url = redirect_info.new_url; common_params_->referrer->url = GURL(redirect_info.new_referrer); common_params_->referrer = Referrer::SanitizeForRequest( common_params_->url, *common_params_->referrer); -
content/browser/android/navigation_handle_proxy.cc:47-81gives Java the referrer at navigation start but omits it from the redirect update:Java_NavigationHandle_didStart( env, java_navigation_handle_, url::GURLAndroid::FromNativeGURL( env, cpp_navigation_handle_->GetReferrer().url), static_cast<jint>(cpp_navigation_handle_->GetReferrer().policy), ...); ... Java_NavigationHandle_didRedirect( env, java_navigation_handle_, url::GURLAndroid::FromNativeGURL(env, cpp_navigation_handle_->GetURL()), cpp_navigation_handle_->IsExternalProtocol()); -
content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java:195-206changes only the target URL and redirect flags:public void didRedirect(GURL url, boolean isExternalProtocol) { mUrl = url; mIsRedirect = true; mIsExternalProtocol = isExternalProtocol; } -
content/public/android/java/src/org/chromium/content_public/browser/NavigationHandle.java:294-312continues exposing the unchanged referrer fields:public GURL getReferrerUrl() { return assumeNonNull(mReferrerUrl); } public @Nullable Referrer getReferrer() { GURL referrerUrl = getReferrerUrl(); ... return new Referrer(referrerUrl.getSpec(), getReferrerPolicy()); } -
components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java:281-298consumes that Java-cached value after the redirect:shouldOverrideUrlLoading( redirectHandler, escapedUrl, ... navigationHandle.getReferrer(), ...); -
components/external_intents/android/java/src/org/chromium/components/external_intents/ExternalNavigationHandler.java:1557-1559forwards the stale URL into the outbound intent path:if (!params.getReferrerUrl().isEmpty()) { mDelegate.maybeSetPendingReferrer(targetIntent, params.getReferrerUrl()); } -
chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java:1468-1471exposes the value to the destination application:intent.putExtra(Intent.EXTRA_REFERRER, Uri.parse(url.getSpec()));
The browser has a policy-compliant post-redirect referrer, but the Java mirror and exported intent retain the more permissive start-time value. The missing redirect synchronization crosses the web-to-application boundary.
Steps to reproduce
- Place all attached files in one directory on a host with Python 3 and
adb. - Follow
repro.txtto installpoc_external_intent_receiver.apk, startpoc_external_intent_server.py, configureadb reverse, and clear the disposable Chromium profile. - Open the synthetic sensitive source URL in Chromium ChromePublic 154.0.8028.0.
- Press Follow same-origin redirect into test app and select Referrer Receiver if Android presents a resolver.
- Repeat with the origin-policy control exposed by the server.
The receiver source and manifest are included for inspection; the attached APK is directly installable. No browser harness or source modification is used.
Actual result
The receiver obtained the full source URL after the server redirect:
android.intent.extra.REFERRER=http://127.0.0.1:8765/account/callback?state=SYNTHETIC_SECRET_27
The origin-policy control delivered only:
android.intent.extra.REFERRER=http://127.0.0.1:8765/
evidence_external_intent_result.txt records both exact runtime targets and the decisive observations. The attached screenshots preserve the full-query disclosure and origin-only control.
Expected result
The external application should receive the referrer derived from the final redirected target and effective policy. Under the demonstrated default policy it should receive only http://127.0.0.1:8765/, never the sensitive source path or query.
Suggested fix
Extend the JNI redirect notification to carry the current sanitized referrer URL and policy, then update the Java fields before any external-navigation observer consumes the handle. Revalidate the authoritative value immediately before exporting an external intent as defense in depth.
fix_external_intent_referrer_redirect.patch implements the production synchronization change and applies cleanly to citation revision 34a482b3cd98d6d599f43091babae366fb9c3d8e.
Incomplete fix
- Public bug 497493136 / commit
c9b35fcba98cc9986bdd859b2ea97a2ac9314315carries the initial referrer policy into Java and fixes fallback-tab clobbering. It does not synchronize the referrer URL or policy after a server redirect. - The adjacent issue concerns policy loss at initial construction; this report concerns stale state after redirect and disclosure through
Intent.EXTRA_REFERRER.
Attachments
| Name | Description | SHA-256 |
|---|---|---|
repro.txt |
Standalone setup, decisive user step, expected observation, control, and cleanup. | 6cafb8736e882d5931517ae8296539469d05f13a52e5fac2c37cb0bb78a5be9d |
poc_external_intent_server.py |
Dependency-free Python server implementing the source, redirect, and control. | 6244eb24a58e38c42d75cab4eafed5e22d8695001044f1705ea7275f42fc9e1e |
poc_external_intent_receiver.apk |
Installable Android application that displays its launch referrer. | 97366a0f22d31fd55a8d4be82cc895dff8be379c0ffa1eff1739740182037b00 |
poc_external_intent_receiver.java |
Source for the minimal receiver activity. | 43a96300d45a956c235b69a086978598e6fdecae291781b12fd3d559bd18bbf5 |
poc_external_intent_manifest.xml |
Android manifest registering the synthetic external scheme. | a2e7c3104382428fbdebf49ccc8522ec6a57a5d5c3572ce239d1a97a9b70b154 |
evidence_external_intent_result.txt |
Exact runtime versions and full-referrer/origin-only observations. | 2c0ed1e82272537b3e2a7cd05272f3e55bd785e5ed2b3c99fa212940a20561fc |
evidence_external_intent_leak.png |
Receiver showing the disclosed synthetic path and query. | 4309ebab520eafcb0723f16262118a8f10058ea8dbef5d8e7f34110326f881e3 |
evidence_external_intent_control.png |
Matching origin-policy control showing an origin-only referrer. | 1e070aa10383a13f35b24382d74e9b0b8d3f940d47518e0faeb38725f113a74c |
fix_external_intent_referrer_redirect.patch |
Proposed redirect referrer synchronization fix. | de09cdca3766d7ea9903a3713bb72f45b63af11ca42fae1cd497da4ba72ac911 |
Impact analysis
Impact
- An attacker-controlled Android application receives source-path and query data that the web page’s referrer policy removed for the final destination.
- Exposed URLs can contain OAuth state, invitation or reset capabilities, document identifiers, account names, search queries, or other sensitive navigation data.
- The disclosure crosses Chrome’s web-to-application boundary through a public intent extra readable by the destination activity without a sensitive permission.
The cause
What version of Chrome have you found the security issue in?
124.0.6367.219 + stable-android
Is the security issue related to a crash?
No, it is not related to a crash.
Choose the type of vulnerability
Sensitive data exposure
How would you like to be publicly acknowledged for your report?
Anonymous
- http://127.0.0.1:8765/
- http://127.0.0.1:8765/account/callback?state=SYNTHETIC_SECRET_27
- https://bughunters.google.com/about/rules/5745167867576320/chrome-vulnerability-reward-program-rules
- https://chromium.googlesource.com/chromium/src/+/9fca5b9a8afc8d7d60faf0c8faf71d8f33f910fd
- https://chromium.googlesource.com/chromium/src/+log/6c12a403d1e790d95b8a362d7026ad52a2755f62..9fca5b9a8afc8d7d60faf0c8faf71d8f33f910fd
- https://issuetracker.google.com/issues/497493136