CVE-2025-11212
Overview
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
DisplayMediaAccessHandlerindisplay_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_FEATUREadded by the patch,FEATURE_DISABLED_BY_DEFAULTin code and enabled for desktop platforms only viafieldtrial_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.
getDisplayMedia request whose application title is longer than 255 characters, before the picker is shown.Attack Path
- 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).
- Call `getDisplayMedia`
The page asks for screen, window or tab capture, which sends the request to
DisplayMediaAccessHandlerin the browser process. - 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). - 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
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
| Function | Change | Notes |
|---|---|---|
Extensionchrome/browser/media/webrtc/display_media_access_handler.h |
modified | |
DisplayMediaAccessHandlerchrome/browser/media/webrtc/display_media_access_handler.h |
modified | |
TEST_Fchrome/browser/media/webrtc/display_media_access_handler_unittest.cc |
modified |
Files Changed
chrome/browser/media/webrtc/display_media_access_handler.ccchrome/browser/media/webrtc/display_media_access_handler.hchrome/browser/media/webrtc/display_media_access_handler_unittest.cctesting/variations/fieldtrial_testing_config.json
Audit Directions
- Unbounded origin strings in permission UILook at other permission and capture prompts (camera/mic,
getDisplayMediatab-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 mitigationsCheck whether checks like this one, which ship
FEATURE_DISABLED_BY_DEFAULTand 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).
Patch
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": [
Regression Test / PoC
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,
Original Bug Report
Screen Share Dialog - Domain Spoof (Similar to permission prompt)
Steps to reproduce the problem
- Open the vulnerable page in long domain name (possible above 75)
- click the button to start screenshare
- you can see the domain name is truncated and diff domain is showed
To reproduce this issue locally we can follow the below steps
- Add a below entry in hosts file 127.0.0.1 google.drivelogin.accounts.login.safeoauth.truested.newsafelogin.google.com.finallargedomain.com
- place the attached files in one folder and start the server by running python3 filename.py
- Now visit https://googledrivelogin.accounts.logins.safeoauth.truested.newsafelogins.google.com.finallargedomain.com:4443/poc.html in latest chrome
- 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