Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Updater
DescriptionInsufficient validation of untrusted input in Updater
ComponentUpdater
Bug ClassLogic Error
Tracker524824730
Fix commit3bb7f0f99e7c (chromium/src) +2/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Files Changed

  • chrome/updater/net/network_fetcher_mac.mm
From 3bb7f0f99e7c003581316497bf742064d19729f3 Mon Sep 17 00:00:00 2001
From: Noah Rose Ledesma <noahrose@google.com>
Date: Wed, 17 Jun 2026 15:36:13 -0700
Subject: [PATCH] Adjust how OutOfProcessNetworkFetcher opens files

When downloading a file via the OutOfProcessNetworkFetcher, the updater
has no reason to follow symlinks.

Fixed: 524824730
Change-Id: I6150ee67085423a7b873b4678c1e07fc6a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7959278
Reviewed-by: Adam Norberg <norberg@google.com>
Auto-Submit: Noah Rose Ledesma <noahrose@google.com>
Commit-Queue: Noah Rose Ledesma <noahrose@google.com>
Cr-Commit-Position: refs/heads/main@{#1648641}
---

diff --git a/chrome/updater/net/network_fetcher_mac.mm b/chrome/updater/net/network_fetcher_mac.mm
index dd09ec0..79911705 100644
--- a/chrome/updater/net/network_fetcher_mac.mm
+++ b/chrome/updater/net/network_fetcher_mac.mm
@@ -823,7 +823,8 @@
       base::BindOnce(
           [](const base::FilePath& file_path) {
             return base::File(file_path, base::File::FLAG_OPEN_ALWAYS |
-                                             base::File::FLAG_WRITE);
+                                             base::File::FLAG_WRITE |
+                                             base::File::FLAG_NO_FOLLOW);
           },
           file_path),
       base::BindOnce(&OutOfProcessNetworkFetcher::DoDownloadFile,
Loading diff…

Original Bug Report

reported by vm...@google.com

macOS sandbox escape via symlink-based file system race in network_fetcher_mac

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 Time-of-Check to Time-of-Use (TOCTOU) symlink vulnerability in the macOS user-scope GoogleUpdater allows a compromised sandboxed process to overwrite arbitrary files outside the sandbox. Because the out-of-process fallback fetcher opens the download target path without the FLAG_NO_FOLLOW flag, it can be coerced into following symbolic links created by a sandboxed process inside the shared temporary directory. An attacker could exploit this to escape the sandbox by overwriting user configuration files or launch agents.

Affected files:

  • chrome/updater/net/network_fetcher_mac.mm
  • components/update_client/url_fetcher_downloader.cc
  • chrome/updater/net/fallback_net_fetcher.cc

Estimated timestamp from git blame: 2024-08-05

Description

A potential Time-of-Check to Time-of-Use (TOCTOU) symbolic link traversal vulnerability exists in the macOS user-scope GoogleUpdater during out-of-process fallback download operations.

In chrome/updater/net/network_fetcher_mac.mm (lines 805-834), OutOfProcessNetworkFetcher::DownloadToFile opens the target download path via base::File using the flags FLAG_OPEN_ALWAYS | FLAG_WRITE but does not include FLAG_NO_FOLLOW:

base::OnceClosure OutOfProcessNetworkFetcher::DownloadToFile(
    const GURL& url,
    const base::FilePath& file_path, ...) {
  ...
  base::ThreadPool::PostTaskAndReplyWithResult(
      FROM_HERE, {base::MayBlock()},
      base::BindOnce(
          [](const base::FilePath& file_path) {
            return base::File(file_path, base::File::FLAG_OPEN_ALWAYS |
                                             base::File::FLAG_WRITE);
          },
          file_path),
      base::BindOnce(&OutOfProcessNetworkFetcher::DoDownloadFile,
                     base::Unretained(this), url, ...));
  return base::DoNothing();
}

On POSIX/macOS, base::File::DoInitialize translates the omission of FLAG_NO_FOLLOW into an open(path, ...) call without O_NOFOLLOW (see base/files/file_posix.cc).

Trust Boundary Crossing

The user-scope GoogleUpdater is unsandboxed and runs under the same UID as the Chrome browser, meaning it shares the macOS per-UID temporary directory _CS_DARWIN_USER_TEMP_DIR with Chrome’s sandboxed child processes.

Because the browser sets this path as a sandbox parameter (darwin-user-temp-dir), macOS Seatbelt profiles for child processes (such as the GPU, Network, or On-Device Model execution processes) explicitly grant full file-write* or subpath-write privileges on that directory:

; sandbox/policy/mac/gpu.sb
(allow file-read* file-write-data file-write-create file-write-owner file-write-unlink
  (subpath (param darwin-user-temp-dir)) ...)

This permits a compromised, sandboxed child process to write, create, and link file entries inside this shared temporary directory.

Potential Attack Scenario

Note: These are potential steps. Our security review tooling does not currently have the capability to execute code or dynamically verify this exploitation path.

  1. An attacker compromises a sandboxed child process (e.g., the Network Service) and monitors the shared temporary directory (_CS_DARWIN_USER_TEMP_DIR) for the creation of component update directories (which match the pattern *_chrome_url_fetcher_*).
  2. When a directory like chrome_url_fetcher_Ab12Cd is detected, the compromised child process immediately creates a symbolic link at the expected download destination path, pointing to a sensitive file in the user’s home directory outside the sandbox (e.g., ~/.zprofile or ~/Library/LaunchAgents/com.victim.plist):
    symlink("/Users/victim/.zprofile", "/var/folders/.../T/chrome_url_fetcher_Ab12Cd/component.crx")
    
  3. The attacker triggers or waits for a network disruption to fail the primary in-process NSURLSession download task, forcing the update client to fallback to the out-of-process downloader via FallbackNetFetcher.
  4. The unsandboxed OutOfProcessNetworkFetcher::DownloadToFile opens the destination file path. Due to the lack of O_NOFOLLOW (FLAG_NO_FOLLOW), the kernel resolves the symlink and opens the target file (~/.zprofile) with write privileges, returning a valid file descriptor.
  5. This file descriptor is sent over Mojo to the network worker helper, which writes the downloaded payload starting from offset 0, overwriting or creating the target file outside the sandbox and enabling a sandbox escape.

Suggested Fix

Modify chrome/updater/net/network_fetcher_mac.mm to include base::File::FLAG_NO_FOLLOW when opening the path in DownloadToFile:

[](const base::FilePath& file_path) {
  return base::File(file_path, base::File::FLAG_OPEN_ALWAYS |
                                   base::File::FLAG_WRITE |
                                   base::File::FLAG_NO_FOLLOW);
}

Evaluated with Chrome root at commit: 75203b87cbf6681eb7c7dda8e1d0bf781538c76a


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.

View on issue tracker