Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Updater
DescriptionInappropriate implementation in Updater
ComponentUpdater
Bug ClassLogic Error
Tracker500108770
Fix commitc55a1e0eb4c3 (chromium/src) +9/-2
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Files Changed

  • chrome/updater/ipc/ipc_security_linux.cc
  • chrome/updater/ipc/ipc_security_mac.cc
  • chrome/updater/ipc/ipc_security_win.cc
From c55a1e0eb4c39d9691f8668d6e933ff50b857bfc Mon Sep 17 00:00:00 2001
From: Noah Rose Ledesma <noahrose@google.com>
Date: Tue, 19 May 2026 11:29:03 -0700
Subject: [PATCH] Consider updater clients as untrusted

Use the MOJO_SEND_INVITATION_FLAG_UNTRUSTED_PROCESS flag when sending
Mojo invitations to clients connecting to the updater's server. This
prevents the passing of certain handle types which are unused in updater
communications. This helps reduce the attack surface of potential LPEs
via the updater's Mojo interface.

Bug: 500108770
Change-Id: Idb3bf3da623e61e6dd7e7e5fe71812b06a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7850556
Reviewed-by: Sorin Jianu <sorin@chromium.org>
Auto-Submit: Noah Rose Ledesma <noahrose@google.com>
Commit-Queue: Sorin Jianu <sorin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1633025}
---

diff --git a/chrome/updater/ipc/ipc_security_linux.cc b/chrome/updater/ipc/ipc_security_linux.cc
index 13b716c..0e88fa8 100644
--- a/chrome/updater/ipc/ipc_security_linux.cc
+++ b/chrome/updater/ipc/ipc_security_linux.cc
@@ -9,6 +9,7 @@
 
 #include "components/named_mojo_ipc_server/connection_info.h"
 #include "components/named_mojo_ipc_server/endpoint_options.h"
+#include "mojo/public/c/system/invitation.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 
 namespace updater {
@@ -25,6 +26,8 @@
   options.message_pipe_id =
       named_mojo_ipc_server::EndpointOptions::kUseIsolatedConnection;
   options.require_same_peer_user = false;
+  options.extra_send_invitation_flags =
+      MOJO_SEND_INVITATION_FLAG_UNTRUSTED_PROCESS;
   return options;
 }
 
diff --git a/chrome/updater/ipc/ipc_security_mac.cc b/chrome/updater/ipc/ipc_security_mac.cc
index 913179e..ee9c991 100644
--- a/chrome/updater/ipc/ipc_security_mac.cc
+++ b/chrome/updater/ipc/ipc_security_mac.cc
@@ -10,6 +10,7 @@
 
 #include "components/named_mojo_ipc_server/connection_info.h"
 #include "components/named_mojo_ipc_server/endpoint_options.h"
+#include "mojo/public/c/system/invitation.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 
 namespace updater {
@@ -22,7 +23,8 @@
 named_mojo_ipc_server::EndpointOptions CreateServerEndpointOptions(
     const mojo::NamedPlatformChannel::ServerName& server_name) {
   return {server_name,
-          named_mojo_ipc_server::EndpointOptions::kUseIsolatedConnection};
+          named_mojo_ipc_server::EndpointOptions::kUseIsolatedConnection,
+          MOJO_SEND_INVITATION_FLAG_UNTRUSTED_PROCESS};
 }
 
 }  // namespace updater
diff --git a/chrome/updater/ipc/ipc_security_win.cc b/chrome/updater/ipc/ipc_security_win.cc
index ece8a7c..13a5686 100644
--- a/chrome/updater/ipc/ipc_security_win.cc
+++ b/chrome/updater/ipc/ipc_security_win.cc
@@ -7,6 +7,7 @@
 #include "chrome/updater/get_updater_scope.h"
 #include "components/named_mojo_ipc_server/connection_info.h"
 #include "components/named_mojo_ipc_server/endpoint_options.h"
+#include "mojo/public/c/system/invitation.h"
 #include "mojo/public/cpp/platform/named_platform_channel.h"
 
 namespace updater {
@@ -25,7 +26,8 @@
     const mojo::NamedPlatformChannel::ServerName& server_name) {
   named_mojo_ipc_server::EndpointOptions options{
       server_name,
-      named_mojo_ipc_server::EndpointOptions::kUseIsolatedConnection};
+      named_mojo_ipc_server::EndpointOptions::kUseIsolatedConnection,
+      MOJO_SEND_INVITATION_FLAG_UNTRUSTED_PROCESS};
 
   if (IsSystemInstall()) {
     // A DACL to grant:
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential Local Privilege Escalation in Chrome Updater via Mojo Ipcz handle relaying

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 without the security team.

Overview: A potential vulnerability in the Windows Chrome Updater could allow an unprivileged local user to escalate privileges to SYSTEM. The flaw arises because the updater establishes isolated IPC connections as fully trusted brokers, allowing an attacker to exploit the Mojo/Ipcz relay mechanism. By establishing two connections, an attacker could force the SYSTEM process to adopt its own internal handles and duplicate them into the attacker’s process.

Affected files:

  • mojo/core/ipcz_driver/transport.cc
  • mojo/core/ipcz_driver/invitation.cc
  • third_party/ipcz/src/ipcz/node_link.cc
  • third_party/ipcz/src/ipcz/node.cc
  • mojo/core/ipcz_driver/wrapped_platform_handle.cc
  • components/named_mojo_ipc_server/named_mojo_message_pipe_server.cc
  • chrome/updater/ipc/ipc_security_win.cc

Estimated timestamp from git blame: 2025-05-08

Summary

There is a potential Local Privilege Escalation (LPE) to SYSTEM in the Chrome Updater on Windows. The vulnerability stems from how NamedMojoMessagePipeServer configures mojo::IsolatedConnections, inadvertently granting connected clients kTrusted and kBroker status within the Mojo Ipcz driver. A local attacker can abuse this trust by establishing two connections and sending a crafted RelayMessage to force the Updater to duplicate its own internal handles (such as process tokens) into the attacker’s process.

Technical Details

The Chrome Updater service runs as SYSTEM and hosts a NamedMojoIpcServer. The DACL for its named pipe (\\.\pipe\mojo.{UpdaterServerName}) correctly allows Authenticated Users to connect. However, the subsequent handling of the connection introduces a critical flaw:

  1. Improper Trust Assignment: When a client connects, NamedMojoMessagePipeServer::OnClientConnected opens the client process with PROCESS_DUP_HANDLE and creates an IsolatedConnection. The IsolatedConnection sets MOJO_SEND_INVITATION_FLAG_ISOLATED but omits MOJO_SEND_INVITATION_FLAG_UNTRUSTED_PROCESS. Consequently, mojo/core/ipcz_driver/invitation.cc treats the remote endpoint as Transport::ProcessTrust::kTrusted and Transport::kBroker.
  2. Handle Adoption Bypass (DecodeHandle): Because the attacker’s connection is considered a kBroker, the attacker can send a serialized handle with HandleOwner::kRecipient. In mojo/core/ipcz_driver/transport.cc, DecodeHandle skips duplication for kRecipient handles arriving from brokers. The SYSTEM process blindly calls GetHandleInformation on the attacker-supplied raw integer and adopts it as a valid PlatformHandle belonging to the SYSTEM process itself.
  3. Relay Mechanism: The attacker can send this handle inside a msg::RelayMessage destined for a second connection they control. The Ipcz Node routing logic blindly forwards the deserialized driver objects (now containing the valid SYSTEM handle) to the destination link.
  4. Unsafe Duplication (EncodeHandle): When serializing the message for the attacker’s second connection, EncodeHandle sees that the destination is kTrusted (due to the missing untrusted flag). It therefore skips the MaybeCheckIfHandleIsUnsafe security check, which would normally block sensitive handles like Tokens or Processes. It proceeds to call DuplicateHandle with DUPLICATE_SAME_ACCESS, copying the SYSTEM handle directly into the attacker’s process using the PROCESS_DUP_HANDLE acquired during connection setup.

Potential Steps to Reproduce

Note: These are potential steps to trigger the vulnerability based on code analysis. Our tooling does not yet have the ability to run a working Proof of Concept.

  1. Run as a non-privileged local user and open two client connections (C1 and C2) to the SYSTEM updater named pipe.
  2. Establish the Mojo/Ipcz NodeLink by exchanging ConnectFromBrokerToBroker messages, registering two different Node names (e.g., N1 for C1 and N2 for C2).
  3. Over C1, send a msg::RelayMessage targeted at N2. Include a kWrappedPlatformHandle driver object with handle_owner = kRecipient and a raw handle value to test (e.g., 0x4).
  4. The SYSTEM updater receives the message on C1. Due to the kBroker trust bypass in DecodeHandle, it adopts its own internal handle 0x4 and prepares to relay it to C2.
  5. During transmission to C2, EncodeHandle skips MaybeCheckIfHandleIsUnsafe() because the remote process is kTrusted. It calls DuplicateHandle to copy the handle into the attacker’s C2 process.
  6. Retrieve the duplicated handle from C2. Use NtQueryObject to inspect the handle type. Repeat the process, incrementing the handle value (e.g., 0x8, 0xC), until a SYSTEM token or process handle is extracted.

Suggested Fix

  1. Update NamedMojoMessagePipeServer::OnClientConnected (or the underlying IsolatedConnection utilities) to explicitly pass MOJO_SEND_INVITATION_FLAG_UNTRUSTED_PROCESS when accepting connections from potentially untrusted clients, even for isolated connections. This will ensure remote_process_trust is set to kUntrusted, enabling the MaybeCheckIfHandleIsUnsafe mitigations.
  2. Review the trust model for IsolatedConnections in Mojo/Ipcz. Treating isolated peers implicitly as kBroker enables powerful handle ownership manipulations (kRecipient) in DecodeHandle. Consider restricting this capability or requiring explicit authorization.

Evaluated with Chrome root at commit: f200f57a19490707ff8bc7aa5de3cbc443a3afad


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; 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