Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in Extensions
DescriptionInsufficient policy enforcement in Extensions
ComponentExtensions
Bug ClassLogic Error
Tracker502256049
Fix commit7446ed80981b (chromium/src) +10/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Files Changed

  • extensions/common/api/_api_features.json
From 7446ed80981ba25be896e4609ddb11226eae19d7 Mon Sep 17 00:00:00 2001
From: Tim Judkins <tjudkins@chromium.org>
Date: Thu, 30 Apr 2026 15:55:25 -0700
Subject: [PATCH] [Extensions] Add an allowlist for metricsPrivate.getHistogram

This function originally belonged to the autotestPrivate namespace, but
was moved to metricsPrivate in order to make it available for some
lacros tests[1]. Since the lacros tests are no longer around, the only
caller to this function should be the autotest extension, so this CL
restricts it with an Allowlist.

Eventually we should actually move this function back to the
autotestPrivate namespace, but that requires some changes on the
ChromeOS side first in the Autotest extension itself.
crbug.com/508312478 has been filed to track that.

Note: in reality, the autotest extension is loaded with
--allowlisted-extension-id, which lets it bypass the feature check, so
really this new feature definition is more just to restrict the function
under normal conditions.

[1] https://chromium-review.googlesource.com/c/chromium/src/+/2108219

Fixed: 502256049
Change-Id: I3d9dbdc869b3fe2118af2a2534c78c24353e086f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7805284
Reviewed-by: Devlin Cronin <rdevlin.cronin@chromium.org>
Commit-Queue: Tim <tjudkins@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1623545}
---

diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json
index 86111029..e0abe3d 100644
--- a/extensions/common/api/_api_features.json
+++ b/extensions/common/api/_api_features.json
@@ -433,6 +433,16 @@
     "channel": "stable",
     "source": "metricsPrivate"
    },
+  "metricsPrivate.getHistogram": {
+    "allowlist": [
+      // The Autotest extension is loaded with --allowlisted-extension-id, so it
+      // technically bypasses this check, but this entry is included anyway to
+      // restrict access to this function under normal circumstances.
+      // TODO(crbug.com/508312478): getHistogram should be moved back to the
+      // autotestPrivate namespace.
+      "0D209B5E4401BB8E7873B5AB5B1346A1CB067015"  // ChromeOS Autotest extension
+    ]
+  },
   "metricsPrivate.getIsCrashReportingEnabled": {
     "allowlist": [
       // This function inherits the extension restrictions of metricsPrivate,
Loading diff…

Original Bug Report

reported by vm...@google.com

Info leak of global UMA data via metricsPrivate.getHistogram in chrome-untrusted://

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 Chrome Security team.

Overview: The metricsPrivate.getHistogram API is inadvertently exposed to several chrome-untrusted:// contexts due to missing explicit feature restrictions. A compromised renderer can call this API to fetch process-global UMA histogram data, bypassing profile isolation. This allows an attacker to fingerprint user activity across all origins, including Incognito mode.

Affected files:

  • extensions/common/api/_api_features.json
  • extensions/browser/api/metrics_private/metrics_private_api.cc
  • extensions/browser/extension_function_dispatcher.cc
  • content/browser/metrics/histogram_synchronizer.cc

Estimated timestamp from git blame: 2025-12-01

Summary

An overly broad API grant in extensions/common/api/_api_features.json exposes the metricsPrivate.getHistogram function to several chrome-untrusted:// origins. Because chrome-untrusted:// surfaces handle untrustworthy content and are treated as potentially compromised, exposing an API that retrieves global browser state allows an attacker with renderer code execution to leak sensitive information that crosses Site Isolation and profile boundaries.

Technical Details

In extensions/common/api/_api_features.json, a ComplexFeature entry for metricsPrivate grants the namespace to contexts: ["webui_untrusted"] for a list of chrome-untrusted:// URLs (e.g., media-app, lens-overlay, boca-app).

The developer’s intent was likely to restrict child methods using the "default_parent": true block, which restricts access to privileged_extension. However, default_parent is only processed at compile time by feature_compiler.py for explicitly listed child features. Because metricsPrivate.getHistogram is entirely omitted from the feature JSON files, it bypasses this compile-time logic.

At runtime, when the renderer’s API bindings check if metricsPrivate.getHistogram is available, ExtensionAPI::IsAvailable falls back to querying the parent metricsPrivate namespace. Since metricsPrivate is a ComplexFeature, its availability check returns true if any of its underlying rules match. Thus, the webui_untrusted rule satisfies the check, and the browser’s ExtensionFunctionDispatcher grants permission.

When metricsPrivate.getHistogram is called, MetricsPrivateGetHistogramFunction::Run() performs the following actions:

  1. Calls content::FetchHistogramsAsynchronously(), which uses HistogramSynchronizer and HistogramController to broadcast a collection request to all live child processes (renderers, GPU, etc.).
  2. The returned data is merged synchronously into the browser process’s global base::StatisticsRecorder.
  3. The requested histogram’s sum and bucket counts are returned to the caller.

Because histograms in Chromium are process-global, they are not partitioned by profile. The returned data aggregates samples from all regular and Incognito profiles.

Potential Exploitation Steps

(Note: These are suggested steps; our tooling agent does not yet execute code to provide a working PoC.)

  1. An attacker gains arbitrary code execution in a chrome-untrusted:// renderer (e.g., by exploiting a Blink vulnerability triggered via a malicious file in the Media App).
  2. From the compromised renderer context, the attacker executes JavaScript: chrome.metricsPrivate.getHistogram('Blink.UseCounter.Features', (res) => { ... });.
  3. The browser process fetches global histogram data and returns it to the attacker.
  4. By repeatedly polling sensitive histograms (such as network error codes, password manager metrics, or Blink use counters) and observing the deltas between counts, the attacker can precisely track user activity, infer visited cross-origin websites, and monitor Incognito mode behavior.

Suggested Fix

Explicitly define metricsPrivate.getHistogram in extensions/common/api/_api_features.json to restrict its contexts to privileged_extension (and possibly specific WebUIs that genuinely need it). Alternatively, add an explicit validation check in MetricsPrivateGetHistogramFunction::Run to ensure the caller has the necessary permissions or an associated extension ID.

Evaluated with Chrome root at commit: 096fc8fdbfacf2546485756d03f160a3d04fcc9b


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