Overview

Medium
Severity
4.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUI misrepresentation in Chromoting
DescriptionUI misrepresentation in Chromoting
ComponentChromoting
Sub-componentRemoting
Bug ClassSpoofing
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.
Tracker514059630
Fix commit940324ec1db9 (chromium/src) +185/-68
CWECWE-451
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:L
CISA KEVNot listed
CreditedGoogle
Reported2026-05-17 (per vendor advisory)
Fix landed2026-08-27 (public git)
Disclosed2026-09-22
Patch gap26 days — fix public in source before the advisory

Background

Chromoting / Chrome Remote Desktop host
The remoting host component that shares a local desktop to an authenticated remote client, injecting the client’s mouse and keyboard input into the local session.
Disconnect window
The always-visible “Stop Sharing” dialog (DisconnectWindowGtk) that acts as the local user’s primary security indicator and manual disconnect control while a session is shared.
`gtk_window_begin_move_drag`
A GTK API that initiates an interactive window move so the window follows subsequent pointer motion, invoked here from the dialog’s button-press-event handler OnButtonPress.
Input injection
Remote client pointer events are delivered into the host desktop as real mouse input, so anything the local pointer can do (including dragging a window) a remote client can also do.

Root Cause Analysis

The DisconnectWindowGtk dialog registered a button-press-event handler (OnButtonPress) that called gtk_window_begin_move_drag, making the entire dialog surface a freeform drag handle. Because remote clients inject mouse input directly into the host session, an authenticated remote client could press and drag anywhere on the dialog and relocate it, violating the security invariant that the disconnect indicator and “Stop Sharing” control must remain visible and reachable by the local user. By moving the window off-screen the client could hide the sole local signal that a session is active and the sole local means to end it.

The fix removes the OnButtonPress drag handler (and the decorative gripper drawn in OnDraw), replacing freeform positioning with a fixed, monitor-anchored placement computed by SetDialogPosition and constrained by kTopMargin / kBottomMargin. A dedicated toggle_button_ lets the local user flip the anchor between top and bottom via ToggleAlignment, and a kToggleCooldown (3s) cooldown_timer_ prevents a remote client from spamming the toggle to dodge user clicks.

Key insight
The core mistake was treating window placement as a trusted local-user action when host input is actually attacker-controllable, letting a remote client drag the security dialog off-screen; the fix removes freeform dragging and pins the dialog to fixed monitor-anchored positions with a rate-limited, discrete toggle.

Attack Path

  1. Establish an authenticated session A remote client connects and is authorized to control the shared desktop, gaining the ability to inject mouse input into the host session.
  2. Target the disconnect dialog The client moves the injected pointer over the “Stop Sharing” dialog, whose entire surface is a move handle because OnButtonPress calls gtk_window_begin_move_drag.
  3. Initiate a move drag The client issues a button-press-and-drag, causing the dialog window to follow the pointer as GTK performs an interactive move.
  4. Hide the security indicator The client drags the dialog off-screen (or onto an inactive monitor), removing the local user’s visual indication that sharing is active and the manual disconnect control. (inferred)
  5. Continue undetected access With the indicator hidden, the local user may not realize the session is active, so the attacker retains control of the desktop. (inferred)

Impact Assessment

An authenticated remote client gains UI misrepresentation on the host: it can relocate the local “Stop Sharing” security indicator and disconnect button out of view, undermining the local user’s awareness of and ability to end an active session. This runs in the context of the Chrome Remote Desktop host on Linux and requires an already-authorized/authenticated remote session. The advisory characterizes this as UI misrepresentation in Chromoting and does not state exploited-in-the-wild status; any further escalation of desktop control beyond obscuring the indicator is (inferred).

Changed Functions

FunctionChangeNotes
DisconnectWindowGtk
remoting/host/disconnect_window_linux.cc
modified
current_height_
remoting/host/disconnect_window_linux.cc
modified
if
remoting/host/disconnect_window_linux.cc
modified

Files Changed

  • remoting/host/disconnect_window_linux.cc

Audit Directions

  • Attacker-controllable input reaching trusted UI
    Audit remoting host windows and overlays for handlers (drag, resize, close, focus) reachable via injected remote mouse/keyboard input that could hide or move security indicators.
  • Security indicators that can be moved or occluded
    Review any always-on-top or notification surfaces across host platforms (disconnect_window_*) to confirm they cannot be repositioned off-screen, minimized, or covered by client-triggered actions.
  • Missing rate limits on UI controls
    Look for toggle/move/dismiss controls that lack a cooldown or debounce, since spammable actions let a remote party time interactions to defeat local user clicks.
From 940324ec1db92ebfb90059e638f73c0ba32ed42e Mon Sep 17 00:00:00 2001
From: Joe Downing <[email protected]>
Date: Thu, 27 Aug 2026 15:30:39 -0700
Subject: [PATCH] [remoting] Anchor Linux disconnect dialog to top/bottom edges

In Chrome Remote Desktop on Linux, the disconnect ("Stop Sharing")
dialog was previously draggable by clicking anywhere on its surface
(using gtk_window_begin_move_drag in button-press-event). Because remote
mouse input injected into the desktop session can move windows, an
authenticated remote client could initiate a move drag to reposition the
dialog off-screen, hiding the local user's primary security indicator
and disconnect button.

This change hardens the disconnect window against remote evasion while
preserving usability for local users:

1. Disables freeform window dragging by removing the button-press-event
   drag handler and the background gripper lines.
2. Adds a dedicated toggle button to the dialog layout displaying "▲" /
   "▼", allowing users to toggle the dialog between the bottom and top
   of the active monitor.
3. Implements a 3-second cooldown on the toggle action to prevent a
   remote client from spamming the toggle button to dodge user clicks.
4. Persists the selected anchor position across dialog instances.
5. Updates SetDialogPosition() to calculate the position using monitor
   geometry with fixed screen margins to remain static and unobstructed
   by auto-hiding or resizing panels/docks, and connects to screen
   change events.

TAG=agy
CONV=f2e64d7b-06ec-48fc-93c3-e9042357ef4e

Bug: 514059630
Change-Id: I46bc955da0ac4e07681c1c770ea1ab19cae437a8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8302253
Reviewed-by: Yuwei Huang <[email protected]>
Commit-Queue: Joe Downing <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1687607}
---
diff --git a/remoting/host/disconnect_window_linux.cc b/remoting/host/disconnect_window_linux.cc
index 8ebb804..0041965 100644
--- a/remoting/host/disconnect_window_linux.cc
+++ b/remoting/host/disconnect_window_linux.cc
@@ -11,9 +11,14 @@
 #include "base/check_op.h"
 #include "base/compiler_specific.h"
 #include "base/functional/bind.h"
+#include "base/location.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/notimplemented.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "remoting/base/string_resources.h"
 #include "remoting/host/client_session_control.h"
 #include "remoting/host/host_window.h"
@@ -24,6 +29,22 @@
 
 namespace {
 
+// The amount of time to wait before allowing another position toggle.
+constexpr base::TimeDelta kToggleCooldown = base::Seconds(3);
+
+// Margins from screen edges to ensure the dialog is not obscured by the top bar
+// or an auto-hiding dock/panel at the bottom.
+constexpr int kTopMargin = 40;
+constexpr int kBottomMargin = 60;
+
+enum class WindowAnchor {
+  kBottom,
+  kTop,
+};
+
+// Remembers the last selected anchor position across dialog instances.
+WindowAnchor g_current_anchor = WindowAnchor::kBottom;
+
 class DisconnectWindowGtk : public HostWindow {
  public:
   DisconnectWindowGtk();
@@ -40,14 +61,32 @@
  private:
   gboolean OnDelete(GtkWidget* window, GdkEvent* event);
   void OnClicked(GtkButton* button);
+  void OnToggleClicked(GtkButton* button);
   gboolean OnConfigure(GtkWidget* widget, GdkEventConfigure* event);
   gboolean OnDraw(GtkWidget* widget, cairo_t* cr);
-  gboolean OnButtonPress(GtkWidget* widget, GdkEventButton* event);
+#if !GTK_CHECK_VERSION(3, 90, 0)
+  void OnMonitorsChanged(GdkScreen* screen);
+#endif
+
+  // Positions the dialog window based on the current anchor.
+  void SetDialogPosition();
+
+  // Toggles the dialog anchor between top and bottom.
+  void ToggleAlignment();
+
+  // Re-enables the toggle button when cooldown expires.
+  void OnCooldownExpired();
+
+  // Updates the toggle button text according to the current anchor.
+  void UpdateToggleButtonText();
 
   // Used to disconnect the client session.
   base::WeakPtr<ClientSessionControl> client_session_control_;
 
+  base::OneShotTimer cooldown_timer_;
+
   raw_ptr<GtkWidget> disconnect_window_;
+  raw_ptr<GtkWidget> toggle_button_;
   raw_ptr<GtkWidget> message_;
   raw_ptr<GtkWidget> button_;
 
@@ -57,6 +96,8 @@
   int current_height_;
 
   std::vector<ScopedGSignal> signals_;
+
+  base::WeakPtrFactory<DisconnectWindowGtk> weak_factory_{this};
 };
 
 // Helper function for creating a rectangular path with rounded corners, as
@@ -102,44 +143,25 @@
   cairo_set_line_width(cairo_context, 6);
   AddRoundRectPath(cairo_context, width, height, kCornerRadius);
   cairo_stroke(cairo_context);
-
-  // Render the window-gripper.  In order for a straight line to light up
-  // single pixels, Cairo requires the coordinates to have fractional
-  // components of 0.5 (so the "/ 2" is a deliberate integer division).
-  double gripper_top = height / 2 - 10.5;
-  double gripper_bottom = height / 2 + 10.5;
-  cairo_set_line_width(cairo_context, 1);
-
-  double x = 12.5;
-  cairo_set_source_rgb(cairo_context, 0.70, 0.70, 0.70);
-  cairo_move_to(cairo_context, x, gripper_top);
-  cairo_line_to(cairo_context, x, gripper_bottom);
-  cairo_stroke(cairo_context);
-  x += 3;
-  cairo_move_to(cairo_context, x, gripper_top);
-  cairo_line_to(cairo_context, x, gripper_bottom);
-  cairo_stroke(cairo_context);
-
-  x -= 2;
-  cairo_set_source_rgb(cairo_context, 0.97, 0.97, 0.97);
-  cairo_move_to(cairo_context, x, gripper_top);
-  cairo_line_to(cairo_context, x, gripper_bottom);
-  cairo_stroke(cairo_context);
-  x += 3;
-  cairo_move_to(cairo_context, x, gripper_top);
-  cairo_line_to(cairo_context, x, gripper_bottom);
-  cairo_stroke(cairo_context);
 }
 
 DisconnectWindowGtk::DisconnectWindowGtk()
-    : disconnect_window_(nullptr), current_width_(0), current_height_(0) {}
+    : disconnect_window_(nullptr),
+      toggle_button_(nullptr),
+      message_(nullptr),
+      button_(nullptr),
+      current_width_(0),
+      current_height_(0) {}
 
 DisconnectWindowGtk::~DisconnectWindowGtk() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (disconnect_window_) {
-    gtk_widget_destroy(disconnect_window_);
+    gtk_widget_destroy(disconnect_window_.get());
     disconnect_window_ = nullptr;
+    toggle_button_ = nullptr;
+    message_ = nullptr;
+    button_ = nullptr;
   }
 }
 
@@ -184,7 +206,7 @@
 
   // Allow custom rendering of the background pixmap.
 #if !GTK_CHECK_VERSION(3, 90, 0)
-  gtk_widget_set_app_paintable(disconnect_window_, TRUE);
+  gtk_widget_set_app_paintable(disconnect_window_.get(), TRUE);
 #endif
   connect(disconnect_window_.get(), "draw", &DisconnectWindowGtk::OnDraw);
 
@@ -197,22 +219,12 @@
   connect(disconnect_window_.get(), "configure-event",
           &DisconnectWindowGtk::OnConfigure);
 
-  // Handle mouse events to allow the user to drag the window around.
-#if !GTK_CHECK_VERSION(3, 90, 0)
-  gtk_widget_set_events(disconnect_window_, GDK_BUTTON_PRESS_MASK);
-#endif
-  connect(disconnect_window_.get(), "button-press-event",
-          &DisconnectWindowGtk::OnButtonPress);
-
-  // All magic numbers taken from screen shots provided by UX.
-  // The alignment sets narrow margins at the top and bottom, compared with
-  // left and right.  The left margin is made larger to accommodate the
-  // window movement gripper.
+  // Layout contains: toggle button, message label, and disconnect button.
   GtkWidget* button_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
   gtk_box_set_homogeneous(GTK_BOX(button_row), FALSE);
 
 #if GTK_CHECK_VERSION(3, 90, 0)
-  gtk_widget_set_margin_start(GTK_WIDGET(button_row), 24);
+  gtk_widget_set_margin_start(GTK_WIDGET(button_row), 12);
   gtk_widget_set_margin_end(GTK_WIDGET(button_row), 12);
   gtk_widget_set_margin_top(GTK_WIDGET(button_row), 8);
   gtk_widget_set_margin_bottom(GTK_WIDGET(button_row), 8);
@@ -220,30 +232,41 @@
 #else
   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
   GtkWidget* align = gtk_alignment_new(0, 0, 1, 1);
-  gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 24, 12);
+  gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 12, 12);
   G_GNUC_END_IGNORE_DEPRECATIONS;
   gtk_container_add(GTK_CONTAINER(window), align);
   gtk_container_add(GTK_CONTAINER(align), button_row);
 #endif
 
+  toggle_button_ = gtk_button_new();
+  UpdateToggleButtonText();
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_box_pack_start(GTK_BOX(button_row), toggle_button_.get());
+#else
+  gtk_box_pack_start(GTK_BOX(button_row), toggle_button_.get(), FALSE, FALSE,
+                     0);
+#endif
+  connect(GTK_BUTTON(toggle_button_.get()), "clicked",
+          &DisconnectWindowGtk::OnToggleClicked);
+
+  message_ = gtk_label_new(nullptr);
+#if GTK_CHECK_VERSION(3, 90, 0)
+  gtk_box_pack_start(GTK_BOX(button_row), message_.get());
+#else
+  gtk_box_pack_start(GTK_BOX(button_row), message_.get(), FALSE, FALSE, 0);
+#endif
+
   button_ = gtk_button_new_with_label(
       l10n_util::GetStringUTF8(IDS_STOP_SHARING_BUTTON).c_str());
 #if GTK_CHECK_VERSION(3, 90, 0)
-  gtk_box_pack_end(GTK_BOX(button_row), button_);
+  gtk_box_pack_end(GTK_BOX(button_row), button_.get());
 #else
-  gtk_box_pack_end(GTK_BOX(button_row), button_, FALSE, FALSE, 0);
+  gtk_box_pack_end(GTK_BOX(button_row), button_.get(), FALSE, FALSE, 0);
 #endif
 
   connect(GTK_BUTTON(button_.get()), "clicked",
           &DisconnectWindowGtk::OnClicked);
 
-  message_ = gtk_label_new(nullptr);
-#if GTK_CHECK_VERSION(3, 90, 0)
-  gtk_box_pack_end(GTK_BOX(button_row), message_);
-#else
-  gtk_box_pack_end(GTK_BOX(button_row), message_, FALSE, FALSE, 0);
-#endif
-
   // Override any theme setting for the text color, so that the text is
   // readable against the window's background pixmap.
   PangoAttrList* attributes = pango_attr_list_new();
@@ -254,14 +277,19 @@
 
 #if !GTK_CHECK_VERSION(3, 90, 0)
   // GTK4 always uses an RGBA visual for windows.
-  GdkScreen* screen = gtk_widget_get_screen(disconnect_window_);
-  GdkVisual* visual = gdk_screen_get_rgba_visual(screen);
-  if (visual) {
-    gtk_widget_set_visual(disconnect_window_, visual);
+  GdkScreen* screen = gtk_widget_get_screen(disconnect_window_.get());
+  if (screen) {
+    connect(screen, "monitors-changed",
+            &DisconnectWindowGtk::OnMonitorsChanged);
+    connect(screen, "size-changed", &DisconnectWindowGtk::OnMonitorsChanged);
+    GdkVisual* visual = gdk_screen_get_rgba_visual(screen);
+    if (visual) {
+      gtk_widget_set_visual(disconnect_window_.get(), visual);
+    }
   }
 
   // GTK4 shows windows by default.
-  gtk_widget_show_all(disconnect_window_);
+  gtk_widget_show_all(disconnect_window_.get());
 #endif
 
   // Extract the user name from the JID.
@@ -271,6 +299,7 @@
   gtk_label_set_text(
       GTK_LABEL(message_.get()),
       l10n_util::GetStringFUTF8(IDS_MESSAGE_SHARED, username).c_str());
+  SetDialogPosition();
   gtk_window_present(window);
 }
 
@@ -283,6 +312,102 @@
   }
 }
 
+void DisconnectWindowGtk::OnToggleClicked(GtkButton* button) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  ToggleAlignment();
+}
+
+void DisconnectWindowGtk::ToggleAlignment() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  g_current_anchor = (g_current_anchor == WindowAnchor::kBottom)
+                         ? WindowAnchor::kTop
+                         : WindowAnchor::kBottom;
+  UpdateToggleButtonText();
+  if (toggle_button_) {
+    gtk_widget_set_sensitive(toggle_button_.get(), FALSE);
+    cooldown_timer_.Start(
+        FROM_HERE, kToggleCooldown,
+        base::BindOnce(&DisconnectWindowGtk::OnCooldownExpired,
+                       weak_factory_.GetWeakPtr()));
+  }
+  SetDialogPosition();
+}
+
+void DisconnectWindowGtk::OnCooldownExpired() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  if (toggle_button_) {
+    gtk_widget_set_sensitive(toggle_button_.get(), TRUE);
+  }
+}
+
+void DisconnectWindowGtk::UpdateToggleButtonText() {
+  if (toggle_button_) {
+    gtk_button_set_label(
+        GTK_BUTTON(toggle_button_.get()),
+        (g_current_anchor == WindowAnchor::kBottom) ? "▲" : "▼");
+  }
+}
+
+void DisconnectWindowGtk::SetDialogPosition() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+#if !GTK_CHECK_VERSION(3, 90, 0)
+  if (!disconnect_window_) {
+    return;
+  }
+
+  GdkDisplay* display = gtk_widget_get_display(disconnect_window_.get());
+  if (!display) {
+    return;
+  }
+
+  GdkMonitor* monitor = nullptr;
+  GdkWindow* gdk_window = gtk_widget_get_window(disconnect_window_.get());
+  if (gdk_window) {
+    monitor = gdk_display_get_monitor_at_window(display, gdk_window);
+  }
+  if (!monitor) {
+    monitor = gdk_display_get_primary_monitor(display);
+  }
+  if (!monitor) {
+    monitor = gdk_display_get_monitor(display, 0);
+  }
+  if (!monitor) {
+    return;
+  }
+
+  GdkRectangle geometry;
+  gdk_monitor_get_geometry(monitor, &geometry);
+
+  int width = current_width_;
+  int height = current_height_;
+  if (width == 0 || height == 0) {
+    GtkRequisition requisition;
+    gtk_widget_get_preferred_size(disconnect_window_.get(), nullptr,
+                                  &requisition);
+    width = requisition.width;
+    height = requisition.height;
+  }
+
+  int left = geometry.x + (geometry.width - width) / 2;
+  int top = (g_current_anchor == WindowAnchor::kTop)
+                ? (geometry.y + kTopMargin)
+                : (geometry.y + geometry.height - height - kBottomMargin);
+
+  gtk_window_move(GTK_WINDOW(disconnect_window_.get()), left, top);
+#else
+  NOTIMPLEMENTED_LOG_ONCE()
+      << "Window positioning is not implemented for GTK4/Wayland.";
+#endif
+}
+
+#if !GTK_CHECK_VERSION(3, 90, 0)
+void DisconnectWindowGtk::OnMonitorsChanged(GdkScreen* screen) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  SetDialogPosition();
+}
+#endif
+
 gboolean DisconnectWindowGtk::OnDelete(GtkWidget* window, GdkEvent* event) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
... (file diff truncated)
Loading diff…

Original Bug Report

reported by [email protected]

Potential bypass of Chrome Remote Desktop persistent security indicator

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: Authenticated remote clients can hide the ‘Stop Sharing’ dialog by dragging it off-screen, suppressing the local user’s primary security indicator and disconnect button. This occurs across Windows, macOS, and Linux because the host application lacks window position clamping and allows remote users to interact with the dialog’s draggable surfaces.

Affected files:

  • remoting/host/disconnect_window_win.cc
  • remoting/host/win/core.rc.jinja2
  • remoting/host/input_injector_win.cc
  • remoting/host/it2me_desktop_environment.cc
  • remoting/host/me2me_desktop_environment.cc
  • remoting/host/input_monitor/local_mouse_input_monitor_win.cc
  • remoting/host/disconnect_window_mac.mm
  • remoting/host/disconnect_window_linux.cc

Estimated timestamp from git blame: 2011-10-24

The Chrome Remote Desktop (CRD) host displays a ‘Stop Sharing’ dialog to satisfy a critical security requirement: notifying the local user that their desktop is being accessed remotely and providing a persistent, one-click mechanism to terminate the session. A defense-in-depth hardening gap allows an authenticated remote client to move this dialog outside the visible screen area, effectively hiding it.

Technical Analysis

On Windows, the disconnect dialog is a borderless WS_POPUP window. Its message handler in remoting/host/disconnect_window_win.cc responds to WM_NCHITTEST by returning HTCAPTION (line 286), which treats the entire dialog surface as a draggable title bar. While the host uses LocalMouseInputMonitorWin to detect local activity and re-center the window, it explicitly ignores injected input (events with a null hDevice handle, as seen in remoting/host/input_monitor/local_mouse_input_monitor_win.cc line 58). This allows a remote attacker to initiate a system move loop and drag the window off-screen without triggering the ’local input’ re-centering logic.

Furthermore, in Me2Me (personal access) sessions, the dialog uses an internal flag local_input_seen_ to manage positioning. Once any genuine local input is detected, this flag is set to true. Subsequent calls to ShowDialog()—which occur after the window auto-hides—will skip the re-centering logic if this flag is set (remoting/host/disconnect_window_win.cc line 370). Consequently, if an attacker drags the window off-screen after the flag is set, it will remain hidden indefinitely.

Similar behavior is reproducible on other platforms:

  • macOS: The window is draggable because movableByWindowBackground is set to YES in remoting/host/disconnect_window_mac.mm (line 240).
  • Linux: The window can be moved via gtk_window_begin_move_drag in remoting/host/disconnect_window_linux.cc (line 324).

Potential Reproduction Steps

  1. Establish a Chrome Remote Desktop session with a Windows host.
  2. From the remote client, perform a mouse-down event on the ‘Stop Sharing’ dialog surface.
  3. Drag the cursor to the far corner of the virtual screen (e.g., coordinate 0,0).
  4. Release the mouse button.
  5. Observe that the dialog remains at the off-screen position, removing the visible indicator and the disconnect button for the local user.

Suggested Fix

  1. Window Clamping: Implement handlers for window movement events (e.g., WM_WINDOWPOSCHANGING on Windows) to clamp the dialog position within the work area of the current monitor.
  2. Restrict Draggability: Disable the ability to drag the window by its background. If draggability is required, it should be limited to a specific, small handle that is less likely to be exploited by remote input.
  3. Forced Re-centering: Modify the ShowDialog() logic to verify that the window is within the visible work area before displaying it. If the window is off-screen, it should be re-centered regardless of the state of the local_input_seen_ flag.

Evaluated with Chrome root at commit: 1a8d40fc44df2088d5945c0bf53584038aa1614a


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

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