CVE-2026-7997
Overview
Background
- Privileged Helper
- A macOS root-privileged helper tool installed by the Chrome updater that performs install/update operations on behalf of the browser over XPC.
- XPC / `NSXPCConnection`
- Apple’s inter-process communication mechanism; a listener’s delegate decides via
-listener:shouldAcceptNewConnection:whether to accept each incoming connection. - `audit_token_t`
- A kernel-provided, unforgeable token identifying the peer process of an XPC connection, usable to verify the peer’s code-signing identity.
- `base::mac::ProcessRequirement`
- A Chromium helper that builds and evaluates a code-signing requirement (e.g. bundle identifier plus same signing identity) against a process’s audit token.
Root Cause Analysis
The helper’s XPC listener delegate method -listener:shouldAcceptNewConnection: previously accepted every incoming connection: it went straight to configuring newConnection.exportedInterface with no check on the identity of the connecting process. Because the helper runs with root privileges and exports PrivilegedHelperServiceProtocol, any local process could connect and invoke its privileged operations, violating the invariant that only the genuine Chrome browser (and its channel variants) may talk to the helper.
The fix builds a base::mac::ProcessRequirement restricting the peer’s bundle identifier to MAC_BROWSER_BUNDLE_IDENTIFIER_STRING and its .beta/.dev/.canary variants and requiring it be SignedWithSameIdentity, then calls requirement->ValidateProcess(newConnection.auditToken). If the requirement cannot be built or the peer fails validation, the method returns NO, rejecting the connection before the interface is exported. This closes the gap by authenticating the caller with an unforgeable audit token prior to exposing any privileged functionality.
auditToken and rejects any peer that is not a same-identity Chrome bundle.Attack Path
- Locate the helper listener A local attacker’s unprivileged process discovers the updater’s privileged-helper XPC service name on the machine.
- Open a connection
The process creates an
NSXPCConnectionto the helper; pre-patch,-listener:shouldAcceptNewConnection:accepts it unconditionally and exportsPrivilegedHelperServiceProtocol. - Invoke privileged operations The attacker calls the helper’s protocol methods, which execute with root privilege, from an otherwise unprivileged context.
- Local privilege escalation Driving the root helper’s operations from an unauthorized caller yields elevation of privilege on the host (the specific privileged operations and their consequences are not shown in the diff) (inferred).
Impact Assessment
Files Changed
chrome/updater/mac/privileged_helper/service.mm
Audit Directions
- Unauthenticated XPC listenersReview every
-listener:shouldAcceptNewConnection:in privileged/helper tools to confirm it validates the peer viaauditTokenand aProcessRequirementrather than accepting all connections. - Audit-token vs PID checksFlag any peer-identity check that relies on PID or process name (which are spoofable/racy) instead of the kernel-provided
audit_token_t. - Root-exported interfacesEnumerate services that export protocols from a root or otherwise elevated process and ensure each gates access to its privileged methods on verified caller identity.
Patch
From de06591e9e1e600355ceb627b49fd9cbe5ee1d86 Mon Sep 17 00:00:00 2001 From: Joshua Pawlicki <[email protected]> Date: Wed, 15 Apr 2026 15:59:06 -0700 Subject: [PATCH] Privileged Helper: Verify XPC connection Bug: 487960705 Change-Id: Iefb022003c6d5e66d4876dad3ae11c0142b3b288 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7684953 Auto-Submit: Joshua Pawlicki <[email protected]> Reviewed-by: Adam Norberg <[email protected]> Commit-Queue: Adam Norberg <[email protected]> Cr-Commit-Position: refs/heads/main@{#1615471} --- diff --git a/chrome/updater/mac/privileged_helper/service.mm b/chrome/updater/mac/privileged_helper/service.mm index 11ca840..bb17d2b 100644 --- a/chrome/updater/mac/privileged_helper/service.mm +++ b/chrome/updater/mac/privileged_helper/service.mm @@ -22,6 +22,7 @@ #include "base/functional/callback.h" #include "base/logging.h" #include "base/mac/mac_util.h" +#include "base/mac/process_requirement.h" #include "base/memory/raw_ptr.h" #include "base/memory/scoped_refptr.h" #include "base/process/launch.h" @@ -38,6 +39,10 @@ #include "chrome/updater/util/posix_util.h" #include "chrome/updater/util/util.h" +@interface NSXPCConnection (Private) +@property(readonly) audit_token_t auditToken; +@end + @interface PrivilegedHelperServiceImpl : NSObject <PrivilegedHelperServiceProtocol> { raw_ptr<updater::PrivilegedHelperService> _service; @@ -106,6 +111,19 @@ - (BOOL)listener:(NSXPCListener*)listener shouldAcceptNewConnection:(NSXPCConnection*)newConnection { + std::optional<base::mac::ProcessRequirement> requirement = + base::mac::ProcessRequirement::Builder() + .IdentifierIsOneOf({MAC_BROWSER_BUNDLE_IDENTIFIER_STRING, + MAC_BROWSER_BUNDLE_IDENTIFIER_STRING ".beta", + MAC_BROWSER_BUNDLE_IDENTIFIER_STRING ".dev", + MAC_BROWSER_BUNDLE_IDENTIFIER_STRING ".canary"}) + .SignedWithSameIdentity() + .Build(); + if (!requirement || !requirement->ValidateProcess(newConnection.auditToken)) { + // TODO(crbug.com/494281198): Consider shutting down and uninstalling. + return NO; + } + newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(PrivilegedHelperServiceProtocol)];