CVE-2026-13902
Overview
Files Changed
ios/chrome/browser/download/coordinator/download_manager_mediator.mmios/chrome/browser/download/model/ar_quick_look_tab_helper.mm
Patch
From a9364d82839ee1641d3f36080db841773b41c38a Mon Sep 17 00:00:00 2001
From: Quentin Pubert <qpubert@google.com>
Date: Tue, 26 May 2026 07:15:28 -0700
Subject: [PATCH] [iOS] Use unique temporary directory per download task
Both AR Quick Look downloads and standard downloads were using the
shared temporary directory <tmp>/downloads with the filename generated
from the task. This could lead to collisions and potential overwrites if
multiple downloads had the same filename, or if a malicious iframe
triggered a download with a predictable filename to overwrite an
in-flight download.
This CL fixes the issue by appending the unique task identifier (UUID)
to the temporary directory path, resulting in
<tmp>/downloads/<uuid>/<filename>. This ensures that each download task
has its own isolated temporary directory.
Fixed: 503725717
Change-Id: I92674daac5f332c8894e1e830a62fab17b18dbb0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7869562
Auto-Submit: Quentin Pubert <qpubert@google.com>
Commit-Queue: Olivier Robin <olivierrobin@chromium.org>
Reviewed-by: Olivier Robin <olivierrobin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1636151}
---
diff --git a/ios/chrome/browser/download/coordinator/download_manager_mediator.mm b/ios/chrome/browser/download/coordinator/download_manager_mediator.mm
index a75ecdca..df7b79d 100644
--- a/ios/chrome/browser/download/coordinator/download_manager_mediator.mm
+++ b/ios/chrome/browser/download/coordinator/download_manager_mediator.mm
@@ -123,8 +123,9 @@
// "Start Download" button.
[consumer_ setState:DownloadManagerState::kInProgress];
- download_task_->Start(
- download_dir.Append(download_task_->GenerateFileName()));
+ base::FilePath task_dir = download_dir.Append(
+ base::SysNSStringToUTF8(download_task_->GetIdentifier()));
+ download_task_->Start(task_dir.Append(download_task_->GenerateFileName()));
// If an upload task associated with the current download task exists, start
// to observe it.
UpdateUploadTask();
diff --git a/ios/chrome/browser/download/model/ar_quick_look_tab_helper.mm b/ios/chrome/browser/download/model/ar_quick_look_tab_helper.mm
index 562c018..bc6a364b 100644
--- a/ios/chrome/browser/download/model/ar_quick_look_tab_helper.mm
+++ b/ios/chrome/browser/download/model/ar_quick_look_tab_helper.mm
@@ -12,6 +12,7 @@
#import "base/functional/bind.h"
#import "base/metrics/histogram_functions.h"
#import "base/strings/escape.h"
+#import "base/strings/sys_string_conversions.h"
#import "base/task/thread_pool.h"
#import "ios/chrome/browser/download/model/ar_quick_look_tab_helper_delegate.h"
#import "ios/chrome/browser/download/model/download_directory_util.h"
@@ -137,8 +138,9 @@
LogHistogram(download_task_.get());
download_task_->AddObserver(this);
- download_task_->Start(
- download_dir.Append(download_task_->GenerateFileName()));
+ base::FilePath task_dir = download_dir.Append(
+ base::SysNSStringToUTF8(download_task_->GetIdentifier()));
+ download_task_->Start(task_dir.Append(download_task_->GenerateFileName()));
// Calling DownloadTask::Start() may cause the task to be immediately
// destroyed (e.g. if it is in error). Only call `LogHistogram` is it
Original Bug Report
Potential download spoofing and data loss via promptless AR Quick Look downloads on iOS
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 without the Chrome Security team. Please see go/chrome-ai-generated-security-bugs-faq for more information.
Overview: A potential logic vulnerability in the AR Quick Look download flow on iOS allows a malicious cross-origin iframe to trigger a promptless download. This download can overwrite a user’s legitimate in-flight download in the shared temporary directory due to unconditional file deletion and predictable paths. As a result, the user may experience silent data loss and be presented with a spoofed file.
Affected files:
ios/chrome/browser/download/model/ar_quick_look_tab_helper.mmios/chrome/browser/download/model/browser_download_service.mmios/web/download/download_task_impl.mmios/web/navigation/crw_wk_navigation_handler.mmios/chrome/browser/download/model/download_directory_util.mm
Estimated timestamp from git blame: 2026-02-19
Technical Details
The AR Quick Look feature in Chrome for iOS handles files with specific MIME types (e.g., model/vnd.usdz+zip). A potential logic flaw in how these downloads are initiated allows an attacker to spoof in-progress downloads and cause data loss.
- Promptless Initiation: In
ios/web/navigation/crw_wk_navigation_handler.mm, thekPreventNavigationWithoutUserInteractionpolicy decision bypasses user-gesture requirements for non-main-frame navigations. This allows an<iframe>to trigger a navigation without user interaction. When the response MIME type is a USDZ format, Chrome refuses to render it and converts it into aWKDownload. - Automatic Start:
BrowserDownloadService::OnDownloadCreatedroutes USDZ tasks toARQuickLookTabHelper::Download(), which immediately callstask->Start()without waiting for user confirmation. - Shared Temporary Path: Both AR Quick Look and standard downloads use a shared temporary directory (
<tmp>/downloads), defined inios/chrome/browser/download/model/download_directory_util.mm. - Unconditional File Deletion: When a download task starts,
DownloadTaskImpl::Start()callsCreateFileForDownload(), which executesbase::DeleteFile()on the target path to clear any existing file. - File Unlinking Race Condition: Because the target filename is derived from the server-supplied
Content-Dispositionheader, an attacker can target the predictable temporary path of a legitimate in-progress download.base::DeleteFile()will unlink the legitimate file. Under iOS/Unix file semantics, theWKDownloadwriting the legitimate file continues to write to the unlinked inode without failing. - Spoofing:
CreateFileForDownload()creates a new file at the exact same path for the attacker’s payload. Once the legitimate download completes,DownloadManagerTabHelperretrieves the file from the temporary path and moves it to the user’sNSDocumentDirectory. It inadvertently moves the attacker’s newly placed file, resulting in download spoofing and the loss of the legitimate file.
Potential Attack Scenario
Note: These are suggested steps; our tooling agent does not have the ability to run code, so this remains a potential exploit chain based on code analysis.
- A user navigates to a legitimate site and initiates a standard download (e.g.,
statement.pdf). Chrome begins downloading the file to<tmp>/downloads/statement.pdf. - A hidden cross-origin iframe (either embedded on the site or open in another tab) triggers a navigation to an attacker-controlled endpoint.
- The attacker’s server responds with
Content-Type: model/vnd.usdz+zipandContent-Disposition: attachment; filename="statement.pdf". - Chrome promptlessly starts an AR Quick Look download. It unlinks the existing
<tmp>/downloads/statement.pdffile and creates a new one, writing the attacker’s USDZ payload into it. - The legitimate download finishes. Chrome’s
DownloadManagerTabHelpermoves the file located at<tmp>/downloads/statement.pdf(now containing the attacker’s payload) into the user’s Documents directory. - The user attempts to open
statement.pdfand opens the attacker’s payload instead, while their actual document is lost.
Suggested Fix
- Unique Temporary Paths: Download tasks should use unique temporary directories (e.g., using a UUID per task) or append a unique identifier to the filename while downloading, rather than sharing a single
<tmp>/downloads/folder where collisions can occur. - Enforce User Interaction: AR Quick Look downloads initiated from subframes should be subject to strict user-gesture requirements before starting automatically.
- Collision Checks: Do not unconditionally delete existing files in the temporary directory if another active task in the browser is currently writing to that exact path.
Evaluated with Chrome root at commit: 661452647ddb2827305122ff3273bd5dea403f09
Results 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.