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)
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
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:
| Edge | Tool to abuse | File |
|---|---|---|
MemberOf | nested group, follow the chain | n/a |
GenericAll, GenericWrite | bloodyAD add genericAll, set SPN, shadow creds, reset password | ACL Abuse |
WriteOwner | bloodyAD set owner then GenericAll | ACL Abuse |
WriteDacl | grant yourself GenericAll | ACL Abuse |
ForceChangePassword | bloodyAD set password | ACL Abuse |
AddMember | bloodyAD add groupMember | ACL Abuse |
ReadGMSAPassword | bloodyAD get object --attr msDS-ManagedPassword | Credential Dumping |
ReadLAPSPassword | bloodyAD get object --attr ms-Mcs-AdmPwd or msLAPS-Password | Credential Dumping |
AllowedToDelegate | constrained delegation, see Delegation | Delegation |
AllowedToAct | RBCD, bloodyAD add rbcd | Delegation |
CanRDP, CanPSRemote, ExecuteDCOM | lateral move | Lateral Movement |
HasSession | wait for user to log in or steal their token | PrivEsc - Credentials & Files |
Owns | implicit WriteDacl, modify ACL | ACL Abuse |
GetChanges, GetChangesAll | DCSync | DCSync |
ADCSESC* | certipy, see ADCS ESC1-16 | ADCS 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.