Skip to content

BloodHound Integration

Weylon Solis edited this page Mar 18, 2026 · 1 revision

BloodHound Integration

ForceHound outputs OpenGraph v1 JSON that BloodHound Community Edition can ingest directly.

Quick upload

# Collect and upload in one step
forcehound --collector api \
  --instance-url https://myorg.my.salesforce.com \
  --username admin@myorg.com --password 'P@ss' --security-token ABC \
  --upload --bh-token-id UUID --bh-token-key BASE64KEY \
  -o output.json

# Or upload a previously collected file
forcehound --setup --bh-token-id UUID --bh-token-key BASE64KEY
# Then use BloodHound CE's File Ingest UI to upload output.json

Register custom node types

ForceHound uses Salesforce-specific node kinds (SF_User, SF_Profile, etc.) that aren't built into BloodHound. Register them first:

forcehound --setup --bh-url http://localhost:8080 \
  --bh-token-id UUID --bh-token-key BASE64KEY

This sets custom icons and colors in the BH UI. Only needs to be done once per BH instance.

Clear database before upload

forcehound ... --upload --clear-db --wait 60 \
  --bh-token-id UUID --bh-token-key BASE64KEY

The --wait flag controls how long to wait after sending the clear request (BH clears asynchronously). Default is 60 seconds — increase if you see canceled ingestion jobs.

Cypher queries

After ingestion, query the graph in BloodHound's Cypher console:

Find users with ModifyAllData

MATCH p=(u:SF_User)-[:HasProfile|HasPermissionSet*1..2]->(ps)-[:ModifyAllData]->(org:SF_Organization)
RETURN p

Find all CRUD paths to Account

MATCH p=(u:SF_User)-[:HasProfile|HasPermissionSet*1..2]->(ps)-[:CanRead|CanCreate|CanEdit|CanDelete]->(o:SF_Object {name: "ACCOUNT"})
RETURN p

Who can access connected apps?

MATCH p=(ps)-[:CanAccessApp]->(ca:SF_ConnectedApp)
RETURN p

Show role hierarchy

MATCH p=(r1:SF_Role)-[:ReportsTo*1..5]->(r2:SF_Role)
RETURN p

Find private objects with broad read access

MATCH (o:SF_Object)
WHERE o.InternalSharingModel = "Private"
WITH o
MATCH p=(ps)-[:CanRead]->(o)
RETURN o.name, count(ps) AS readers
ORDER BY readers DESC

API notes

  • Auth: HMAC-SHA256 signature (bhesignature scheme), not Bearer tokens
  • Cypher endpoint: POST /api/v2/graphs/cypher with {"query": "...", "include_properties": true}
  • 404 = empty result: Non-mutation queries that match zero results return 404, not an empty 200
  • Uppercase properties: BH uppercases all string property values during ingestion — query with "ACCOUNT" not "Account"
  • Ingestion is async: Upload starts a background job. Poll GET /api/v2/file-upload until status = 2 (Complete)
  • Large graphs: 3500+ nodes / 145k+ edges can take 60-90 seconds to ingest

Clone this wiki locally