Overview

Medium
Severity
8.1
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in Chromoting
DescriptionIncorrect authorization in Chromoting
ComponentChromoting
Sub-componentServices (Use Subcomponents)>Chromoting
Bug ClassAuthorization Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety — upheld by independent review by AI
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.
Independent review by AIConfirmed
Reviewer notes: Minor: the new check compares the caller's SID with the owner of the caller's own session (WTSQueryUserToken(caller.session_id)), not with the host's session. Saying it stops access to 'another user's session' host is an interpretation the diff does not show.
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker517798926
Fix commit8941f7c9070f (chromium/src) +82/-1
CWECWE-863
CVSS vectorCVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-05-29 (per vendor advisory)
Fix landed2026-06-01 (public git)
Disclosed2026-09-01
Patch gap92 days — fix public in source before the advisory

Background

Chromoting
Chrome Remote Desktop’s host component, which exposes host services to local clients over a named Mojo IPC pipe.
Named Mojo Pipe Server
A Windows named-pipe endpoint (named_mojo_ipc_server) that brokers Mojo IPC connections between the Chromoting host process and connecting client processes.
`WTSQueryUserToken`
A Windows Terminal Services API that retrieves the access token of the user logged into a given session, requiring SE_TCB_NAME privilege held by LocalSystem.
User SID
The Security Identifier uniquely naming a Windows user account, used here to confirm a connecting process belongs to the legitimate session owner.

Root Cause Analysis

On Windows, IsTrustedMojoEndpoint in remoting/host/mojo_caller_security_checker.cc authorized a connecting Mojo client solely by verifying that the caller’s process image was code-signed and trusted via IsBinaryTrusted(caller_process_image_path). This check confirmed what binary connected but never confirmed who owned the connecting process, so any local user running a trusted, code-signed Chromoting binary could connect to a host service running in another user’s session, violating the invariant that a client must belong to the same session user the host is serving. Additionally, the Named Mojo Pipe Server did not mark client invitations as untrusted, leaving the Mojo broker reachable to a connecting process that should not have full channel-broker capabilities.

The fix adds IsWinCallerUserSidValid, which computes the expected session-owner SID (via WTSQueryUserToken when running as LocalSystem, or the current process user as a developer fallback) and rejects any caller whose process-token User SID does not match. It also sets MOJO_SEND_INVITATION_FLAG_UNTRUSTED_PROCESS on invitations so the broker restricts what the client can do. Together these restore the requirement that a client be both a trusted binary and owned by the correct session user.

Key insight
The core mistake was authorizing the Mojo peer by binary trust alone while omitting any identity check on the caller’s owning user, so a low-privileged local user could impersonate a legitimate client to a host serving a different session. The fix closes this by enforcing a session-aware User SID match (IsWinCallerUserSidValid) in addition to the binary-trust check, and by marking invitations MOJO_SEND_INVITATION_FLAG_UNTRUSTED_PROCESS.

Attack Path

  1. Local foothold An attacker runs an unprivileged process in their own Windows logon session on a machine where the Chromoting host service runs as LocalSystem.
  2. Locate the pipe The attacker connects to the host’s named Mojo pipe exposed by the Named Mojo Pipe Server, which serves another session’s user.
  3. Pass the weak check Because authorization only required IsBinaryTrusted, the attacker leverages a trusted, code-signed Chromoting binary so the endpoint accepts the connection despite the caller belonging to a different user.
  4. Cross-session access The host treats the attacker’s process as a legitimate client and grants access to host-services IPC that should have been scoped to the active session user.
  5. Privilege escalation By interacting with services brokered by the LocalSystem-owned host, the attacker gains capabilities beyond their own account, escalating local privilege.

Impact Assessment

An attacker with a low-privileged local account gains unauthorized access to Chromoting host services running in a different Windows session, potentially served by a LocalSystem host process, enabling local privilege escalation. This occurs on Windows in the host-services IPC channel exposed by the Named Mojo Pipe Server. Preconditions are local code execution on the machine, a running Chromoting host, and possession of (or ability to run) a code-signed trusted Chromoting binary to satisfy the pre-fix binary check.

Changed Functions

FunctionChangeNotes
if
remoting/host/mojo_caller_security_checker.cc
modified
if
remoting/host/BUILD.gn
modified

Files Changed

  • remoting/host/BUILD.gn
  • remoting/host/chromoting_host_services_server.cc
  • remoting/host/mojo_caller_security_checker.cc

Audit Directions

  • Binary trust without identity
    Flag any IPC authorization that trusts a caller based on process image signing (IsBinaryTrusted) without also verifying the caller’s owning user or session SID.
  • Cross-session Windows IPC
    Review named-pipe and Mojo endpoints reachable across Windows sessions to ensure they validate the connecting process token against the intended session owner, e.g. via WTSQueryUserToken and base::win::AccessToken::FromProcess.
  • Unrestricted Mojo invitations
    Check that Mojo invitations sent to less-privileged clients set MOJO_SEND_INVITATION_FLAG_UNTRUSTED_PROCESS to limit broker capabilities granted to the peer.
From 8941f7c9070f1e9e6c7cd465f1072e1700630e36 Mon Sep 17 00:00:00 2001
From: Yuwei Huang <[email protected]>
Date: Mon, 01 Jun 2026 13:25:44 -0700
Subject: [PATCH] Harden Windows Named Mojo Pipe Server for Chromoting Host Services

Harden the Named Mojo Pipe Server on Windows against potential local
privilege escalation.

Mitigations:
*   Set MOJO_SEND_INVITATION_FLAG_UNTRUSTED_PROCESS on client
    invitations to restrict Mojo channel broker access.
*   Enforce session-aware User SID verification of the client process.
*   Add a safe fallback for local user-mode developer environments.
*   Use move semantics for token extraction and explicitly link
    wtsapi32.lib.

Bug: 517798926
Change-Id: I406538b61990fd35084904f433cfc7f37cbbb171
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7886655
Auto-Submit: Yuwei Huang <[email protected]>
Reviewed-by: Joe Downing <[email protected]>
Commit-Queue: Yuwei Huang <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1639627}
---
diff --git a/remoting/host/chromoting_host_services_server.cc b/remoting/host/chromoting_host_services_server.cc
index 77c6ce0..8cc4dce 100644
--- a/remoting/host/chromoting_host_services_server.cc
+++ b/remoting/host/chromoting_host_services_server.cc
@@ -23,6 +23,7 @@
 #if BUILDFLAG(IS_WIN)
 #include "base/strings/strcat_win.h"
 #include "base/win/win_util.h"
+#include "mojo/public/c/system/invitation.h"
 #endif
 
 namespace remoting {
@@ -44,6 +45,8 @@
   options.security_descriptor =
       base::StrCat({L"O:", user_sid, L"G:", user_sid, L"D:(A;;GA;;;AU)"});
   options.include_peer_process_info = true;
+  options.extra_send_invitation_flags =
+      MOJO_SEND_INVITATION_FLAG_UNTRUSTED_PROCESS;
 #elif BUILDFLAG(IS_LINUX)
   // Allow the endpoint to be connected by any users iff the server is run as
   // root.
diff --git a/remoting/host/mojo_caller_security_checker.cc b/remoting/host/mojo_caller_security_checker.cc
index b0080bb..20bddec 100644
--- a/remoting/host/mojo_caller_security_checker.cc
+++ b/remoting/host/mojo_caller_security_checker.cc
@@ -26,6 +26,14 @@
 #endif
 
 #if BUILDFLAG(IS_WIN)
+#include <windows.h>
+
+#include <wtsapi32.h>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/access_token.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/sid.h"
 #include "remoting/host/win/trust_util.h"
 #endif
 
@@ -56,6 +64,72 @@
     std::to_array<const std::string_view>({kBundleId, "remote_webauthn"});
 #endif
 
+#if BUILDFLAG(IS_WIN)
+bool IsWinCallerUserSidValid(
+    const named_mojo_ipc_server::ConnectionInfo& caller) {
+  std::optional<base::win::AccessToken> current_token =
+      base::win::AccessToken::FromCurrentProcess();
+  if (!current_token.has_value()) {
+    PLOG(ERROR) << "Failed to open current process token.";
+    return false;
+  }
+
+  std::optional<base::win::Sid> expected_sid;
+
+  // Verify the client's identity depending on the host service context:
+  // - If running as `LocalSystem` (standard service/production mode), the
+  //   host has the necessary TCB privileges (`SE_TCB_NAME`) to query the
+  //   legitimate active session user's token via `WTSQueryUserToken`.
+  // - If running as a standard user process (developer diagnostics/testing
+  //   environments or developer unit tests), the host lacks TCB privileges
+  //   to query other session tokens. In this case, the active remote session's
+  //   owner is simply the current process owner itself, so we securely fall
+  //   back to validating the client against the current host process token.
+  if (current_token->User() ==
+      base::win::Sid::FromKnownSid(base::win::WellKnownSid::kLocalSystem)) {
+    // SYSTEM Mode: Retrieve the target Windows session owner's User SID.
+    HANDLE session_token_raw = nullptr;
+    if (!::WTSQueryUserToken(caller.session_id, &session_token_raw)) {
+      PLOG(ERROR) << "WTSQueryUserToken failed for session ID "
+                  << caller.session_id;
+      return false;
+    }
+    base::win::ScopedHandle session_token_handle(session_token_raw);
+
+    std::optional<base::win::AccessToken> session_token =
+        base::win::AccessToken::FromToken(std::move(session_token_handle));
+    if (!session_token.has_value()) {
+      PLOG(ERROR) << "Failed to get access token from session token handle.";
+      return false;
+    }
+    expected_sid = session_token->User();
+  } else {
+    // User/Developer Mode Fallback: Use current process User SID as the
+    // expected value.
+    expected_sid = current_token->User();
+  }
+
+  std::optional<base::win::AccessToken> client_token =
+      base::win::AccessToken::FromProcess(caller.process.Handle());
+  if (!client_token.has_value()) {
+    PLOG(ERROR) << "Failed to open client process token for PID " << caller.pid;
+    return false;
+  }
+  base::win::Sid client_sid = client_token->User();
+
+  if (client_sid != *expected_sid) {
+    LOG(ERROR) << "Client user SID ("
+               << base::WideToUTF8(client_sid.ToSddlString().value_or(L""))
+               << ") does not match expected user SID ("
+               << base::WideToUTF8(expected_sid->ToSddlString().value_or(L""))
+               << ")";
+    return false;
+  }
+
+  return true;
+}
+#endif  // BUILDFLAG(IS_WIN)
+
 }  // namespace
 
 bool IsTrustedMojoEndpoint(
@@ -114,7 +188,10 @@
     return false;
   }
 #if BUILDFLAG(IS_WIN)
-  return IsBinaryTrusted(caller_process_image_path);
+  if (!IsBinaryTrusted(caller_process_image_path)) {
+    return false;
+  }
+  return IsWinCallerUserSidValid(caller);
 #else
   // Linux binaries are not code-signed, so we just return true.
   return true;
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 302f780a..5a78f0fb 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -356,6 +356,7 @@
   }
   if (is_win) {
     deps += [ "//remoting/host/win:trust_util" ]
+    libs = [ "wtsapi32.lib" ]
   }
 }
 
Loading diff…

Original Bug Report

reported by [email protected]

Local Privilege Escalation in Chrome Remote Desktop via Process Hollowing

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: Chrome Remote Desktop on Windows contains a potential security bypass where Mojo endpoint validation verifies the signature of a caller’s on-disk binary rather than its executing memory. A local unprivileged attacker could spawn an allowed binary in a suspended state, inject code, and connect to the LocalSystem named pipe. The service accepts the hollowed process as a trusted Mojo peer, enabling potential local privilege escalation.

Affected files:

  • remoting/host/mojo_caller_security_checker.cc
  • remoting/host/win/trust_util.cc
  • remoting/host/base/process_util.cc
  • components/named_mojo_ipc_server/named_mojo_message_pipe_server.cc
  • remoting/host/chromoting_host_services_server.cc

Estimated timestamp from git blame: 2022-02-25

Summary

A potential security bypass and local privilege escalation vulnerability exists in Chrome Remote Desktop for Windows. The validation mechanism in IsTrustedMojoEndpoint verifies the Authenticode signature of the caller’s on-disk binary rather than the running code executing inside the process memory space. This allows a local, non-privileged attacker to spawn an allowed binary in a suspended state, inject arbitrary code, and connect to the LocalSystem daemon’s named pipe. Consequently, the injected process is accepted as a fully-trusted Mojo peer of the LocalSystem broker.

Affected Code Paths

  1. Named Pipe Setup: ChromotingHostServicesServer [remoting/host/chromoting_host_services_server.cc] exposes the named pipe \\.\pipe\chromoting.host_services_mojo_ipc with a DACL of D:(A;;GA;;;AU) (all Authenticated Users can connect).
  2. Caller Identity Query: When a client connects, the service queries the client’s PID and opens a process handle with PROCESS_QUERY_LIMITED_INFORMATION access.
  3. Path and Signature Verification: IsTrustedMojoEndpoint [remoting/host/mojo_caller_security_checker.cc] queries the process image path via QueryFullProcessImageName and verifies the cryptographic signature with IsBinaryTrusted [remoting/host/win/trust_util.cc].

The Security Flaw

The verification mechanism relies on:

  1. QueryFullProcessImageName to resolve the client’s path. However, the Windows kernel resolves this to the original executable image that was loaded into the section object during process creation. If the process is subsequently hollowed or injected with code, the returned path remains unchanged.
  2. WinVerifyTrust called with dwUnionChoice = WTD_CHOICE_FILE. This validates the signature of the file resting on disk rather than the actual code executing in memory.

Potential Attack Steps

An attacker could theoretically follow these steps to trigger the bypass:

  1. Launch one of the allowed binaries (e.g., remote_open_url.exe) in a suspended state (CREATE_SUSPENDED).
  2. Write a malicious payload into the process memory using standard injection APIs (e.g., VirtualAllocEx / WriteProcessMemory).
  3. Resume the thread, allowing the payload to execute and connect to \\.\pipe\chromoting.host_services_mojo_ipc.
  4. The service resolves the client’s process path to the legitimate executable and confirms its signature on disk. The connection is accepted as a trusted, non-isolated Mojo peer.

Note: Since our testing tools do not have execution capability, these steps represent a potential attack path based on static code analysis and Windows API behaviors.

Suggested Remediation

To fix this issue, Chrome Remote Desktop should avoid relying on on-disk signature validation of client processes. Potential remedies include:

  1. Mojo Isolation: Restrict the Mojo connection by setting the client invitation as isolated or untrusted (using MOJO_SEND_INVITATION_FLAG_UNTRUSTED_PROCESS). This prevents a compromised client process from gaining control over the Mojo broker channel and performing privilege escalation.
  2. Access Control Tokens: Rather than relying solely on signature verification of the executable, utilize operating-system-level sandbox, AppContainer, or explicit user SID checks on the client process token to restrict access to the host services pipe.

Evaluated with Chrome root at commit: 5133b93d189b383c37805b1cf3a9d2dbfe8d7379


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