CVE-2026-18018
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/updater/ipc/update_service_dialer_win.cc |
modified |
Files Changed
chrome/updater/ipc/update_service_dialer_win.cc
Patch
From ec052a60bfc5adfdc425f979fd1510be0b0313e9 Mon Sep 17 00:00:00 2001
From: S Ganesh <ganesh@chromium.org>
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 <sorin@chromium.org>
Commit-Queue: S Ganesh <ganesh@chromium.org>
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) {
Original Bug Report
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.ccchrome/updater/ipc/ipc_security_win.ccmojo/public/cpp/platform/named_platform_channel_win.cccomponents/named_mojo_ipc_server/named_mojo_server_endpoint_connector_win.ccchrome/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:
- 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).
- UI Spoofing: The attacker can send serialized mojom messages that are parsed in the browser process and displayed on internal
chrome://updaterandchrome://policydiagnostic pages. - Limited token disclosure: The client requests the pipe with
allow_impersonation = true(mapping toSECURITY_IMPERSONATION). Since standard users lackSeImpersonatePrivilege, Windows restricts the impersonation toIdentificationlevel, 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
- 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.GetUpdateServiceServerNameconcatenates the product full name and the scope (System), yielding\.\pipe\mojo.<ProductFullName>System(resolved byGetPipeNameFromServerNameinnamed_platform_channel_win.cc). - The legitimate system-scope updater service defines the DACL for this pipe in
chrome/updater/ipc/ipc_security_win.cc:32-43asD:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;AU). The(A;;GRGW;;;AU)ACE grants “Generic Read/Generic Write” to all “Authenticated Users”. - In Windows, Generic Write (
FILE_GENERIC_WRITE) inherently maps to theFILE_CREATE_PIPE_INSTANCE(0x04) permission, permitting any authenticated user to create additional instances of this pipe. - The legitimate updater service configures the Mojo server endpoint to allow multiple clients by setting
options.enforce_uniqueness = false;incomponents/named_mojo_ipc_server/named_mojo_server_endpoint_connector_win.cc:118. - Because
enforce_uniquenessis false,mojo/public/cpp/platform/named_platform_channel_win.cc:110-113omits theFILE_FLAG_FIRST_PIPE_INSTANCEflag when callingCreateNamedPipeW. - An unprivileged attacker logs into the Windows host, invokes
CreateNamedPipeWwith the predictable pipe name, and callsConnectNamedPipeto place their rogue instance into a listening state.
2. Victim Trigger Path
- A victim user logs into the host and launches Chrome.
- Background tasks are scheduled via
updater::SchedulePeriodicTasks(chrome/browser/chrome_browser_main.cc:1053->chrome/browser/updater/scheduler.cc:30). - On Windows,
DoPeriodicTasksexecutesCheckUpdaterHealthTask(chrome/browser/updater/scheduler_win.cc:20-23). CheckUpdaterHealthTaskcallsBrowserUpdaterClient::Create(scope)->GetUpdaterVersion(...)(chrome/browser/updater/check_updater_health_task.cc:109).- This initiates a connection:
UpdateServiceProxyMojoImpl::GetVersion->EnsureConnecting->ConnectMojo(chrome/updater/ipc/update_service_proxy_mojo.cc:191, 445). ConnectMojoImpl(chrome/updater/ipc/update_service_dialer_win.cc:62) first callsDialUpdateService.DialUpdateServicemakes a blocking COM call (::CoCreateInstance) to activate the legitimate updater service.- The legitimate service starts, calls
CreateNamedPipeW(succeeding withoutFILE_FLAG_FIRST_PIPE_INSTANCE), and begins listening. Now, both the attacker’s older instance and the legitimate service’s newer instance are listening. - Once
CoCreateInstancereturns, the browser client callsnamed_mojo_ipc_server::ConnectToServer, explicitly settingoptions.allow_impersonation = trueandoptions.verify_server_privilege = true(chrome/updater/ipc/update_service_dialer_win.cc:87-94). - The client invokes
CreateFileWto 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
- The browser invokes
VerifyServerPrivilege(mojo/public/cpp/platform/named_platform_channel_win.cc:37-57). VerifyServerPrivilegeonly checks if the server’s Integrity Level is greater than or equal to the client’s (server_token->IntegrityLevel() >= client_token->IntegrityLevel()).- Because the attacker and victim both run at Medium IL, the condition evaluates to true. The connection is accepted.
- The browser client wraps the pipe in a
mojo::IsolatedConnectionand fuses it to amojom::UpdateServiceremote viamojo::FusePipes(chrome/updater/ipc/update_service_proxy_mojo.cc:409-421). - The browser sends Mojo requests to the attacker’s rogue server. The attacker responds with arbitrary spoofed Mojo replies (e.g.,
UpdateState::State::kUpdated). - The spoofed responses deceive the browser into believing Chrome is up-to-date, suppressing updates.
- Additionally, because the client connected with
SECURITY_IMPERSONATION, the attacker callsImpersonateNamedPipeClient. Windows caps this at the “Identification” level due to missingSeImpersonatePrivilege, 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:
-
IPC Names Validation:
chrome/updater/ipc/ipc_names_win.cc:22-43confirmed 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.” -
GetNamedPipeServerSessionIdContrast Validation:remoting/host/chromoting_host_services_client.cc:50-62implements 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.” -
enforce_uniquenessandFILE_FLAG_FIRST_PIPE_INSTANCE:components/named_mojo_ipc_server/named_mojo_server_endpoint_connector_win.cc:118explicitly setsoptions.enforce_uniqueness = false;.mojo/public/cpp/platform/named_platform_channel_win.cc:110-113demonstrates that ifenforce_uniquenessis false,FILE_FLAG_FIRST_PIPE_INSTANCEis bypassed. -
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.
-
VerifyServerPrivilegeImplementation: Confirmed atmojo/public/cpp/platform/named_platform_channel_win.cc:37-57. It retrieves IL viaserver_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.