BloodHound

Sections BloodHound

Graph the domain, find the shortest path. BloodHound CE is the current default (BloodHound Legacy and SharpHound are deprecated for new work).

DC=10.10.11.50
DOMAIN=corp.local
USER='svc_user'
PASS='Password1'

i. Spin up BloodHound CE

Docker compose, one-shot:

curl -L https://ghst.ly/getbhce -o docker-compose.yml
docker-compose up -d
## Wait 30s, then watch logs for the initial admin password:
docker-compose logs bloodhound | grep "Initial Password Set To"
## UI: http://127.0.0.1:8080  user: admin  pw: (from log)
CE vs Legacy

rusthound-ce, bloodyAD get bloodhound, and modern nxc all output CE-format JSON. Old SharpHound and bloodhound-python output Legacy format. Don’t mix, your edges will be missing or mislabeled.

ii. rusthound-ce, primary collector

Faster and more accurate than bloodhound-python. Generates the BloodHound CE-compatible zip directly.

Basic collection:

rusthound-ce -d $DOMAIN -u $USER@$DOMAIN -p $PASS -i $DC -z
## With PtH:
rusthound-ce -d $DOMAIN -u $USER@$DOMAIN -H $NTHASH -i $DC -z
## LDAPS:
rusthound-ce -d $DOMAIN -u $USER@$DOMAIN -p $PASS -i $DC --ldaps -z
## Custom output dir:
rusthound-ce -d $DOMAIN -u $USER@$DOMAIN -p $PASS -i $DC -o /tmp/bh -z

Stealthier collection (LDAP only, no SMB session enum):

rusthound-ce -d $DOMAIN -u $USER@$DOMAIN -p $PASS -i $DC --dc-only -z

ADCS enumeration (collects cert templates and CA objects for ADCS analysis):

rusthound-ce -d $DOMAIN -u $USER@$DOMAIN -p $PASS -i $DC --adcs -z

FQDN resolution module (when DNS is funky from your attacker box):

rusthound-ce -d $DOMAIN -u $USER@$DOMAIN -p $PASS -i $DC --fqdn-resolver -z

Through a SOCKS pivot (use proxychains, rusthound-ce is just an LDAP client):

proxychains -q rusthound-ce -d $DOMAIN -u $USER@$DOMAIN -p $PASS -i $DC -z

iii. bloodyAD get bloodhound, the fallback

bloodyAD’s collector is simpler and sometimes catches edges rusthound-ce skips, especially outbound ACL edges on objects outside your OU. Run it as a supplement.

bloodyAD -d $DOMAIN -u $USER -p $PASS --host $DC get bloodhound
## Output: BLOODHOUND-DATE.zip in CWD
## Custom path:
bloodyAD -d $DOMAIN -u $USER -p $PASS --host $DC get bloodhound --path /tmp/bh-bloody.zip
## Walk trusts (start from a DC of your home domain):
bloodyAD -d $DOMAIN -u $USER -p $PASS --host $DC get bloodhound --transitive

PtH:

bloodyAD -d $DOMAIN -u $USER -p ":$NTHASH" --host $DC get bloodhound

Through SOCKS pivot:

proxychains -q bloodyAD -d $DOMAIN -u $USER -p $PASS --host $DC get bloodhound
What bloodyAD’s collector misses

The wiki notes the bloodhound collector is still “basics only” and ADCS ESC nodes plus some complex relationship types aren’t included yet. Use it together with rusthound-ce, not instead of.

iv. Run both, upload both

The graph in BloodHound CE merges edges from multiple ingests. Run both tools, drop both zips into the BloodHound CE upload UI:

rusthound-ce -d $DOMAIN -u $USER@$DOMAIN -p $PASS -i $DC --adcs -z -o /tmp/rh
bloodyAD -d $DOMAIN -u $USER -p $PASS --host $DC get bloodhound --path /tmp/bloody.zip
## Then in BloodHound UI: Administration -> File Ingest -> drag both .zip files in

Catches the outbound ACL edges rusthound-ce silently skips, and gives you the ADCS analysis rusthound-ce includes.

v. nxc as a third-pass collector

When rusthound-ce or bloodyAD fail in a weird environment, nxc has a bloodhound module that uses bloodhound-python internally. Slower, more compatible:

nxc ldap $DC -u $USER -p $PASS --bloodhound --collection All --dns-server $DC

vi. Get writable as a mini-graph

bloodyAD get writable --bh outputs a BloodHound zip containing only the objects YOU can modify. Drop it into BloodHound for instant “what can I attack from here” view:

bloodyAD -d $DOMAIN -u $USER -p $PASS --host $DC get writable --bh
## Upload the resulting zip alongside your full graph

vii. Working with the BloodHound CE UI

Default tabs you’ll use:

  • Search - find any object by name or SID
  • Pathfinding - from owned principal to high-value (Domain Admins, DCs, etc)
  • Cypher - raw query language for custom paths
  • Group Management - mark owned principals, mark high-value targets

Mark yourself as Owned after you finish the collection. BloodHound highlights paths starting from any Owned node.

viii. Useful pre-built queries

Pre-built queries are in the right sidebar. The ones to run first:

  • “Find Shortest Paths to Domain Admins”
  • “Find all Domain Admins”
  • “Find shortest paths to Domain Admins from owned principals”
  • “List all Kerberoastable users”
  • “List all AS-REP Roastable users”
  • “Find computers where Domain Users are local admin”
  • “Shortest paths from owned to high value targets”

ix. Custom Cypher queries worth saving

Open the Cypher tab and paste:

Owned -> any high-value object, shortest path:

MATCH p = shortestPath((u {owned: true})-[*1..]->(t {highvalue: true}))
RETURN p

Users with paths to DA, with edge counts (find easy wins):

MATCH (u:User)-[r*1..]->(g:Group {name: 'DOMAIN ADMINS@CORP.LOCAL'})
RETURN u.name, length(r) AS hops
ORDER BY hops ASC
LIMIT 20

All users with SPNs (kerberoastable, exclude disabled):

MATCH (u:User {hasspn: true, enabled: true})
RETURN u.name, u.serviceprincipalnames

All ASREP-roastable users:

MATCH (u:User {dontreqpreauth: true, enabled: true})
RETURN u.name

Users with DCSync rights:

MATCH (u)-[:GetChanges|GetChangesAll]->(d:Domain)
RETURN u.name, d.name

Computers configured for unconstrained delegation:

MATCH (c:Computer {unconstraineddelegation: true})
RETURN c.name

Groups whose membership leads to DA in N hops or fewer:

MATCH p = (g:Group)-[*1..3]->(da:Group {name: 'DOMAIN ADMINS@CORP.LOCAL'})
RETURN p

Users with PASSWORD_NOT_REQUIRED set (empty password possible):

MATCH (u:User {passwordnotreqd: true, enabled: true})
RETURN u.name

x. Reading attack paths

Once you have a path, decode the edges:

EdgeTool to abuseFile
MemberOfnested group, follow the chainn/a
GenericAll, GenericWritebloodyAD add genericAll, set SPN, shadow creds, reset passwordACL Abuse
WriteOwnerbloodyAD set owner then GenericAllACL Abuse
WriteDaclgrant yourself GenericAllACL Abuse
ForceChangePasswordbloodyAD set passwordACL Abuse
AddMemberbloodyAD add groupMemberACL Abuse
ReadGMSAPasswordbloodyAD get object --attr msDS-ManagedPasswordCredential Dumping
ReadLAPSPasswordbloodyAD get object --attr ms-Mcs-AdmPwd or msLAPS-PasswordCredential Dumping
AllowedToDelegateconstrained delegation, see DelegationDelegation
AllowedToActRBCD, bloodyAD add rbcdDelegation
CanRDP, CanPSRemote, ExecuteDCOMlateral moveLateral Movement
HasSessionwait for user to log in or steal their tokenPrivEsc - Credentials & Files
Ownsimplicit WriteDacl, modify ACLACL Abuse
GetChanges, GetChangesAllDCSyncDCSync
ADCSESC*certipy, see ADCS ESC1-16ADCS ESC1-16

xi. Re-collect after every privesc step

Every time you compromise a new account, mark it Owned and re-run collection (or just re-run bloodyAD get writable --bh for that user). New paths often open up that weren’t visible before.