Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in Passwords
DescriptionInsufficient policy enforcement in Passwords
ComponentPasswords
Bug ClassLogic Error
Tracker497632199
Fix commite3e793570c81 (chromium/src) +9/-4
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-12

Changed Functions

FunctionChangeNotes
if
chrome/browser/password_manager/chrome_password_manager_client.cc
modified

Files Changed

  • chrome/browser/password_manager/chrome_password_manager_client.cc
  • chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
From e3e793570c810d3ae89eb741c4afff9e999a43b7 Mon Sep 17 00:00:00 2001
From: Vasilii Sukhanov <vasilii@chromium.org>
Date: Mon, 30 Mar 2026 09:30:19 -0700
Subject: [PATCH] Check correctness of PasswordManagerDriver before updating the pop-up for password generation.

Fixed: 497632199
Change-Id: I39dbd2d076e17e938e98582850072265d55d3ec0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7705508
Commit-Queue: Vasilii Sukhanov <vasilii@chromium.org>
Reviewed-by: Anna Tsvirchkova <atsvirchkova@google.com>
Cr-Commit-Position: refs/heads/main@{#1607152}
---

diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index d565e61..9d833d67 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -1583,15 +1583,16 @@
     return;
   }
 
+  PasswordManagerDriver* driver =
+      password_manager::ContentPasswordManagerDriver::GetForRenderFrameHost(
+          rfh);
+
 #if !BUILDFLAG(IS_ANDROID)
-  if (popup_controller_) {
+  if (popup_controller_ && popup_controller_->driver().get() == driver) {
     popup_controller_->UpdateGeneratedPassword(password_value);
   }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-  PasswordManagerDriver* driver =
-      password_manager::ContentPasswordManagerDriver::GetForRenderFrameHost(
-          rfh);
   // This method is called over Mojo via a RenderFrameHostReceiverSet; the
   // current target frame must be live.
   CHECK(driver);
diff --git a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
index 4245a5ad..6331192 100644
--- a/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
+++ b/chrome/browser/ui/passwords/password_generation_popup_controller_impl.h
@@ -121,6 +121,10 @@
   void OnZoomChanged(
       const zoom::ZoomController::ZoomChangedEventData& data) override;
 
+  base::WeakPtr<password_manager::PasswordManagerDriver> driver() const {
+    return driver_;
+  }
+
 #if defined(UNIT_TEST)
   PasswordGenerationPopupView* view() const { return view_; }
   void SetViewForTesting(PasswordGenerationPopupView* view) { view_ = view; }
Loading diff…

Original Bug Report

reported by rj...@google.com

Potential Cross-Origin Password Generation Poisoning in ChromePasswordManagerClient

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: A compromised renderer in a cross-origin iframe can potentially poison the shared password generation popup state for a victim origin. ChromePasswordManagerClient::PresaveGeneratedPassword fails to verify that the calling frame owns the active popup before updating its password value. If the user accepts the poisoned suggestion, the attacker-controlled password is saved for the victim’s account, leading to account takeover.

Affected files:

  • chrome/browser/password_manager/chrome_password_manager_client.cc
  • chrome/browser/ui/passwords/password_generation_popup_controller_impl.cc

Estimated timestamp from git blame: 2024-05-24

Vulnerability Details

ChromePasswordManagerClient is a WebContentsUserData instance, meaning a single instance is shared across all frames (main frame and iframes) within a given tab. When a password generation popup is triggered, its state is managed by a PasswordGenerationPopupControllerImpl instance, which is stored in the ChromePasswordManagerClient::popup_controller_ member.

The vulnerability exists in the autofill.mojom.PasswordGenerationDriver::PresaveGeneratedPassword Mojo IPC handler. When this IPC is received, the method updates the popup_controller_ with the provided password without verifying that the sender frame is the actual owner of the currently active popup.

void ChromePasswordManagerClient::PresaveGeneratedPassword(
    const autofill::FormData& form_data,
    const std::u16string& password_value) {
  content::RenderFrameHost* rfh =
      password_generation_driver_receivers_.GetCurrentTargetFrame();
  // ... checks ...

#if !BUILDFLAG(IS_ANDROID)
  if (popup_controller_) {
    // FLAW: No check is performed to ensure `rfh` owns the `popup_controller_`.
    // A cross-origin iframe can overwrite the victim's generated password.
    popup_controller_->UpdateGeneratedPassword(password_value);
  }
#endif
// ...
}

Because the popup_controller_ correctly retains the victim frame’s driver_ and form_data_ from when the popup was initially created, accepting the poisoned password binds the attacker’s string to the victim’s origin. This bypasses Site Isolation and results in an account takeover.

Potential Attack Steps

(Note: These are suggested/potential steps based on code analysis; our tooling agent does not yet have the ability to run live code to provide a working proof of concept.)

  1. An attacker compromises a cross-origin iframe (e.g., via a v8 bug) embedded on a victim’s registration or password-change page.
  2. The user interacts with a password field on the victim site, prompting the browser to display a password generation popup.
  3. The victim’s frame legitimately creates the popup, and the browser stores its controller in the shared ChromePasswordManagerClient::popup_controller_.
  4. The compromised iframe observes the interaction and sends a PresaveGeneratedPassword Mojo IPC to the browser, containing an attacker-known password.
  5. ChromePasswordManagerClient::PresaveGeneratedPassword receives the IPC from the attacker’s frame and unconditionally updates the shared popup_controller_ with the attacker’s password.
  6. The user clicks “Use suggested password” (or accepts it via keyboard), believing it to be a secure, browser-generated password for the victim site.
  7. The browser saves the attacker’s password under the victim’s origin and fills it into the form. The attacker can now log into the victim’s account remotely.

Suggested Fix

Before calling popup_controller_->UpdateGeneratedPassword(password_value), ChromePasswordManagerClient must verify that the incoming IPC originates from the same frame or driver that the popup_controller_ was instantiated for.

For example, the controller could expose its associated RenderFrameHost or PasswordManagerDriver, allowing ChromePasswordManagerClient to enforce an ownership check:

  if (popup_controller_ && 
      popup_controller_->driver().get() == 
      password_manager::ContentPasswordManagerDriver::GetForRenderFrameHost(rfh)) {
    popup_controller_->UpdateGeneratedPassword(password_value);
  }

Evaluated with Chrome root at commit: 876d480da1f794d87813cfa2e6ff4fcf9771e939


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.

View on issue tracker