CVE-2026-11689
Overview
Files Changed
chrome/browser/password_manager/chrome_password_manager_client.cc
Patch
From 2d746a076298e2c2a3ac8a6566606c2cb79dc78c Mon Sep 17 00:00:00 2001
From: Rafal Godlewski <rgod@google.com>
Date: Fri, 29 May 2026 07:46:16 -0700
Subject: [PATCH] [Passwords] Add missing ChildProcessSecurityPolicy checks
Fixed: 517486004
Change-Id: I12798b90166685a07819e3fb0172247d6ce07901
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7885496
Reviewed-by: Maria Kazinova <kazinova@google.com>
Commit-Queue: Rafał Godlewski <rgod@google.com>
Cr-Commit-Position: refs/heads/main@{#1638470}
---
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index e545d78..3d4286e 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -1586,6 +1586,11 @@
const std::u16string& password_value) {
content::RenderFrameHost* rfh =
password_generation_driver_receivers_.GetCurrentTargetFrame();
+ if (!password_manager::bad_message::CheckChildProcessSecurityPolicyForURL(
+ rfh, form_data.url(),
+ BadMessageReason::CPMD_BAD_ORIGIN_PRESAVE_GENERATED_PASSWORD)) {
+ return;
+ }
if (!password_manager::bad_message::CheckFrameNotPrerendering(rfh)) {
return;
}
@@ -1615,6 +1620,11 @@
const autofill::FormData& form_data) {
content::RenderFrameHost* rfh =
password_generation_driver_receivers_.GetCurrentTargetFrame();
+ if (!password_manager::bad_message::CheckChildProcessSecurityPolicyForURL(
+ rfh, form_data.url(),
+ BadMessageReason::CPMD_BAD_ORIGIN_PASSWORD_NO_LONGER_GENERATED)) {
+ return;
+ }
if (!password_manager::bad_message::CheckFrameNotPrerendering(rfh)) {
return;
}
Original Bug Report
Missing ChildProcessSecurityPolicy check in PasswordGenerationDriver
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 PasswordGenerationDriver Mojo interface in ChromePasswordManagerClient does not validate that the calling renderer has the privilege to access or modify password data for the target URL. If a sandboxed iframe process is compromised, it can potentially exploit this missing validation to trigger password generation or presaving under its precursor origin, bypassing Site Isolation limits on sandboxed frames. This can be resolved by adding ChildProcessSecurityPolicy checks during message handling.
Affected files:
chrome/browser/password_manager/chrome_password_manager_client.cc
Estimated timestamp from git blame: 2021-08-11
Root Cause Analysis
In chrome/browser/password_manager/chrome_password_manager_client.cc, the browser binds and handles the autofill::mojom::PasswordGenerationDriver interface. While binding includes a check for credentialless frames:
void ChromePasswordManagerClient::BindPasswordGenerationDriver(
mojo::PendingAssociatedReceiver<autofill::mojom::PasswordGenerationDriver> receiver,
content::RenderFrameHost* rfh) {
if (rfh->IsCredentialless()) {
return;
}
...
}
there are no security checks against sandboxed processes (e.g., processes running with ProcessLock.is_sandboxed() == true under the kIsolateSandboxedIframes feature) during interface binding or message execution.
Specifically, the message handlers PresaveGeneratedPassword and PasswordNoLongerGenerated in ChromePasswordManagerClient process renderer-supplied forms without verifying process capabilities via ChildProcessSecurityPolicy (CPSP):
void ChromePasswordManagerClient::PresaveGeneratedPassword(
const autofill::FormData& form_data,
const std::u16string& password_value) {
content::RenderFrameHost* rfh =
password_generation_driver_receivers_.GetCurrentTargetFrame();
if (!password_manager::bad_message::CheckFrameNotPrerendering(rfh)) {
return;
}
...
password_manager_.OnPresaveGeneratedPassword(
driver,
password_manager::GetFormWithFrameAndFormMetaData(
password_generation_driver_receivers_.GetCurrentTargetFrame(),
form_data),
password_value);
}
In contrast, the sibling ContentPasswordManagerDriver interface performs validation using HasValidURL(), which delegates to bad_message::CheckChildProcessSecurityPolicyForURL() and kills the renderer process if it lacks the capability to access data for the target origin:
bool HasValidURL(content::RenderFrameHost* render_frame_host) {
GURL url = GetURLFromRenderFrameHost(render_frame_host);
if (!url.is_valid())
return false;
return password_manager::bad_message::CheckChildProcessSecurityPolicyForURL(
render_frame_host, url,
password_manager::BadMessageReason::CPMD_BAD_ORIGIN_FORM_SUBMITTED);
}
For sandboxed frames, ChildProcessSecurityPolicyImpl::IsAccessAllowedForSandboxedProcess restricts data access checks under AccessType::kCanAccessDataForCommittedOrigin by returning false. However, the lack of CPSP checks in the password generation pipeline allows Mojo messages to bypass this enforcement.
Potential Attack Scenario
An attacker with control of a compromised sandboxed iframe process (where IsolateSandboxedIframes is enabled) could theoretically execute the following sequence of steps:
- The compromised process binds to the un-gated
mojom::AutofillDriverinterface and sendsFormsSeencontaining a fake form structure. - The browser-side
AutofillDriverprocesses the form, retrieves the trusted precursor URL (https://victim.com), and updates its form cache. - The browser triggers server predictions, resulting in a call to
PasswordManager::ProcessAutofillPredictions, which creates aPasswordFormManagertracking the form for the sandboxed frame’s driver. - The compromised process binds to
mojom::PasswordGenerationDriverand sends aPresaveGeneratedPasswordmessage containing matching form data and an attacker-controlled password. - Due to the missing CPSP validation in
PresaveGeneratedPassword, the browser matches the existingPasswordFormManagerand proceeds to save/presave the attacker’s chosen credential in thePasswordStoreunder the precursor origin (https://victim.com).
Note: These are potential steps. Our security review tooling does not have the capability to run code or construct a working live exploit.
Suggested Fix
Update ChromePasswordManagerClient::PresaveGeneratedPassword and ChromePasswordManagerClient::PasswordNoLongerGenerated to validate the frame’s access capability against the target URL/origin using ChildProcessSecurityPolicy before forwarding to the password manager logic, terminating the renderer process via appropriate bad message reasons if the validation fails. For example:
GURL url = password_manager::GetURLFromRenderFrameHost(rfh);
if (!password_manager::bad_message::CheckChildProcessSecurityPolicyForURL(
rfh, url,
password_manager::BadMessageReason::CPMD_BAD_ORIGIN_PRESAVE_GENERATED_PASSWORD)) {
return;
}
Evaluated with Chrome root at commit: b1520ef4a76878853a31f0943b565e42060edec8
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.