High watch CVSS 7.5 chrome Policy Bypass 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
7.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in Passwords
DescriptionInsufficient policy enforcement in Passwords
ComponentPasswords
Bug ClassPolicy Bypass
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker497632199
Fix commite3e793570c81 (chromium/src) +9/-4
CWECWE-862
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-03-30 (per vendor advisory)
Fix landed2026-03-30 (public git)
Disclosed2026-05-12
Patch gap43 days — fix public in source before the advisory

Background

`PasswordManagerDriver`
the per-frame object that mediates password-manager communication between the browser and a specific RenderFrameHost; each frame (including subframes) has its own driver.
`PasswordGenerationPopupControllerImpl`
the browser-side controller for the password-generation popup; it is bound to the frame/driver that opened it.
`ContentPasswordManagerDriver::GetForRenderFrameHost(rfh)`
resolves the driver for the frame that actually sent the current Mojo message.
Mojo `RenderFrameHostReceiverSet`
routes renderer IPC such that the target rfh identifies which frame the call came from.

Root Cause Analysis

The patched method in chrome_password_manager_client.cc (enclosing function name not shown in the @@ -1583,15 +1583,16 @@ hunk; it is the Mojo endpoint that updates the generation popup, per the commit subject) received a generated-password update over Mojo from some frame’s rfh, but then updated the generation popup based only on if (popup_controller_) — never checking that the popup actually belonged to the frame that sent the update. Any frame with a live popup_controller_ could therefore drive popup_controller_->UpdateGeneratedPassword(password_value) for a popup owned by a different frame.

The patch RELAXES nothing and TIGHTENS the guard: it computes driver (via ContentPasswordManagerDriver::GetForRenderFrameHost(rfh)) before the popup update and requires popup_controller_->driver().get() == driver before calling UpdateGeneratedPassword, so the update only applies when the popup’s own driver matches the calling frame’s driver. A new driver() accessor on PasswordGenerationPopupControllerImpl exposes the controller’s driver_ WeakPtr for that comparison. This closes the cross-frame mismatch while leaving the subsequent CHECK(driver) (the frame must be live) intact.

Key insight
The core mistake was updating the shared password-generation popup on any incoming driver without confirming the popup belonged to the requesting frame; the fix adds a popup_controller_->driver().get() == driver equality check so only the owning frame can update it.

Attack Path

  1. Open a generation popup The user (or an attacker-influenced frame) causes a password-generation popup to appear, so popup_controller_ is live and bound to one frame’s driver.
  2. Send an update from another frame A different frame (e.g. a cross-origin subframe) sends a generated-password update over Mojo, arriving with its own rfh.
  3. Pre-patch unconditional update The handler sees popup_controller_ is non-null and calls UpdateGeneratedPassword(password_value) without checking the driver, so the second frame’s value is applied to the first frame’s popup.
  4. Policy bypass The frame that does not own the popup influences the generated password shown/populated for the owning frame, bypassing the per-frame enforcement the advisory describes as insufficient policy enforcement.

Impact Assessment

A frame that does not own the active password-generation popup could update that popup’s generated password, violating the per-frame binding and enabling cross-frame interference with password generation in the browser process UI. The advisory rates this high severity (insufficient policy enforcement in Passwords); any stronger consequence such as credential theft is (inferred) and not demonstrated by the diff, and no in-the-wild status is stated. Precondition: a live generation popup and the ability for another frame to send the update IPC.

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

Audit Directions

  • Shared UI, per-frame data
    Audit browser-side singletons/controllers (popups, bubbles, infobars) updated from frame-scoped IPC; each update should verify the target frame/driver matches the UI’s owner.
  • Driver/frame identity on Mojo endpoints
    For handlers reached via RenderFrameHostReceiverSet, confirm they compare the resolved driver for rfh against the object they mutate rather than assuming a single active frame.
  • Null-but-not-matching guards
    Flag checks that only test existence (if (controller_)) where identity is what matters; existence is not ownership.
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