CVE-2026-11029
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcontent/browser/renderer_host/data_transfer_util.cc |
modified | |
TEST_Fcontent/browser/renderer_host/render_widget_host_unittest.cc |
modified |
Files Changed
content/browser/renderer_host/data_transfer_util.cccontent/browser/renderer_host/render_widget_host_unittest.cc
Patch
From 5ef49809a501c2d5144127f62896e4557567b538 Mon Sep 17 00:00:00 2001
From: Shu Yang <shuyng@google.com>
Date: Thu, 16 Apr 2026 12:36:22 -0700
Subject: [PATCH] Sanitize file extension in DragDataToDropData to prevent path traversal
A compromised renderer could supply a malicious `filename_extension`
containing path traversal characters (e.g., `../../`) during a
drag-and-drop operation. This could potentially allow a sandbox escape
by writing arbitrary files to a vulnerable third-party app.
To fix this, we now use `BaseName()` on the `filename_extension` in
`DragDataToDropData` to strip out any directory components.
We also added a regression test in `render_widget_host_unittest.cc` to
verify that malicious paths are correctly sanitized.
Bug: 497651688
Change-Id: Ic6fcfd598c3bd4a45a88f5218a93e0d80add22f8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7762535
Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org>
Commit-Queue: Shu Yang <shuyng@google.com>
Cr-Commit-Position: refs/heads/main@{#1616053}
---
diff --git a/content/browser/renderer_host/data_transfer_util.cc b/content/browser/renderer_host/data_transfer_util.cc
index 38929861..42236d3d 100644
--- a/content/browser/renderer_host/data_transfer_util.cc
+++ b/content/browser/renderer_host/data_transfer_util.cc
@@ -398,7 +398,7 @@
binary_item->is_image_accessible;
result.file_contents_source_url = binary_item->source_url;
result.file_contents_filename_extension =
- binary_item->filename_extension.value();
+ binary_item->filename_extension.BaseName().value();
if (binary_item->content_disposition) {
result.file_contents_content_disposition =
*binary_item->content_disposition;
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index bef04fb..fc7fe932 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -2411,6 +2411,28 @@
EXPECT_TRUE(drop_data().download_metadata.has_value());
}
+TEST_F(RenderWidgetHostDragTest, SanitizeFilenameExtensionOnDrag) {
+ NavigateAndCommit(GURL("https://example.com"));
+ EXPECT_EQ(start_dragging_count(), 0);
+
+ auto drag_data = blink::mojom::DragData::New();
+ blink::mojom::DragItemBinaryPtr item = blink::mojom::DragItemBinary::New();
+ item->data = mojo_base::BigBuffer(std::vector<uint8_t>{1, 2, 3});
+ item->is_image_accessible = true;
+ item->source_url = GURL("http://example.com/image.png");
+ item->filename_extension =
+ base::FilePath(FILE_PATH_LITERAL("png/../../payload.so"));
+ drag_data->items.push_back(
+ blink::mojom::DragItem::NewBinary(std::move(item)));
+
+ StartDragWithDragData(std::move(drag_data));
+
+ EXPECT_EQ(start_dragging_count(), 1);
+ // BaseName() should strip the path traversal components.
+ EXPECT_EQ(drop_data().file_contents_filename_extension,
+ FILE_PATH_LITERAL("payload.so"));
+}
+
// Hiding the RenderWidgetHostImpl instance via a call to WasHidden should
// not reject a pending pointer lock, if the operation is waiting for the
// user to make a selection on the permission prompt.
Regression Test / PoC
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index bef04fb..fc7fe932 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -2411,6 +2411,28 @@
EXPECT_TRUE(drop_data().download_metadata.has_value());
}
+TEST_F(RenderWidgetHostDragTest, SanitizeFilenameExtensionOnDrag) {
+ NavigateAndCommit(GURL("https://example.com"));
+ EXPECT_EQ(start_dragging_count(), 0);
+
+ auto drag_data = blink::mojom::DragData::New();
+ blink::mojom::DragItemBinaryPtr item = blink::mojom::DragItemBinary::New();
+ item->data = mojo_base::BigBuffer(std::vector<uint8_t>{1, 2, 3});
+ item->is_image_accessible = true;
+ item->source_url = GURL("http://example.com/image.png");
+ item->filename_extension =
+ base::FilePath(FILE_PATH_LITERAL("png/../../payload.so"));
+ drag_data->items.push_back(
+ blink::mojom::DragItem::NewBinary(std::move(item)));
+
+ StartDragWithDragData(std::move(drag_data));
+
+ EXPECT_EQ(start_dragging_count(), 1);
+ // BaseName() should strip the path traversal components.
+ EXPECT_EQ(drop_data().file_contents_filename_extension,
+ FILE_PATH_LITERAL("payload.so"));
+}
+
// Hiding the RenderWidgetHostImpl instance via a call to WasHidden should
// not reject a pending pointer lock, if the operation is waiting for the
// user to make a selection on the permission prompt.
Original Bug Report
Potential path traversal in DropDataProvider via unsanitized filename_extension
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A compromised renderer can supply a malicious filename_extension containing path traversal characters during a drag-and-drop operation. Chrome’s Android implementation fails to sanitize this extension in its fallback logic, exposing it to drop-target applications. This could allow a sandbox escape by writing arbitrary files to a vulnerable third-party app.
Affected files:
content/browser/android/drop_data_android.cccontent/browser/renderer_host/render_widget_host_impl.ccui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderImpl.javacontent/browser/renderer_host/data_transfer_util.ccui/android/java/src/org/chromium/ui/dragdrop/DropDataProviderUtils.javamojo/public/cpp/base/file_path_mojom_traits.cc
Estimated timestamp from git blame: 2022-06-21
Overview
There is a potential path traversal vulnerability in Chrome’s Android drag-and-drop implementation (DropDataProvider). A compromised renderer can control the filename_extension field of a dragged image. By injecting path traversal sequences (e.g., ../../../), an attacker can control the OpenableColumns.DISPLAY_NAME exposed to a target application. If the target application naively uses this display name to save the dropped file, it can lead to an arbitrary file write within the target application’s private data directory, effectively acting as a sandbox escape.
Note: This is a potential vulnerability identified via static code analysis. Our tooling agent does not yet have the ability to run code to produce a live proof-of-concept.
Vulnerability Details
- Mojo IPC: A compromised renderer can construct a
blink.mojom.DragItemBinarystruct and set thefilename_extensionfield to a path traversal string (e.g.,png/../../../../data/data/com.victim/files/payload). - Missing Validation: In the browser process,
RenderWidgetHostImpl::StartDraggingconverts this IPC data into aDropDataobject. Security checks are applied to file paths, but thefile_contents_filename_extensionfield is not sanitized because it is assumed to be a short, benign extension. - Unsanitized Fallback in Android JNI: In
content/browser/android/drop_data_android.cc, the browser attempts to generate a safe filename usingGetSafeFilenameForImageFileContents(). Because the malicious extension is not a recognized MIME type, this fails. The code then relies on fallback logic that blindly concatenates the extension to a timestamp:base::NumberToString(...) + "." + drop_data.file_contents_filename_extension - Confused Deputy: This tainted string is passed to the Android Java layer and cached in
DropDataProviderImpl. When a target application receives the drop and queries theContentProviderURI for file metadata, Chrome returns the tainted string asOpenableColumns.DISPLAY_NAME.
Potential Attack Steps
- An attacker compromises a Chrome renderer process (e.g., via a v8 exploit).
- The attacker triggers a
LocalFrameHost::StartDraggingIPC, embedding aDragItemBinarypayload with a maliciousfilename_extension(e.g.,png/../../../../data/data/com.victim/files/libmalicious.so) and custom file bytes. - The attacker tricks the user into dragging the malicious element from Chrome into a vulnerable target application (e.g., in split-screen mode).
- The target application resolves the dropped
ContentProviderURI, queries theDISPLAY_NAME, and receives the traversal string. - If the target app uses this
DISPLAY_NAMEto construct a file path (e.g.,new File(targetDir, displayName)), the payload is written outside the intended directory, overwriting sensitive files or native libraries to achieve code execution in the victim app.
Suggested Fix
The browser process must aggressively sanitize the file_contents_filename_extension before using it or passing it to the OS.
- In
content/browser/android/drop_data_android.cc(or earlier inDragDataToDropData), ensure that any path separators (/,\) or directory traversal sequences (..) are stripped from the extension. - A simple fix in
ToJavaDropDatacould be utilizingbase::FilePath(drop_data.file_contents_filename_extension).BaseName().value()or explicitly checking that the extension only contains alphanumeric characters.
Evaluated with Chrome root at commit: a9cbf6e8b275fe4147435aa905f3b7f5a656f5f0
Results from so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; 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.