CVE-2026-8008
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
is_trusted_chrome/browser/devtools/protocol/page_handler.cc |
modified | |
ifchrome/browser/devtools/protocol/page_handler.cc |
modified |
Files Changed
chrome/browser/devtools/chrome_devtools_session.ccchrome/browser/devtools/protocol/page_handler.ccchrome/browser/devtools/protocol/page_handler.h
Patch
From d283fa55777a691531f3eceb8020dd890548e680 Mon Sep 17 00:00:00 2001 From: Danil Somsikov <[email protected]> Date: Wed, 01 Apr 2026 01:21:37 -0700 Subject: [PATCH] Restrict Page.setSPCTransactionMode and Page.setRPHRegistrationMode to trusted DevTools clients The experimental DevTools commands Page.setSPCTransactionMode and Page.setRPHRegistrationMode lacked trust restrictions, allowing untrusted DevTools clients (such as Chrome extensions with the `debugger` permission) to automatically accept dialogs without user interaction. For Page.setSPCTransactionMode, this bypassed the primary UI where users review transaction details (payee, amount) for Secure Payment Confirmation (SPC), leaving only a generic OS-level biometric prompt that lacks context and could potentially be exploited to authorize tampered payments. Similarly, Page.setRPHRegistrationMode could be exploited to silently accept custom protocol handler registration prompts, bypassing user consent. Fixed: 496426191, 496373088 Change-Id: I49cafe716e5c81c3715f8bde30e579d4cc9a01b8 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7707756 Commit-Queue: Danil Somsikov <[email protected]> Auto-Submit: Danil Somsikov <[email protected]> Reviewed-by: Andrey Kosyakov <[email protected]> Cr-Commit-Position: refs/heads/main@{#1608355} --- diff --git a/chrome/browser/devtools/chrome_devtools_session.cc b/chrome/browser/devtools/chrome_devtools_session.cc index e01701b..07560364 100644 --- a/chrome/browser/devtools/chrome_devtools_session.cc +++ b/chrome/browser/devtools/chrome_devtools_session.cc @@ -61,7 +61,8 @@ if (IsDomainAvailableToUntrustedClient<PageHandler>() || channel->GetClient()->IsTrusted()) { page_handler_ = std::make_unique<PageHandler>( - agent_host, agent_host->GetWebContents(), &dispatcher_); + agent_host, agent_host->GetWebContents(), &dispatcher_, + channel->GetClient()->IsTrusted()); } if (IsDomainAvailableToUntrustedClient<SecurityHandler>() || channel->GetClient()->IsTrusted()) { diff --git a/chrome/browser/devtools/protocol/page_handler.cc b/chrome/browser/devtools/protocol/page_handler.cc index c5c79bf..bc3122f4 100644 --- a/chrome/browser/devtools/protocol/page_handler.cc +++ b/chrome/browser/devtools/protocol/page_handler.cc @@ -37,8 +37,11 @@ PageHandler::PageHandler(scoped_refptr<content::DevToolsAgentHost> agent_host, content::WebContents* web_contents, - protocol::UberDispatcher* dispatcher) - : agent_host_(agent_host), web_contents_(web_contents->GetWeakPtr()) { + protocol::UberDispatcher* dispatcher, + bool is_trusted) + : agent_host_(agent_host), + web_contents_(web_contents->GetWeakPtr()), + is_trusted_(is_trusted) { protocol::Page::Dispatcher::wire(dispatcher, this); } @@ -86,6 +89,11 @@ protocol::Response PageHandler::SetSPCTransactionMode( const protocol::String& mode) { + if (!is_trusted_) { + return protocol::Response::ServerError( + "Permission denied: Page.setSPCTransactionMode requires a trusted " + "client"); + } if (!web_contents_) return protocol::Response::ServerError("No web contents to host a dialog."); @@ -117,6 +125,11 @@ if (!web_contents_) { return protocol::Response::ServerError("No web contents to host a dialog."); } + if (!is_trusted_) { + return protocol::Response::ServerError( + "Permission denied: Page.setRPHRegistrationMode requires a trusted " + "client"); + } custom_handlers::RphRegistrationMode rph_mode = custom_handlers::RphRegistrationMode::kNone; diff --git a/chrome/browser/devtools/protocol/page_handler.h b/chrome/browser/devtools/protocol/page_handler.h index 90b717f..4c2c12fc 100644 --- a/chrome/browser/devtools/protocol/page_handler.h +++ b/chrome/browser/devtools/protocol/page_handler.h @@ -31,7 +31,8 @@ public: PageHandler(scoped_refptr<content::DevToolsAgentHost> agent_host, content::WebContents* web_contents, - protocol::UberDispatcher* dispatcher); + protocol::UberDispatcher* dispatcher, + bool is_trusted); PageHandler(const PageHandler&) = delete; PageHandler& operator=(const PageHandler&) = delete; @@ -99,6 +100,7 @@ base::WeakPtr<content::WebContents> web_contents_; bool enabled_ = false; + const bool is_trusted_; base::WeakPtrFactory<PageHandler> weak_ptr_factory_{this}; };
Original Bug Report
Bypass of Secure Payment Confirmation dialog via Page.setSPCTransactionMode
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: The experimental DevTools command Page.setSPCTransactionMode lacks trust restrictions, allowing extensions with the debugger permission to auto-accept Secure Payment Confirmation (SPC) dialogs. This bypasses the primary UI where users review transaction details (payee, amount), leaving only a generic OS-level biometric prompt that lacks context.
Affected files:
chrome/browser/devtools/protocol/page_handler.ccchrome/browser/devtools/chrome_devtools_session.ccthird_party/blink/public/devtools_protocol/domains/Page.pdlcomponents/payments/content/secure_payment_confirmation_controller.cccomponents/payments/content/secure_payment_confirmation_app.cc
Estimated timestamp from git blame: 2025-12-05
Disclaimer
Please note: As an AI agent (Fortify), I do not currently have the ability to run code or build a working Proof of Concept. The following analysis and steps are potential, based on a rigorous static analysis of the Chromium codebase.
Description
The Secure Payment Confirmation (SPC) API relies on a browser-level dialog to securely display the transaction amount and payee to the user before they authorize the payment. However, the DevTools command Page.setSPCTransactionMode (intended for automated testing) is improperly exposed to untrusted DevTools clients, such as Chrome extensions with the debugger permission.
By setting the transaction mode to autoAccept, an attacker can completely bypass the browser’s SPC dialog. The user will only see the subsequent OS-level biometric prompt (e.g., Windows Hello or macOS Touch ID). Because OS-level prompts are generic and do not display transaction details, the user can be tricked into authorizing a tampered payment (e.g., with a highly inflated amount or different payee) that they never had the opportunity to review.
Technical Details
- Lack of Command Restriction: In
third_party/blink/public/devtools_protocol/domains/Page.pdl, thesetSPCTransactionModecommand is markedexperimentalbut lacks therestrictedattribute. - Untrusted Client Access: In
chrome/browser/devtools/chrome_devtools_session.cc,PageHandleris explicitly made available to untrusted clients. Therefore, extensions using thedebuggerAPI can invoke this command without anIsTrusted()block. - UI Bypass: When invoked with
autoAccept,PageHandler::SetSPCTransactionModesets the mode tokAutoAcceptin thePaymentRequestWebContentsManager. Later, whenSecurePaymentConfirmationController::SetupModelAndShowDialogIfApplicableprocesses aPaymentRequest, it checks this mode. SeeingkAutoAccept, it immediately callsOnConfirm()synchronously, skipping the UI review phase entirely and directly triggering the WebAuthn authenticator.
Potential Attacker Steps
- An attacker convinces a user to install a malicious Chrome extension requiring the
debuggerpermission. - The extension attaches the debugger to a tab on a merchant site where the user has an enrolled SPC credential.
- The extension sends the command to auto-accept SPC prompts:
chrome.debugger.sendCommand({tabId: targetTabId}, 'Page.setSPCTransactionMode', {mode: 'autoAccept'}) - The extension injects JavaScript (via
Runtime.evaluateor a content script) to initiate aPaymentRequest, maliciously modifying theamountorpayee_name. - The browser instantly bypasses the SPC dialog. The user only sees a generic OS-level biometric prompt (e.g., “Verify your identity”).
- Unaware of the tampered details, the user completes the biometric challenge, returning a valid cryptographic assertion for the fraudulent transaction.
Suggested Fix
The command should be restricted to trusted DevTools clients.
Modify third_party/blink/public/devtools_protocol/domains/Page.pdl to add the restricted attribute to setSPCTransactionMode:
experimental restricted command setSPCTransactionMode
Alternatively, enforce a strict IsTrusted() or equivalent security check within the implementation of PageHandler::SetSPCTransactionMode.
Evaluated with Chrome root at commit: 0eb4855bda702feaaa8b899336664f97e3df88b8
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. Please feel free to reach out to me if you have concerns or feedback.