
Active Directory recon and read-only security audit over ADWS (net.tcp/9389), in Go
adit is a single-binary Active Directory reconnaissance and read-only security-audit tool that talks to a Domain Controller over ADWS (Active Directory Web Services, TCP/9389) instead of LDAP.
An adit is the horizontal entrance to a mine — the quiet way in. Querying AD
through ADWS rather than LDAP/389 means the traffic is WCF net.tcp framed,
binary-XML SOAP over an uncommon port, so it is far less likely to be inspected,
filtered, or signatured than classic LDAP — while still returning the same
directory data. adit implements the full ADWS protocol stack in Go, with no
external runtime dependencies.
┌────────────┐ net.tcp/9389 ┌──────────────┐
│ adit │ ───────────────▶ │ ADWS on DC │ ──▶ local LDAP
│ (Linux) │ NMF · NNS · │ (port 9389) │
│ │ NBFSE · SOAP └──────────────┘
└────────────┘
adit is a defensive/audit and authorized-assessment tool. Only run it against directories you are authorized to assess.
| Capability | Details |
|---|---|
| Transport | ADWS over WCF net.tcp (TCP/9389) — MC-NMF framing, MS-NNS handshake, MC-NBFS/NBFSE binary XML, WS-Enumeration/WS-Transfer SOAP |
| Authentication | NTLMv2 (sign + seal) and Kerberos (ccache / keytab / password; RFC 4121 sealed GSS) |
| Recon collectors | 31 modules (see below) → per-module CSV |
| Reporting | Styled multi-sheet XLSX workbook + self-contained HTML dashboard (search/sort, severity badges, scroll-spy) |
| Security audit | 27 read-only rules (incl. ADCS ESC1–4/9) → severity-ranked JSON, also surfaced in the dashboard |
| Proxy / jump host | HTTP CONNECT, SOCKS5/5h, SOCKS4/4a (with proxy auth + remote DNS) |
| Paging | Server-side WS-Enumeration Pull paging, configurable page size |
| Offline reporting | Rebuild XLSX/HTML from previously collected CSVs, no DC needed |
| Dependencies | Pure Go; no LDAP library, no .NET, no external services |
forest, domain, trusts, sites, subnets, schemahistory, dcs,
users, userspns, groups, computers, computerspns, ous, gpos,
gplinks, passwordpolicy, fgpp, dnszones, dnsrecords, kerberoastable,
asreproastable, krbtgt, adcs, cas, laps, protectedgroups,
groupmembers, gmsa, dmsa, printers, bitlocker
ADCS (adcs) flags ESC1–ESC4/ESC9; laps reports who can read each password;
protectedgroups audits Protected Users. Run adit -list-modules for the
current list.
-audit runs 27 read-only rules. Findings carry a rule id, severity, affected
objects, evidence, and remediation, and are written to Adit-Audit.json (and,
when combined with -collect, an "Audit Findings" section that leads the
dashboard). The engine never reads secret attribute values (LAPS / gMSA /
BitLocker), even though the recon collectors can.
| Rule | Severity | What it flags |
|---|---|---|
| AD-DOM-010 | Critical | DCSync (replication) rights held by a non-Tier-0 principal |
| AD-ACL-001/005/006/007/011–016 | Critical | Dangerous control (GenericAll/Write, WriteDACL/Owner, ForceChangePassword, Write SPN / KeyCredentialLink / member / AllowedToAct) over a privileged object |
| AD-SDH-001/002 | Critical | Unexpected control / owner of AdminSDHolder |
| AD-CS-001 | Critical–Medium | ESC1 — certificate template allows enrollee-supplied subject |
| AD-CS-002 | Critical–Medium | ESC2 — Any-Purpose / no-EKU certificate template |
| AD-CS-003 | High–Medium | ESC3 — Certificate Request Agent template |
| AD-CS-004 | High | ESC4 — weak access control on a certificate template |
| AD-CS-009 | High | ESC9 — certificate template lacks the security extension |
| AD-CS-005 | Critical/High | ESC5 — dangerous control of a PKI object (NTAuthCertificates, CA store, CA object) |
| AD-CS-006 | Info | AD CS present — ESC6/ESC7/ESC8 need host-level verification (not observable over ADWS) |
| AD-DOM-008 | High | Reversible password encryption enabled domain-wide |
| AD-USR-001 | High | Kerberos pre-auth disabled (AS-REP roastable) |
| AD-USR-009 | High | Account uses DES encryption only |
| AD-CMP-006 | High | Unconstrained delegation on a non-DC |
| AD-KCL-001 | High | Key Credential (shadow credential) on a privileged account |
| AD-KRB-001 | High | krbtgt password is stale |
| AD-DOM-005 | Medium | Machine Account Quota > 0 (any user can join computers) |
| AD-USR-002 | Medium | Kerberoastable service account (High if privileged) |
| AD-USR-005 | Medium | PASSWD_NOTREQD set |
| AD-USR-006 | Medium | Privileged account password never expires |
| AD-USR-011 | Medium | Privileged account password is stale |
| AD-CMP-008 | Medium | Resource-based constrained delegation configured |
| AD-CMP-012 | Medium | Legacy / unsupported operating system |
| AD-GRP-009 | Medium | Foreign security principal in a privileged group |
| AD-GRP-020 | Medium | Hidden privileged membership via primaryGroupID |
| AD-TRU-001/003 | High/Medium | Trust without SID filtering / with SID history |
| AD-DNS-003/004 | Medium/Low | WPAD / ISATAP record present |
| AD-CMP-011 | Low | Stale computer account |
| AD-INT-005 | High | sIDHistory contains a privileged SID |
Thresholds are tunable: -krbtgt-max-age, -priv-pwd-max-age, -stale-days.
ADCS note: ESC1–ESC5 and ESC9 are assessed from AD objects over ADWS. ESC6 (
EDITF_ATTRIBUTESUBJECTALTNAME2), ESC7 (CA role ACLs) and ESC8 (web enrollment / NTLM relay) live in the CA host registry/HTTP endpoints and are not observable over ADWS —AD-CS-006flags that a CA exists so they can be verified on the host (e.g. with Certipy).
Requires Go 1.26+. No other build dependencies.
# Build the binary
go build -o adit ./cmd/adit
./adit -version
# …or run without installing
go run ./cmd/adit -h
# Cross-compile release archives for linux/windows/darwin (amd64+arm64) into dist/
bash scripts/build-release.sh
-fqdn must be the DC's real FQDN (not its IP): the ADWS SOAP To header is
matched against the endpoint by WCF's AddressFilter. Find it with, e.g.,
nslookup -type=SRV _ldap._tcp.dc._msdcs.<domain> <dc-ip>.
adit -dc <host> -fqdn <dc-fqdn> -d <DOMAIN> -u <user> -p <pass> [flags]
# RootDSE handshake only (verifies auth + the full protocol stack)
adit -dc 10.0.0.10 -fqdn dc01.example.local -d EXAMPLE.LOCAL -u user -p 'pass'
# Run every collector -> ./report/CSV-Files/*.csv + Adit-Report.{xlsx,html}
adit -dc 10.0.0.10 -fqdn dc01.example.local -d EXAMPLE.LOCAL -u user -p 'pass' \
-collect all -o ./report
# A subset of collectors
adit -dc 10.0.0.10 -fqdn dc01.example.local -d EXAMPLE.LOCAL -u user -p 'pass' \
-collect users,groups,computers,kerberoastable
# Audit only -> Adit-Audit.json
adit -dc 10.0.0.10 -fqdn dc01.example.local -d EXAMPLE.LOCAL -u user -p 'pass' -audit
# Recon + audit: findings lead the XLSX + HTML dashboard
adit -dc 10.0.0.10 -fqdn dc01.example.local -d EXAMPLE.LOCAL -u user -p 'pass' \
-collect all -audit -o ./report
# Password (synthesises a krb5.conf from the domain + DC if none is present)
adit -dc 10.0.0.10 -fqdn dc01.example.local -d EXAMPLE.LOCAL -u user -p 'pass' -auth kerberos