Closed Bug 2042311 Opened 5 months ago Closed 4 months ago

Remote drag file-promise principal confusion lets compromised content hand a parent/chrome-read file to a native GTK drop target

Categories

(Core :: DOM: Copy & Paste and Drag & Drop, defect)

defect

Tracking

()

RESOLVED DUPLICATE of bug 1500946

People

(Reporter: iifizzyx, Unassigned)

Details

(Keywords: ai-involved, reporter-external, sec-moderate, Whiteboard: [client-bounty-form])

Attachments

(4 files)

Overview

Under the compromised-content-process threat model, an ordinary web content process can send PBrowser::InvokeDragSession with internal drag-and-drop file-promise flavors that normal web JavaScript is not allowed to set.

The parent accepts those remote drag transferables and installs the normal file-promise provider. When the parent later creates the real drag session, nsBaseDragSession::InvokeDragSession overwrites each transferable's data principal with the parent chrome <browser> source node principal. The file-promise provider and GTK native drag sink then resolve the child-supplied source URL with parent/chrome authority.

I validated this on Linux/GTK Nightly default configuration. The full native-drop PoC starts from a normal web content process, stages a remote drag whose promised source is chrome://browser/content/browser.xhtml, drives a real native GTK drag, and uses a separate GTK receiver process to request text/uri-list. The receiver gets a Firefox-created /tmp/dnd_file-* URI and reads the materialized chrome URL bytes outside the content sandbox.

This is a native-drop-mediated sandbox escape / local privileged read primitive. It is not code execution, not arbitrary destination write, and not silent web-readable exfiltration.

Build / Environment

Validated build:

Firefox Nightly 153.0a1
Build ID: 20260526085427
Source stamp: 4addf98c4ca88d363eed3b9bf63cc7f467c9aa61
Platform: Linux x86_64 / Ubuntu 24.04
Toolkit: gtk
e10s: true
Fission: true
has_sandbox: true
Build type: opt
Nightly build: true

No content sandbox prefs were relaxed. The native receiver run uses xvfb-run and GDK_BACKEND=x11 only to automate a GTK/XDND drag in CI-like conditions.

Steps To Reproduce

  1. Add the test-only nsIDOMWindowUtils helper described in the attached poc_helper_requirements.md, or port the same helper logic into an equivalent local harness.

    The helper does not create the vulnerability. It only simulates compromised-content raw IPC so the parent-side drag handling and production GTK native drag sink can be tested deterministically.

  2. Copy the direct PoC attachments into dom/base/test/:

poc_native_gtk_receiver.js     -> dom/base/test/browser_raw_drag_filepromise_native_gtk_receiver_poc.js
poc_gtk_uri_list_receiver.py   -> dom/base/test/poc_gtk_uri_list_receiver.py
  1. Register the browser test in dom/base/test/browser.toml:
["browser_raw_drag_filepromise_native_gtk_receiver_poc.js"]
support-files = ["poc_gtk_uri_list_receiver.py"]
skip-if = [
  "true", # Local PoC harness drives a real native GTK drag and must be run in isolation with --force.
]
  1. Build normally:
./mach build
  1. Run the isolated native GTK receiver validation:
timeout 120s xvfb-run -a env GDK_BACKEND=x11 \
  ./mach test --force dom/base/test/browser_raw_drag_filepromise_native_gtk_receiver_poc.js

The native receiver test is intentionally skipped by default in browser.toml because it drives the real GTK drag service and should be run in isolation with --force.

Actual Results

The native receiver validation passes:

PASS external GTK receiver window is mapped
PASS external choreographer pressed the source
PASS content sent raw chrome-url PBrowser::InvokeDragSession
PASS external choreographer moved the pointer across the drag threshold
native GTK receiver result: {
  "files": [
    {
      "contains": true,
      "exists": true,
      "path": "/tmp/dnd_file-6/native-gtk-chrome-url-leak.xhtml",
      "read": 326817,
      "size": 326817,
      "uri": "file:///tmp/dnd_file-6/native-gtk-chrome-url-leak.xhtml"
    }
  ],
  "status": "drop",
  "target": "text/uri-list",
  "uris": [
    "file:///tmp/dnd_file-6/native-gtk-chrome-url-leak.xhtml"
  ]
}
PASS native GTK receiver observed a drop
PASS native GTK receiver received Firefox-created dnd_file URI
PASS native GTK receiver could read materialized chrome URL bytes
TEST_END: Test PASS. Subtests passed 7/7. Unexpected 0
Overall Summary: OK

This proves that a separate native process receives and reads a file whose bytes came from chrome://browser/content/browser.xhtml, fetched by Firefox with parent/chrome authority after a content-process remote drag staged the source URL.

The attached validation log also includes additional local sanity checks for the parent file-promise provider and targeted GTK text/uri-list path. Those checks are supporting evidence, but the direct attached PoC above is the smallest initial triage reproducer.

Expected Results

A content process must not be able to use remote drag IPC to make the parent or native drag service resolve internal file-promise flavors under the chrome <browser> principal.

Expected behavior would be one of:

  • reject internal file-promise flavors (application/x-moz-file-promise, application/x-moz-file-promise-url, application/x-moz-file-promise-dest-filename, application/x-moz-file-promise-dir) when they originate from remote content drag IPC;
  • preserve and enforce the original content principal on remote drag transferables all the way through file-promise resolution;
  • reject privileged or non-web source schemes such as file:, chrome:, resource:, and moz-extension: for remote-content file-promise data;
  • avoid overwriting remote-content transferable principals with the chrome browser-element source node principal.
Flags: sec-bounty?
Attached file Root_cause.md —
Group: firefox-core-security → dom-core-security
Component: Security → DOM: Copy & Paste and Drag & Drop
Product: Firefox → Core
Severity: -- → S3

The rating assigned by our bug rating tool (currently under development) is:

Rating: sec-moderate

Explanation: The vulnerability is plausible: code analysis confirms that in nsBaseDragSession::InvokeDragSession (lines 367-376 of nsBaseDragService.cpp), each transferable's data principal is overwritten with mSourceNode->NodePrincipal() — which for a remote drag is the parent chrome <browser> element's principal. When a compromised content process sends PBrowser::InvokeDragSession with application/x-moz-file-promise-url set to a chrome:// (or potentially file://) URI, the file-promise provider (nsContentAreaDragDropDataProvider::GetFlavorData) later reads the overwritten chrome principal from the transferable and uses it to call SaveURIToFile, causing the parent process to fetch the privileged URL and materialize it to /tmp/dnd_file-*/. A native GTK drop target can then read the file. This would normally qualify as a sandbox escape (sec-high), but requires: (1) a prior, separate content-process compromise, (2) unusual user interaction — the user must physically drag from Firefox to an external application, which is not a normal browsing action, and (3) is limited to Linux/GTK only. The combination of unusual required user interaction and limited platform scope reduces this from sec-high to sec-moderate per the triage guidelines.

Status: UNCONFIRMED → NEW
Ever confirmed: true
Keywords: sec-moderate
Status: NEW → RESOLVED
Closed: 4 months ago
Duplicate of bug: 1500946
Resolution: --- → DUPLICATE
Flags: sec-bounty? → sec-bounty-
Group: dom-core-security
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: