Medium watch CVSS 8.8 chrome Input Validation 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient data validation in Extensions
DescriptionInsufficient data validation in Extensions
ComponentExtensions
Sub-componentPlatform>DevTools>Extensions
Bug ClassInput Validation
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.
Tracker376625003
Fix commitb50c1dbc6b70 (devtools/devtools-frontend) +9/-1
CWECWE-79 (Cross-site scripting)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedAnonymous
Reported2024-10-31 · bounty $1000 (per vendor advisory)
Fix landed2024-11-04 (public git)
Disclosed2025-01-14
Patch gap71 days — fix public in source before the advisory

Background

DevTools Extension API
the ExtensionAPI.ts surface that DevTools exposes to third-party extensions, gating access to page resources through checks like canAccessResource.
Prototype pollution
an attack where a script mutates a shared prototype object (e.g. URL.prototype) so that later property lookups return attacker-controlled values instead of the built-in ones.
`canAccessResource`
the guard function that decides whether an extension may read a given resource, permitting it unless the resource’s protocol is file: and file access is disallowed.
`URL.prototype.protocol`
a built-in accessor whose getter normally returns a URL’s scheme, but which can be redefined on the shared prototype at runtime.

Root Cause Analysis

The vulnerable path is canAccessResource, which computed a resource’s scheme with (new URL(resource.url)).protocol and allowed access whenever that value was not the literal string file:. Because .protocol is resolved dynamically through the URL.prototype.protocol accessor, an attacker running in the same realm could redefine that getter (prototype pollution) so it returns something other than file: even for a file:-scheme resource, defeating the intended check. The invariant violated is that the security decision must reflect the resource’s true scheme rather than a caller-influenceable property lookup.

The fix captures the genuine getter once at module load via Object.getOwnPropertyDescriptor(URL.prototype, 'protocol')?.get and invokes it directly through a helper getProtocol, so a later mutation of URL.prototype.protocol no longer affects the comparison. This restores the guarantee that the file: comparison is made against the real, unspoofable protocol value.

Key insight
The core mistake was reading a security-critical property (protocol) through the live prototype chain, which an attacker can pollute; the fix pins the original accessor getter at load time and calls it directly, so the check can no longer be subverted.

Attack Path

  1. Redefine the accessor Attacker-controlled code in the DevTools/extension realm overrides URL.prototype.protocol (or its getter) so that it no longer reports the true scheme.
  2. Reference a file resource The attacker triggers an extension API call referencing a resource whose real scheme is file:, which should be blocked when file access is not granted.
  3. Bypass the guard Inside canAccessResource, (new URL(resource.url)).protocol now returns the polluted value, so the !== 'file:' comparison evaluates true.
  4. Gain access The function returns true, granting the extension read access to the file:-scheme resource it should have been denied.

Impact Assessment

An attacker able to run script in the DevTools extension context can pollute URL.prototype.protocol to bypass the file: scheme restriction and read local file resources that allowFileAccess was meant to protect. This requires the ability to execute code in that realm and a resource resolving to a file: URL, and the gain is unauthorized local resource disclosure through the DevTools extension API rather than remote code execution.

Changed Functions

FunctionChangeNotes
if
front_end/models/extensions/ExtensionAPI.ts
modified

Files Changed

  • front_end/models/extensions/ExtensionAPI.ts

Audit Directions

  • Live property reads in security checks
    Look for guards that resolve DOM/JS built-in properties (protocol, hostname, origin, array/object methods) through the prototype chain when making trust decisions, as these are pollutable.
  • Unpinned built-in accessors
    Audit code paths that rely on URL, Array, Object, or other intrinsics without capturing their descriptors at load time, especially where extension or page script shares the realm.
  • String-comparison authorization
    Review any allow/deny logic that compares a dynamically derived string (scheme, extension, MIME) against a constant, since the derivation source may be attacker-influenced.
From b50c1dbc6b709196064bb26ee75b1ed6cee4f974 Mon Sep 17 00:00:00 2001
From: Danil Somsikov <dsv@chromium.org>
Date: Mon, 04 Nov 2024 12:09:10 +0000
Subject: [PATCH] Protect canAccessResource in DevTools API form prototype pollution

Bug: 376625003
Change-Id: Ib07a65da8f342c4727bceb6afcbd920bcfd07b81
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/5987430
Reviewed-by: Philip Pfaffe <pfaffe@chromium.org>
Commit-Queue: Danil Somsikov <dsv@chromium.org>
---

diff --git a/front_end/models/extensions/ExtensionAPI.ts b/front_end/models/extensions/ExtensionAPI.ts
index 54afa52..a346408 100644
--- a/front_end/models/extensions/ExtensionAPI.ts
+++ b/front_end/models/extensions/ExtensionAPI.ts
@@ -1146,9 +1146,17 @@
     },
   };
 
+  const protocolGet = Object.getOwnPropertyDescriptor(URL.prototype, 'protocol')?.get;
+  function getProtocol(url: string): string {
+    if (!protocolGet) {
+      throw new Error('URL.protocol is not available');
+    }
+    return protocolGet.call(new URL(url));
+  }
+
   function canAccessResource(resource: APIImpl.ResourceData): boolean {
     try {
-      return extensionInfo.allowFileAccess || (new URL(resource.url)).protocol !== 'file:';
+      return extensionInfo.allowFileAccess || getProtocol(resource.url) !== 'file:';
     } catch (e) {
       return false;
     }
Loading diff…

Original Bug Report

reported by ba...@gmail.com

Local file access restrictions in chrome.devtools can be bypassed through prototype manipulation.

Steps to reproduce the problem

  1. Install the attached extension on macOS, and disable “Allow access to file URLs”.
  2. Open devtools on the new tab opened by the extension.
  3. Edit the /etc/hosts file highlighted in the sources panel, and save the changes.
  4. Observe that extension pops up an alert with the new contents of this file.

Problem Description

Various APIs under chrome.devtools use the canAccessResource function (https://chromium.googlesource.com/devtools/devtools-frontend/+/refs/heads/main/front_end/models/extensions/ExtensionAPI.ts#1149) for checking if local resources should be accessible to a given extension. However, the checks in this function can currently be entirely bypassed by overriding URL.prototype’s protocol getter as is done in the attached POC. Since there are no additional checks on the ExtensionServer side when adding event listeners through chrome.devtools.inspectedWindow.onResourceContentCommitted.addListener, an extension can gain access to local resources this way if the user interacts with such resources in the sources panel.

Additional Comments

Previous issues relating to this include:

Summary

Local file access restrictions in chrome.devtools can be bypassed through prototype manipulation.

Additional Data

Category: Security
Chrome Channel: Stable
Regression: N/A

View on issue tracker