BloodHound CE
BloodHound CE (Community Edition) is the primary analysis surface for SFHound graphs. SFHound emits a BloodHound OpenGraph v2-compatible JSON file that BloodHound CE ingests natively.
Prerequisites
- BloodHound CE running (Docker Compose or standalone)
- SFHound collector has produced an output JSON in
./opengraph_output/
Loading data
Run the collector with --auto-ingest:
python sfhound.py --auto-ingestWhat happens:
- Validates the JSON against the OpenGraph schema.
- Checks for stuck/active jobs — aborts if any exist.
- Creates a BloodHound file-upload job, uploads the graph, and starts ingestion.
- Polls until ingestion completes (prints status every 15 s).
- Prints completed task details including any errors or warnings.
All credentials can be passed via config.yaml or command line:
python sfhound.py --auto-ingest \ --bh-url http://127.0.0.1:8080 \ --bh-username admin \ --bh-password YOUR_BLOODHOUND_PASSWORD- Open BloodHound CE in your browser.
- Navigate to the File Ingest icon (top navigation).
- Drag the JSON file from
./opengraph_output/into the upload modal. - Wait for ingestion to complete.
Register custom icons
SFHound ships custom BloodHound node icons for all Salesforce node types. Register them once after BloodHound is running:
python examples/post_custom_icons.pyThis POSTs SVG icon definitions for SFUser, SFProfile, SFPermissionSet, SFPermissionSetGroup, SFRole, SFGroup, SFQueue, SFConnectedApp, SFSObject, SFField, and SFOrganization to the BloodHound API.
Configure Tier Zero rules
System-level compromise
Add this Cypher rule in BloodHound CE → Configuration → Tier Zero → Add Cypher Rule:
MATCH (u:SFUser)-[:AssignedProfile|AssignedPermissionSet]->(ps)-[:ModifyAllData|ManageUsers|ManageProfilesPermissionsets|AuthorApex|CustomizeApplication|ManageSharing]->(:SFOrganization)WHERE ps:SFProfile OR ps:SFPermissionSetRETURN DISTINCT u;Crown-jewel object access
MATCH (u:SFUser)-[:AssignedProfile|AssignedPermissionSet|AssignedPermissionSetGroup|HasPermissionSet|IncludesPermissionSet*1..5]->(ps)-[:CanCreate|CanRead|CanEdit|CanDelete|CanViewAll|CanModifyAll]->(obj:SFSObject)WHERE obj.name IN ["SECRETDATA__C", "SENSITIVEDATA__C", "HIDDENDATA__C"] AND (ps:SFPermissionSet OR ps:SFProfile)RETURN DISTINCT u;Crown-jewel field access
MATCH (u:SFUser)-[:AssignedProfile|AssignedPermissionSet|AssignedPermissionSetGroup|HasPermissionSet|IncludesPermissionSet|CanCreate|CanRead|CanEdit|CanDelete|CanViewAll|CanModifyAll|IsVisible|ReadOnly|Contains*1..10]->(f:SFField)WHERE f.name IN ["SECRETDATA__C.HIGHLYSENSITIVEFIELD__C","SECRETDATA__C.OTHERSENSITIVEFIELD__C"]RETURN DISTINCT uLIMIT 1000;Browsing the graph
Useful starting points
- Search for SFOrganization to see all system permissions entering the org node.
- Search for a specific user by
Usernameornameto explore their assignment chain. - Use the Pathfinding tab to find the shortest path between any two nodes.
Node properties panel
Click any node or edge to view its properties. Named edges (system permissions, CRUD, FLS, assignment) display:
| Property | What you see |
|---|---|
General | Plain-language description of the permission |
AbuseInfo | How an attacker exploits this edge |
RemediationInfo | Steps to restrict access + SOQL queries |
OPSEC | Logging gaps an attacker could exploit |
References | MITRE ATT&CK mappings + Salesforce docs |
BloodHound CE Docker Compose quickstart
If you don’t already have BloodHound CE running:
curl -L https://ghst.ly/getbhce | docker compose -f - upDefault credentials: admin / check the container logs for the initial password on first run.
BloodHound CE is available at http://127.0.0.1:8080.
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
Auto-ingest aborts: active job detected | A previous ingestion is stuck | Clear it in Administration → File Ingest |
| Nodes appear but edges are missing | Partial extraction from limited user | Re-run with a more privileged user; see Quickstart |
| Custom icons not showing | post_custom_icons.py not run, or run before BloodHound was ready | Re-run the script after BloodHound is fully started |
400 Bad Request on upload | JSON does not validate against OpenGraph schema | Check SFHound version; run with --debug |