Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Views
DescriptionInappropriate implementation in Views
ComponentViews
Bug ClassLogic Error
Tracker447172715
Fix commit083cac2c44a5 (chromium/src) +9/-2
CISA KEVNot listed
CreditedAlesandro Ortiz
Disclosed2025-11-05

Files Changed

  • ui/aura/env_input_state_controller.cc
From 083cac2c44a50242c7ac100f8fed5fc66ead9958 Mon Sep 17 00:00:00 2001
From: David Bienvenu <[email protected]>
Date: Thu, 16 Oct 2025 14:29:16 -0700
Subject: [PATCH] Fix handling of touch state in EnvInputStateController

Change-Id: I7357dff83de347207e6f0016429b3314daf2d5a1
Bug: 447172715
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7032454
Reviewed-by: Nico Weber <[email protected]>
Commit-Queue: David Bienvenu <[email protected]>
Reviewed-by: Mustaq Ahmed <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1531080}
---

diff --git a/ui/aura/env_input_state_controller.cc b/ui/aura/env_input_state_controller.cc
index 75da86584..9371488 100644
--- a/ui/aura/env_input_state_controller.cc
+++ b/ui/aura/env_input_state_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ui/aura/env_input_state_controller.h"
 
+#include "build/build_config.h"
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/env.h"
 #include "ui/events/event.h"
@@ -48,11 +49,17 @@
       env_->SetTouchDown(touch_ids_down_ != 0);
       break;
 
-    // Handle EventType::kTouchCancelled only if it has a native event.
     case ui::EventType::kTouchCancelled:
-      if (!event.HasNativeEvent())
+#if BUILDFLAG(IS_CHROMEOS)
+      // Handle EventType::kTouchCancelled only if it has a native event.
+      // ChromeOS exo touch drag relies on the ability to cancel touch
+      // downs with synthetic events when handing off to the new consumer,
+      // without losing the global env touch down state.
+      if (!event.HasNativeEvent()) {
         break;
+      }
       [[fallthrough]];
+#endif  // BUILDFLAG(IS_CHROMEOS)
     case ui::EventType::kTouchReleased:
       touch_ids_down_ = (touch_ids_down_ | (1 << event.pointer_details().id)) ^
                         (1 << event.pointer_details().id);
Loading diff…

Original Bug Report

reported by [email protected]

Security: Compromised renderer can control mouse after single tap (UXSS, sandbox escape, and more)

SUMMARY

Similar to issue 370856871, on Windows a compromised renderer can call StartDragging() to control the mouse and perform clicks. The existing mitigations can be bypassed after a single user tap until browser shutdown, allowing for many of the same impacts as the referenced issue. After the initial tap, all attacks can be performed an infinite amount of times, in any combination, at any time (such as when user is away from computer).

Standalone, we have a one-tap sandbox escape, UXSS, and other impacts.

When chained with other vulnerabilities, we have a one-tap sandbox escape with Mark of the Web (MOTW) bypass. I’ll post the chain in comments.

VULNERABILITY DETAILS

Background

A compromised renderer can call RFHI::StartDragging(), which eventually calls WebContentsViewAura::StartDragging() which ultimately calls DesktopDragDropClientWin::StartDragAndDrop().

StartDragAndDrop() performs mouse clicks through OS API calls in DesktopWindowTreeHostWin::StartTouchDrag() + ::FinishTouchDrag() and also calls the OS ::DoDragDrop() method.

Important concept: An aura::Window “represents virtual windows, including tabs, bubbles and menus” (source: crbug comment). It doesn’t always correspond to what a user typically considers a window. As a mental shortcut, in the main browser window, any UI that is typically visible immediately after navigating to a web page is likely within the same window as the web page content.

Bugs in existing mitigations

The fix for issue 370856871 added two checks within StartDragAndDrop():

  1. touch_down mitigation: Checks if Aura is_touch_down() returns true. This will return true if on any aura::Window, a touch was started (kTouchPressed) but has not finished (kTouchReleased) or cancelled (kTouchCancelled). Aura only tracks touches initiated by physical touch, privileged CDP client, or OS input APIs.
  2. touch_over_other_window mitigation: Checks if cursor is over the current aura::Window. This fails if it’s over a non-aura::Window or a different aura::Window.

Unfortunately, bugs prevent both of these mitigations from working as intended. There are also no restrictions on drag/clicks within the same aura::Window, which can still have serious impacts.

  if (source == ui::mojom::DragEventSource::kTouch) {
    // ...
    aura::Window* window =
        screen->GetWindowAtScreenPoint(screen->GetCursorScreenPoint());  // <-- Gets window at cursor (cursor is not necessarily at touch_screen_point)
    // ...
    bool touch_down = aura::Env::GetInstance()->is_touch_down();
    bool touch_over_other_window =
        !window || window->GetRootWindow() != root_window;
    // Check that the cursor is over the window being dragged from. If not,
    // don't start the drag because ::DoDragDrop will not do the drag.
    if (!touch_down || touch_over_other_window) {                        // <-- Checks safe states before starting drag
      return ui::PreferredDragOperation(
          ui::DragDropTypes::DropEffectToDragOperation(DROPEFFECT_NONE));
    }
    desktop_host_->StartTouchDrag(touch_screen_point);                   // <-- Sends mouse left down and mouse move events
  }
  // ...
  ::DoDragDrop(...)                                                      // <-- Starts OS drag. Waits until drag finishes before continuing with funciton.
  // ...
  desktop_host_->FinishTouchDrag(touch_screen_point);                    // <-- Sends mouse left up event (to finish click) after drag completed

touch_down bypass

Due to an Aura bug, is_touch_down() can persistently return true and bypass the check after a single tap by user. The bypass persists until a full browser restart.

In our PoC, to reach the persistent touch down state, the user only needs to make a single tap. During the tap, the page opens a tab in the same window (and optionally closes the tab) within the touchstart event handler. After this, the touch down state is persistently true even after user finishes the tap, and attacks can be launched at any time until browser restart.

See Root Cause Analysis in comment for details, including other (potentially natural) ways to reach persistent touch down state.

touch_over_other_window bypass

This is a generally solid mitigation that mitigates most impacts against bubbles, menus, other browser windows, and non-browser windows. However, a race condition allows menus (e.g. browser menu, context menu) to bypass this check, despite being a different aura::Window.

If timed correctly, touch_over_other_window will be false when calling StartDragging() over a menu that is opening. The menu item will then be clicked as it opens. I am not sure exactly why this occurs, but it’s reliable enough for an attacker to use (usually works the first time, but in case of failure we can repeat infinitely with adjusted timing until we succeed). This means top-level menu options (i.e. not nested menu options) can be clicked on using StartDragging().

We can open and click on the browser menu (three dots on top-right of browser window) and context menu (renderer can call ShowContextMenu() to open it).

Same aura::Window drag/click allowed

As mentioned earlier, an aura::Window is conceptually different than what a user typically considers a window. If an interesting target exists within the same aura::Window, it can be clicked on because there is no mitigation against this.

We can click on any page contents, bookmarks bar, address bar, docked DevTools, and anything else within same aura::Window. We can also drag to bookmarks bar and address bar. We may also be able to drag to other UI elements.

IMPACTS

After the initial tap to trigger persistent touch down state, all attacks can be performed an infinite amount of times, in any combination, at any time (such as when user is away from computer).

Roughly divided by cause, or prerequsite of extension or chained vuln:

Impacts when chained with unfixed issue 443255991

  • Sandbox escape with MOTW bypass: We can remove all user interaction requirements for issue 443255991’s PoCs. I’ll post details and chained PoCs in comments. Current PoC requires an extension.

Impacts due to same aura::Window allowed

  • Create, click JS bookmarks (universal XSS): Web pages can create drags with javascript: URLs, and the bookmarks bar will create/run bookmarks with javascript: URLs (bookmarklets). Therefore, attacker can drag a javascript: URL to the bookmarks bar, open a tab to the target website, and then click the attacker-created bookmark in the bookmarks bar. This gives us Universal XSS (UXSS) on any http(s):// page and non-component chrome-extension:// page. WebUI pages and component chrome-extension:// pages are not allowed to run bookmarklets therefore they are not impacted.
  • Click on other page: We can open a tab to any web page and click anywhere on it. We can also click on any visible tab content, such as WebUI pages and docked DevTools.
  • Click on side panel: We can click on any open side panel, such as Bookmarks side panel, GLIC side panel, extension side panels, etc.

Impacts due to touch_over_other_window bypass (menu race condition)

  • Open Downloads page, run/open download (sandbox escape): We can click the browser menu button, then click “Downloads” to open the WebUI page. We can then click on downloaded items to run/open them and escape sandbox, bypassing user interaction requirements within browser.
  • Open, click on split tab: Renderer can call ShowContextMenu() to open context menu, click on “Open link in Split View”, and then click on the other side of the split tab.
  • Open Settings, change most settings: We can click the browser menu button, then click “Settings” to open the WebUI page. Most settings can be be changed, if they are buttons, toggles, dropdown menus, or otherwise clickable. This includes disabling Safe Browsing, and showing/hiding bookmarks bar (useful for UXSS impact).
  • Open, click on DevTools: Renderer can call ShowContextMenu() to open context menu, and then click on “Inspect” to open DevTools.

Impacts with malicious extension

An extension with no permissions can open any chrome:// URL that web pages cannot open. We can also enable internal debugging URLs in chrome://chrome-urls and open them.

  • Open Extensions page, change extension settings (enable file access, incognito access): We can navigate to chrome://extensions/?id={extId}, then click on the page to enable file access and incognito access.
  • Open site permissions, allow permissions: We can navigate to chrome://settings/content/siteDetails?site={origin} and click to allow any listed permission, notably: camera, microphone, location, clipboard read, and local network access.
  • Toggle any chrome://flags: We can navigate to chrome://flags, change any flags, and then restart the browser. This could be used to further exploit vulnerabilities in experimental Web/JS/GPU features or relax some security features.
  • Open DevTools for any target: We can open DevTools for any target through chrome://inspect. We can then interact with the DevTools instance if it is docked to the same Aura window. With additional user interaction, there are significant impacts here that I’m still exploring (similar to issue 402791076: unrestricted CDP access, sandbox escape).

Prior impacts when chained with fixed issue 404000989

Prior to issue 404000989 being fixed in March 2025, a web page could drag/drop restricted URLs such as chrome:// or devtools:// URLs. The standalone vuln required drag and click user interactions to create bookmarks with restricted URLs and then navigate to them. With this drag/click vuln we can do both with one tap (or zero clicks after persistent touch down state). This would allow web pages to launch attacks that currently require extensions.

Prior impacts when chained with fixed issue 402791076

Prior to issue 402791076 being fixed in March 2025, we could gain XSS on DevTools. The standalone vuln required user clicking on a specific part of a page and then opening DevTools manually, but with this drag/click vuln we can do both with one tap (or zero clicks after persistent touch down state). This would allow further impacts, such as unrestricted CDP access, sandbox escape, and UXSS on any page (including WebUI pages).

BISECT

Can’t do proper bisect with custom build, but the initial fix was introduced in r1371525 (Oct 21, 2024). I started testing in ~r1508700 (August 29th, 2025) where it reproduced, although it likely reproduced since the fix on October 2024.

PROPOSED FIXES

One or more of these should mitigate most impacts:

  • Check position of renderer-provided drag start position in StartDragAndDrop() to ensure it is within web page boundaries. Currently, code checks if the cursor position is within the aura::Window, which also allows parent aura::Windows such as the main browser window. The cursor check should remain for the functional reasons documented in code, but a security check should be added to validate the drag position itself. This does not mitigate all attacks (such as clicking on page context menu or on web page content), but mitigates most impacts.

  • Ensure renderer that initiates drag is visible when calling StartDragging(). This would prevent attacks on other tabs in the same window.

  • Remove event.HasNativeEvent() early break in EnvInputStateController::UpdateStateForTouchEvent. After doing some archeology on the HasNativeEvent() check, it was added in January 2013 as a fix for an X11 bug. The function TouchEventIsGeneratedHack() is still present and used in X11 code, so it may still be needed to not regress the bug. If it’s still needed, we can add the HasNativeEvent() check only when using X11 (only Linux?) as a short-term fix. In theory the persistent is_touch_down() state may also occur in Linux, but should not have any security impacts there.

  • Ensure only one drag is occuring at a time, and/or throttling drag starts. Some of the behaviors currently depend on multiple drags happening simultaneously or in close succession, so restricting active drags and throttling drags may make exploitation more difficult.

Additional mitigation:

  • Prevent drag start from prerendered pages. I noticed that when we are in the persistent touch down state, we can initiate drags from a prerendered page. You can test this by triggering persistent touch down state, closing the page, and then typing/pasting the URL into address bar without navigating to the page. This may be useful in some attack scenarios.

VERSION

Chrome version: Verified with custom build based on 0ecd794f626107687c4844683a828953f49fb2b3 (Sept 11th, 2025)

Operating System: Windows 10

REPRODUCTION CASE

Patch to simulate compromised renderer

For all scenarios, apply attached patch and build Chromium.

The patch adds exploit logic to renderer:

  1. Adds calls to StartDragging() and ShowContextMenu() in third_party/blink/renderer/core/dom/document.cc for use by the exploit page. (This is loosely based on patch from issue 370856871.)
  2. Bypasses popup blocker in content/renderer/render_frame_impl.cc (this is same as earlier issue’s patch).
  3. Disables multiple context menu check in third_party/blink/renderer/core/page/context_menu_controller.cc which helps with context menu attacks.

The patch also adds logging and disables a DCHECK, only for development/debugging purposes:

  1. Disables DCHECK in base/message_loop/message_pump_win.cc that we hit during PoCs. You can alternatively build with DCHECKs disabled.
  2. Adds logging in content/browser/renderer_host/render_widget_host_view_aura.cc to show when Hide() is called (which would trigger persistent touch down state if tap is in progress).
  3. Adds logging in ui/aura/env.cc to show SetTouchDown(bool) calls.
  4. Adds logging in ui/aura/env_input_state_controller.cc to show state of touch tracking and processed touch events.
  5. Adds logging in ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc to show when operations occur, and if touch_over_other_window mitigation is hit.
  6. Adds logging in ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc to show when mouse events are generated due to touch/drag operations.

Prerequisites

  • A touch-enabled device or another way for OS/Aura to receive touch events (CDP should work).
  • Compromised renderer (apply attached patch to simulate).

Web page scenario: Universal XSS (UXSS)

  1. Navigate to https://alesandroortiz.com/security/chromium/touch-drag-click-cr.html?mode=uxss
  2. Tap anywhere, or do nothing if using autorun.

Observed:

  • If bookmarks bar is hidden: Renderer opens Settings page through browser menu, clicks on Appearance, and enables “Show bookmarks bar”. Then continues below:
  • If bookmarks bar is shown: Renderer opens tab to target website, drags a javascript: URL to bookmarks bar to create bookmarklet, then clicks bookmarklet to run the JS payload on target site.

Expected: Renderer cannot click on bookmarks bar. Drag cannot start from background tab. Renderer can still start a javascript: URL drag to create a bookmarklet, since this is supported functionality.

Web page scenario: Click on page

  1. Navigate to https://alesandroortiz.com/security/chromium/touch-drag-click-cr.html?mode=click-page
  2. Tap anywhere, or do nothing if using autorun.

Observed: Renderer opens tab to target website, and performs multiple clicks on target page. In this case, the clicks trigger navigations, but any action triggerable with clicks is possible.

Expected: Renderer cannot click on page on another tab. Drag cannot start from background tab.

Web page scenario: Open and click on split tab

  1. Navigate to https://alesandroortiz.com/security/chromium/touch-drag-click-cr.html?mode=click-split-tab
  2. Tap anywhere, or do nothing if using autorun.

Observed: Renderer opens split tab to target website by opening and clicking on context menu (this may take several attempts). After opening split tab, renderer performs multiple clicks on the other split tab. In this case, the clicks trigger navigations, but any action triggerable with clicks is possible.

Expected: Renderer cannot click on context menu. Renderer cannot click on the other split tab.

Web page scenario: Open Settings and disable Safe Browsing

  1. Navigate to https://alesandroortiz.com/security/chromium/touch-drag-click-cr.html?mode=open-settings-disable-safebrowsing
  2. Tap anywhere, or do nothing if using autorun.

Observed: Renderer opens Settings page by clicking on browser menu button, and then clicking “Settings” menu item. Renderer then performs multiple clicks on Settings page to disable Safe Browsing.

Expected: Renderer cannot click on browser menu button nor on browser menu items. Renderer cannot click on page on another tab. Drag cannot start from background tab.

Web page scenario: pwn all the things! 💥

Runs all web page scenarios. Use autorun for maximum pwnage.

  1. Navigate to https://alesandroortiz.com/security/chromium/touch-drag-click-cr.html?mode=combo&autorun=1
  2. Do nothing. :)

Observed: After one tap, renderer can make multiple StartDragging() calls anytime until browser restart. This means attacker can run any scenario with no user interaction.

Expected: After one tap, renderer can only make a single StartDragging() call if drag isn’t currently occurring. is_touch_down() is true only when a physical or otherwise safe touch event is in progress.

Credit Information

Reporter credit: Alesandro Ortiz https://AlesandroOrtiz.com

View on issue tracker
Links in the report