Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Updater
DescriptionInappropriate implementation in Updater
ComponentUpdater
Bug ClassLogic Error
Tracker524467747
Fix commitec052a60bfc5 (chromium/src) +31/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
if
chrome/updater/ipc/update_service_dialer_win.cc
modified

Files Changed

  • chrome/updater/ipc/update_service_dialer_win.cc
From ec052a60bfc5adfdc425f979fd1510be0b0313e9 Mon Sep 17 00:00:00 2001
From: S Ganesh <[email protected]>
Date: Tue, 16 Jun 2026 16:36:52 -0700
Subject: [PATCH] [updater] Verify that system-scope Mojo connection servers are elevated

When a client connects to the system-scope updater Mojo server on
Windows, it expects to connect to a trusted Administrative/SYSTEM
context. However, Mojo's standard named-pipe connection verify
privilege logic checks that the server's integrity level is greater
than or equal to the client's integrity level. Consequently, a
medium-integrity context client connecting to a system-scope updater
can be tricked into connecting to a spoofed medium-integrity server
instance.

This CL resolves the issue by adding a verification check in the
client dialer. If the scope is system, it verifies that the owner of
the named pipe is either LOCAL_SYSTEM or BUILTIN_ADMINISTRATORS
before completing the pipe connection, and rejects it if it is not.

Bug: 524467747
Change-Id: I930a9264ccb82d883b4929b2827a8381d3174f0e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7952897
Reviewed-by: Sorin Jianu <[email protected]>
Commit-Queue: S Ganesh <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1647974}
---

diff --git a/chrome/updater/ipc/update_service_dialer_win.cc b/chrome/updater/ipc/update_service_dialer_win.cc
index 5c5f06f..efe5c80 100644
--- a/chrome/updater/ipc/update_service_dialer_win.cc
+++ b/chrome/updater/ipc/update_service_dialer_win.cc
@@ -17,6 +17,8 @@
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
+#include "base/win/security_descriptor.h"
+#include "base/win/sid.h"
 #include "chrome/updater/app/server/win/updater_idl.h"
 #include "chrome/updater/app/server/win/updater_internal_idl.h"
 #include "chrome/updater/constants.h"
@@ -59,6 +61,27 @@
   return server;
 }
 
+bool IsServerElevated(HANDLE pipe_handle) {
+  std::optional<base::win::SecurityDescriptor> sd =
+      base::win::SecurityDescriptor::FromHandle(
+          pipe_handle, base::win::SecurityObjectType::kFile,
+          OWNER_SECURITY_INFORMATION);
+  if (!sd) {
+    PLOG(ERROR) << "Failed to get security descriptor for pipe";
+    return false;
+  }
+
+  const std::optional<base::win::Sid>& owner = sd->owner();
+  if (!owner) {
+    LOG(ERROR) << "Pipe owner is missing";
+    return false;
+  }
+
+  return *owner == base::win::Sid(base::win::WellKnownSid::kLocalSystem) ||
+         *owner ==
+             base::win::Sid(base::win::WellKnownSid::kBuiltinAdministrators);
+}
+
 void ConnectMojoImpl(
     UpdaterScope scope,
     bool is_internal_service,
@@ -90,7 +113,14 @@
                               : GetUpdateServiceServerName(scope);
     options.allow_impersonation = true;
     options.verify_server_privilege = true;
-    return named_mojo_ipc_server::ConnectToServer(options);
+    mojo::PlatformChannelEndpoint connected_endpoint =
+        named_mojo_ipc_server::ConnectToServer(options);
+    if (IsSystemInstall(scope) && connected_endpoint.is_valid() &&
+        !IsServerElevated(
+            connected_endpoint.platform_handle().GetHandle().get())) {
+      return std::nullopt;
+    }
+    return connected_endpoint;
   }();
 
   if (tries >= 1 && !endpoint) {
Loading diff…

Original Bug Report

reported by [email protected]

Potential cross-user named-pipe spoofing of system-scope Chromium Updater Mojo channel on Windows

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 vulnerability in the Windows implementation of the system-scope Chromium Updater allows unprivileged co-resident users to spoof the Mojo IPC named pipe. Due to a permissive DACL, disabled pipe uniqueness, and weak client verification, an attacker can hijack browser connections. This leads to cross-user update suppression, restricted token disclosure, and internal UI spoofing.

Affected files:

  • chrome/updater/ipc/update_service_dialer_win.cc
  • chrome/updater/ipc/ipc_security_win.cc
  • mojo/public/cpp/platform/named_platform_channel_win.cc
  • components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_win.cc
  • chrome/updater/ipc/ipc_names_win.cc

Estimated timestamp from git blame: Unknown (Google3 checkout)

Section 1: Summary of the Issue (Meant for Human Triage)

A potential vulnerability on Windows multi-user environments allows an unprivileged, co-resident user to perform cross-user named pipe spoofing against the system-scope Chromium Updater Mojo channel. This occurs because the Chrome browser process connects to a predictable named pipe where the server-side DACL allows Authenticated Users (AU) FILE_CREATE_PIPE_INSTANCE privileges.

Additionally, the legitimate service disables pipe uniqueness enforcement, meaning Windows allows multiple instances of the pipe to coexist. Finally, the client-side authentication in Chrome only validates that the server’s Integrity Level (IL) is greater than or equal to its own. It fails to validate the session ID or SID of the server process.

As a result, a non-admin attacker can pre-create a rogue instance of the \.\pipe\mojo.<ProductFullName>System pipe. When another user starts or runs Chrome at Medium IL, the browser connects to this pipe. Because the attacker’s server also runs at Medium IL, the integrity level check passes, establishing a trusted Mojo connection.

This enables the following impacts:

  1. Cross-user update suppression: The attacker can provide fake update replies, deceiving the browser into believing the updater is fully healthy and up-to-date, thereby suppressing critical updates indefinitely (Denial of Service).
  2. UI Spoofing: The attacker can send serialized mojom messages that are parsed in the browser process and displayed on internal chrome://updater and chrome://policy diagnostic pages.
  3. Limited token disclosure: The client requests the pipe with allow_impersonation = true (mapping to SECURITY_IMPERSONATION). Since standard users lack SeImpersonatePrivilege, Windows restricts the impersonation to Identification level, safely preventing LPE code execution but disclosing the victim’s SID and group memberships to the attacker.

Per Chromium’s severity guidelines, local-LPE vulnerabilities that are restricted to DoS-only (e.g., update suppression) and spoofing on non-primary security surfaces are rated as Low severity (S3).


Section 2: Proof-of-Concept & Detailed Execution Flow

Note: The following steps are potential and suggested; our tooling agent does not have the ability to run code to demonstrate a fully weaponized proof-of-concept, but the logic has been exhaustively verified against the codebase.

1. Attacker Setup and Pipe Squatting

  1. The Chromium Updater uses Mojo over named pipes for IPC. The server’s pipe name is predictably generated in chrome/updater/ipc/ipc_names_win.cc:22-31. GetUpdateServiceServerName concatenates the product full name and the scope (System), yielding \.\pipe\mojo.<ProductFullName>System (resolved by GetPipeNameFromServerName in named_platform_channel_win.cc).
  2. The legitimate system-scope updater service defines the DACL for this pipe in chrome/updater/ipc/ipc_security_win.cc:32-43 as D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;AU). The (A;;GRGW;;;AU) ACE grants “Generic Read/Generic Write” to all “Authenticated Users”.
  3. In Windows, Generic Write (FILE_GENERIC_WRITE) inherently maps to the FILE_CREATE_PIPE_INSTANCE (0x04) permission, permitting any authenticated user to create additional instances of this pipe.
  4. The legitimate updater service configures the Mojo server endpoint to allow multiple clients by setting options.enforce_uniqueness = false; in components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_win.cc:118.
  5. Because enforce_uniqueness is false, mojo/public/cpp/platform/named_platform_channel_win.cc:110-113 omits the FILE_FLAG_FIRST_PIPE_INSTANCE flag when calling CreateNamedPipeW.
  6. An unprivileged attacker logs into the Windows host, invokes CreateNamedPipeW with the predictable pipe name, and calls ConnectNamedPipe to place their rogue instance into a listening state.

2. Victim Trigger Path

  1. A victim user logs into the host and launches Chrome.
  2. Background tasks are scheduled via updater::SchedulePeriodicTasks (chrome/browser/chrome_browser_main.cc:1053 -> chrome/browser/updater/scheduler.cc:30).
  3. On Windows, DoPeriodicTasks executes CheckUpdaterHealthTask (chrome/browser/updater/scheduler_win.cc:20-23).
  4. CheckUpdaterHealthTask calls BrowserUpdaterClient::Create(scope)->GetUpdaterVersion(...) (chrome/browser/updater/check_updater_health_task.cc:109).
  5. This initiates a connection: UpdateServiceProxyMojoImpl::GetVersion -> EnsureConnecting -> ConnectMojo (chrome/updater/ipc/update_service_proxy_mojo.cc:191, 445).
  6. ConnectMojoImpl (chrome/updater/ipc/update_service_dialer_win.cc:62) first calls DialUpdateService.
  7. DialUpdateService makes a blocking COM call (::CoCreateInstance) to activate the legitimate updater service.
  8. The legitimate service starts, calls CreateNamedPipeW (succeeding without FILE_FLAG_FIRST_PIPE_INSTANCE), and begins listening. Now, both the attacker’s older instance and the legitimate service’s newer instance are listening.
  9. Once CoCreateInstance returns, the browser client calls named_mojo_ipc_server::ConnectToServer, explicitly setting options.allow_impersonation = true and options.verify_server_privilege = true (chrome/updater/ipc/update_service_dialer_win.cc:87-94).
  10. The client invokes CreateFileW to connect. The Windows NPFS deterministically routes incoming client connections to the oldest listening instance. Since the attacker’s pipe was created before COM activation, the victim’s connection routes to the attacker’s pipe.

3. Authentication Bypass and Exploitation

  1. The browser invokes VerifyServerPrivilege (mojo/public/cpp/platform/named_platform_channel_win.cc:37-57).
  2. VerifyServerPrivilege only checks if the server’s Integrity Level is greater than or equal to the client’s (server_token->IntegrityLevel() >= client_token->IntegrityLevel()).
  3. Because the attacker and victim both run at Medium IL, the condition evaluates to true. The connection is accepted.
  4. The browser client wraps the pipe in a mojo::IsolatedConnection and fuses it to a mojom::UpdateService remote via mojo::FusePipes (chrome/updater/ipc/update_service_proxy_mojo.cc:409-421).
  5. The browser sends Mojo requests to the attacker’s rogue server. The attacker responds with arbitrary spoofed Mojo replies (e.g., UpdateState::State::kUpdated).
  6. The spoofed responses deceive the browser into believing Chrome is up-to-date, suppressing updates.
  7. Additionally, because the client connected with SECURITY_IMPERSONATION, the attacker calls ImpersonateNamedPipeClient. Windows caps this at the “Identification” level due to missing SeImpersonatePrivilege, allowing the attacker to read the token’s properties (SID/group memberships).

Suggested Fix

The Chromium VerifyServerPrivilege logic for the Updater client should be updated to strictly enforce that the named pipe server is running in Session 0, verifying the service’s identity similarly to how Chromoting handles it in remoting/host/chromoting_host_services_client.cc:50-62:

DWORD peer_session_id;
if (!GetNamedPipeServerSessionId(endpoint.platform_handle().GetHandle().get(), &peer_session_id)) { ... }
if (peer_session_id != 0) { ... }

Alternatively, IsConnectionTrusted in chrome/updater/ipc/ipc_security_win.cc could enforce stricter constraints, or the pipe’s DACL could be hardened to restrict AU from possessing FILE_GENERIC_WRITE (specifically FILE_CREATE_PIPE_INSTANCE).


Section 3: Technical Verification Details (Automated Audit Logs - Reviewers may skip this section)

> The vulnerability report correctly identifies a cross-user named pipe spoofing vulnerability in the system-scope Chromium Updater Mojo channel on Windows. > > The root cause involves three factors: > 1. The updater’s named pipe DACL grants Authenticated Users (AU) Generic Write (GW), which maps to FILE_GENERIC_WRITE and crucially includes the FILE_CREATE_PIPE_INSTANCE (0x04) permission. > 2. The legitimate SYSTEM server creates its pipe with enforce_uniqueness = false, meaning it does not set FILE_FLAG_FIRST_PIPE_INSTANCE. > 3. The browser client’s VerifyServerPrivilege function only ensures that server_IL >= client_IL without checking the server’s session ID or SID. > > As a result, any unprivileged user on a multi-user Windows system can create a rogue instance of the predictable \\.\pipe\mojo.<ProductFullName>System pipe. When the victim’s browser process connects, it runs at Medium IL, and the attacker’s rogue server also runs at Medium IL, so the IL check passes. > > Although the client connects with SECURITY_IMPERSONATION, a standard non-admin attacker lacks SeImpersonatePrivilege, meaning Windows silently caps the impersonation token at the Identification level. This prevents full Local Privilege Escalation (LPE) and restricts the attacker to identifying the victim (SID/group disclosure). > > The impact is cross-user update suppression (the attacker can return spoofed UpdateService responses claiming Chrome is up-to-date) and UI spoofing of the internal chrome://updater page. According to Chromium’s severity guidelines, LPE vulnerabilities that are restricted to DoS-only (e.g., update suppression) and spoofing on non-primary security surfaces are rated as Low severity (S3).

Codebase Investigator Output Summaries:

  1. IPC Names Validation: chrome/updater/ipc/ipc_names_win.cc:22-43 confirmed entirely predictable. “GetUpdateServiceServerName: Constructed via GetMojoServerName(scope, false) which concatenates PRODUCT_FULLNAME_STRING and either ‘System’ or ‘User’. It does NOT include a version string or a random token.”

  2. GetNamedPipeServerSessionId Contrast Validation: remoting/host/chromoting_host_services_client.cc:50-62 implements this securely. “Inside the ConnectToServer function, the client retrieves the peer named pipe server’s session ID and verifies that it is 0… There is no direct call to GetNamedPipeServerSessionId in chrome/updater.”

  3. enforce_uniqueness and FILE_FLAG_FIRST_PIPE_INSTANCE: components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_win.cc:118 explicitly sets options.enforce_uniqueness = false;. mojo/public/cpp/platform/named_platform_channel_win.cc:110-113 demonstrates that if enforce_uniqueness is false, FILE_FLAG_FIRST_PIPE_INSTANCE is bypassed.

  4. Connection Routing: “Windows deterministically routes incoming client connections to the oldest listening instance of the named pipe.” Since the attacker establishes the pipe before COM triggers the legitimate process, the attacker’s pipe captures the victim’s request.

  5. VerifyServerPrivilege Implementation: Confirmed at mojo/public/cpp/platform/named_platform_channel_win.cc:37-57. It retrieves IL via server_token->IntegrityLevel() >= client_token->IntegrityLevel() and performs absolutely no Session or SID verification.

Evaluated with Chrome root at commit: 8c517fbcbb533e59ec9cedac868c8a9bdc30beb2


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