Workflow: Active Directory Enumeration
Collect the AD graph (BloodHound) and LDAP/SMB intelligence needed to choose an attack path.
Preconditions
- Reachable DC (port 389/445/88)
- At least one valid domain credential (even low-priv) OR confirmed anonymous LDAP (rare)
workflows/recon.mdcompleted;dc_candidates.txtexists
Inputs
- Valid credential:
USER,PASS(orHASH) DC_IP,DOMAIN
Outputs
bh/— SharpHound zip(s), importable to BloodHound CEusers.txt,computers.txt,groups.txtspn_users.txt,asrep_users.txtacl_findings.json
Step sequence
1. Validate credentials and domain config
nxc smb "$DC_IP" -u "$USER" -p "$PASS" --pass-pol # confirm auth + policy
nxc ldap "$DC_IP" -u "$USER" -p "$PASS" -M maq # MachineAccountQuotaRecord password policy — lockout threshold drives credential-attack workflow.
2. BloodHound collection (parallelizable by collection method)
BloodHound's collection is cleanly split into independent passes. Fan them out:
mkdir -p bh && cd bh
# Sub-agent 1
bloodhound-python -u "$USER" -p "$PASS" -d "$DOMAIN" -dc "$DC_FQDN" \
-ns "$DC_IP" -c Default --zip &
# Sub-agent 2
bloodhound-python -u "$USER" -p "$PASS" -d "$DOMAIN" -dc "$DC_FQDN" \
-ns "$DC_IP" -c ACL --zip &
# Sub-agent 3
bloodhound-python -u "$USER" -p "$PASS" -d "$DOMAIN" -dc "$DC_FQDN" \
-ns "$DC_IP" -c LocalGroup --zip &
# Sub-agent 4 (slowest — session enumeration needs SMB to each computer)
bloodhound-python -u "$USER" -p "$PASS" -d "$DOMAIN" -dc "$DC_FQDN" \
-ns "$DC_IP" -c Session --zip &
waitOn Windows, equivalent with SharpHound:
.\SharpHound.exe -c Default,ACL,LocalGroup,Session --zip --outputprefix bhImport: drop all zips into BloodHound CE Ingest UI or use bloodhound-cli / bhctl.
3. LDAP extraction (parallelizable per object class)
LDAP="ldap://$DC_IP"
BASE="DC=${DOMAIN//./,DC=}"
BIND="-D ${USER}@${DOMAIN} -w ${PASS}"
# Fan-out: users, computers, groups, SPNs, ASREP
( ldapsearch -x -H $LDAP $BIND -b "$BASE" "(objectClass=user)" \
sAMAccountName userAccountControl pwdLastSet description \
> users.ldif ) &
( ldapsearch -x -H $LDAP $BIND -b "$BASE" "(objectClass=computer)" \
dNSHostName operatingSystem operatingSystemVersion \
> computers.ldif ) &
( ldapsearch -x -H $LDAP $BIND -b "$BASE" "(objectClass=group)" \
cn member > groups.ldif ) &
( ldapsearch -x -H $LDAP $BIND -b "$BASE" \
"(&(objectClass=user)(servicePrincipalName=*))" \
sAMAccountName servicePrincipalName > spn_users.ldif ) &
( ldapsearch -x -H $LDAP $BIND -b "$BASE" \
"(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))" \
sAMAccountName > asrep_users.ldif ) &
wait
# Reduce to plain lists
grep -i '^sAMAccountName:' users.ldif | awk '{print $2}' | sort -u > users.txt4. ADCS enumeration
certipy-ad find -u "$USER@$DOMAIN" -p "$PASS" -dc-ip "$DC_IP" \
-vulnerable -stdout > adcs_vuln.txtLook for ESC1/ESC2/ESC3/ESC4/ESC6/ESC8/ESC9/ESC11 hits.
5. BloodHound path analysis (reasoning-heavy)
Use extended thinking here. Feed the model the set of outbound edges from owned principals and ask it to prioritize attack paths by: chain length, tool availability, noise level, and blast radius.
Core queries — see references/bloodhound_queries.md:
- Shortest path from owned to Domain Admins
- Kerberoastable users with low pwdlastset (i.e., stale passwords)
- Unconstrained delegation hosts (non-DC)
- ACL write primitives over high-value objects
- LAPS/gMSA readability
6. Triage matrix
For each identified path, record:
| Field | Example |
|---|---|
| Entry point | alice@corp.local (owned) |
| Target | DOMAIN ADMINS@CORP.LOCAL |
| Chain | Alice -> WriteDacl(GRP_SQL) -> AddMember(self) -> local admin on SQL01 -> DA session -> DCSync |
| Noise | Medium (ACL edit + lsass dump) |
| Blast radius | Domain-wide |
| Approval needed | Yes — DCSync step |
| Reversibility | Partial (ACL edit must be reverted) |
Parallelism checklist
| Step | Parallel? |
|---|---|
| SharpHound Default / ACL / LocalGroup / Session | Yes, independent LDAP/SMB streams |
| LDAP object-class queries | Yes |
| Certipy find | Sequential (single-threaded) |
| Path analysis / reasoning | Sequential (serial LLM call with extended thinking) |
Safety hints
- Session collection generates SMB connection to every computer — can trip EDR "SMB scanning" detections. Consider running at low concurrency (
--workers 5) during business hours. - Avoid Session enumeration against DCs unless specifically scoped.
- Never modify BloodHound's
ownedproperty on production data — keep local markings in a separate neo4j database if concerned.
Next workflows
- Attack paths identified:
workflows/credential_attacks.md(if you need more creds) or straight toworkflows/lateral_movement.md/workflows/privilege_escalation.md.