High watch CVSS 8.8 chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Google Updater
DescriptionInappropriate implementation in Google Updater
ComponentGoogle Updater
Sub-componentInternals>Updater
Bug ClassLogic Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker448113221
Fix commitbbecc30052b6 (chromium/src) +4/-10
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedJota Domingos
Reported2025-09-29 · bounty $3000 (per vendor advisory)
Fix landed2025-10-13 (public git)
Disclosed2025-12-02
Patch gap50 days — fix public in source before the advisory

Background

Google Updater usage-stats check (macOS)
usage_stats_permissions_mac.mm decides whether the updater may send usage stats by checking whether any managed app’s directory holds a Crashpad database with uploads enabled (AppAllowsUsageStats).
`crashpad::CrashReportDatabase::Initialize`
opens a Crashpad database at a path and creates it if needed, meaning the database directory tree and settings file. It is a read-write operation, not a pure query.
`CrashReportDatabase::GetSettingsReaderForDatabasePath`
returns a read-only settings reader for the database at a path, used to query values such as GetUploadsEnabled without opening the database for writing.
App directories
the per-app paths returned by GetAppDirectories, which AppAllowsUsageStats inspects. The diff does not show where they are or who owns them.

Root Cause Analysis

Before the patch, AppAllowsUsageStats checked base::PathExists(app_directory.Append("Crashpad")) and then called crashpad::CrashReportDatabase::Initialize(crashpad) only to read a single boolean through GetSettings()->GetUploadsEnabled. Because Initialize is a read-write open, the updater acted on a database it does not own. A pure read query thus became a filesystem write operation on a path inside another application’s directory, and the updater may run with higher privileges than whoever controls that directory. The PathExists check did not prevent this, since it only confirmed that the Crashpad entry existed and did not check ownership or type.

The patch removes both the PathExists check and the Initialize call and uses crashpad::CrashReportDatabase::GetSettingsReaderForDatabasePath(app_directory.Append("Crashpad"))->GetUploadsEnabled(&enabled). The check now reads only, and the return condition (GetUploadsEnabled(...) && enabled) is unchanged.

Key insight
To answer “are uploads enabled?”, the updater fully opened another app’s Crashpad database with CrashReportDatabase::Initialize, an operation that can create or modify files. The fix uses a read-only settings reader, so the check never writes to that location.

Attack Path

  1. Control of an app's `Crashpad` path a local, lower-privileged attacker controls the contents of an app directory that GetAppDirectories returns, for example by creating or replacing a Crashpad entry there (inferred; the diff does not show who owns these paths).
  2. Updater runs the usage-stats check the updater calls AppAllowsUsageStats, and the old code reaches CrashReportDatabase::Initialize on the attacker-influenced path once PathExists succeeds.
  3. Privileged database initialization Initialize opens the database for read and write and may create directories or files under that path with the updater’s privileges. A link the attacker planted could redirect those writes elsewhere (inferred).
  4. Escalation writing or creating files at attacker-chosen locations as a more privileged updater could lead to local privilege escalation (inferred; the advisory states only “Inappropriate implementation”, rated high).

Impact Assessment

The patch shows that a privileged updater code path performed a read-write Crashpad database open on paths it does not own, when only a read was needed. If the updater runs at system scope and the target directory is attacker-writable, this can become privileged file creation or modification and possibly local privilege escalation (inferred). The advisory rates it high severity and reports no in-the-wild exploitation.

Changed Functions

FunctionChangeNotes
if
chrome/updater/usage_stats_permissions_mac.mm
modified

Files Changed

  • chrome/updater/usage_stats_permissions_mac.mm

Audit Directions

  • Read-write opens used for read-only queries in privileged code
    in chrome/updater/ and other elevated helpers, find any Initialize, Open or create-if-missing call on paths in user-owned or app-owned locations whose result is only read.
  • Privileged traversal of other apps' directories
    review the other callers that iterate over GetAppDirectories or similar enumerations on macOS and Windows. Check symlink handling and whether file-creating operations run as root or SYSTEM.
  • Existence check as a stand-in for ownership
    patterns like PathExists followed by an open are race-prone and say nothing about owner or type. Look for this pattern anywhere a privileged process acts on a path a less-privileged user could control.
diff --git a/chrome/updater/usage_stats_permissions_mac.mm b/chrome/updater/usage_stats_permissions_mac.mm
index 6eb29377..9edb9d3 100644
--- a/chrome/updater/usage_stats_permissions_mac.mm
+++ b/chrome/updater/usage_stats_permissions_mac.mm
@@ -31,17 +31,11 @@
 // Returns true if the directory contains a crashpad database with uploads
 // enabled.
 bool AppAllowsUsageStats(const base::FilePath& app_directory) {
-  base::FilePath crashpad = app_directory.Append("Crashpad");
-  if (!base::PathExists(crashpad)) {
-    return false;
-  }
-  std::unique_ptr<crashpad::CrashReportDatabase> app_database =
-      crashpad::CrashReportDatabase::Initialize(crashpad);
-  if (!app_database) {
-    return false;
-  }
   bool enabled = false;
-  return app_database->GetSettings()->GetUploadsEnabled(&enabled) && enabled;
+  return crashpad::CrashReportDatabase::GetSettingsReaderForDatabasePath(
+             app_directory.Append("Crashpad"))
+             ->GetUploadsEnabled(&enabled) &&
+         enabled;
 }
 
 std::vector<base::FilePath> GetAppDirectories(
Loading diff…

Original Bug Report

reported by jo...@gmail.com

LPE - Arbitrary File Write in Google Chrome Enterprise (MacOS): The GoogleUpdater, which is executed by root, follows symlinks when writing the file settings.dat in the user folder

Summary: LPE - Arbitrary File Write in Google Chrome Enterprise (MacOS): The GoogleUpdater, which is executed by root, follows symlinks when writing the file settings.dat in the user folder

Program: Google VRP

Vulnerability type: Privilege Escalation

Details

Vulnerability Description

When accessing chrome://settings/help in Google Chrome Enterprise - Version 140.0.7339.214 (Official Build) (x86_64) for MacOS, the GoogleUpdater.app ("/Library/Application Support/Google/GoogleUpdater/Current/GoogleUpdater.app/Contents/Helpers/launcher") is executed as root.

The launcher executes the following command:

  • /Library/Application\ Support/Google/GoogleUpdater/Current/GoogleUpdater.app/Contents/MacOS/GoogleUpdater –server –service=update –system

Which will execute the following command:

  • /Library/Application Support/Google/GoogleUpdater/142.0.7416.0/GoogleUpdater.app/Contents/MacOS/GoogleUpdater –crash-handler –system –database=/Library/Application Support/Google/GoogleUpdater/142.0.7416.0/Crashpad –url=https://clients2.google.com/cr/report –annotation=prod=Update4 –annotation=ver=142.0.7416.0 –handshake-fd=5

This will trigger a file read and write of:

  • /Users/<User>/Library/Application Support/Google/Chrome/Crashpad/settings.dat

The user has full permissions on the folder Crashpad and the file settings.dat. This allows the user to create a symbolic link in the folder which will be followed by the GoogleUpdater application when writing the file settings.dat.

This allows a non-privileged user to obtain a arbitrary file write. However, I did not found a way to control the content of the file.

Attack Preconditions

To exploit this issue, an attacker must have access as non-privileged user to the machine.

Reproduction Steps / POC

To exploit this issue, perform the following steps:

  1. Open Google Chrome Enterpise
  2. Remove the file /Users/<User>/Library/Application Support/Google/Chrome/Crashpad/settings.dat if it exists
  3. Create the symlink: ln -sf /tmp/arbitrary_write /Users/$USER/Library/Application\ Support/Google/Chrome/Crashpad/settings.dat
  4. Go to Help -> About Google Chrome
  5. Verify that the file /tmp/arbitrary_write was created by root

Attack scenario

Every user in the system independently of their privileges is able to exploit this issue, since the GoogleUpdater will go through every user folder at /Users.

This allows a non-privileged user to overwrite any file in the filesystem. However, I did not found a way to control the content of the settings.dat file, which means that in the end the impact will depend on the file that the user overwrites.

View on issue tracker