CVE-2026-7965
Overview
Background
- `devtools_frame_token`
- a per-frame token that identifies a frame to the DevTools protocol; a
DevToolsAgentHostis registered under this token in a process-global map. - Fenced frame
- an embedded frame type whose creation is requested from the renderer and processed by
RenderFrameHostImpl(the hunk sits in the fenced-frame creation path inrender_frame_host_impl.cc). - `DevToolsAgentHost::GetForId`
- a lookup that returns the
DevToolsAgentHostregistered globally for a given id string, i.e. across all tabs/instances rather than one frame tree. - `DevToolsAgentHostImpl::NotifyCreated`
- the registration step that inserts
thisinto the global DevTools instances map keyed byid_.
Root Cause Analysis
When a fenced frame was created, the browser validated the supplied devtools_frame_token for uniqueness only within the current tab, by iterating GetOutermostMainFrame()->frame_tree()->NodesIncludingInnerTreeNodes() and comparing each node’s devtools_frame_token(). The DevTools target map, however, is process-global, so a token that was already in use by a cross-origin page in another tab passed the per-tab check. NotifyCreated then inserted into the global map under a DCHECK (compiled out in release), so on collision it silently overwrote the existing DevToolsAgentHost entry, allowing a target hijack. The invariant that each devtools_frame_token maps to exactly one DevTools target globally was not enforced.
The fix replaces the per-tab loop with a global lookup DevToolsAgentHost::GetForId(devtools_frame_token.ToString()); if any host already exists for that token, it is rejected as a bad message and the renderer is terminated. As defense in depth, NotifyCreated’s DCHECK is upgraded to a CHECK so a bypassing collision crashes safely instead of silently overwriting.
GetForId (bad-message/kill on collision) and hardens the map insert with a CHECK.Attack Path
- Compromise a renderer
Obtain a renderer that can issue the fenced-frame creation IPC with an attacker-chosen
devtools_frame_token. - Supply a stolen token
Provide a
devtools_frame_tokenalready associated with a cross-origin page whose DevTools target lives in another tab/instance. - Evade per-tab validation The old check only scanned the current tab’s frame tree, so a token in use elsewhere was not detected and creation proceeded.
- Overwrite the global target map
NotifyCreatedinserted under the colliding id, silently replacing the existingDevToolsAgentHost(theDCHECKdid nothing in release), hijacking the DevTools session for the cross-origin target (inferred: cross-origin DevTools control/information disclosure).
Impact Assessment
devtools_frame_token during fenced-frame creation, gaining DevTools-level influence over a target it should not control (inferred, scoped to what the commit message describes as “target confusion”). The context is the browser process’s fenced-frame creation path, and the precondition is a compromised renderer able to send the creation IPC. The advisory rates this medium and classifies it as “Insufficient validation of untrusted input in DevTools”; no in-the-wild exploitation is stated.Files Changed
content/browser/devtools/devtools_agent_host_impl.cccontent/browser/renderer_host/render_frame_host_impl.cc
Audit Directions
- Scope mismatch between check and storeLook for uniqueness/validation checks scoped to a single frame tree or tab while the underlying registry is process-global (or vice-versa), especially for renderer-supplied identifiers.
- Release-stripped assertions guarding security invariantsAudit
DCHECKs that protect map inserts or overwrites of security-relevant state; these do nothing in production and should beCHECKs or explicit error handling. - Renderer-controlled tokensTrace other renderer-supplied frame/target tokens (DevTools, portals, prerender) to confirm the browser enforces global uniqueness before trusting them as map keys.
Patch
From a77ddcdbea95bd88b0534a0d33fcc7f2e30ab4a2 Mon Sep 17 00:00:00 2001 From: Danil Somsikov <[email protected]> Date: Thu, 02 Apr 2026 05:00:50 -0700 Subject: [PATCH] Fix DevTools Target Confusion vulnerability via Fenced Frames A compromised renderer can hijack DevTools sessions for cross-origin pages by supplying a stolen 'devtools_frame_token' during fenced frame creation. Previously, the browser improperly scoped token uniqueness validation to the current tab and silently overwrote the process-global DevTools target map on collision. This CL mitigates the vulnerability by: 1. Updating the validation in to check for global uniqueness across all DevTools instances using `DevToolsAgentHost::GetForId()`. If a collision is detected, the browser will now treat it as a Bad Message and terminate the malicious renderer process. 2. Hardening the map insertion logic in `DevToolsAgentHostImpl::NotifyCreated` by upgrading a `DCHECK` to a `CHECK`. This ensures that even if a collision somehow bypasses initial validation, the browser will crash safely in production rather than allowing a silent target hijack. Bug: 497255035 Change-Id: I9857e1378ab289be626e71aa12944ef8c9e9cb2f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7716666 Auto-Submit: Danil Somsikov <[email protected]> Reviewed-by: Andrey Kosyakov <[email protected]> Reviewed-by: Arthur Sonzogni <[email protected]> Commit-Queue: Danil Somsikov <[email protected]> Cr-Commit-Position: refs/heads/main@{#1609111} --- diff --git a/content/browser/devtools/devtools_agent_host_impl.cc b/content/browser/devtools/devtools_agent_host_impl.cc index e47209e..d7c727ed 100644 --- a/content/browser/devtools/devtools_agent_host_impl.cc +++ b/content/browser/devtools/devtools_agent_host_impl.cc @@ -556,7 +556,7 @@ } void DevToolsAgentHostImpl::NotifyCreated() { - DCHECK(!GetDevtoolsInstances().contains(id_)); + CHECK(!GetDevtoolsInstances().contains(id_)); GetDevtoolsInstances()[id_] = this; for (auto& observer : GetDevtoolsObservers()) observer.DevToolsAgentHostCreated(this); diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc index 3f5964a6..cb6aa69 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc @@ -200,6 +200,7 @@ #include "content/public/browser/content_browser_client.h" #include "content/public/browser/context_menu_params.h" #include "content/public/browser/cookie_access_details.h" +#include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/disallow_activation_reason.h" #include "content/public/browser/document_ref.h" #include "content/public/browser/document_service_internal.h" @@ -10586,17 +10587,12 @@ return; } - // Ensure the devtools frame token doesn't exist in the FrameTree for - // this tab. - for (FrameTreeNode* node : - GetOutermostMainFrame()->frame_tree()->NodesIncludingInnerTreeNodes()) { - if (node->current_frame_host()->devtools_frame_token() == - devtools_frame_token) { - bad_message::ReceivedBadMessage( - GetProcess(), - bad_message::RFHI_CREATE_FENCED_FRAME_BAD_DEVTOOLS_FRAME_TOKEN); - return; - } + // Ensure the devtools frame token doesn't exist globally. + if (DevToolsAgentHost::GetForId(devtools_frame_token.ToString())) { + bad_message::ReceivedBadMessage( + GetProcess(), + bad_message::RFHI_CREATE_FENCED_FRAME_BAD_DEVTOOLS_FRAME_TOKEN); + return; } // Inactive pages cannot create fenced frames. If the page is in the BFCache,
Original Bug Report
DevTools Target Confusion via Fenced Frame Token Collision
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A compromised renderer can hijack DevTools sessions for cross-origin pages by supplying a stolen devtools_frame_token during fenced frame creation. The browser improperly scopes token uniqueness validation to the current tab and silently overwrites the process-global DevTools target map on collision, allowing an attacker to intercept Chrome DevTools Protocol (CDP) commands.
Affected files:
content/browser/devtools/devtools_agent_host_impl.cccontent/browser/devtools/render_frame_devtools_agent_host.cccontent/browser/renderer_host/render_frame_host_impl.cccontent/browser/renderer_host/render_frame_proxy_host.cc
Estimated timestamp from git blame: 2026-01-06
Overview
A logic error exists in DevToolsAgentHostImpl where process-global DevTools target IDs can be overwritten due to insufficient validation during Fenced Frame creation. A compromised renderer can exploit this to hijack DevTools automation sessions (e.g., Puppeteer) connecting to cross-origin targets, leading to cross-origin data theft.
Vulnerability Details
The issue stems from a combination of three factors:
- Cross-WebContents Token Leak: When a renderer opens a cross-origin popup, the browser sends a
PageBroadcast.CreateRemoteMainFrameMojo IPC to the opener renderer so it can build a remote frame proxy. This IPC leaks the victim frame’sdevtools_frame_tokento the potentially compromised opener renderer. - Insufficient Validation Scope: When a renderer creates a Fenced Frame, it supplies a
devtools_frame_tokenvia theLocalFrameHost.CreateFencedFrameMojo IPC.RenderFrameHostImpl::CreateFencedFrameattempts to ensure this token is unique to prevent collisions. However, it only checks nodes within the current tab’sFrameTree(GetOutermostMainFrame()->frame_tree()->NodesIncludingInnerTreeNodes()). It fails to check for collisions across differentWebContents. - Silent Map Overwrite: When the DevTools agent host is created for the new fenced frame,
DevToolsAgentHostImpl::NotifyCreatedregisters it in a process-global map:Becausevoid DevToolsAgentHostImpl::NotifyCreated() { DCHECK(!GetDevtoolsInstances().contains(id_)); GetDevtoolsInstances()[id_] = this; // ... }DCHECKis compiled out in Release builds, the global map entry for the victim’s token is silently overwritten with a pointer to the attacker’s new fenced frame agent host.
Potential Attack Steps
Note: These are theoretical steps as our tooling does not yet have the ability to run code to produce a working proof-of-concept.
- An attacker gains code execution in a renderer process (e.g., via a V8 bug).
- The compromised renderer opens a cross-origin window to a sensitive site (e.g.,
window.open('https://victim.com')). - The browser sends a
CreateRemoteMainFrameIPC to the attacker’s renderer. The attacker intercepts this IPC and extracts the victim’sdevtools_frame_token. - The compromised renderer crafts a
LocalFrameHost.CreateFencedFrameMojo IPC to create a fenced frame in its own document, passing the stolen victim’sdevtools_frame_tokeninstead of a randomly generated one. - The browser’s validation passes because the victim is in a different
WebContents. - The browser creates a
RenderFrameDevToolsAgentHostfor the attacker’s fenced frame, which silently overwrites the victim’s entry in the globalGetDevtoolsInstances()map. - An automated DevTools client (like Puppeteer or a Chrome Extension) attempts to attach to the victim’s target ID.
DevToolsAgentHostImpl::GetForIdreturns the attacker’s fenced frame. The DevTools client unknowingly sends CDP commands (e.g.,Runtime.evaluateto inject scripts orInput.dispatchKeyEventto type passwords) into the attacker’s frame, allowing the attacker to steal credentials or hijack the session.
Suggested Fix
- Harden Map Insertion: In
DevToolsAgentHostImpl::NotifyCreated(), upgrade theDCHECKto aCHECK, or safely handle the collision without overwriting existing valid instances. - Enforce Global Uniqueness: Update the validation in
RenderFrameHostImpl::CreateFencedFrameto check the globalDevToolsAgentHost::GetForId()(or a truly global registry) to ensure the token doesn’t collide with any existing target across allWebContents. - Browser-Side Token Generation: Consider having the browser unconditionally generate the
devtools_frame_tokenfor Fenced Frames rather than trusting the token provided by the renderer via IPC.
Evaluated with Chrome root at commit: a9cbf6e8b275fe4147435aa905f3b7f5a656f5f0
Results from 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.