Medium watch CVSS 6.3 chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
6.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Media
DescriptionInappropriate implementation in Media
ComponentMedia
Sub-componentUI>Browser>MediaCapture
Bug ClassLogic Error
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.
Tracker420734141
Fix commit74e314f5f71c (chromium/src) +78/-0
CWECWE-451
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L
CISA KEVNot listed
CreditedAmeen Basha M K
Reported2025-05-28 · bounty $2000 (per vendor advisory)
Fix landed2025-09-02 (public git)
Disclosed2025-09-30
Patch gap28 days — fix public in source before the advisory

Background

`getDisplayMedia`
the web API that lets a page ask the user to pick a screen, window or tab to capture, brokered in the browser process by DisplayMediaAccessHandler in display_media_access_handler.cc.
`GetApplicationTitle`
an existing helper in the access handler that derives the requesting application’s display name (the site’s domain) from the WebContents; this string is what the capture-permission UI identifies the requester by.
`kDisplayMediaRejectLongDomains`
a new BASE_FEATURE added by the patch, FEATURE_DISABLED_BY_DEFAULT in code and enabled for desktop platforms only via fieldtrial_testing_config.json, that gates the new length check.
`MediaStreamRequestResult::INVALID_STATE`
the rejection code the handler now returns (via RejectRequest) when the domain is too long.

Root Cause Analysis

Before the patch, the browser-side getDisplayMedia handler in display_media_access_handler.cc accepted a capture request from any committed origin no matter how long its domain was, and went on to show the capture picker with that domain as the requester’s name. URL parsing will accept hostnames well beyond DNS limits (the regression test commits https:// plus 256 'a' characters, a single label far longer than DNS’s 63-byte limit). So nothing stopped a site from presenting an arbitrarily long name to the capture UI.

The patch adds an early guard right after the DCHECK(web_contents) at the top of the request-processing method (the hunk does not show the method’s name). If the feature is enabled and GetApplicationTitle(web_contents).size() > 255u, it calls RejectRequest(web_contents, MediaStreamRequestResult::INVALID_STATE) and returns before any UI is built (the ui_web_contents / TOOLKIT_VIEWS path that follows).

The patch does not say what harm the long domain caused. The likeliest reading is that a long name could be elided or laid out in the picker or sharing indicators so that the real requesting origin was hidden or misrepresented (inferred). The commit comment notes that 255 matches the punycode-encoded limit for real domains, so legitimate sites should not be affected.

Key insight
The display-capture permission flow trusted the requesting site’s domain string to fit in its security UI, and never limited its length. The fix tightens the flow by refusing (behind a feature flag) any getDisplayMedia request whose application title is longer than 255 characters, before the picker is shown.

Attack Path

  1. Host page on an over-long domain The attacker serves content from a hostname longer than 255 characters, which URL parsing accepts even though no DNS-valid name would be that long (as the 256-character test URL shows). In practice this needs a resolvable name, for example through wildcard DNS with many labels (inferred).
  2. Call `getDisplayMedia` The page asks for screen, window or tab capture, which sends the request to DisplayMediaAccessHandler in the browser process.
  3. Permission UI built from the long title Before the fix, the handler went on to build the capture UI. GetApplicationTitle(web_contents) gives the site’s name in that UI (the patch shows the length check on it).
  4. User misled about the requester Because of the long name, the user may not be able to see or correctly identify which origin is asking to capture their screen, and may grant capture they would otherwise refuse (inferred).

Impact Assessment

The likely impact is security-UI spoofing or obscuring in the browser’s screen-capture permission prompt, which could lead a user to grant screen, window or tab capture to an origin they did not mean to trust (inferred). There is no memory corruption and no direct privilege gain in the patch. Exploitation needs ordinary web content in a stock build plus a user gesture: the user has to choose a capture source and confirm. It also needs the attacker to control a hostname longer than 255 characters. No compromised renderer is needed. The vendor severity is Medium (“Inappropriate implementation in Media”), and the metadata gives no in-the-wild exploitation status. Note that the mitigation is behind kDisplayMediaRejectLongDomains, which is disabled by default in code and enabled through field-trial configuration. Builds where the feature is not turned on keep the old behavior.

Changed Functions

FunctionChangeNotes
Extension
chrome/browser/media/webrtc/display_media_access_handler.h
modified
DisplayMediaAccessHandler
chrome/browser/media/webrtc/display_media_access_handler.h
modified
TEST_F
chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
modified

Files Changed

  • chrome/browser/media/webrtc/display_media_access_handler.cc
  • chrome/browser/media/webrtc/display_media_access_handler.h
  • chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
  • testing/variations/fieldtrial_testing_config.json

Audit Directions

  • Unbounded origin strings in permission UI
    Look at other permission and capture prompts (camera/mic, getDisplayMedia tab-share infobars, notifications, File System Access, WebUSB/HID choosers) that show a hostname or app title. Check that they bound its length or elide it from the security-relevant end, such as the eTLD+1, rather than truncating the part that identifies the origin.
  • DNS-invalid hosts accepted by `GURL`
    Find places that assume hostnames follow DNS limits (253-character names, 63-byte labels) when deciding layout, storage or comparisons, since URL parsing lets much longer hosts through.
  • Feature-gated security mitigations
    Check whether checks like this one, which ship FEATURE_DISABLED_BY_DEFAULT and are turned on only through field trials, are actually enabled on every channel and platform, Android included (the trial config here lists only desktop platforms).
From 74e314f5f71cc35cc723bbfe940c09d2559a764a Mon Sep 17 00:00:00 2001
From: Tove Petersson <tovep@chromium.org>
Date: Tue, 02 Sep 2025 06:02:50 -0700
Subject: [PATCH] Reject getDisplayMedia calls from domains longer than 255 characters

This change is gated by the DisplayMediaRejectLongDomains feature-flag.

Bug: 420734141
Change-Id: I6e117c4a4ae27786e2096a0da8bb0cb2e8cf6ae7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6905222
Reviewed-by: Elad Alon <eladalon@chromium.org>
Commit-Queue: Tove Petersson <tovep@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1509516}
---

diff --git a/chrome/browser/media/webrtc/display_media_access_handler.cc b/chrome/browser/media/webrtc/display_media_access_handler.cc
index 860c3f27..3cf02f53 100644
--- a/chrome/browser/media/webrtc/display_media_access_handler.cc
+++ b/chrome/browser/media/webrtc/display_media_access_handler.cc
@@ -63,6 +63,10 @@
 #include "chrome/browser/glic/host/guest_util.h"
 #endif
 
+BASE_FEATURE(kDisplayMediaRejectLongDomains,
+             "DisplayMediaRejectLongDomains",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 namespace {
 using ::blink::mojom::MediaStreamRequestResult;
 using ::content::DesktopMediaID;
@@ -491,6 +495,17 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK(web_contents);
 
+  // Reject captures for domains with more than 255 characters.
+  //
+  // Note, this check does not fully account for international characters, but
+  // since the puny-encodings of international domains are limited to 255 bytes,
+  // it is unlikely that valid domains are excluded by this check.
+  if (base::FeatureList::IsEnabled(kDisplayMediaRejectLongDomains) &&
+      GetApplicationTitle(web_contents).size() > 255u) {
+    RejectRequest(web_contents, MediaStreamRequestResult::INVALID_STATE);
+    return;
+  }
+
   WebContents* ui_web_contents = web_contents;
 
 #if defined(TOOLKIT_VIEWS)
diff --git a/chrome/browser/media/webrtc/display_media_access_handler.h b/chrome/browser/media/webrtc/display_media_access_handler.h
index 9d8a877..d7a7151 100644
--- a/chrome/browser/media/webrtc/display_media_access_handler.h
+++ b/chrome/browser/media/webrtc/display_media_access_handler.h
@@ -25,6 +25,8 @@
 class Extension;
 }
 
+BASE_DECLARE_FEATURE(kDisplayMediaRejectLongDomains);
+
 // MediaAccessHandler for getDisplayMedia API, see
 // https://w3c.github.io/mediacapture-screen-share.
 class DisplayMediaAccessHandler : public CaptureAccessHandlerBase,
diff --git a/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc b/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
index 4e834c2..e86da0b5 100644
--- a/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
+++ b/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
@@ -9,6 +9,7 @@
 #include <string>
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
@@ -334,6 +335,48 @@
   EXPECT_EQ(0u, blink::CountDevices(devices));
 }
 
+TEST_F(DisplayMediaAccessHandlerTest, MaxLengthDomainAccepted) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(kDisplayMediaRejectLongDomains);
+
+  std::unique_ptr<content::NavigationSimulator> navigation =
+      content::NavigationSimulator::CreateBrowserInitiated(
+          GURL("https://" + std::string(255, 'a')), web_contents());
+  navigation->Commit();
+
+  blink::mojom::MediaStreamRequestResult result;
+  blink::mojom::StreamDevices devices;
+  ProcessRequest(content::DesktopMediaID(content::DesktopMediaID::TYPE_WINDOW,
+                                         content::DesktopMediaID::kFakeId),
+                 &result, devices, false /* request_audio */);
+
+  EXPECT_THAT(
+      result,
+      testing::AnyOf(
+#if BUILDFLAG(IS_MAC)
+          // TODO(crbug.com/40802122): Fix screen-capture permissions on mac.
+          blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED_BY_SYSTEM,
+#endif
+          blink::mojom::MediaStreamRequestResult::OK));
+}
+
+TEST_F(DisplayMediaAccessHandlerTest, OverMaxLengthDomainRejected) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(kDisplayMediaRejectLongDomains);
+
+  std::unique_ptr<content::NavigationSimulator> navigation =
+      content::NavigationSimulator::CreateBrowserInitiated(
+          GURL("https://" + std::string(256, 'a')), web_contents());
+  navigation->Commit();
+
+  blink::mojom::MediaStreamRequestResult result;
+  blink::mojom::StreamDevices devices;
+  ProcessRequest(content::DesktopMediaID(content::DesktopMediaID::TYPE_WINDOW,
+                                         content::DesktopMediaID::kFakeId),
+                 &result, devices, false /* request_audio */);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::INVALID_STATE, result);
+}
+
 #if BUILDFLAG(IS_CHROMEOS)
 TEST_F(DisplayMediaAccessHandlerTest, DlpRestricted) {
   const content::DesktopMediaID media_id(content::DesktopMediaID::TYPE_SCREEN,
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index d1acfd9..55485ba1 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -9248,6 +9248,24 @@
             ]
         }
     ],
+    "DisplayMediaRejectLongDomains": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "DisplayMediaRejectLongDomains"
+                    ]
+                }
+            ]
+        }
+    ],
     "DlpRegionalizedEndpoints": [
         {
             "platforms": [
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc b/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
index 4e834c2..e86da0b5 100644
--- a/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
+++ b/chrome/browser/media/webrtc/display_media_access_handler_unittest.cc
@@ -9,6 +9,7 @@
 #include <string>
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/run_loop.h"
@@ -334,6 +335,48 @@
   EXPECT_EQ(0u, blink::CountDevices(devices));
 }
 
+TEST_F(DisplayMediaAccessHandlerTest, MaxLengthDomainAccepted) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(kDisplayMediaRejectLongDomains);
+
+  std::unique_ptr<content::NavigationSimulator> navigation =
+      content::NavigationSimulator::CreateBrowserInitiated(
+          GURL("https://" + std::string(255, 'a')), web_contents());
+  navigation->Commit();
+
+  blink::mojom::MediaStreamRequestResult result;
+  blink::mojom::StreamDevices devices;
+  ProcessRequest(content::DesktopMediaID(content::DesktopMediaID::TYPE_WINDOW,
+                                         content::DesktopMediaID::kFakeId),
+                 &result, devices, false /* request_audio */);
+
+  EXPECT_THAT(
+      result,
+      testing::AnyOf(
+#if BUILDFLAG(IS_MAC)
+          // TODO(crbug.com/40802122): Fix screen-capture permissions on mac.
+          blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED_BY_SYSTEM,
+#endif
+          blink::mojom::MediaStreamRequestResult::OK));
+}
+
+TEST_F(DisplayMediaAccessHandlerTest, OverMaxLengthDomainRejected) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(kDisplayMediaRejectLongDomains);
+
+  std::unique_ptr<content::NavigationSimulator> navigation =
+      content::NavigationSimulator::CreateBrowserInitiated(
+          GURL("https://" + std::string(256, 'a')), web_contents());
+  navigation->Commit();
+
+  blink::mojom::MediaStreamRequestResult result;
+  blink::mojom::StreamDevices devices;
+  ProcessRequest(content::DesktopMediaID(content::DesktopMediaID::TYPE_WINDOW,
+                                         content::DesktopMediaID::kFakeId),
+                 &result, devices, false /* request_audio */);
+  EXPECT_EQ(blink::mojom::MediaStreamRequestResult::INVALID_STATE, result);
+}
+
 #if BUILDFLAG(IS_CHROMEOS)
 TEST_F(DisplayMediaAccessHandlerTest, DlpRestricted) {
   const content::DesktopMediaID media_id(content::DesktopMediaID::TYPE_SCREEN,
Loading diff…

Original Bug Report

reported by am...@gmail.com

Screen Share Dialog - Domain Spoof (Similar to permission prompt)

Steps to reproduce the problem

  1. Open the vulnerable page in long domain name (possible above 75)
  2. click the button to start screenshare
  3. you can see the domain name is truncated and diff domain is showed

To reproduce this issue locally we can follow the below steps

  1. Add a below entry in hosts file 127.0.0.1 google.drivelogin.accounts.login.safeoauth.truested.newsafelogin.google.com.finallargedomain.com
  2. place the attached files in one folder and start the server by running python3 filename.py
  3. Now visit https://googledrivelogin.accounts.logins.safeoauth.truested.newsafelogins.google.com.finallargedomain.com:4443/poc.html in latest chrome
  4. You can see the popup shows google.com at the end instead of finallargedomain.com

Problem Description

Note: I have already reported this case but was closed as obsolete without further investigation, i have added details here, kindly check the poc video and this comment https://issues.chromium.org/issues/420574505#comment4

Firefox Ref of similar attack in permission prompt: https://bugzilla.mozilla.org/show_bug.cgi?id=1920423

I have seen the similar case in chrome too but i wasnt able to get that bug reference

HI Team, Chrome latest on windows is vulnerable for screenshare domain name spoof which allows the attacker to trick the victim to share the screen to malicious site thinking it of as google.com

Tested on windows 11 pro, chrome canary 138.0.7204.4 (Official Build) canary (64-bit)

Summary

Screen Share Dialog - Domain Spoof (Similar to permission prompt)

Custom Questions

Reporter credit:

Ameen Basha M K

Additional Data

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

View on issue tracker