CVE-2026-7978
Overview
Files Changed
chrome/enterprise_companion/app/app_net_worker.cc
Patch
From 6398ee594917045f09ab227b3008ca54b92c71b1 Mon Sep 17 00:00:00 2001
From: Noah Rose Ledesma <noahrose@google.com>
Date: Wed, 01 Apr 2026 13:02:16 -0700
Subject: [PATCH] Reset supplementary group list in CECA network fetcher
It is prudent to reset the supplementary group list for "nobody" in
addition to setting gid and uid to "nobody". A more robust solution
might be to use seatbelt for sandboxing.
Note that CECA utilizes an out-of-process network fetcher primarily
to authenticate with proxies. The application does not handle
untrusted data; communications occur exclusively over HTTPS with
trusted endponints.
Bug: 497828892
Change-Id: Ia0725266ab11714a8e30545429e13d486a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7712334
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Noah Rose Ledesma <noahrose@google.com>
Cr-Commit-Position: refs/heads/main@{#1608734}
---
diff --git a/chrome/enterprise_companion/app/app_net_worker.cc b/chrome/enterprise_companion/app/app_net_worker.cc
index 1def3ccc..a813b96 100644
--- a/chrome/enterprise_companion/app/app_net_worker.cc
+++ b/chrome/enterprise_companion/app/app_net_worker.cc
@@ -56,6 +56,13 @@
// If running as root, drop down to "nobody".
if (getuid() == 0) {
+ // Clear supplementary groups inherited from root.
+ if (initgroups("nobody", kNobodyGid) != 0) {
+ VPLOG(1) << "Failed to initgroups";
+ Shutdown(EnterpriseCompanionStatus::FromPosixErrno(errno));
+ return;
+ }
+
if (setgid(kNobodyGid)) {
VPLOG(1) << "Failed to set gid " << kNobodyGid;
Shutdown(EnterpriseCompanionStatus::FromPosixErrno(errno));
Original Bug Report
Potential Local Privilege Escalation in CECA net worker via retained supplementary groups on macOS
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: The Chrome Enterprise Companion App (CECA) on macOS spawns a network worker that drops root privileges to the nobody user using setuid and setgid. However, it fails to clear supplementary groups, leaving the process with highly privileged admin and wheel group memberships. If the network worker is compromised, an attacker can leverage these retained groups to escalate to full root privileges.
Affected files:
chrome/enterprise_companion/app/app_net_worker.ccchrome/enterprise_companion/url_loader_factory_provider.cc
Estimated timestamp from git blame: 2024-11-27
Summary
The Chrome Enterprise Companion App (CECA) on macOS utilizes an out-of-process network worker to isolate its networking tasks. When launched by the root LaunchDaemon (e.g., when no user is logged in), the worker correctly attempts to drop its privileges to the unprivileged nobody user. However, the implementation only calls setgid() and setuid(), which by POSIX standard do not alter a process’s supplementary group list. Consequently, the network worker retains root’s highly privileged supplementary groups, notably wheel (GID 0) and admin (GID 80).
Because this process handles untrusted network data without an additional Seatbelt sandbox, a compromise of the network stack allows an attacker to abuse the retained admin group membership to achieve Local Privilege Escalation (LPE) to root, entirely bypassing the intended UID isolation.
Vulnerability Details
In chrome/enterprise_companion/app/app_net_worker.cc, the FirstTaskRun method drops privileges as follows:
// If running as root, drop down to "nobody".
if (getuid() == 0) {
if (setgid(kNobodyGid)) {
// ... error handling ...
}
if (setuid(kNobodyUid)) {
// ... error handling ...
}
}
When a root process spawns a child, the child inherits its real/effective UID, GID, and supplementary groups. Calling setuid() and setgid() changes the primary IDs, but leaves the supplementary groups intact. Because there is no call to setgroups() or initgroups(), the process continues executing with admin and wheel group privileges.
Potential Attack Scenario
(Note: Our tooling currently lacks the ability to run code, so these are suggested/potential steps an attacker would follow based on architectural analysis.)
- Worker Launch: The CECA daemon (running as root) spawns the network worker process when no user is currently logged in. The child process inherits root’s supplementary groups.
- Incomplete Privilege Drop: The worker executes
AppNetWorker::FirstTaskRun, changing its UID and GID tonobody(-2), but retainswheelandadminin its supplementary groups list. - Network Stack Compromise: The worker begins processing untrusted data from the enterprise Device Management server or telemetry endpoints. An attacker exploits a vulnerability (e.g., memory corruption in
//net) via a malicious server response or MitM attack. - Code Execution: The attacker achieves Remote Code Execution (RCE) within the network worker. Their shellcode runs as
nobodybut holdsadmingroup privileges. - Escalation to Root: On macOS, the
admingroup has write access to sensitive system directories. The attacker writes a malicious CoreAudio plugin to/Library/Audio/Plug-Ins/HAL/. - Triggering Root Execution: The attacker triggers a system audio event or restarts the
coreaudiodservice (which runs as root).coreaudiodloads the malicious plugin, executing the attacker’s payload with full root privileges.
Suggested Fix
Update AppNetWorker::FirstTaskRun to clear the supplementary groups before dropping the UID. You can achieve this by calling setgroups(0, NULL) to remove all supplementary groups, or initgroups("nobody", kNobodyGid) to set them to the default for the nobody user.
if (getuid() == 0) {
// Clear supplementary groups inherited from root.
if (setgroups(0, nullptr) != 0) {
VPLOG(1) << "Failed to clear supplementary groups";
Shutdown(EnterpriseCompanionStatus::FromPosixErrno(errno));
return;
}
if (setgid(kNobodyGid)) {
// ...
Evaluated with Chrome root at commit: a9cbf6e8b275fe4147435aa905f3b7f5a656f5f0
Results from 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.