Spotting BloodHound on LDAP: Wire Patterns from bloodhound-python

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

  1. DetectionLab or any small AD with one DC + one workstation
  2. Kali on the same L2/L3 segment
  3. Mirror/span the DC port or capture on the DC vNIC
  4. 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_NET DCs 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

  1. Capture a full BloodHound collect in lab
  2. Export LDAP conversations only
  3. Note top 5 byte-unique search patterns
  4. Write 1–2 high-signal rules, not 40 noisy ones
  5. Replay against 24h of production-like LDAP (or a staging DC mirror)
  6. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *