Medium chrome Memory Corruption 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMemory corruption in Core
DescriptionMemory corruption in Core
ComponentCore
Sub-componentInternals>Core
Bug ClassMemory Corruption
Tracker553150372
Fix commit47c56d620da3 (chromium/src) +132/-3
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-20

Changed Functions

FunctionChangeNotes
TEST_F
content/browser/web_contents/web_contents_view_aura_unittest.cc
modified

Files Changed

  • ui/aura/window_event_dispatcher_unittest.cc
  • ui/aura/input_state_lookup_win.h
  • ui/aura/input_state_lookup_win.cc
  • content/browser/web_contents/web_contents_view_aura_unittest.cc
  • content/browser/web_contents/web_contents_view_aura.h
  • ui/aura/input_state_lookup.h
  • ui/aura/env.h
  • content/browser/web_contents/web_contents_view_aura.cc
  • ui/aura/env.cc
From 47c56d620da30da8b6b9850ac5ea3cfafb68a649 Mon Sep 17 00:00:00 2001
From: Daniel Clark <[email protected]>
Date: Fri, 25 Sep 2026 17:25:21 -0700
Subject: [PATCH] Require left mouse button for renderer drags

In MouseEventManager::HandleMouseDraggedEvent the render process only
initiates drags when the left mouse button is held down. The
corresponding browser-process check in
WebContentsViewAura::StartDragging allows the drag to proceed if any
mouse button is held down. This could allow a compromised renderer to
cause drags to be triggered from mouse interactions that would not
normally initiate a drag.

Fix this mismatch by tightening the browser-process check to require the
left mouse button.

Fixed: 553150372
Change-Id: I01da0d2e5d7dff53f5b6cd6bf92b0c7217c868db
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8404057
Reviewed-by: Mitsuru Oshima <[email protected]>
Reviewed-by: Alex Moshchuk <[email protected]>
Commit-Queue: Dan Clark <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1705348}
---
diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index 244b015..9f40205 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -1291,14 +1291,14 @@
     // touch event would reach `::SendInput` via
     // DesktopWindowTreeHostWin::StartTouchDrag and could redirect the
     // synthesized click to an overlapping HWND (e.g. a permission bubble).
-    // Require an in-flight touch or mouse button, depending on the event
-    // source, and refuse the drag if requirement is not met.
+    // Require an in-flight touch or a left mouse button, depending on the
+    // event source, and refuse the drag if the requirement is not met.
     gfx::Point trusted_location = event_info.location;
     aura::Env* env = aura::Env::GetInstance();
     if ((event_info.source == ui::mojom::DragEventSource::kTouch &&
          !env->is_touch_down()) ||
         (event_info.source == ui::mojom::DragEventSource::kMouse &&
-         !env->IsMouseButtonDown())) {
+         !env->IsLeftMouseButtonDown())) {
       EndDrag(std::move(source_rwh_weak_ptr), result_op);
       return;
     }
diff --git a/content/browser/web_contents/web_contents_view_aura.h b/content/browser/web_contents/web_contents_view_aura.h
index f25d8c9..95b3192 100644
--- a/content/browser/web_contents/web_contents_view_aura.h
+++ b/content/browser/web_contents/web_contents_view_aura.h
@@ -196,6 +196,10 @@
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest,
                            ClampTouchLocationToBrowserObservedPoint);
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest,
+                           StartDraggingMouseRequiresLeftButton);
+  FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest,
+                           RefusedDragDoesNotBlockSubsequentDrags);
+  FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest,
                            UrlInDropDataReturnsUrlInOSExchangeDataGetString);
   FRIEND_TEST_ALL_PREFIXES(WebContentsViewAuraTest,
                            IgnoreInputs_OngoingDropGetsCleared);
diff --git a/ui/aura/env.cc b/ui/aura/env.cc
index 52500727..12c682a 100644
--- a/ui/aura/env.cc
+++ b/ui/aura/env.cc
@@ -22,6 +22,7 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_occlusion_tracker.h"
 #include "ui/display/screen.h"
+#include "ui/events/event_constants.h"
 #include "ui/events/event_observer.h"
 #include "ui/events/event_target_iterator.h"
 #include "ui/events/gestures/gesture_recognizer_impl.h"
@@ -134,6 +135,12 @@
       mouse_button_flags_ != 0;
 }
 
+bool Env::IsLeftMouseButtonDown() const {
+  return input_state_lookup_.get()
+             ? input_state_lookup_->IsLeftMouseButtonDown()
+             : (mouse_button_flags_ & ui::EF_LEFT_MOUSE_BUTTON) != 0;
+}
+
 void Env::SetLastMouseLocation(const gfx::Point& last_mouse_location) {
   last_mouse_location_ = last_mouse_location;
 }
diff --git a/ui/aura/env.h b/ui/aura/env.h
index dd950c60..8ff5f95 100644
--- a/ui/aura/env.h
+++ b/ui/aura/env.h
@@ -76,6 +76,12 @@
   // otherwise it uses |mouse_button_flags_|.
   bool IsMouseButtonDown() const;
 
+  // Returns true if the left mouse button specifically is down. This may query
+  // the native OS, otherwise it uses |mouse_button_flags_|. Prefer this over
+  // IsMouseButtonDown() when validating that a drag gesture is in progress:
+  // drags are only ever initiated with the left button.
+  bool IsLeftMouseButtonDown() const;
+
   // Gets/sets the last mouse location seen in a mouse event in the screen
   // coordinates.
   const gfx::Point& last_mouse_location() const { return last_mouse_location_; }
diff --git a/ui/aura/input_state_lookup.h b/ui/aura/input_state_lookup.h
index 6419309..8039d79 100644
--- a/ui/aura/input_state_lookup.h
+++ b/ui/aura/input_state_lookup.h
@@ -21,6 +21,9 @@
 
   // Returns true if any mouse button is down.
   virtual bool IsMouseButtonDown() const = 0;
+
+  // Returns true if the left mouse button is down.
+  virtual bool IsLeftMouseButtonDown() const = 0;
 };
 
 }  // namespace aura
diff --git a/ui/aura/input_state_lookup_win.cc b/ui/aura/input_state_lookup_win.cc
index adb5847e..d46eae2a 100644
--- a/ui/aura/input_state_lookup_win.cc
+++ b/ui/aura/input_state_lookup_win.cc
@@ -31,4 +31,8 @@
          (::GetKeyState(VK_XBUTTON2) & 0x80);
 }
 
+bool InputStateLookupWin::IsLeftMouseButtonDown() const {
+  return (::GetKeyState(VK_LBUTTON) & 0x80) != 0;
+}
+
 }  // namespace aura
diff --git a/ui/aura/input_state_lookup_win.h b/ui/aura/input_state_lookup_win.h
index 22219b4..5a0914d 100644
--- a/ui/aura/input_state_lookup_win.h
+++ b/ui/aura/input_state_lookup_win.h
@@ -21,6 +21,7 @@
 
   // InputStateLookup overrides:
   bool IsMouseButtonDown() const override;
+  bool IsLeftMouseButtonDown() const override;
 };
 
 }  // namespace aura
diff --git a/content/browser/web_contents/web_contents_view_aura_unittest.cc b/content/browser/web_contents/web_contents_view_aura_unittest.cc
index 9e4388c..267b777 100644
--- a/content/browser/web_contents/web_contents_view_aura_unittest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_unittest.cc
@@ -1361,6 +1361,109 @@
   env->SetTouchDown(false);
 }
 
+// Blink only ever starts a mouse drag from a left-button gesture, so a
+// renderer-initiated mouse drag while a different button is held must be
+// refused. This matters most on Windows, where the OLE drag loop would turn
+// such a gesture into an unprompted drop (see
+// DragSourceWin::QueryContinueDrag).
+TEST_F(WebContentsViewAuraTest, StartDraggingMouseRequiresLeftButton) {
+  NavigateAndCommit(GURL("https://google.com/"));
+
+  TestDragDropClient drag_drop_client;
+  aura::client::SetDragDropClient(root_window(), &drag_drop_client);
+
+  WebContentsViewAura* view = GetView();
+  aura::Env* const env = aura::Env::GetInstance();
+  const gfx::Point location =
+      view->GetContentNativeView()->GetBoundsInScreen().CenterPoint();
+  env->SetLastMouseLocation(location);
+
+  DropData drop_data;
+  drop_data.text.emplace(u"Hello World!");
+  const blink::mojom::DragEventSourceInfo event_info(
+      location, ui::mojom::DragEventSource::kMouse);
+
+  // Needed to avoid calling WebContentsViewAura::EndDrag, which results in
+  // NOTREACHED being called in
+  // `RenderWidgetHostViewBase::TransformPointToCoordSpaceForView`.
+  view->drag_in_progress_ = true;
+
+  env->set_mouse_button_flags(ui::EF_MIDDLE_MOUSE_BUTTON |
+                              ui::EF_RIGHT_MOUSE_BUTTON |
+                              ui::EF_BACK_MOUSE_BUTTON);
+  view->StartDragging(
+      *main_rfh(), drop_data, blink::DragOperationsMask::kDragOperationNone,
+      gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(), event_info);
+
+  EXPECT_FALSE(drag_drop_client.GetDragDropData())
+      << "A mouse drag started without the left button held must not reach "
+         "the platform drag and drop client.";
+
+  // The same drag is allowed once the left button is held.
+  env->set_mouse_button_flags(ui::EF_LEFT_MOUSE_BUTTON);
+  view->StartDragging(
+      *main_rfh(), drop_data, blink::DragOperationsMask::kDragOperationNone,
+      gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(), event_info);
+
+  EXPECT_TRUE(drag_drop_client.GetDragDropData());
+}
+
+// A drag that is refused before it starts must not leave `drag_security_info_`
+// initiated, or every later drag from this WebContents would be dropped by the
+// reentrancy check. Covers both refusal paths in StartDragging: a mismatched
+// input state, and a hidden source view.
+TEST_F(WebContentsViewAuraTest, RefusedDragDoesNotBlockSubsequentDrags) {
+  NavigateAndCommit(GURL("https://google.com/"));
+
+  TestDragDropClient drag_drop_client;
+  aura::client::SetDragDropClient(root_window(), &drag_drop_client);
+
+  WebContentsViewAura* view = GetView();
+  aura::Env* const env = aura::Env::GetInstance();
+  const gfx::Point location =
+      view->GetContentNativeView()->GetBoundsInScreen().CenterPoint();
+  env->SetLastMouseLocation(location);
+
+  DropData drop_data;
+  drop_data.text.emplace(u"Hello World!");
+  const blink::mojom::DragEventSourceInfo event_info(
+      location, ui::mojom::DragEventSource::kMouse);
+
+  // Needed to avoid calling WebContentsViewAura::EndDrag, which results in
+  // NOTREACHED being called in
+  // `RenderWidgetHostViewBase::TransformPointToCoordSpaceForView`.
+  view->drag_in_progress_ = true;
+
+  // No button is held, e.g. because the user released it before the renderer's
+  // StartDragging message arrived, so the drag is refused.
+  env->set_mouse_button_flags(ui::EF_NONE);
+  view->StartDragging(
+      *main_rfh(), drop_data, blink::DragOperationsMask::kDragOperationNone,
+      gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(), event_info);
+
+  ASSERT_FALSE(drag_drop_client.GetDragDropData());
+  EXPECT_FALSE(view->drag_security_info_.did_initiate());
+
+  // A hidden source view is refused by the second check, which must not latch
+  // the state either.
+  env->set_mouse_button_flags(ui::EF_LEFT_MOUSE_BUTTON);
+  view->GetContentNativeView()->Hide();
+  view->StartDragging(
+      *main_rfh(), drop_data, blink::DragOperationsMask::kDragOperationNone,
+      gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(), event_info);
+
+  ASSERT_FALSE(drag_drop_client.GetDragDropData());
+  EXPECT_FALSE(view->drag_security_info_.did_initiate());
+
+  // A subsequent genuine drag gesture must still be able to start.
+  view->GetContentNativeView()->Show();
+  view->StartDragging(
+      *main_rfh(), drop_data, blink::DragOperationsMask::kDragOperationNone,
+      gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(), event_info);
+
+  EXPECT_TRUE(drag_drop_client.GetDragDropData());
+}
+
 // Test that a drag from an event located outside the source view doesn't start.
 TEST_F(WebContentsViewAuraTest, EmptyTextInDropDataIsNonNullInOSExchangeData) {
   const char kGoogleUrl[] = "https://google.com/";
diff --git a/ui/aura/window_event_dispatcher_unittest.cc b/ui/aura/window_event_dispatcher_unittest.cc
index 6f88f91..43a73ae 100644
--- a/ui/aura/window_event_dispatcher_unittest.cc
+++ b/ui/aura/window_event_dispatcher_unittest.cc
@@ -3280,6 +3280,7 @@
  private:
   // InputStateLookup:
   bool IsMouseButtonDown() const override { return true; }
+  bool IsLeftMouseButtonDown() const override { return true; }
 };
 
 TEST_F(WindowEventDispatcherTest,
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/web_contents/web_contents_view_aura_unittest.cc b/content/browser/web_contents/web_contents_view_aura_unittest.cc
index 9e4388c..267b777 100644
--- a/content/browser/web_contents/web_contents_view_aura_unittest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_unittest.cc
@@ -1361,6 +1361,109 @@
   env->SetTouchDown(false);
 }
 
+// Blink only ever starts a mouse drag from a left-button gesture, so a
+// renderer-initiated mouse drag while a different button is held must be
+// refused. This matters most on Windows, where the OLE drag loop would turn
+// such a gesture into an unprompted drop (see
+// DragSourceWin::QueryContinueDrag).
+TEST_F(WebContentsViewAuraTest, StartDraggingMouseRequiresLeftButton) {
+  NavigateAndCommit(GURL("https://google.com/"));
+
+  TestDragDropClient drag_drop_client;
+  aura::client::SetDragDropClient(root_window(), &drag_drop_client);
+
+  WebContentsViewAura* view = GetView();
+  aura::Env* const env = aura::Env::GetInstance();
+  const gfx::Point location =
+      view->GetContentNativeView()->GetBoundsInScreen().CenterPoint();
+  env->SetLastMouseLocation(location);
+
+  DropData drop_data;
+  drop_data.text.emplace(u"Hello World!");
+  const blink::mojom::DragEventSourceInfo event_info(
+      location, ui::mojom::DragEventSource::kMouse);
+
+  // Needed to avoid calling WebContentsViewAura::EndDrag, which results in
+  // NOTREACHED being called in
+  // `RenderWidgetHostViewBase::TransformPointToCoordSpaceForView`.
+  view->drag_in_progress_ = true;
+
+  env->set_mouse_button_flags(ui::EF_MIDDLE_MOUSE_BUTTON |
+                              ui::EF_RIGHT_MOUSE_BUTTON |
+                              ui::EF_BACK_MOUSE_BUTTON);
+  view->StartDragging(
+      *main_rfh(), drop_data, blink::DragOperationsMask::kDragOperationNone,
+      gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(), event_info);
+
+  EXPECT_FALSE(drag_drop_client.GetDragDropData())
+      << "A mouse drag started without the left button held must not reach "
+         "the platform drag and drop client.";
+
+  // The same drag is allowed once the left button is held.
+  env->set_mouse_button_flags(ui::EF_LEFT_MOUSE_BUTTON);
+  view->StartDragging(
+      *main_rfh(), drop_data, blink::DragOperationsMask::kDragOperationNone,
+      gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(), event_info);
+
+  EXPECT_TRUE(drag_drop_client.GetDragDropData());
+}
+
+// A drag that is refused before it starts must not leave `drag_security_info_`
+// initiated, or every later drag from this WebContents would be dropped by the
+// reentrancy check. Covers both refusal paths in StartDragging: a mismatched
+// input state, and a hidden source view.
+TEST_F(WebContentsViewAuraTest, RefusedDragDoesNotBlockSubsequentDrags) {
+  NavigateAndCommit(GURL("https://google.com/"));
+
+  TestDragDropClient drag_drop_client;
+  aura::client::SetDragDropClient(root_window(), &drag_drop_client);
+
+  WebContentsViewAura* view = GetView();
+  aura::Env* const env = aura::Env::GetInstance();
+  const gfx::Point location =
+      view->GetContentNativeView()->GetBoundsInScreen().CenterPoint();
+  env->SetLastMouseLocation(location);
+
+  DropData drop_data;
+  drop_data.text.emplace(u"Hello World!");
+  const blink::mojom::DragEventSourceInfo event_info(
+      location, ui::mojom::DragEventSource::kMouse);
+
+  // Needed to avoid calling WebContentsViewAura::EndDrag, which results in
+  // NOTREACHED being called in
+  // `RenderWidgetHostViewBase::TransformPointToCoordSpaceForView`.
+  view->drag_in_progress_ = true;
+
+  // No button is held, e.g. because the user released it before the renderer's
+  // StartDragging message arrived, so the drag is refused.
+  env->set_mouse_button_flags(ui::EF_NONE);
+  view->StartDragging(
+      *main_rfh(), drop_data, blink::DragOperationsMask::kDragOperationNone,
+      gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(), event_info);
+
+  ASSERT_FALSE(drag_drop_client.GetDragDropData());
+  EXPECT_FALSE(view->drag_security_info_.did_initiate());
+
+  // A hidden source view is refused by the second check, which must not latch
+  // the state either.
+  env->set_mouse_button_flags(ui::EF_LEFT_MOUSE_BUTTON);
+  view->GetContentNativeView()->Hide();
+  view->StartDragging(
+      *main_rfh(), drop_data, blink::DragOperationsMask::kDragOperationNone,
+      gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(), event_info);
+
+  ASSERT_FALSE(drag_drop_client.GetDragDropData());
+  EXPECT_FALSE(view->drag_security_info_.did_initiate());
+
+  // A subsequent genuine drag gesture must still be able to start.
+  view->GetContentNativeView()->Show();
+  view->StartDragging(
+      *main_rfh(), drop_data, blink::DragOperationsMask::kDragOperationNone,
+      gfx::ImageSkia(), gfx::Vector2d(), gfx::Rect(), event_info);
+
+  EXPECT_TRUE(drag_drop_client.GetDragDropData());
+}
+
 // Test that a drag from an event located outside the source view doesn't start.
 TEST_F(WebContentsViewAuraTest, EmptyTextInDropDataIsNonNullInOSExchangeData) {
   const char kGoogleUrl[] = "https://google.com/";
diff --git a/ui/aura/window_event_dispatcher_unittest.cc b/ui/aura/window_event_dispatcher_unittest.cc
index 6f88f91..43a73ae 100644
--- a/ui/aura/window_event_dispatcher_unittest.cc
+++ b/ui/aura/window_event_dispatcher_unittest.cc
@@ -3280,6 +3280,7 @@
  private:
   // InputStateLookup:
   bool IsMouseButtonDown() const override { return true; }
+  bool IsLeftMouseButtonDown() const override { return true; }
 };
 
 TEST_F(WindowEventDispatcherTest,
Loading diff…

Original Bug Report

reported by [email protected]

Drop injection on Windows via mouse button mismatch in StartDragging

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: On Windows, the browser-side drag gesture validation in WebContentsViewAura accepts any of the five mouse buttons, whereas Blink only legitimately initiates mouse drags using the left button. Furthermore, DragSourceWin treats the absence of left and right mouse buttons as an immediate drop (DRAGDROP_S_DROP) and right-button holds as a sustained drag. A compromised renderer could potentially abuse this discrepancy during common middle-button or right-button user interactions to perform unprompted drag-and-drop operations.

Affected files:

  • content/browser/web_contents/web_contents_view_aura.cc
  • ui/aura/input_state_lookup_win.cc
  • ui/base/dragdrop/drag_source_win.cc
  • ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc

Estimated timestamp from git blame: 2026-07-30

Summary

There is an architectural mismatch across the drag-and-drop validation pipeline on Windows. While Blink restricts mouse drag initiation strictly to the left mouse button, the browser process’s validation gate checks whether any mouse button is held down. In conjunction with DragSourceWin’s handling of OLE key states, this allows a compromised renderer to forge drag requests during non-left-click user interactions (such as middle-click autoscroll or right-click holds), potentially leading to unprompted drops into overlapping victim surfaces or the Windows shell.

Root Cause Analysis

  1. Blink Gesture Restriction: In third_party/blink/renderer/core/input/mouse_event_manager.cc:833-839, Blink enforces that standard mouse drags can only be started from a left-button gesture:
if ((!is_pen &&
     event.Event().button != WebPointerProperties::Button::kLeft) ||
    (is_pen && event.Event().button != pen_drag_button)) {
  mouse_down_may_start_drag_ = false;
  return WebInputEventResult::kNotHandled;
}

Therefore, legitimate renderer mouse drags are never initiated with middle, right, or X buttons.

  1. Browser Validation Gate Allows Any Button: When LocalFrameHost.StartDragging is received by the browser, content/browser/web_contents/web_contents_view_aura.cc:1290-1296 checks:
if ((event_info.source == ui::mojom::DragEventSource::kTouch &&
     !env->is_touch_down()) ||
    (event_info.source == ui::mojom::DragEventSource::kMouse &&
     !env->IsMouseButtonDown())) {
  web_contents_->SystemDragEnded(source_rwh);
  return;
}

env->IsMouseButtonDown() calls ui/aura/input_state_lookup_win.cc:26-32, which returns true if any of the 5 mouse buttons is pressed:

bool InputStateLookupWin::IsMouseButtonDown() const {
  return (::GetKeyState(VK_LBUTTON) & 0x80) ||
         (::GetKeyState(VK_RBUTTON) & 0x80) ||
         (::GetKeyState(VK_MBUTTON) & 0x80) ||
         (::GetKeyState(VK_XBUTTON1) & 0x80) ||
         (::GetKeyState(VK_XBUTTON2) & 0x80);
}
  1. Drag Client Passes Mouse Drags Without Window Checks: In ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc:83-124, the check ensuring the cursor is over the source window only applies to kTouch. For kMouse, it immediately invokes Win32 ::DoDragDrop.

  2. DragSourceWin OLE Termination Mismatch: In ui/base/dragdrop/drag_source_win.cc:17-27:

HRESULT DragSourceWin::QueryContinueDrag(BOOL escape_pressed, DWORD key_state) {
  if (cancel_drag_ || escape_pressed)
    return DRAGDROP_S_CANCEL;
  if (!(key_state & MK_LBUTTON) && !(key_state & MK_RBUTTON)) {
    OnDragSourceDrop();
    return DRAGDROP_S_DROP;
  }
  return S_OK;
}
  • If only the middle or an X-button is held, QueryContinueDrag observes neither MK_LBUTTON nor MK_RBUTTON and returns DRAGDROP_S_DROP on the initial poll, converting the forged drag into an immediate drop.
  • If the right mouse button is held, QueryContinueDrag returns S_OK, sustaining an OLE drag loop until the user releases the right button.

Potential Attack Scenarios

(Note: These are potential steps deduced from code analysis; our static analysis tooling does not execute dynamic exploits).

  • Scenario A (Middle-Click Instant Drop):

    1. User holds the middle mouse button to use autoscroll (enabled by default on Windows) in an active window.
    2. A compromised renderer in an overlapping window sends LocalFrameHost.StartDragging with source = kMouse and an attacker payload (text, HTML, URLs, or virtual files).
    3. The browser gate passes because VK_MBUTTON is down and the process-global cursor position is within the source view bounds.
    4. ::DoDragDrop runs; QueryContinueDrag returns DRAGDROP_S_DROP because neither L nor R is down, resulting in an immediate drop event delivered to the target window under the cursor with zero user drag/release motion.
  • Scenario B (Right-Click Latched Drag):

    1. User holds the right mouse button (e.g., following a prompt on an attacker site, prior to context menu display on mouse-up).
    2. Compromised renderer invokes LocalFrameHost.StartDragging with source = kMouse.
    3. QueryContinueDrag returns S_OK because MK_RBUTTON is set, sustaining the drag. Context menu display is suppressed by OLE mouse capture.
    4. When the user releases the right button, DRAGDROP_S_DROP is returned, dropping attacker data into the target window or opening the Windows Explorer right-drag menu.

Impact

A compromised renderer could bypass the browser-side drag gesture security gate on Windows, enabling unauthorized injection of text, URLs, or virtual files into overlapping victim browser documents or the host OS shell without requiring a genuine user drag-and-drop gesture.

Suggested Remediation

  1. Modify the browser-side gate in content/browser/web_contents/web_contents_view_aura.cc to verify specifically that the left mouse button (or appropriate pen button) is down when event_info.source == ui::mojom::DragEventSource::kMouse, rather than accepting any mouse button via IsMouseButtonDown().
  2. In ui/base/dragdrop/drag_source_win.cc:QueryContinueDrag, consider returning DRAGDROP_S_CANCEL if the drag was initiated without the expected initiating button state.

Evaluated with Chrome root at commit: c6718ebbf2c677c6f63691e28c7801c563f6b19c


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