{"vulnerability": "CVE-2023-28802", "sightings": [{"uuid": "81893cc8-a413-4280-a995-e6657d7fe9e4", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2023-28802", "type": "seen", "source": "https://gist.github.com/usualdork/84d156ab09027755f4c405b2f87af230", "content": "# Local Unprivileged Bypass of Zscaler Client Connector for macOS via Process Suspension During Service Restart (Fail-Open / TOCTOU)\n\n**Researcher:** Manish Tripathy\n**Coordination:** CERT/CC VU#372528 \u00b7 Zscaler Support Case #06171397 \u00b7 CVE dispute filed with the CVE Program (MITRE)\n**Affected product:** Zscaler Client Connector (ZCC) for macOS\n**Affected versions:** 4.3.x; confirmed by Zscaler's own Research team on 4.5.2 LTS (a currently supported version); fixed only in 4.5.2.4 with the \"Firewall Enhancement for Security\" configuration enabled\n**Primary weakness:** CWE-1188 (Insecure Default Initialization) \u2014 compounded by CWE-636 (Not Failing Securely / Failing Open) and CWE-367 (TOCTOU Race Condition)\n**Researcher-assessed severity:** CVSS 3.1 7.8 (High) \u2014 `AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N`\n\n---\n\n## 1. Summary\n\nZscaler Client Connector (ZCC) for macOS ships, in its default configuration, in a **fail-open** state. A local, unprivileged user (no administrative rights, no `sudo`) can cause the agent's user-space enforcement processes to enter a suspended state during the client's \"Restart Services\" / \"Repair App\" workflow by issuing POSIX `SIGSTOP` signals to processes the user already owns. While those processes are suspended, the anti-tampering watchdog does not recover them \u2014 the process remains present in the process table and therefore continues to *appear* alive \u2014 and the endpoint's network traffic reverts to the system default gateway with **no Zscaler enforcement**.\n\nDuring this window:\n\n- ZIA/ZPA policy enforcement (URL filtering, SSL inspection, cloud firewall, malware scanning) is not applied.\n- DLP content inspection is not applied.\n- Endpoint logging/telemetry to Zscaler is halted.\n- The management console continues to show the device as **Connected** (healthy), providing false assurance to the SOC.\n\nThe bypass is trivially scriptable and requires only standard user privileges.\n\nThis document is written to be self-contained for CVE Program / CNA-LR review. It states what the researcher observed and, separately and explicitly, what **Zscaler's own teams confirmed in writing**, so the two can be evaluated independently.\n\n---\n\n## 2. Threat model\n\n- **Attacker:** a standard, local, interactive user of a managed macOS endpoint, operating with their own (non-admin) privileges.\n- **Goal:** evade enterprise egress controls (URL filtering, SSL inspection, DLP, telemetry) enforced by ZCC, in order to exfiltrate data, reach blocked destinations, or fetch payloads without inspection.\n- **Capability required:** ability to run a shell script in Terminal and to invoke the product's user-facing \"Restart Services\" / \"Repair App\" function \u2014 both available to an ordinary user in the default product configuration.\n- **Not required:** administrative privileges, `sudo`, kernel access, physical access, or any network-side position.\n\nThis is the same class of local-user threat that Zscaler markets ZCC to defend against on managed endpoints (the entire purpose of on-device DLP, URL filtering, and SSL inspection is enforcement against the local user's own traffic).\n\n---\n\n## 3. Root cause\n\nThe defect is an **insecure default** combined with a **time-of-check / time-of-use (TOCTOU) race** in the restart sequence:\n\n1. **Insecure default (CWE-1188):** Out of the box, ZCC for macOS does not fail closed when its user-space enforcement components stop processing. The protective (\"fail-close\" / Firewall Enhancement) behavior is an **opt-in** administrative configuration that must be explicitly deployed via MDM/JAMF. Absent that explicit configuration \u2014 i.e., in the factory-default state \u2014 the product fails open.\n\n2. **TOCTOU race + watchdog gap (CWE-367 / CWE-636):** During \"Restart Services\" / \"Repair App\", there is a window in which an enforcement process can be suspended (`SIGSTOP`) before the anti-tampering watchdog re-attaches and validates process health. The watchdog's liveness check is satisfied by the process's continued existence in the process table; a *stopped* (state `T`) process still satisfies that check. The watchdog therefore does not detect that enforcement has ceased, and does not restore it. The result is a persistent fail-open condition rather than a momentary one.\n\nBecause the secure state requires an explicit administrative opt-in, and the default state is exploitable by an unprivileged user, this is the condition described by **CWE-1188**, not a user-introduced misconfiguration.\n\n---\n\n## 4. Reproduction\n\n&gt; The following is the researcher's reproduction procedure. Section 5 separately documents Zscaler's own written confirmation of reproduction on a supported version.\n\n**Environment used by the researcher:** managed macOS endpoint, ZCC for macOS, anti-tampering enabled, in the as-deployed/default enforcement configuration (no fail-close / Firewall Enhancement profile present \u2014 see the configuration audit in Section 6).\n\n**Steps:**\n\n1. Open Terminal on a macOS device with ZCC active and enforcing.\n2. Confirm enforcement is active before the test:\n   ```\n   curl ifconfig.me          # returns a Zscaler egress IP (traffic is tunneled/enforced)\n   ```\n3. Run the PoC scripts, which polls for the relevant Zscaler user-space process(es) during the \"Restart Services\" window and issues `SIGSTOP` within the race window.\n4. Trigger the client's \"Restart Services\" / \"Repair App\" function.\n5. Observe process state:\n   ```\n   ps aux | grep -i zscaler  # target process(es) in 'T' (stopped) state, still present in the table\n   ```\n6. Confirm the bypass is active:\n   ```\n   curl ifconfig.me          # now returns the host's real ISP IP \u2014 traffic egresses via the\n                             # system default gateway with no Zscaler enforcement\n   ```\n7. Observe that the condition **persists** \u2014 the suspended processes are not auto-recovered by the watchdog, and enforcement does not resume on its own, until the processes are resumed or the machine is rebooted.\n\n**Observed impact during the window:** real ISP IP returned by `ifconfig.me` (confirming traffic is not tunneled), no policy enforcement, no inspection, no telemetry \u2014 while the management console continues to report the device as Connected.\n\nA PoC video demonstrating the before/after external-IP change and the persistent stopped-process state is available to the CVE Program on request. (The earlier Bugcrowd closure rationale \u2014 that `SIGSTOP` \"only affects a minor process\" and that the tunnel keeps enforcing \u2014 is directly contradicted by the external-IP change shown in the video: if enforcement continued, the external IP would remain the Zscaler egress IP throughout. It does not.)\nhttps://drive.google.com/file/d/14KNjqfMUf66IFyXFu9KfeUyFyiZ6sMaH/view\n\n---\n\n## 5. Vendor's own written confirmation (independent of researcher testing)\n\nThis section is the core of the case, because it does not depend on the researcher's environment at all. **Zscaler's own teams confirmed the finding in writing, on a currently supported version**, via Support Case #06171397:\n\n- **April 14, 2026 \u2014 Zscaler Support, in writing:** Zscaler's Research team reproduced the issue on **macOS ZCC 4.5.2 LTS** \u2014 a currently supported Long-Term-Support release \u2014 and stated it was fixed only in **4.5.2.4** with the \"Firewall Enhancement for Security\" JAMF configuration deployed.\n\n- **May 8, 2026 \u2014 Zscaler RSH (Research) team document**, \"macOS Local Bypass via SIGSTOP of Zscaler Client Connector Processes\" (provided via the same support case). Verbatim:\n  &gt; \"A local macOS user can bypass Zscaler security enforcement by running a shell script that sends a recursive SIGSTOP to Zscaler-related processes. When the processes are paused, core protections such as ZIA/ZPA enforcement and endpoint logging/telemetry are halted, leaving the device in an effectively unprotected state.\"\n\n  and:\n  &gt; \"These remediation options are supported across all active supported Zscaler Client Connector versions for macOS, specifically 4.5.2 LTS and subsequent releases.\"\n\nTogether these establish, from Zscaler's own documentation, that the behavior is (1) real, (2) impactful (complete enforcement bypass / \"effectively unprotected state\"), and (3) present on currently supported versions (explicitly 4.5.2 LTS and later).\n\n---\n\n## 6. Default-configuration (fail-open) audit\n\nTo address the vendor's suggestion that the bypass only reproduces where an administrator has not enabled fail-close, the researcher performed a forensic audit of the managed endpoint's deployed configuration. Findings:\n\n- `ZscalerTunnelStatus.plist` \u2014 empty (no configuration present)\n- `com.zscaler.ZscalerTunnel` MDM managed preference \u2014 empty (no policy pushed)\n- `ZscalerTunneldb` fail strings \u2014 empty\n- `sudo profiles show -all` \u2014 no tunnel policy, no app profile, no fail-close configuration of any kind\n\nThe endpoint's MDM had pushed exactly one Zscaler-related profile: a Zscaler SSL inspection certificate. **No tunnel policy or fail-close configuration was ever deployed.** The device was therefore in the factory-default ZCC enforcement state, which is fail-open.\n\nThis is the point that maps the finding to CWE-1188: the **default** shipped state is the exploitable one, and an explicit administrative action is required to reach the *protected* state \u2014 not to reach the vulnerable state.\n\n---\n\n## 7. Impact\n\n- **Complete bypass of enterprise egress controls** by an unprivileged local user: URL filtering, SSL inspection, cloud firewall, malware scanning all non-enforcing during the window.\n- **DLP bypass:** content inspection is not applied; data can be transmitted to otherwise-blocked destinations uninspected.\n- **Telemetry/logging gap:** endpoint logging to Zscaler is halted during the window, so the activity is not recorded.\n- **False assurance to the SOC (a second protection-mechanism failure):** the management console continues to report the device as **Connected** / healthy while enforcement is fully suspended. Security operations responding to an incident have no console indication that enforcement has failed.\n- **Persistence:** the condition is not a momentary blip during reconnect \u2014 the suspended processes are not auto-recovered, and the fail-open state persists until the processes are resumed or the device is rebooted.\n\n---\n\n## 8. Why this meets CVE criteria (and why the \"configuration choice\" rationale does not hold)\n\nZscaler (and, citing Zscaler's framing, CISA) declined CVE assignment on the basis that the behavior is a \"documented configuration choice,\" that it is \"local user action,\" and \u2014 per CNA Rule 4.1.3 \u2014 that it reflects a \"non-default configuration or runtime change made by an authorized user.\" Each point is addressed:\n\n1. **It is the default, not a user-made change (Rule 4.1.3 does not apply).** Rule 4.1.3 excludes insecure states that an authorized user *changes into*. Here, the fail-open state is the **default**; the user/administrator action is required to become **protected** (deploying fail-close), not to become vulnerable. The audit in Section 6 confirms the default, unconfigured state is the exploitable one. That is CWE-1188, the opposite of the 4.1.3 exclusion.\n\n2. **\"Documented\" does not equal \"not a vulnerability.\"** That a vendor documents an insecure default does not remove it from the CWE taxonomy. CWE-1188 (insecure default initialization) and CWE-636 (failing open) are established, CVE-mapped weaknesses precisely because documenting an insecure default does not make the product secure.\n\n3. **\"Local\" does not exclude it \u2014 and Zscaler's own CVE record proves the point.** Zscaler assigned **CVE-2021-26737** for ZCC macOS prior to 3.6: *\"a local adversary without sufficient privileges may be able to shutdown the Zscaler tunnel by exploiting a race condition.\"* That is the **same product, same platform, same local-unprivileged attacker, and same race-condition primitive** as this finding. The \"local and configurable\" rationale now applied would equally have excluded CVE-2021-26737, which Zscaler nonetheless assigned. Zscaler also assigned **CVE-2023-28802** for a Service Restart bypass on the same product line. The current declination is inconsistent with Zscaler's own published precedent.\n\n4. **Secure-by-default is an established requirement.** NIST SP 800-160v1 \u00a7F.2.5 (Secure Defaults), NIST SP 800-53 Rev. 5 control SA-8(23) (Secure Defaults), and the OWASP \"Fail Safe / Secure Defaults\" principle all require that a security product ship in a secure state, with insecure modes requiring explicit opt-in. ZCC's default fail-open posture is the condition these standards exist to prevent.\n\n---\n\n## 9. Coordination timeline (factual)\n\n- **2026-02-11** \u2014 Reported to CERT/CC (VINCE), VRF#26-02-HTQRQ \u2192 case VU#372528.\n- **2026-02-12** \u2014 Submitted to Zscaler via Bugcrowd.\n- **2026-03 (Bugcrowd)** \u2014 Closed with the rationale that `SIGSTOP` only affects a minor process and the tunnel keeps enforcing. Rebutted by PoC video showing the external IP reverting to the real ISP IP during the window.\n- **2026-03-27** \u2014 Zscaler Support call (Akshat Sinha). Written call summary acknowledged that, under the SIGSTOP sequence, \"ZCC stays in the frozen state persistently, unless the machine is rebooted,\" that traffic bypasses ZCC in a fail-open state during that time, and that the issue is a potential TOCTOU match. Reproduced version recorded as 4.3.0.240.\n- **2026-04-14** \u2014 Zscaler Support confirms in writing that the Research team reproduced the issue on **4.5.2 LTS** and that it is fixed only in 4.5.2.4 with the Firewall Enhancement configuration.\n- **2026-04-21** \u2014 Meeting with Zscaler PSIRT/leadership. Zscaler characterized the issue as a \"known issue\" in 4.3 and initially declined a CVE on the basis that 4.3 is outside the n-2 support window; the prior 4.5.2 written confirmation was characterized on the call as a \"miscommunication.\" Researcher subsequently performed the default-configuration audit in Section 6.\n- **2026-05-08** \u2014 Zscaler RSH document acknowledging the bypass and the \"effectively unprotected state\" across \"all active supported \u2026 versions \u2026 specifically 4.5.2 LTS and subsequent releases.\"\n- **2026-05-12** \u2014 Zscaler Support issues a written CVE declination (local user action / documented policy / configurable mitigations).\n- **2026-05-12** \u2014 CVE dispute filed with the CVE Program (MITRE).\n- **2026-06 (early)** \u2014 During later correspondence Zscaler raised testing on version 4.7 and requested a live debugging session; this followed the prior written confirmations on 4.5.2 LTS. The researcher's position is that the 4.5.2 LTS confirmation (by Zscaler's own team) already establishes the finding on a supported version, and that the repeated change of requested version does not alter that written confirmation.\n- **2026-06-23** \u2014 Zscaler (Sidharth Krishnan) issues a final position: the behavior is real but is \"a configuration choice\" and \"does not meet the criteria for a CVE.\" CISA/CERT-CC declines under Rule 4.1.3 and closes the VINCE case.\n\n---\n\n## 10. Recommended remediation\n\n- **Default fail-close:** ZCC for macOS should fail closed by default when its enforcement components are not processing, rather than requiring an opt-in configuration to do so.\n- **Robust watchdog liveness check:** the anti-tampering watchdog should detect a *stopped* (state `T`) process as a non-enforcing state and restore enforcement (or fail closed), rather than treating process-table presence as sufficient evidence of health.\n- **Signal interception:** on macOS, the Endpoint Security framework provides `ES_EVENT_TYPE_AUTH_SIGNAL` (intercepts POSIX signals including `SIGSTOP`, since macOS 10.15) and `ES_EVENT_TYPE_AUTH_PROC_SUSPEND_RESUME` (intercepts task-level suspend/resume, since macOS 11.0). Either can deny suspension of enforcement processes before it takes effect. Competing endpoint products implement signal interception to generate tamper alerts.\n- **Console fidelity:** the management console should not report a device as Connected/healthy while enforcement is suspended.\n\n---\n\n## 11. References\n\n- CERT/CC VU#372528\n- Zscaler Support Case #06171397\n- CWE-1188: Insecure Default Initialization of Resource\n- CWE-636: Not Failing Securely ('Failing Open')\n- CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition\n- CVE-2021-26737 (Zscaler ZCC macOS prior to 3.6 \u2014 local unprivileged race-condition tunnel shutdown)\n- CVE-2023-28802 (Zscaler \u2014 Service Restart bypass)\n- NIST SP 800-160v1 \u00a7F.2.5 (Secure Defaults); NIST SP 800-53 Rev. 5 SA-8(23); OWASP Secure Defaults / Fail Safe\n- PoC video and PoC script \u2014 available to the CVE Program on request\n\n---\n", "creation_timestamp": "2026-06-24T15:07:47.001425Z"}]}