CVE-2026-12468
Overview
Files Changed
chrome/browser/ui/webui/updater/updater_page_handler.cc
Patch
From 591ea6ba7679ca32b09009904894820c3983b9dc Mon Sep 17 00:00:00 2001 From: Noah Rose Ledesma <[email protected]> Date: Tue, 09 Jun 2026 13:27:38 -0700 Subject: [PATCH] Avoid TOCTOU in UnzipUpdaterHistoryFilesImpl On some platforms, the temporary directory created by ScopedTempDir is in a location accessible by some sandboxed processes. Avoid possible sandbox escapes leveraging the UnzipUpdaterHistoryFilesImpl functionality which replace the "input.zip" with a symbolic link. Fixed: 521485244 Change-Id: Ic7e64f34f5f6f6740ca456b67a2ad18a6a6a6964 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7911844 Auto-Submit: Noah Rose Ledesma <[email protected]> Commit-Queue: Sorin Jianu <[email protected]> Reviewed-by: Sorin Jianu <[email protected]> Cr-Commit-Position: refs/heads/main@{#1644216} --- diff --git a/chrome/browser/ui/webui/updater/updater_page_handler.cc b/chrome/browser/ui/webui/updater/updater_page_handler.cc index a99a9f3..711d174 100644 --- a/chrome/browser/ui/webui/updater/updater_page_handler.cc +++ b/chrome/browser/ui/webui/updater/updater_page_handler.cc @@ -14,6 +14,7 @@ #include "base/barrier_closure.h" #include "base/containers/flat_map.h" +#include "base/files/file.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/file_util.h" @@ -226,7 +227,10 @@ } base::FilePath archive_path = temp_dir.GetPath().AppendASCII("input.zip"); - if (!base::WriteFile(archive_path, zip_data)) { + base::File archive(archive_path, + base::File::FLAG_CREATE | base::File::FLAG_WRITE); + if (!archive.IsValid() || + !archive.WriteAtCurrentPosAndCheck(base::span(zip_data))) { std::move(callback).Run( base::unexpected(updater_ui::mojom::UnzipUpdaterHistoryFilesError::New( "Failed to write user-supplied zip data to storage")));
Original Bug Report
Symlink TOCTOU in UnzipUpdaterHistoryFilesImpl allows macOS Sandbox Escape
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: A potential race condition in UnzipUpdaterHistoryFilesImpl on macOS allows a compromised sandboxed process to plant a symbolic link in a shared temporary directory. Because base::WriteFile uses POSIX creat(), which follows symbolic links, the browser process can be tricked into truncating and overwriting arbitrary files outside the sandbox. This can potentially be leveraged to escape sandbox isolation.
Affected files:
chrome/browser/ui/webui/updater/updater_page_handler.cc
Estimated timestamp from git blame: 2026-03-19
Description
A potential Time-of-Check to Time-of-Use (TOCTOU) vulnerability exists in the UnzipUpdaterHistoryFilesImpl function on macOS. The issue arises from the use of a shared temporary directory in combination with a file creation utility that follows symbolic links.
Root Cause
-
Shared Temporary Directory: In
chrome/browser/ui/webui/updater/updater_page_handler.cc(lines 220-221),UnzipUpdaterHistoryFilesImplcreates a temporary directory usingtemp_dir.CreateUniqueTempDir(). On macOS, this resolves toNSTemporaryDirectory()(defined via_CS_DARWIN_USER_TEMP_DIRinbase/files/file_util_apple.mm). Several heavily sandboxed child processes (such as the GPU, Network, and On-Device Model Execution processes) are explicitly granted read and write access todarwin-user-temp-dirin their Seatbelt profiles (e.g.,sandbox/policy/mac/network.sblines 50-53,sandbox/policy/mac/gpu.sblines 124-128). Because these child processes run under the same User ID (UID) as the browser process, standard UNIX Discretionary Access Control (DAC) does not isolate their files. -
Symlink-Following File Write: The browser process subsequently creates a file with a static name within the temporary directory and writes the user-supplied ZIP data into it:
base::FilePath archive_path = temp_dir.GetPath().AppendASCII("input.zip"); if (!base::WriteFile(archive_path, zip_data)) { ... }On POSIX platforms,
base::WriteFileis implemented using thecreat()system call (base/files/file_util_posix.ccline 1157), which is equivalent toopen(..., O_WRONLY | O_CREAT | O_TRUNC):int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666));Because this call does not specify the
O_NOFOLLOWflag, it resolves and follows symbolic links.
Potential Attack Scenario
Note: The following steps are potential/theoretical. Our analysis is based on static code review, and we have not validated this path with an active exploit payload.
- An attacker compromises a sandboxed child process (e.g., the Network or GPU process) via an independent renderer-to-helper vulnerability.
- In the compromised process, the attacker executes a fast loop calling
readdiron the directory returned byconfstr(_CS_DARWIN_USER_TEMP_DIR). It monitors the creation of any new directories starting withscoped_dir. - The victim user navigates to
chrome://updaterand triggers a history log ZIP upload. - The browser process executes
temp_dir.CreateUniqueTempDir()to create a directory (e.g.,scoped_dirA1B2C3). - The attacker’s loop instantly detects the new directory and creates a symbolic link inside it:
symlink("/Users/[username]/.zshrc", "scoped_dirA1B2C3/input.zip"); - The browser process proceeds to call
base::WriteFile(archive_path, zip_data). The kernel follows theinput.zipsymbolic link, truncating and overwriting the user’s.zshrcfile with the ZIP payload. - When the user later opens a terminal, the overwritten
.zshrcis sourced, executing arbitrary commands outside the sandbox with full user privileges.
Suggested Remediation
To mitigate this risk, avoid using symbolic-link-following file writes in shared temporary directories.
- Use
O_NOFOLLOW: Modify the file-writing logic to open the target path with theO_NOFOLLOWoption (or usebase::Filewithbase::File::FLAG_WIN_SHARE_DELETEor similar flags that prevent symbolic link resolution on POSIX). - Use Safe Temp File APIs: Instead of appending a static filename to a directory, use
base::CreateTemporaryFileInDirwhich safely creates a uniquely named file with restrictive file permissions. - Isolate Extraction: Alternatively, perform extraction in a location that is not shared with sandboxed child processes.
Evaluated with Chrome root at commit: 3947e01999a53d4e2382e39736cb79d79c7dffcf
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.