BloodHound / SharpHound / bloodhound-python are not subtle on LDAP if you know what “enumerate the world” looks like. Host telemetry helps. On a span or IDS sitting in front of DCs, you can often catch the shape of the recon without waiting for a noisy binary alert.
This is lab methodology from running Kali’s bloodhound-python against a disposable domain and staring at LDAP on 389 — not a promise that one signature owns all versions forever.
What BloodHound is doing on the wire
At a high level, collection walks directory objects with a handful of very characteristic filters and attribute sets. On the wire you see repeated LDAP search requests (BER-encoded) toward DCs on tcp/389 (or 636 if LDAPS — different story).
Useful anchors from the open-source collector logic (see the Kali bloodhound.py domain module):
- Broad
objectClass/ schema-ish probes - Domain object queries
crossRef/ partition discovery patterns- Computer objects via
sAMAccountType=805306369 - User / group enumeration with large attribute requests (SPNs, memberof, nTSecurityDescriptor, etc.)
- MSA / gMSA class probes
None of those strings are evil alone. The combination + volume + source host that is not a DC/admin jumpbox is the tell.
Lab setup that produces clean PCAPs
- DetectionLab or any small AD with one DC + one workstation
- Kali on the same L2/L3 segment
- Mirror/span the DC port or capture on the DC vNIC
- Run collection with known creds (this is your lab — you own it)
# Example shape only — use lab accounts
bloodhound-python -d detectionlab.local -u 'user' -p '***' -ns 10.0.0.10 -c All
Wireshark display filter starting point:
tcp.port == 389 && ldap
What “interesting” looks like in LDAP
You are looking for search requests that:
- Hit the DC from a workstation or random server subnet
- Request fat attribute sets (security descriptors, SPNs, lastLogon, servicePrincipalName, member, …)
- Chain objectClass / sAMAccountType filters that match collector source
- Burst: many distinct searches in seconds, not a single Outlook Address Book query
Decoded filter fragments you will recognize from collectors (plaintext once BER is decoded in Wireshark):
# Computers (sAMAccountType for machine accounts)
(sAMAccountType=805306369)
# Users (person/user classes + attribute shopping list)
(objectCategory=person)(objectClass=user)
# Groups
(objectClass=group)
# Kerberoast-relevant users later in the kill chain often look like:
(&(samAccountType=805306368)(servicePrincipalName=*)(!(samAccountName=krbtgt)))
BloodHound collection is wider than kerberoast — treat kerberoast filters as a cousin signal, not the whole story.
IDS approach (lab signatures, not gospel)
Snort/Suricata content matches on BER are brittle but workable for known collector builds. Pattern:
- Match LDAP search on 389
- Match distinctive BER snippets for
objectClass+ specific class names your collector emits - Scope
$HOME_NETDCs as destination - Alert on non-DC sources if your rules language allows IP lists
Illustrative Suricata-style idea (rewrite SIDs; validate on your PCAP; expect false positives on legitimate identity tools):
alert tcp any any -> $DC_SERVERS 389 (
msg:"LAB AD_RECON LDAP search - computer sAMAccountType 805306369";
flow:to_server,established;
content:"|33 30 35 33 30 36 33 36 39|"; # ASCII 805306369 in BER context — verify!
threshold:type limit, track by_src, count 5, seconds 60;
classtype:attempted-recon;
sid:9100001;
rev:1;
)
Do not ship that untested. BER encoding means “string in PCAP” ≠ “string in rule” until you prove it. The professional workflow is: PCAP → find unique byte sequences → rule → replay → tune threshold.
I have had better luck with tcp-pkt / raw content matches on distinctive multi-field sequences from a specific collector version than with hoping app-layer LDAP keywords exist on every sensor build.
Host-side correlates (pair them)
- Process lineage: python/bloodhound/sharpHound/AzureHound-ish tools
- LDAP search volume from a single non-DC machine account
- Kerberos ticket behavior after recon (later stage)
Wire-only detections die in encrypted LDAPS-only estates unless you terminate TLS on a broker you instrument. Know your estate.
False positives you will hate
- Microsoft Identity / Entra Connect / provisioning agents
- Privileged access workstations running ADUC / deep directory scripts
- Vulnerability scanners with LDAP enum modules
- Backup / DR tools walking directory objects
Mitigations: source allowlists for known identity infra, thresholds, and “never fire on DC→DC.”
Operator checklist
- Capture a full BloodHound collect in lab
- Export LDAP conversations only
- Note top 5 byte-unique search patterns
- Write 1–2 high-signal rules, not 40 noisy ones
- Replay against 24h of production-like LDAP (or a staging DC mirror)
- Document expected legitimate sources before enabling block actions
Next up in this vein: Rubeus kerberoast/AS-REP filters — narrower LDAP, different intent, cleaner story for 4769 correlation.