Medium watch CVSS 9.8 chrome Policy Bypass 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
9.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in Network
DescriptionInsufficient policy enforcement in Network
ComponentNetwork
Sub-componentInternals>Network>Logging
Bug ClassPolicy Bypass
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker465466773
Fix commit0f08f8e435a8 (chromium/src) +14/-6
CWECWE-200 (Information exposure)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2025-12-02 (per vendor advisory)
Fix landed2025-12-04 (public git)
Disclosed2026-01-13
Patch gap40 days — fix public in source before the advisory

Background

NetLog
Chrome’s network event log, captured through chrome://net-export or the --log-net-log switch. It is often shared with developers or support for debugging, and its capture_mode decides whether sensitive data such as cookies and credentials is included.
`net::NetLogCaptureMode`
the capture level that logging helpers check to decide whether to strip privacy- or security-sensitive values. The default mode is meant to exclude them.
`HttpRequestHeaders::NetLogParams`
the standard helper for logging request headers. It takes a request line and the capture_mode and elides sensitive header values (for example, authentication and cookie headers) unless the capture mode allows them.
`CorsURLLoader`
the Network Service loader in services/network/cors/cors_url_loader.cc that enforces CORS and emits NetLog events describing the request, including preflight-related parameters.

Root Cause Analysis

In services/network/cors/cors_url_loader.cc, a NetLog parameter builder logged the request’s headers with net::NetLogStringValue(request.headers.ToString()). The builder is the function that ends just before NetLogPreflightRequiredParams; its name is not shown in the hunk. That serialises every header verbatim regardless of capture_mode, while the URL in the same dictionary was already passed through SanitizeUrlForNetLog(request.url, capture_mode). This broke the rule that NetLog output in a non-sensitive capture mode must not contain secrets, so header values that other request-header logging elides, such as credentials or cookies, were written into the log in plain text. The commit message says the purpose is “to have the same eliding as other request headers logging”.

The fix removes the raw headers and method fields. When request.url.is_valid(), it builds a request line with net::HttpUtil::GenerateRequestLine and logs request.headers.NetLogParams(request_line, capture_mode) under request_headers, which applies the capture-mode-dependent eliding. This tightens what gets logged; CORS enforcement itself does not change.

Key insight
The CORS loader’s NetLog parameters serialised raw request headers with ToString() and bypassed the capture_mode-aware eliding used everywhere else for request headers. Switching to HttpRequestHeaders::NetLogParams brings the CORS path into line with the standard sensitive-header stripping.

Attack Path

  1. Sensitive headers on a CORS-mode request A request goes through CorsURLLoader carrying sensitive headers, such as an Authorization header set by a page (inferred: the diff does not name specific headers).
  2. NetLog capture is active The user (or an administrator or support flow) records a NetLog with a default, non-sensitive capture mode, for example through chrome://net-export.
  3. Headers logged verbatim Before the fix, the CORS loader’s NetLog params included request.headers.ToString() in full, without the eliding that capture_mode would otherwise apply.
  4. Log is shared The user sends the log file, which they believe has secrets stripped, to a third party, who can then read the credentials or session tokens in it (inferred).

Impact Assessment

This is an information-disclosure and policy-enforcement gap in the Network Service’s logging, not a memory-safety bug. Sensitive request header values could end up in NetLog files recorded in a capture mode meant to omit them. Reaching it requires NetLog capture to be on, which is off by default and needs a user action such as chrome://net-export or a command-line flag. Exposure also requires the resulting log to reach someone else. Ordinary web content alone cannot read the log. Any account takeover from leaked credentials is (inferred). The vendor rates it medium severity (“Insufficient policy enforcement in Network”). The advisory text given here says nothing about in-the-wild exploitation.

Files Changed

  • services/network/cors/cors_url_loader.cc

Audit Directions

  • Raw header serialisation into logs
    Search for NetLogStringValue(...ToString()), HttpRequestHeaders::ToString() and HttpResponseHeaders raw dumps passed to NetLog or other diagnostics, and check that they go through NetLogParams or ElideHeaderValueForNetLog with the active capture_mode.
  • Partially sanitised log dictionaries
    Look for NetLog params that sanitise one field (such as the URL through SanitizeUrlForNetLog) but not others in the same dictionary, especially in Network Service components like CORS, preflight, Private Network Access and trust-token loaders.
  • Other diagnostic sinks
    Review DevTools protocol events, crash keys and chrome:// debugging pages that echo request or response headers, and confirm they apply the same sensitive-header policy as NetLog.
From 0f08f8e435a81e6d3e8442715a195d5efbe00ac5 Mon Sep 17 00:00:00 2001
From: Kenichi Ishibashi <bashi@chromium.org>
Date: Wed, 03 Dec 2025 23:52:59 -0800
Subject: [PATCH] Use HttpRequestHeaders::NetLogParams() for CORS preflight request netlog

To have the same eliding as other request headers logging.

Bug: 465466773
Change-Id: I1476d57902c3430c8698b68af849c8d52b1fb8ab
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7226493
Reviewed-by: mmenke <mmenke@chromium.org>
Commit-Queue: Kenichi Ishibashi <bashi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1553835}
---

diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc
index 119edf0..6bc7eff 100644
--- a/services/network/cors/cors_url_loader.cc
+++ b/services/network/cors/cors_url_loader.cc
@@ -23,7 +23,9 @@
 #include "net/cookies/cookie_partition_key.h"
 #include "net/cookies/cookie_setting_override.h"
 #include "net/cookies/cookie_util.h"
+#include "net/http/http_log_util.h"
 #include "net/http/http_status_code.h"
+#include "net/http/http_util.h"
 #include "net/log/net_log_util.h"
 #include "net/log/net_log_values.h"
 #include "net/shared_dictionary/shared_dictionary.h"
@@ -113,12 +115,18 @@
       break;
   }
 
-  return base::Value::Dict()
-      .Set("url", SanitizeUrlForNetLog(request.url, capture_mode))
-      .Set("method", request.method)
-      .Set("headers", net::NetLogStringValue(request.headers.ToString()))
-      .Set("is_revalidating", request.is_revalidating)
-      .Set("cors_preflight_policy", cors_preflight_policy);
+  auto params = base::Value::Dict()
+                    .Set("url", SanitizeUrlForNetLog(request.url, capture_mode))
+                    .Set("is_revalidating", request.is_revalidating)
+                    .Set("cors_preflight_policy", cors_preflight_policy);
+
+  if (request.url.is_valid()) {
+    std::string request_line = net::HttpUtil::GenerateRequestLine(
+        request.method, request.url, /*is_for_get_to_http_proxy=*/false);
+    params.Set("request_headers",
+               request.headers.NetLogParams(request_line, capture_mode));
+  }
+  return params;
 }
 
 base::Value::Dict NetLogPreflightRequiredParams(
Loading diff…

Original Bug Report

reported by ye...@google.com

NetLog: Unredacted sensitive credential in CORS Preflight Request Header

Security Bug


In chrome://net-export -> "Strip private information" mode, I found that the header of CORS Preflight requests contain credentials, e.g., Chrome Sync OAuth token: https://screenshot.googleplex.com/C6zW7g32E4bHuE4

Related code:

I imagine this information should be stripped away with NetLogRequestHeaders.

VERSION

  • Chrome Version:
    • Linux: 142.0.7444.175 stable
    • MacOS arm64: 142.0.7444.177 stable
  • Operating System: Tested on
    • gLinux: Linux 6.16.12-1rodete1-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.16.12-1rodete1 (2025-10-16) x86_64 GNU/Linux
    • MacOS: Darwin 24.6.0 Darwin Kernel Version 24.6.0: Wed Oct 15 21:09:41 PDT 2025; root:xnu-11417.140.69.703.14~1/RELEASE_ARM64_T8122 arm64

REPRODUCTION CASE

Go to chrome://net-export and record network traffic with the “Strip private information” option.

View on issue tracker