Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in SiteIsolation
DescriptionInsufficient validation of untrusted input in SiteIsolation
ComponentSiteIsolation
Bug ClassLogic Error
Tracker497341787
Fix commitd0efcafb889f (chromium/src) +5/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-05

Files Changed

  • chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.cc
From d0efcafb889fec8a81c2fbc8abf305896c82a254 Mon Sep 17 00:00:00 2001
From: Minoru Chikamune <[email protected]>
Date: Thu, 02 Apr 2026 00:52:40 -0700
Subject: [PATCH] [LCPP] Restrict LCPCriticalPathPredictorHost to the outermost main frame

This CL adds a frame gate to LCPCriticalPathPredictorHost::Create to
ensure that only the outermost main frame can bind the interface.

Bug: 497341787
Change-Id: I1fd830f0b302810fb3afd00e61e25e4d9602084d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7724500
Reviewed-by: Yoshisato Yanagisawa <[email protected]>
Commit-Queue: Minoru Chikamune <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1609011}
---

diff --git a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.cc b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.cc
index e204d3ea..d2edc6b 100644
--- a/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.cc
+++ b/chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.cc
@@ -30,6 +30,11 @@
     content::RenderFrameHost* render_frame_host,
     mojo::PendingReceiver<blink::mojom::LCPCriticalPathPredictorHost>
         receiver) {
+  // Only valid for the main frame.
+  if (render_frame_host->GetParentOrOuterDocument()) {
+    return;
+  }
+
   // The object is bound to the lifetime of the |render_frame_host| and the mojo
   // connection. See DocumentService for details.
   new LCPCriticalPathPredictorHost(*render_frame_host, std::move(receiver));
Loading diff…

Original Bug Report

reported by [email protected]

Site Isolation bypass via missing frame gate in LCPCriticalPathPredictorHost

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

Overview: A compromised renderer in a cross-origin subframe can bind the LCPCriticalPathPredictorHost Mojo interface to inject arbitrary URLs into the parent frame’s prefetch database. When the user later navigates to the parent site, the browser automatically prefetches these injected URLs with full credentials, including SameSite=Strict cookies. This results in a persistent, credentialed blind GET CSRF against the embedder’s origin.

Affected files:

  • chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.cc
  • chrome/browser/page_load_metrics/observers/lcp_critical_path_predictor_page_load_metrics_observer.cc
  • chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_util.cc

Estimated timestamp from git blame: 2024-10-21

Summary

There is a potential Site Isolation bypass in the LCP Critical Path Predictor (LCPP) implementation. A compromised renderer hosting a cross-origin iframe can poison the LCPP prefetch database for the parent frame’s origin. By binding the LCPCriticalPathPredictorHost Mojo interface, which lacks a subframe check, an attacker can inject arbitrary same-site URLs. These URLs are then automatically prefetched with credentials (including SameSite=Strict cookies) when the user later navigates to the victim’s site.

Note: The following analysis is based on code review by an AI tooling agent. The steps below are a suggested/potential attack path, as we do not currently have the capability to run a live Proof of Concept.

Vulnerability Details

This issue arises from a combination of missing frame gating and incorrect page resolution during Mojo IPC handling:

  1. Missing Frame Gate: LCPCriticalPathPredictorHost::Create() (chrome/browser/predictors/lcp_critical_path_predictor/lcp_critical_path_predictor_host.cc) fails to verify that the binding RenderFrameHost is an outermost main frame. This contrasts with other predictors like NavigationPredictor::Create(), which safely reject subframes using GetParentOrOuterDocument().
  2. Page Resolution Escalation: When the compromised subframe renderer calls NotifyFetchedSubresource, the browser retrieves the Page object via render_frame_host().GetPage(). Because a Page represents the entire document tree, calling this on a subframe returns the outermost main frame’s Page.
  3. Unvalidated Data Append: The host then retrieves the LcpCriticalPathPredictorPageLoadMetricsObserver attached to the main frame and calls AppendFetchedSubresourceUrl. This blindly records the attacker’s URL into the main frame’s lcpp_data_inputs_ without verifying the origin of the reporting frame.
  4. Database Persistence: When the user navigates away or closes the tab, FinalizeLCP persists this data into the SQLite database, keying it to the main frame’s host.
  5. Credentialed Execution: Upon a future navigation to the victim site (e.g., via the Omnibox, where initiator_origin is null), the browser reads the poisoned LCPP entry. It constructs a prefetch request (network::ResourceRequest) with site_for_cookies and request_initiator set to the victim’s origin, resulting in a fully authenticated GET request that bypasses SameSite and Fetch Metadata protections.

Suggested Attack Steps

  1. An attacker sets up a malicious page that is loaded as a cross-origin iframe on a benign site (victim.com).
  2. The attacker exploits a standard renderer bug (e.g., in V8) to gain code execution within their cross-origin subframe.
  3. The compromised renderer requests to bind blink::mojom::LCPCriticalPathPredictorHost.
  4. The attacker sends a NotifyFetchedSubresource IPC message containing a sensitive GET endpoint on the parent origin (e.g., https://victim.com/sensitive_action?update=1).
  5. The user eventually closes the tab or navigates away, saving the poisoned prediction to the browser’s local LCPP database under the victim.com key.
  6. Days later, the user types victim.com into the Omnibox.
  7. The browser attempts to optimize the load, retrieves the poisoned LCPP data, and issues a prefetch request to https://victim.com/sensitive_action?update=1. Since the browser believes it is optimizing a first-party load, it attaches all first-party cookies (including SameSite=Strict), resulting in a successful GET CSRF.

Suggested Fix

Add a subframe check in LCPCriticalPathPredictorHost::Create() to prevent cross-origin iframes from binding the interface:

void LCPCriticalPathPredictorHost::Create(
    content::RenderFrameHost* render_frame_host,
    mojo::PendingReceiver<blink::mojom::LCPCriticalPathPredictorHost> receiver) {
  // Only valid for the main frame.
  if (render_frame_host->GetParentOrOuterDocument()) {
    return;
  }
  // ... existing code ...
}

Additionally, AppendFetchedSubresourceUrl should optionally validate that the reported subresource’s origin makes sense in the context of the reporting frame, though blocking the interface entirely for subframes is the most robust fix.

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.

View on issue tracker