Active Directory Web Services (ADWS) Enumeration & Stealth Collection

Tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

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).

ADWS interactions are implemented over WS-Enumeration: every query starts with an Enumerate message that defines the LDAP filter/attributes and returns an EnumerationContext GUID, followed by one or more Pull messages that stream up to the server-defined result window. Contexts age out after ~30 minutes, so tooling either needs to page results or split filters (prefix queries per CN) to avoid losing state. When asking for security descriptors, specify the LDAP_SERVER_SD_FLAGS_OID control to omit SACLs, otherwise ADWS simply drops the nTSecurityDescriptor attribute from its SOAP response.

NOTE: ADWS is also used by many RSAT GUI/PowerShell tools, so traffic may blend with legitimate admin activity.

SoaPy – Native Python Client

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.

Targeted collection flags & write operations

SoaPy ships with curated switches that replicate the most common LDAP hunting tasks over ADWS: --users, --computers, --groups, --spns, --asreproastable, --admins, --constrained, --unconstrained, --rbcds, plus raw --query / --filter knobs for custom pulls. Pair those with write primitives such as --rbcd <source> (sets msDs-AllowedToActOnBehalfOfOtherIdentity), --spn <service/cn> (SPN staging for targeted Kerberoasting) and --asrep (flip DONT_REQ_PREAUTH in userAccountControl).

Example targeted SPN hunt that only returns samAccountName and servicePrincipalName:

soapy corp.local/alice:'Winter2025!'@dc01.corp.local \
      --spns -f samAccountName,servicePrincipalName --parse

Use the same host/credentials to immediately weaponise findings: dump RBCD-capable objects with --rbcds, then apply --rbcd 'WEBSRV01$' --account 'FILE01$' to stage a Resource-Based Constrained Delegation chain (see Resource-Based Constrained Delegation for the full abuse path).

Installation (operator host)

python3 -m pip install soapy-adws   # or git clone && pip install -r requirements.txt

ADWSDomainDump – LDAPDomainDump over ADWS (Linux/Windows)

  • Fork of ldapdomaindump that swaps LDAP queries for ADWS calls on TCP/9389 to reduce LDAP-signature hits.
  • Performs an initial reachability check to 9389 unless --force is passed (skips the probe if port scans are noisy/filtered).
  • Tested against Microsoft Defender for Endpoint and CrowdStrike Falcon with successful bypass in the README.

Installation

pipx install .

Usage

adwsdomaindump -u 'thewoods.local\mathijs.verschuuren' -p 'password' -n 10.10.10.1 dc01.thewoods.local

Typical output logs the 9389 reachability check, ADWS bind, and dump start/finish:

[*] Connecting to ADWS host...
[+] ADWS port 9389 is reachable
[*] Binding to ADWS host
[+] Bind OK
[*] Starting domain dump
[+] Domain dump finished

Sopa - A practical client for ADWS in Golang

Similarly as soapy, sopa implements the ADWS protocol stack (MS-NNS + MC-NMF + SOAP) in Golang, exposing command-line flags to issue ADWS calls such as:

  • Object search & retrieval - query / get
  • Object lifecycle - create [user|computer|group|ou|container|custom] and delete
  • Attribute editing - attr [add|replace|delete]
  • Account management - set-password / change-password
  • and others such as groups, members, optfeature, info [version|domain|forest|dcs], etc.

Protocol mapping highlights

  • LDAP-style searches are issued via WS-Enumeration (Enumerate + Pull) with attribute projection, scope control (Base/OneLevel/Subtree) and pagination.
  • Single-object fetch uses WS-Transfer Get; attribute changes use Put; deletions use Delete.
  • Built-in object creation uses WS-Transfer ResourceFactory; custom objects use an IMDA AddRequest driven by YAML templates.
  • Password operations are MS-ADCAP actions (SetPassword, ChangePassword).

Unauthenticated metadata discovery (mex)

ADWS exposes WS-MetadataExchange without credentials, which is a quick way to validate exposure before authenticating:

sopa mex --dc <DC>

DNS/DC discovery & Kerberos targeting notes

Sopa can resolve DCs via SRV if --dc is omitted and --domain is provided. It queries in this order and uses the highest-priority target:

_ldap._tcp.<domain>
_kerberos._tcp.<domain>

Operationally, prefer a DC-controlled resolver to avoid failures in segmented environments:

  • Use --dns <DC-IP> so all SRV/PTR/forward lookups go through the DC DNS.
  • Use --dns-tcp when UDP is blocked or SRV answers are large.
  • If Kerberos is enabled and --dc is an IP, sopa performs a reverse PTR to obtain an FQDN for correct SPN/KDC targeting. If Kerberos is not used, no PTR lookup happens.

Example (IP + Kerberos, forced DNS via the DC):

sopa info version --dc 192.168.1.10 --dns 192.168.1.10 -k --domain corp.local -u user -p pass

Auth material options

Besides plaintext passwords, sopa supports NT hashes, Kerberos AES keys, ccache, and PKINIT certificates (PFX or PEM) for ADWS auth. Kerberos is implied when using --aes-key, -c (ccache) or certificate-based options.

# NT hash
sopa --dc <DC> -d <DOMAIN> -u <USER> -H <NT_HASH> query --filter '(objectClass=user)'

# Kerberos ccache
sopa --dc <DC> -d <DOMAIN> -u <USER> -c <CCACHE> info domain

Custom object creation via templates

For arbitrary object classes, the create custom command consumes a YAML template that maps to an IMDA AddRequest:

  • parentDN and rdn define the container and relative DN.
  • attributes[].name supports cn or namespaced addata:cn.
  • attributes[].type accepts string|int|bool|base64|hex or explicit xsd:*.
  • Do not include ad:relativeDistinguishedName or ad:container-hierarchy-parent; sopa injects them.
  • hex values are converted to xsd:base64Binary; use value: "" to set empty strings.

SOAPHound – High-Volume ADWS Collection (Windows)

FalconForce SOAPHound is a .NET collector that keeps all LDAP interactions inside ADWS and emits BloodHound v4-compatible JSON. It builds a complete cache of objectSid, objectGUID, distinguishedName and objectClass once (--buildcache), then re-uses it for high-volume --bhdump, --certdump (ADCS), or --dnsdump (AD-integrated DNS) passes so only ~35 critical attributes ever leave the DC. AutoSplit (--autosplit --threshold <N>) automatically shards queries by CN prefix to stay under the 30-minute EnumerationContext timeout in large forests.

Typical workflow on a domain-joined operator VM:

# Build cache (JSON map of every object SID/GUID)
SOAPHound.exe --buildcache -c C:\temp\corp-cache.json

# BloodHound collection in autosplit mode, skipping LAPS noise
SOAPHound.exe -c C:\temp\corp-cache.json --bhdump \
              --autosplit --threshold 1200 --nolaps \
              -o C:\temp\BH-output

# ADCS & DNS enrichment for ESC chains
SOAPHound.exe -c C:\temp\corp-cache.json --certdump -o C:\temp\BH-output
SOAPHound.exe --dnsdump -o C:\temp\dns-snapshot

Exported JSON slots directly into SharpHound/BloodHound workflowsβ€”see BloodHound methodology for downstream graphing ideas. AutoSplit makes SOAPHound resilient on multi-million object forests while keeping the query count lower than ADExplorer-style snapshots.

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:

soapy ludus.domain/jdoe:'P@ssw0rd'@10.2.10.10 \
      -q '(objectClass=domain)' \
      | tee data/domain.log
  1. Collect ADCS-related objects from the Configuration NC:
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
  1. Convert to BloodHound:
bofhound -i data --zip   # produces BloodHound.zip
  1. 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)

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 (see Resource-Based Constrained Delegation).

Tooling Summary

PurposeToolNotes
ADWS enumerationSoaPyPython, SOCKS, read/write
High-volume ADWS dumpSOAPHound.NET, cache-first, BH/ADCS/DNS modes
BloodHound ingestBOFHoundConverts SoaPy/ldapsearch logs
Cert compromiseCertipyCan be proxied through same SOCKS
ADWS enumeration & object changessopaGeneric client to interface with known ADWS endpoints - allows for enumeration, object creation, attribute modifications, and password changes

References

Tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks