# BloodHound Configuration Source: https://bloodhound.specterops.io/analyze-data/configuration Applies to BloodHound Enterprise only This article explains the multiple tenant-wide configurations supported by BloodHound Enterprise. The configurations can be changed by a BloodHound Administrator in ⚙️ > Administration > BloodHound Configuration. ## Reconciliation Configuration When enabled, BloodHound Enterprise will perform data reconciliation and retention. The configuration also allows for the changing of the default retention time. See [Data reconciliation and retention](/collect-data/enterprise-collection/data-retention). ## Citrix RDP Support When enabled, BloodHound Enterprise will prevent false-positive CanRDP findings for Citrix VDAs. This configuration adds to the [CanRDP edge conditions](/resources/edges/can-rdp) that a non-administrative principal must also be a member of the computer's local "Direct Access Users" group, which Citrix created to allow non-brokered access to VDAs. # Search with Cypher Source: https://bloodhound.specterops.io/analyze-data/explore/cypher-search Start exploring BloodHound's prebuilt Cypher queries to uncover relationships and gain deeper insights into your environment. Applies to BloodHound Enterprise and CE ## Purpose This article describes how to use Cypher queries to extend the basic search functionality of BloodHound. BloodHound offers a variety of prebuilt queries to help you get started. You can search and filter queries by various criteria, create and manage custom queries, and import and export queries in JSON format. ## What is Cypher? Cypher is a query language for graph databases (similar to SQL for relational databases). It uses an ASCII-art style syntax to describe nodes and relationships. If you can describe the path you're looking for, you can write it in Cypher. This article provides an introduction to Cypher queries in BloodHound, including how to access prebuilt queries and manage saved queries. See [Write Custom Queries](/analyze-data/explore/cypher-search#write-custom-queries) for more advanced information. ## Quickstart A great way to start exploring Cypher queries is through the community-driven [BloodHound Query Library](https://queries.specterops.io/). This comprehensive collection includes both community-contributed queries and the prebuilt queries that are available in BloodHound. When you're ready to explore prebuilt queries inside BloodHound, follow these steps: Click **Explore** > **Cypher** > **Saved Queries**. BloodHound displays prebuilt queries by default when you expand the **Saved Queries** section. Default view of the Saved Queries section Select a query from the list to display the Cypher syntax and automatically run the query. Click the **Auto-run selected query** checkbox if you prefer to run queries manually. Review the results in the graph view. You can modify the Cypher syntax and re-run the query to explore different relationships. ## Features BloodHound provides several features to help you work with Cypher queries. These features enable you to search and manage your queries effectively. ### Search and Filter BloodHound offers several search and filtering options to help you find the right query quickly. Search and filter queries * **Search saved queries by name**: Quickly locate specific queries using the search text box. * **Filter queries**: Narrow down the list of queries by selecting one of the following options: * **Platforms**: Displays queries based on the platform that they target, such as Active Directory or Azure. You can also filter to show only your saved queries. * **Categories**: Displays queries based on logical groups, such as shortest path and dangerous privileges. * **Source**: Displays queries based on their source, such as prebuilt, personal, and shared. ### Create and Manage Queries BloodHound provides several features to help you create and manage custom queries. For example, you can: * **Save a query**: Write a custom query and store it for future use. * **Save As**: Create a copy of an existing query with a new name, description, and updated parameters. * **Share a saved query**: Share your custom query with all users or specific users in your BloodHound environment. * **Edit a saved query**: Modify the Cypher syntax, metadata, and shared access of a custom query. * **Delete saved queries**: Remove custom queries that you no longer need. You can only edit, share, and delete queries that you have created. You cannot modify prebuilt queries directly, but you can use the **Save As** feature to create a copy that you can then edit. When you're ready to create and manage custom queries, follow these steps: In the left menu, click **Explore** > **Cypher**. Choose one of the following options to create a custom query: In the query editor, [write a custom query](/analyze-data/explore/cypher-search#write-custom-queries) and run it to see the results. Select a prebuilt or saved query from the list. Click the drop-down arrow beside **Save As** to create a copy of the selected query. In the *Save Query* dialog, enter a unique name and description for your query. Save query dialog In the *Manage Shared Queries* dialog, select **Set to Public** to enable collaboration with all users in your BloodHound environment or select specific users. You can change these settings later if needed. Click **Save** to store your custom query. It will now appear in the list of saved queries. To edit or delete your query, click the vertical ellipsis (three dots) next to the query name and select **Edit/Share** or **Delete**. Edit and delete saved queries ### Import and Export BloodHound allows you to import and export queries for easy sharing and backup. Import and export queries * **Import queries from JSON files**: Easily add new queries by dragging and dropping JSON files or compressed JSON files into the UI. BloodHound validates the files for correct syntax and notifies you of any errors. * **Export a saved query to a JSON file**: Share or back up your queries by exporting them in JSON format. Export is available for saved queries only. You cannot export prebuilt queries directly. ## Write Custom Queries BloodHound lets you run raw Cypher queries directly in the user interface. Use Cypher when you need to inspect relationships, answer targeted questions, or build analyses that go beyond the prebuilt queries. Cypher supports everything from simple lookups to complex identity attack-path analysis. For example, you can answer questions such as: * Which users have not reset their passwords in 180 days? * Which low-privileged users can reach machines that host an unconstrained gMSA? * What are the shortest paths from low-privilege users to Domain Admins? * Which objects are in Tier Zero? For example, use the following query to return all Tier Zero objects: ```cql theme={null} MATCH (n:Tag_Tier_Zero) RETURN n ``` If you need to narrow the results to a specific object type, combine labels in the same pattern. For example, to return only Tier Zero users: ```cql theme={null} MATCH (n:User:Tag_Tier_Zero) RETURN n ``` Do not search for Tier Zero objects by inspecting `system_tags` properties with patterns such as `coalesce(n.system_tags, [])`. Support for this pattern is temporary and will be deprecated in a future release. BloodHound represents Tier Zero membership with the `Tag_Tier_Zero` label, so label matching is the supported and preferred approach. ### Elements of the graph database Everything in the graph database is represented using common terms from graph theory, particularly [edges](/resources/edges/overview) and [nodes](/resources/nodes/overview). Nodes represent discrete objects in your environment. In BloodHound, a node can represent a user, group, device, repository, or another object collected from a built-in platform or an OpenGraph extension. Edges represent relationships between nodes and often describe how one object connects to (or can affect) another. In BloodHound, an edge can represent membership, administrative control, trust, or another relationship defined in the graph. Together, nodes and edges form the paths BloodHound uses to model how access, permissions, and control flow through your environment. ### Basic Cypher When building Cypher queries, it's important to note that you're generally trying to build a path using the relationships available to you. Let's look at an extremely basic query: ```cql theme={null} MATCH (B)-[A]->(R) RETURN B ``` Let's break down how this Cypher query is constructed. When querying the database, we start our queries with the MATCH keyword. The MATCH clause lets you specify a pattern in the database. * Each variable in the Cypher query is defined using an identifier, in this case, the following ones: B, A, and R. The identifier for variables can be anything you want, including entire words, such as 'groups'. * In Cypher queries, nodes are specified using parentheses, so B and R are nodes in the sample query above. * Relationships are specified using brackets, so in this example, A represents relationships. The dashes between the nodes and relationships can be used to specify direction. Relationships in BloodHound always go in the direction of compromise or further privilege, whether through group membership or user credentials from a session. In the above query, the **->** specifies that the query should return relationships that go from B to R. Removing the **>** will allow the query to search relationships in both directions. Finally, the RETURN statement instructs the database to return the item matched with the corresponding variable name B. Now, let's take our previous query and make it a bit more complex: ```cql theme={null} MATCH (n:User),(m:Group) MATCH p=(n)-[r:MemberOf*1..3]->(m) RETURN p ``` This query is a bit more refined than the previous one. By using labels on both nodes and edges, we can make our query a lot more specific. We also pre-assign the variables **n** and **m** and give them labels to make the query easier to read. In this particular case, we're asking BloodHound to find nodes with the labels User and Group, and then match those nodes using the *MemberOf* relationship. We added a length modifier as well to the relationship. Adding \***1..3** limits the search to relationships that are between one and three links. In simple terms, give me any users that are a member of a group up to three links away. Additionally, we're assigning the result of the pattern to the variable **p** and returning that variable. When we get **p** back, it will contain the result of each path it can find that matches our pattern we asked for. Now that we've looked at the basic building blocks of queries, let's look at a more complicated one. As an example, here's the query we use to calculate shortest paths to Domain Admins, one of the most important queries in the BloodHound interface: ```cql theme={null} MATCH p=shortestPath((n:User)-[*1..]->(m:Group)) WHERE m.name = "DOMAIN ADMINS@INTERNAL.LOCAL" RETURN p ``` Cypher is case-sensitive, and the node property "name" is always all uppercase and postfixed with the directory's domain. In the code above, "Domain Admins" in the domain "internal.local" has become **"DOMAIN [ADMINS@INTERNAL.LOCAL](mailto:ADMINS@INTERNAL.LOCAL)"**. In this query, we add a few more elements to our previous ones. We still use labels to specify our nodes, but we also add another degree of specificity to our group node by restricting the group nodes that can be returned to only the **DOMAIN [ADMINS@INTERNAL.LOCAL](mailto:ADMINS@INTERNAL.LOCAL)** by specifying the name parameter. We also use the shortestPath function. Using this function, we ask the graph to give us the shortest path it can find between each node **n** and the Domain Admins group. Because we didn't specify any relationship labels, the query will use any possible relationship it can find. We also removed the limit on how many hops the database can search. By not specifying an upper limit, the database will go as many hops as possible to find a path. There is also an allShortestPaths function available, which, as the name implies, will find every shortest path from each node to your target. Note that this results in more data analysis to perform the query and could result in higher resource consumption. Another important part of Cypher to note is that wildcard matches are possible using regex, although the syntax for the query changes slightly. As an example, here's the query that's run each time you type a letter in the search bar: ```cql theme={null} MATCH (n) WHERE n.name =~ "(?i).*searchterm.*" RETURN n LIMIT 10 ``` In this query, we ask the graph to return any nodes of any type that match the search term given. The (**?i**) tells the graph this is a case-insensitive regex, with the **.**\* on each side indicating that we want to match anything on either side. We limit the number of items returned to the first ten using the **LIMIT** keyword. ### Advanced Concepts As you build into more complicated queries, the **WITH** keyword will become important. The **WITH** keyword allows you to use multiple queries and pass the results of each query to the next step. An example of this is in the BloodHound interface whenever you click on a group node. The "Session" section displays the number of places where users in this group (including its subgroups) currently have sessions. The UI calculates the number of sessions for the group using two separate queries put together: ```cql theme={null} MATCH p=shortestPath((m:User)-[r:MemberOf*1..]->(n:Group)) WHERE n.name = "$name_of_group" WITH m MATCH q=((m)<-[:HasSession]-(o:Computer)) RETURN count(o) ``` This query looks more complicated than we had before, so let's break it down into two components. ```cql theme={null} MATCH p=shortestPath((m:User)-[r:MemberOf*1..]->(n:Group)) WHERE n.name = "$name_of_group" ``` This is the first query we run. We ask BloodHound to find the shortestPath possible from any user node to the group we specify. Note that we allow the *MemberOf* relationship to span any number of hops, allowing us to include users inside nested groups. This first query gives us all the effective members of the group we ask for. ```cql theme={null} MATCH q=((m)<-[:HasSession]-(o:Computer)) RETURN count(o) ``` This is the second query that actually gives us the session data. The variable **m** is carried over from the previous query and contains all the users relevant to the group we're attempting to find sessions for. We ask BloodHound to find any computer where any of the users we found in the first step has a session using the *HasSession* relationship. We're not interested in returning the relationships in this particular case, so we don't assign a variable. Finally, we return the count of the number of computers we have sessions on. The two queries we execute are joined together using the **WITH** keyword. When using the keyword, you specify any variables you want to carry over from the previous part of the query. These variables will be available with the data for the next query in your chain. ## Outcome Now that we've explained Cypher and the syntax and all the cool ways you can narrow down search results, the next step is for you to build some new and interesting queries and start examining how you can view relationships. # Supported Cypher Syntax Source: https://bloodhound.specterops.io/analyze-data/explore/cypher-supported This page documents the supported openCypher Syntax that BloodHound officially supports Applies to BloodHound Enterprise and CE # Purpose This article describes how to use Cypher Search within BloodHound. Users of BloodHound should use it to extend the basic search functionality of BloodHound. # Supported Query Components Below are the currently supported openCypher query components translated by [CySQL](https://github.com/SpecterOps/DAWGS/blob/main/cypher/Cypher%20Syntax%20Support.md). ## Pattern Matching ### Node Matching ```Cypher theme={null} match (n) where n.name = 'my name' return n ``` ### Inline Node Label Matchers ```Cypher theme={null} match (n:User) return n ``` BloodHound-specific labels also use this syntax. For example, Tier Zero membership is represented with the `Tag_Tier_Zero` label. For guidance and examples, see [Write Custom Queries](/analyze-data/explore/cypher-search#write-custom-queries). ### Inline Node Pattern Property Matchers ```Cypher theme={null} match (n {prop: 'value'}) return n ``` ### Relationship Matching ```Cypher theme={null} match (:User)-[r]->() return r ``` ```Cypher theme={null} match (:User)-[r:MemberOf|GenericWrite]->() return r ``` ### Recursive Expansion ```Cypher theme={null} match (:User)-[r:MemberOf*..]->(:Group) return r ``` ### Ranges for Recursive Expansion ```Cypher theme={null} match (:User)-[r:MemberOf*2..]->(:Group) return r ``` ```Cypher theme={null} match (:User)-[r:MemberOf*2..4]->(:Group) return r ``` ### Pattern Projections ```Cypher theme={null} match p = (:Computer)-[:HasSession]->(:User) return p ``` ```Cypher theme={null} match p = (:User)-[:MemberOf*..]->(:Group) return p ``` ### Multiple Reading Clauses ```Cypher theme={null} match (u:User) where u.is_eligible match p = (u)<-[:HasSession]-(c:Computer) return p ``` ### Shortest Paths ```Cypher theme={null} match p = shortestPath((u:User)-[*..]->(:Domain)) where u.objectid = 'UUID-1234-567890' return p ``` ### All Shortest Paths ```Cypher theme={null} match p = allShortestPaths((u:User)-[*..]->(:Domain)) where u.objectid = 'UUID-1234-567890' return p ``` ## Build multi-part queries Use `WITH` to pass intermediate results from one query part to the next. You can use `WITH` to filter, aggregate, sort, or alias results before continuing the query. ```cypher theme={null} MATCH p = shortestPath((u:User)-[:MemberOf*1..]->(g:Group)) WITH DISTINCT g AS targetGroup, COUNT(u) AS userCount RETURN targetGroup.name, userCount ORDER BY userCount DESC LIMIT 5 ``` ## Result Ordering and Pagination ### `ORDER BY` Use `ORDER BY` with `RETURN` or `WITH` to sort results by a projected value. Ascending order is the default; append `DESC` for descending order. ```cypher theme={null} match (u:User) return u.name order by u.name desc limit 10 ``` ### `SKIP` and `LIMIT` Use `SKIP` and `LIMIT` with `RETURN` or `WITH` to paginate or trim a result set after sorting. ```cypher theme={null} match (n:User) return n order by n.name skip 10 limit 100 ``` ## Entity Creation[](#entity-creation) You must enable the clause by setting `enable_cypher_mutations: true` in your BloodHound [configuration file](/manage-bloodhound/bh-config#enable_cypher_mutations). ### Create nodes ```Cypher theme={null} create (n:User {name: 'alice', objectid: 'UUID-1234-567890'}) return n ``` ### Create relationships ```Cypher theme={null} create (u:User {name: 'alice'})-[r:MemberOf]->(g:Group {name: 'admins'}) return r ``` ## Entity Updates[](#entity-updates) You must enable the clause by setting `enable_cypher_mutations: true` in your BloodHound [configuration file](/manage-bloodhound/bh-config#enable_cypher_mutations). ### Setting Properties and Labels ```Cypher theme={null} match (n:Base) where n.obviously_is_user set n.other = 1 set n:User return n ``` ```Cypher theme={null} match ()-[r:HasSession]->(:User) set r.special_property = true ``` ### Removing Properties and Labels ```Cypher theme={null} match (n:User) remove n.name remove n:User return n ``` ```Cypher theme={null} match ()-[r:HasSession]->(:User) remove r.special_property ``` ## Entity Deletion[](#entity-deletion) You must enable the clause by setting `enable_cypher_mutations: true` in your BloodHound [configuration file](/manage-bloodhound/bh-config#enable_cypher_mutations). ```Cypher theme={null} match (s:User) detach delete s ``` ```Cypher theme={null} match ()-[r:MemberOf]->() delete r ``` ## Unwind a list into rows Expand a list into individual rows for further filtering, aggregation, or sorting. ```cypher theme={null} match (u:User) where u.name contains "A" with collect(u.name) as temp unwind temp as usernames return usernames limit 100 ``` Queries that use `UNWIND` return tabular results only. ## Supported Query Filters ### Comparison Expressions The following operators are supported in authoring comparison expressions: * `=` * `<>` * `<` * `>` * `<=` * `>=` When authoring comparison statements, users must be aware of the typing requirements of CySQL compared to Cypher as executed by Neo4j. For more information see the `Differences between Cypher and CySQL` subsection `Stricter Typing Requirements`. ### Negation Negation in query filters is supported with the `not` operator: ```Cypher theme={null} match (n:User) where not(n.eligible) return n ``` ### Conjunction and Disjunction Conjunction `and` and disjunction `or` operators are both supported: ```Cypher theme={null} match (n:User) where n.eligible and n.enabled return n ``` ```Cypher theme={null} match (n:User) where n.eligible or n.seen_as_active return n ``` ### String Searching Searching strings may be performed in a variety of ways. These matches are case-sensitive and do not support wildcard expansions. #### String Prefix Matching A string property may be filtered by prefix matching: ```Cypher theme={null} match (n:User) where n.name starts with 'my prefix' return n ``` #### String Contains Matching A string property may be filtered by contains matching: ```Cypher theme={null} match (n:User) where n.details contains 'something interesting' return n ``` #### String Suffix Matching A string property may be filtered by suffix matching: ```Cypher theme={null} match (n:User) where n.name ends with 'my suffix' return n ``` ### Regular Expressions ```Cypher theme={null} match (n:User) where n.name =~ 'userPrefix.*' return n ``` ### Pattern Predicates Query filters may also include pattern lookups. For example, searching for users with no active login sessions: ```Cypher theme={null} match (n:User) where not((n)<-[:HasSession]-(:Computer)) return n ``` ## Quantifier Expressions ### `any` Returns true if at least one item in the list contains the specified value. ```Cypher theme={null} WITH "KEYWORD" as SPNKeyword MATCH (n:User) WHERE ANY(keyword IN n.serviceprincipalnames WHERE toUpper(keyword) CONTAINS toUpper(SPNKeyword)) RETURN n ``` ### `single` Indicates that exactly one item in the list contains the specified value. ```Cypher theme={null} WITH "KEYWORD" as SPNKeyword MATCH (n:User) WHERE SINGLE(keyword IN n.serviceprincipalnames WHERE toUpper(keyword) CONTAINS toUpper(SPNKeyword)) RETURN n ``` ### `none` Indicates that none of the objects in the list contain the specified value. ```Cypher theme={null} WITH "KEYWORD" as SPNKeyword MATCH (n:User) WHERE NONE(keyword IN n.serviceprincipalnames WHERE toUpper(keyword) CONTAINS toUpper(SPNKeyword)) RETURN n ``` ### `all` Indicates that all objects in the list contain the specified value. ```Cypher theme={null} WITH "KEYWORD" as SPNKeyword MATCH (n:User) WHERE ALL(keyword IN n.serviceprincipalnames WHERE toUpper(keyword) CONTAINS toUpper(SPNKeyword)) RETURN n ``` ## Supported Subquery Expressions[](#supported-subquery-expressions) ### `collect` A collect subquery expression can be used to create a list with the rows returned by a given subquery. ### `count` Aggregates and counts the results of the given subquery as an integer. ## Supported Cypher Functions[](#supported-cypher-functions) ### `duration` Function Parses a valid duration string into a time duration that can be used in conjunction with other duration or date types. ```Cypher theme={null} match (s) where s.created_at = date() - duration('P1D') return s ``` ### `id` Function Returns the entity identifier of the node or relationship. ```Cypher theme={null} match (s) where id(s) in [1, 2, 3, 4] return s ``` ### `localtime` Returns the local time without timezone information. ```Cypher theme={null} match (s) where s.created_at <= localtime() return s ``` ### `localdatetime` Returns the local datetime without timezone information. ```Cypher theme={null} match (s) where s.created_at > localdatetime() return s ``` ### `date` Returns the current date with timezone information. ```Cypher theme={null} match (s) where s.created_at = date() return s ``` ### `datetime` Returns the current datetime with timezone information. ```Cypher theme={null} match (s) where s.created_at < datetime() return s ``` ### `type` Returns the type of the given relationship reference. This function returns the relationship's type as a text value. Type checks utilizing this function will not be index accelerated and may exhibit poor performance. ```Cypher theme={null} match ()-[r]->() where type(r) = 'EdgeKind1' return r ``` ### `split` Takes a given expression and text delimiter and returns a text array containing split components, if any. If the given expression does not evaluate to a text value this function will raise an error. ```Cypher theme={null} match (u:User) where '255' in split(u.ip_addr, '.') return u ``` ### `tolower` Returns the localized lower-case variant of a given expression. If the given expression does not evaluate to a text value this function will raise an error. ```Cypher theme={null} match (u:User) return tolower(u.name) ``` ### `toupper` Returns the localized upper-case variant of a given expression. If the given expression does not evaluate to a text value this function will raise an error. ```Cypher theme={null} match (u:User) return toupper(u.name) ``` ### `tostring` Returns the text value of a given expression. If the given expression represents a type that can not be converted to text this function will raise an error. ```Cypher theme={null} match (u:User) return tostring(u.num_active_logins) ``` ### `toint` Returns the integer value of a given expression. If the given expression represents a type that can not be converted or parsed to an integer this function will raise an error. ```Cypher theme={null} match (u:User) return toint(u.integer_in_text_property) ``` ### `coalesce` Returns the first non-null value in a list of expressions. This is critically useful for navigating differences in `null` behavior between Cypher and CySQL. ```Cypher theme={null} match (n:NodeKind1) where n.target = coalesce(n.a, n.b, 'last_resort') return n ``` ### `size` Returns the number of items in an expression that evaluates to any array type. ```Cypher theme={null} match (n:NodeKind1) where size(n.array_value) > 0 return n ``` #### Caveats The `size` function is expected to behave differently if the given expression evaluates to a text value. In this case, the function returns the number of Unicode characters present in the text value. This behavior is currently not supported in CySQL translation. ### `nodes` Returns an ordered list of all nodes in a matched path. ```cypher theme={null} match p = ()-[]->() return nodes(p) ``` ### `relationships` Returns the ordered relationship list for a path. ```cypher theme={null} match p = ()-[]->() return relationships(p) ``` ### `startNode` Returns the start node for a relationship reference. ```cypher theme={null} match p = (a)-[*1..]->(b) where none(r in relationships(p) where startNode(r).name = 'blocked') return p ``` ### `endNode` Returns the end node for a relationship reference. ```cypher theme={null} match ()-[r]->() return endNode(r) ``` ### `head` Returns the first element of a list. ```cypher theme={null} match p = ()-[]->() return head(nodes(p)) ``` ### `tail` Returns all elements of a list except the first. ```cypher theme={null} match p = ()-[]->() return tail(nodes(p)) ``` ## Known Defects in Supported Components[](#known-defects) The below issues are known defects. They are classified as defects of CySQL as the intent is to correctly support their use. ### `labels` Function Returns the labels of the given node reference. This function returns the node's labels as a text array value. Label checks utilizing this function will not be index accelerated and may exhibit poor performance. ```Cypher theme={null} match (n) where 'User' in labels(n) return n ``` #### Caveats While currently implemented this function returns the smallint array of labels associated with a node when referenced in CySQL. Future support to convert the smallint array of node labels into text values is planned. ### Unpacking Arrays of Entities for Comparison Arrays containing graph entities are not unpacked during comparisons: ```Cypher theme={null} match (n:User) where n.disabled with collect(n) as disabled match p = (:Computer)-[:HasSession]->(u:User) where not u in disabled return p limit 1 ``` Queries that contain similar constructs will result in the following translation error: `ERROR: column notation .id applied to type nodecomposite[], which is not a composite type (SQLSTATE 42809)`. ### Right-Hand Bound Node Lookups Patterns that utilize a bound reference in the right-hand node pattern will not correctly author the required SQL joins: ```Cypher theme={null} match (e) match p = ()-[]->(e) return p limit 1 ``` Queries that contain similar constructs will result in the following translation error: `ERROR: invalid reference to FROM-clause entry for table "s0" (SQLSTATE 42P01)`. ### Untyped Array References and Literals Untyped array references, including empty arrays, fail to pass type inference checks in CySQL. Support for additional type hinting and inference is required to better support these use-cases. ```Cypher theme={null} match (n:User) where n.auth_modes = [] return n ``` Queries that contain similar constructs will result in the following translation error: `Error: array literal has no available type hints`. ## Unsupported Constructs[](#unsupported-constructs) Below are constructs of the Cypher language that did not make the 1.0 definition of the CySQL specification. Future efforts may be pursued to add support for these language features. * XOR Operations * Case Expressions * List Comprehensions * Pattern Comprehensions * Existential Subqueries (e.g. exists) * Merge Statements * Pattern Predicates using Recursive Expansion ## Differences between Cypher and CySQL[](#cysql-differences) Translating Cypher to SQL via CySQL comes with a few semantic differences that users should be aware of. ### Stricter Typing Requirements SQL comparisons are stricter than comparisons executed in Neo4j. Some of these typing constraints are handled automatically by CySQL, however, some type mismatches do make it down to the underlying SQL database. Given the Cypher query: `match (n:User) where n.name = 123 return n limit 1;` The translated SQL, when executed, results in the following error: `Error: ERROR: invalid input syntax for type bigint: "MYUSER@DOMAIN.COM" (SQLSTATE 22P02)` This indicates that there is a node with a value for `n.name` that is not parsable as an integer. In the future, CySQL translation will cover most of the strict typing requirements automatically for users. ### Index Utilization Indexing in CySQL does not require a label specifier to be utilized. If the node property `name` is indexed in CySQL, both: ```Cypher theme={null} match (n:User) where n.name = '1234' return n ``` and ```Cypher theme={null} match (n) where n.name = '1234' return n ``` will use the `name` index regardless of node label. ### null Behavior Behavior around `null` in SQL differs from how Neo4j executes Cypher. Certain expression operators in Neo4j's implementation of Cypher will treat `null` differently than their SQL counterparts while some semantics are very similar. Ideally, entity properties should strive to remove `null` as a conditional case as much as possible. In cases where this is not possible, users are advised to exercise the `coalesce(...)` function: ```Cypher theme={null} match (n:User) where coalesce(n.name, '') contains '123' return n limit 1 ``` #### Silent Query Failure `null` can taint result sets and also further complicate future comparisons in the query: ```Cypher theme={null} match (n:User) with n.name as n where n = '123' return 1 ``` The reference `n` is being projected by the multipart `with` statement but this projection removes the resultset from the original query, allowing for ambiguity to slip into future operations against `n.name` where some values of `n.name` may be `null`. # Search and pathfinding Source: https://bloodhound.specterops.io/analyze-data/explore/search Search for objects and visualize relationships between them in the graph. Applies to BloodHound Enterprise and CE After [uploading data](/get-started/quickstart/community-edition-quickstart#get-data-into-bloodhound) to BloodHound, use the **Explore** page to search for objects and visualize their relationships. The graph displays nodes and edges, helping you understand your environment and identify potential attack paths. If your account uses [Environment Targeted Access Control (ETAC)](/manage-bloodhound/auth/environment-targeted-access-control), search results and graph data are limited to the environments you can access. BloodHound supports multiple data sources, including Active Directory, Azure (Entra ID), and other identity services through [OpenGraph](/opengraph/overview). The **Explore** page provides the following methods for searching for objects and visualizing their relationships: BloodHound supports all search methods for [structured](/opengraph/extensions/manage#structured-graphs) graphs. If you're exploring [generic](/opengraph/extensions/manage#generic-graphs) graphs, you can use the **Search** and **Cypher** methods only. Find specific objects by name or node type Discover relationships between objects Perform complex search with Cypher queries Which method you choose depends on your specific use case and what you're trying to accomplish. This page describes each of the search methods in more detail and provides guidance on when to use each one. You can interact with objects in the [graph](#graph-view) and customize the view to explore the data more effectively, regardless of which search method you use. ## Search The **Search** tab allows you to quickly find specific nodes in the graph by name or object ID. As you type in the search text box, BloodHound automatically suggests nodes that match your search query. You can click on any of the suggestions to select and display that node in the graph. OpenGraph node [IDs](/opengraph/developer/nodes#param-id) that contain a colon (`:`) are not supported. interprets all text *before* a colon as a [node-type filter](/analyze-data/explore/search#filter-by-node-type). Any ID that contains a colon will have part of the ID dropped, resulting in an unrecognized ID. This can clear the input or interfere with returning the expected results. Use cases for the search method include: * **Object discovery:** Quickly locate a known object by name or type to inspect its properties * **Investigation prep:** Find starting points for deeper exploration using Pathfinding or Cypher queries * **Data validation:** Verify specific objects are present in your environment after data ingestion ### Search by name or object ID For example, if you want to find a user named "bob", type "bob" in the search box and click the appropriate node from the suggestions. The suggestions display the node type next to each match, making it easy to identify the correct object when multiple objects share similar or identical names. OpenGraph data also displays custom icons configured for node types in this dropdown, which can further help you identify the intended object. An animated view showing how to search for a user named bob in the Explore page ### Filter by node type You can also constrain your search to particular node types by prepending your search with the appropriate node label. This works for both built-in node types (AD/AZ) and OpenGraph node types. For example, use the following search query to find group nodes that contain the word "admin": ```text theme={null} group:admin ``` Note that all suggestions for the `group:admin` search query include the group node type icon: A view showing how to search for group nodes containing the word admin in the Explore page ## Pathfinding The **Pathfinding** tab allows you to discover relationships between objects by finding paths between them. This is particularly useful for investigating potential attack paths across identity providers and cloud services in a single graph view. When [ETAC](/manage-bloodhound/auth/environment-targeted-access-control) applies to your user account, pathfinding returns data from the environments you can access only. BloodHound currently supports the **Search** and **Cypher** search methods for OpenGraph data. Pathfinding is available for [structured](/opengraph/extensions/manage#structured-graphs) graphs only. OpenGraph node [IDs](/opengraph/developer/nodes#param-id) that contain a colon (`:`) are not supported. interprets all text *before* a colon as a [node-type filter](/analyze-data/explore/search#filter-by-node-type). Any ID that contains a colon will have part of the ID dropped, resulting in an unrecognized ID. This can clear the input or interfere with returning the expected results. Use cases for the pathfinding search method include: * **Attack path analysis:** Identify potential compromise chains between two objects * **Relationship mapping:** Understand how objects are connected within your environment * **Filtered exploration:** Focus on relevant relationships by excluding edge types or reversing path direction For example, you can find all paths from a user named "bob" to a group containing the name "domain admins" using the previously described [search](/analyze-data/explore/search#search) method for the start and end points: Like the search method, you can use partial matches and node labels to find your start and end points. A view showing how to search for paths from a user named bob to groups containing the word domain admins in the Explore page Pathfinding also includes options to customize your search: * **Reverse path** —Swap your start and end points to explore paths in the opposite direction without re-entering your search queries. This is useful for finding how high-value targets connect back to entry points. * **Filter edges** —Select which edge types to include in the results. By default, all edge types are selected; deselect any you don't want included in the paths to focus on relevant relationships. ## Cypher The **Cypher** tab allows you to perform complex searches using Cypher queries. Cypher is a powerful query language for graph databases. It enables you to manipulate and examine BloodHound data in custom ways to help you further understand your network or identify interesting relationships. See [Search with Cypher](/analyze-data/explore/cypher-search) for more information. ## Graph view The graph on the **Explore** page provides a visual representation of the objects in your data based on your search criteria. You can interact with the graph by clicking on nodes and edges to view detailed information about them in the **Entity** panel, and by using various visualization options to customize the graph view. The following example shows a graph based on the example in the [Pathfinding](/analyze-data/explore/search#pathfinding) section above, which finds paths from a user named "bob" to a group named "domain admins". An example graph view on the Explore page The graph displays the nodes and edges that connect user `BOB@PHANTOM.CORP` to group `DOMAIN ADMINS@PHANTOM.CORP`, allowing you to visually explore the relationships between objects. ### Object interaction You can interact with nodes and edges in the graph to view detailed information about them in the [Entity panel](/analyze-data/explore/search#entity-panel). For nodes, you can right-click to perform more actions using the [context menu](/analyze-data/explore/search#context-menu). To keep dense graphs readable, BloodHound clips long node labels by default. Click a node to view its full label. When you click a node, BloodHound dims unrelated nodes and edges and highlights every path that traverses the selected node. This includes inbound and outbound object control paths, making it easier to isolate how the selected node participates in the current graph. For example, clicking `USERS@PHANTOM.CORP` in the previous graph highlights the path through that node to `DOMAIN ADMINS@PHANTOM.CORP` and dims the other paths that do not traverse it. An example graph view showing full path highlighting in the Explore page #### Context menu Right-click on any node in the graph to access the context menu. Options in the context menu include: * **Set as starting node**—Set the node as the starting point in the **Pathfinding** tab and immediately draw a new graph showing paths between that node and the current ending node * **Set as ending node**—Set the node as the ending point in the **Pathfinding** tab and immediately draw a new graph showing paths between the current starting node and that node * **Add to/Remove from Tier Zero**—Mark or unmark the node as a member of the Tier Zero privilege zone. Adding automatically triggers analysis to tag the object; removing requires [manually editing](/analyze-data/privilege-zones/rules#edit-or-delete-a-rule) the zone rule to remove the object. * **Add to/Remove from Owned**—Mark or unmark the node as compromised in the Privilege Zones page. Adding automatically triggers analysis to tag the object; removing requires [manually editing](/analyze-data/privilege-zones/rules#edit-or-delete-a-rule) the label rule to remove the object. * **Copy**—Copy the node's name, object ID, or a Cypher query to your clipboard for use in other searches or documentation #### Entity panel The **Entity** panel on the **Explore** page displays detailed object properties and relationships. The information is displayed in an accordion format based on the selected node or edge, which can vary depending on your data source. For built-in node and edge types (AD/AZ), BloodHound displays structured data organized into the accordions described below. For OpenGraph data, BloodHound displays all values from the [`properties`](/opengraph/developer/nodes) object as a flat list, without the structured accordions. For nodes, expanding each accordion reveals more detail and dynamically updates the graph. For example, expanding the **Sessions** accordion shows all computers where the node has active sessions and updates the graph. BloodHound displays the following information in the **Entity** panel when you click on a node (if the information is available in your data): | Accordion | Description | | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Object Information** | Displays the collected properties and attributes of a selected object

See [node reference](/resources/nodes/overview) for details about each node type | | **Sessions** | List of objects where the selected node has active sessions | | **Members** | List of objects that are members of the selected node | | **Member Of** | List of objects where the selected node is a member | | **Local Admin Privileges** | List of objects where the selected node has local administrator privileges | | **Execution Privileges** | List of objects where the selected node has execution privileges | | **Inbound Object Control** | List of objects that can control the selected node | | **Outbound Object Control** | List of objects that the selected node can control | BloodHound displays the following information in the **Entity** panel when you click on an edge (if the information is available in your data): | Accordion | Description | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Relationship Information** | Details about the selected relationship, including the source node, target node, ACL status, and the last time BloodHound observed it | | **General** | A detailed description of the relationship between the two nodes connected by a selected edge | | **Abuse** | Step-by-step guidance, tools, and techniques for abusing the relationship represented by an edge to compromise or gain control over a target principal | | **OPSEC** | Operational security implications and detection risks associated with abusing a particular edge | | **References** | Links to publicly available resources used to create the above information | This information is also available for each edge in the [reference](/resources/edges/overview) documentation. ### Visualization options Use the graph visualization options at the bottom of the **Explore** page to customize how the graph is displayed based on your preferences. This can be useful for large, complex graphs with many nodes and edges. Layout behavior depends on whether you explicitly choose a layout: | Scenario | No layout selected | Layout explicitly selected | | ---------------------------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | Default results | Uses edition default | Keeps your selected layout for future results and sessions.

Click the selected layout again to clear it and return to default. | | Multi-node
Cypher results
(no edges) | Uses **Table** layout | Keeps your selected layout; does not use **Table** | | After closing **Table** | Uses edition default | Returns to your previously selected layout | A view showing the graph visualization options on the Explore page 1. **Reset graph view**—Restore the graph view to its default layout and zoom level 2. **Hide Labels**—Toggle the visibility of labels on nodes and edges to reduce clutter and focus on the structure of the graph (also useful for obfuscating sensitive information before sharing graph images) 3. **Layout**—Choose from the following layout options to organize the graph visually: Applies to BloodHound Enterprise only Uses a force-directed layout algorithm to position objects based on their relationships, creating a natural and intuitive view of the graph. A view showing the Organic graph layout option on the Explore page Applies to BloodHound Enterprise only The default layout for BloodHound Enterprise. Combines hierarchical layering with grid arrangement, organizing objects left to right in ranked layers while arranging multiple nodes within each layer in a structured grid pattern. A view showing the Stacked graph layout option on the Explore page The default layout for BloodHound Community. Uses a hierarchical layout algorithm that organizes objects from left to right in ranked layers based on their relationships, ideal for visualizing directional paths and dependencies. A view showing the Sequential graph layout option on the Explore page Uses a balanced force-directed algorithm that pulls connected nodes together while maintaining spacing between unconnected nodes, creating an evenly distributed layout. A view showing the Standard graph layout option on the Explore page Displays objects in a tabular format, showing properties in rows and columns for easy comparison. The **Table** layout is available for Cypher searches only and is useful for searching and sorting large sets of data. When no layout is selected, BloodHound automatically opens this layout for Cypher results that return multiple nodes and no edges. A view showing the Table graph layout option on the Explore page The table includes the following columns by default: | Column | Description | | ------------- | --------------------------------------------------------------- | | **Node Type** | The type of the object (node label) | | **Name** | The name of the object | | **Object ID** | The unique identifier of the object | | **Tier Zero** | Indicates if the object is part of the Tier Zero privilege zone | * Click the (ellipsis) icon in each row to access the [context menu](#context-menu) for that object. * Resize columns to view more or less information as needed by clicking and dragging the edges of the column dividers (or double-clicking the column dividers to auto-size). The table layout provides the following options: * **Search**—Quickly identify specific objects among the nodes displayed in the graph * **Export**—Download the current graph view as a CSV file for further analysis or sharing * **Expand**—Maximize the graph view to fill the screen for better visibility * **Columns**—Search, add/remove, sort, reorder, reset column size, reset defaults, and pin columns in the table layout Graph visualization options are available across all search methods. The **Table** layout is available for Cypher searches only. # Analysis Process Source: https://bloodhound.specterops.io/analyze-data/findings/analysis Understand how the BloodHound Enterprise analysis process works to surface findings and prioritize risk. Applies to BloodHound Enterprise only BloodHound Enterprise's analysis process includes several key steps that work together to surface findings and prioritize risk. ## Analysis stages By default, BloodHound runs the full analysis pipeline in the following order: 1. Active Directory post-processing 2. Azure post-processing 3. Tagging 4. Analysis BloodHound uses the full analysis pipeline for all standard and scheduled analysis runs. [Variable Analysis Mode](/analyze-data/findings/analysis#variable-analysis-mode) enables BloodHound Enterprise to skip post-processing for some analysis runs to speed up the process (for example, when updating Privilege Zones). Scheduled analysis is a SpecterOps-managed feature. ## Choke point analysis BloodHound Enterprise generates one choke point view view per environment, such as an Active Directory domain or Azure tenant. The choke point view organizes findings by category and shows the number of exposed principals in each, helping you quickly understand where risk concentrates. [Exposure and impact](/analyze-data/findings/attack-paths#exposure-and-impact) metrics are calculated from this analysis and surfaced with findings. ## Relationships and zone boundaries Attack Path analysis includes both relationship-driven path analysis and principal-level risky configuration findings. BloodHound evaluates how abusable relationships connect principals across privilege boundaries and flags principals with configurations that increase risk. This includes boundaries between Tier Zero and user-defined [Privilege Zones](/analyze-data/privilege-zones/overview). A path that crosses zones can represent a stepping stone into higher-privilege assets, which is why zone-specific findings can differ in severity and priority. ## Post-processing BloodHound does not rely only on directly collected relationships. During **post-processing**, it derives additional relationships that are relevant to Attack Path analysis. One result is a **composite edge**. A composite edge is a derived relationship between two nodes that represents a group of underlying relationships condensed into a single, meaningful connection. BloodHound uses composite edges to simplify understanding of that complexity and surface Attack Paths that are not visible from any single relationship alone. Some attack techniques require a combination of permissions before they can be abused, so BloodHound models those combined conditions as one simplified relationship. For example, the [DCSync](/resources/edges/dc-sync) edge requires a combination of permissions to create an abusable path. BloodHound models this as a composite edge, which allows it to surface Attack Paths that would otherwise be invisible if analysis relied only on directly collected relationships. BloodHound creates the following edges during post-processing: * [`ADCSESC1`](/resources/edges/adcs-esc1) * [`ADCSESC3`](/resources/edges/adcs-esc3) * [`ADCSESC4`](/resources/edges/adcs-esc4) * [`ADCSESC6a`](/resources/edges/adcs-esc6a) * [`ADCSESC6b`](/resources/edges/adcs-esc6b) * [`ADCSESC9a`](/resources/edges/adcs-esc9a) * [`ADCSESC9b`](/resources/edges/adcs-esc9b) * [`ADCSESC10a`](/resources/edges/adcs-esc10a) * [`ADCSESC10b`](/resources/edges/adcs-esc10b) * [`ADCSESC13`](/resources/edges/adcs-esc13) * [`AddMember`](/resources/edges/add-member) * [`AdminTo`](/resources/edges/admin-to) * [`AZAddOwner`](/resources/edges/az-add-owner) * [`AZRoleApprover`](/resources/edges/az-role-approver) * [`CanPSRemote`](/resources/edges/can-ps-remote) * [`CanRDP`](/resources/edges/can-rdp) * [`CoerceAndRelayNTLMToADCS`](/resources/edges/coerce-and-relay-ntlm-to-adcs) * [`CoerceAndRelayNTLMToLDAP`](/resources/edges/coerce-and-relay-ntlm-to-ldap) * [`CoerceAndRelayNTLMToLDAPS`](/resources/edges/coerce-and-relay-ntlm-to-ldaps) * [`CoerceAndRelayNTLMToSMB`](/resources/edges/coerce-and-relay-ntlm-to-smb) * [`DCSync`](/resources/edges/dc-sync) * [`EnrollOnBehalfOf`](/resources/edges/enroll-on-behalf-of) * [`EnterpriseCAFor`](/resources/edges/enterprise-ca-for) * [`ExecuteDCOM`](/resources/edges/execute-dcom) * [`ExtendedByPolicy`](/resources/edges/extended-by-policy) * [`GoldenCert`](/resources/edges/golden-cert) * [`HasTrustKeys`](/resources/edges/has-trust-keys) * [`IssuedSignedBy`](/resources/edges/issued-signed-by) * [`OwnsLimitedRights`](/resources/edges/owns-limited-rights) * [`ProtectAdminGroups`](/resources/edges/protect-admin-groups) * [`SyncLAPSPassword`](/resources/edges/sync-laps-password) * [`SyncedToADUser`](/resources/edges/synced-to-ad-user) * [`SyncedToEntraUser`](/resources/edges/synced-to-entra-user) * [`TrustedForNTAuth`](/resources/edges/trusted-for-nt-auth) * [`WriteOwnerLimitedRights`](/resources/edges/write-owner-limited-rights) ## Variable Analysis Mode When updating Privilege Zones, you likely want to see updated object membership and related findings as quickly as possible. **Variable Analysis Mode** can speed up this process. This feature is available under early access and is enabled by default. **Variable Analysis Mode** skips the post-processing stages of analysis. BloodHound still updates normal analysis completion tracking after these runs, including timestamps and related status information. This option applies to Privilege Zone-triggered analysis only. Other actions that trigger analysis still run the full pipeline. ## Remediation After reviewing findings on the **Attack Paths** page, you can: * **Remediate** to sever the edges that create the risk and improve your environment's security posture. * **Accept** when risk is known and temporarily tolerated. For acceptance workflow steps, see [Risk Acceptance](/analyze-data/findings/risk-acceptance). To track remediation progress over time, see [Posture](/analyze-data/findings/posture). # Attack Paths Source: https://bloodhound.specterops.io/analyze-data/findings/attack-paths Learn how to interpret Attack Path findings and use them to prioritize remediation efforts. Applies to BloodHound Enterprise only The **Attack Paths** page in BloodHound Enterprise gives you a high-level overview of where identity risk exists, how much of your environment is exposed, and which areas need attention first. These risks are represented as findings with exposure and impact metrics that quantify risk, so you can quickly assess overall risk in one place and then use those metrics to inform prioritization as you drill into specific finding types. The **Attack Paths** page combines two key views needed for prioritization: * **Choke Point view**: An aggregate view of the graph for a selected environment and privilege zone. This view simplifies large volumes of nodes and edges into a compact visualization optimized for readability. It shows the number of exposed principals in each finding category, giving you a quick summary of where risk concentrates in an environment. * **Attack Paths details**: An expandable list of all Attack Path types detected, showing detailed finding descriptions, severity, principals involved, exposure and impact, and remediation plans for each. A view of the Attack Paths page that shows an aggregated graph view and attack path details ## Filters The **Attack Paths** page includes filters to help you focus on specific environments and zones. You can use these filters to narrow down the findings shown in the **Choke Point** view and the **Attack Paths details** list to focus on specific areas of your environment. * **Environment**: Filter findings by specific platforms, such as an Active Directory domain, Azure tenant, or OpenGraph environment. You can also filter on severity levels to focus on high-risk Attack Paths. * **Zone**: Filter findings by specific privilege zones. By default, Tier Zero is selected, but you can choose to view findings for other zones. You can also use the zone filter to focus on findings by the Hygiene category. ## Findings An **Attack Path** is a chain of abusable privileges and user behaviors that creates direct or indirect connections between principals. A **finding** is a specific instance of an Attack Path that BloodHound Enterprise has identified as a high-value remediation point. Findings can be relationship-based (abusable paths between principals) or principal-based (risky configurations on a principal). Findings are organized by the environment of the target nodes, not the environment where the finding is defined. For example, an OpenGraph extension can define a finding that targets Active Directory nodes. That finding appears under the relevant Active Directory domain, not the OpenGraph environment. ### Exposure and impact Each finding includes exposure and impact metrics. Use these metrics together when prioritizing remediation efforts. Findings with high exposure and high impact are typically the highest-priority remediation targets. #### Exposure A risk measurement that quantifies the extent to which principals can reach a privileged asset through one or more Attack Paths. It encompasses all principals upstream of a finding's source, including any principals that can reach the source through intermediaries. Exposure is measured in two ways: * **Exposure count**—The number of principals that can reach a privileged asset through one or more Attack Paths. * **Exposure percentage**—The percentage of principals in an environment that have at least one Attack Path to a privileged asset. Exposure calculation only includes principals *outside* the selected zone (and higher zones) that can reach a privileged asset through an Attack Path. #### Impact A risk measurement that quantifies potential blast radius if a finding is abused. Impact is measured in two ways: * **Impact count**—The number of principals that could be compromised through an Attack Path. * **Impact percentage**—The percentage of the environment that could be impacted by a specific identity vulnerability. Together, these metrics help organizations prioritize remediation by understanding which Attack Paths pose the greatest risk. ### Finding types BloodHound Enterprise groups findings into types to help you separate structural access risks from principal-level configuration risks. #### Relationship-based finding A relationship-based finding identifies a directional path from a lower-privileged source principal to a privileged target asset. The path represents one or more abusable connections (potentially through intermediate principals or objects) through which the source principal can take control of the target. A single finding may include multiple Attack Paths when different intermediate nodes all enable the same type of access from source to target. Relationship-based findings can have an exposure metric and an impact metric. #### List-based finding A list-based finding identifies a vulnerability in a specific principal where the risk originates from the principal itself (like a misconfiguration). Because the vulnerability is inherent to the principal and not based on its connection to other principals, there is no exposure to measure. List-based findings do not have an exposure metric, but they will have an impact metric. # Posture Source: https://bloodhound.specterops.io/analyze-data/findings/posture Learn how to use the Posture page to track your organization's risk posture over time and measure the impact of your remediation efforts. Applies to BloodHound Enterprise only While the [Attack Paths](/analyze-data/findings/attack-paths) page helps you investigate and remediate specific findings related to Attack Paths, the **Posture** page provides a high-level view of your organization's security posture over time. It aggregates Attack Path data to provide insights about where the biggest risks originate and how your remediation efforts are reducing risk. A view of the Posture page filter options ## Filters The **Posture** page includes filters to help you focus on specific environments and zones. You can use these filters to view and track trends over time. ### Environment The environment filter allows you to view posture trends for a specific platform, such as an Active Directory domain or Azure tenant. This can help you understand how posture is changing within that environment and identify areas that may require additional attention. You can also filter on severity levels to focus on trends in high-risk Attack Paths. For example, you can filter to show only **CRITICAL** Attack Paths to see how the most severe risks are changing over time. ### Zone The zone filter allows you to view posture trends for a specific privilege zone. By default, Tier Zero is selected, but you can choose to view findings for other zones. You can also use the zone filter to focus on findings by the Hygiene category. ### Date range The date range filter allows you to compare posture trends between analysis runs. For example, you can compare the current state of your environment to a previous point in time to see how your risk posture has changed. Choose from preset ranges or set a custom range to compare specific analysis runs. Custom date ranges include a time picker, so you can set exact start and end times. For meaningful trend comparisons, use the same filter scope and similar date ranges across reviews. Custom date ranges include a time picker, so you can set exact start and end times. ### Chart scale The chart scale filter allows you to adjust the scale of the posture graphs to better visualize trends. For example, if you have a large number of findings, you may want to use a logarithmic scale to better see changes over time. The linear scale shows consistent ranges clearly, while the logarithmic scale highlights outliers and wide variations in the data. ## Interpreting Posture The **Posture** page tracks how your risk posture changes between analysis runs. It includes several sections that show trends in attack path severity, findings, and exposure over time. When reviewing posture trends, consider the following: * Attack Paths—Each attack path is made up of one or more relationships. * Findings—Each finding can be composed of one or more attack paths, so finding counts and Attack Path counts can differ. ## Attack Paths The **Attack Paths** table shows attack paths with active findings in the selected date range. This list can also include attack paths that were fully resolved (by your remediation efforts) or deprecated (by SpecterOps) during the same range. This table is designed for trend tracking and reporting. For per-finding details such as description, impacted principals, exposure/impact metrics, and remediation guidance, use the [Attack Paths](/analyze-data/findings/attack-paths) page. | Column | Description | | ------------ | ------------------------------------------------------------------------------------------------------------------ | | **Severity** | The severity level of the Attack Path at the end date of the selected range. | | **Name** | The name of the Attack Path. | | **Category** | The category of the Attack Path. | | **Count** | The number of findings that existed on the end date of the selected range. | | **Change** | The calculated difference in the number of findings between the beginning and end date of the selected time range. | Count is calculated on a per-day basis. Depending on the selected date and time range, displayed counts may differ if analysis runs span day boundaries. A view of the Attack Paths table on the Posture page BloodHound Enterprise calculates severity from the percentage of users and computers that can abuse the Attack Path. For example, a **CRITICAL** Attack Path is abusable by 95% to 100% of all users and computers in the environment. The severity levels map to the following exposure percentages: * **CRITICAL**: 95%-100% * **HIGH**: 80%-94% * **MODERATE**: 40%-79% * **LOW**: 0%-39% These are expressed with colors in the Severity column. A view of the Attack Path severity scale ## Attack Path Summary This section provides a summary of risk within the applied filter on the selected end date, including the change in Attack Paths, findings, and Tier Zero objects within the selected time frame. A view of the Attack Path Summary panel on the Posture page ## Posture Over Time Graphs This series of visualizations shows posture over time based on the selected filter parameters. They provide insights about trends in exposure levels, findings, attack paths, and privilege zone objects. * **Attack Path Exposure** - This graph represents the trend (by percentage) of principals within the environment (and trusted or connected environments) that can compromise the selected zone. A view of the Total Tier Zero Attack Path Exposure graph on the Posture page * **Historical Findings** - This graph represents the trend (by count) in the total number of findings within the selected filter parameters. As you remediate findings (or newly created misconfigurations generate new ones), this chart helps you track the changes in the number of identified findings over time. A view of the Historical Findings graph on the Posture page * **Total Attack Paths** - This graph represents the trend (by count) in the total number of active Attack Paths within the selected filter parameters. As you remediate findings that contribute to Attack Paths (or newly created misconfigurations generate new ones), this chart helps you track the changes in the total number of identified Attack Paths over time. A view of the Total Attack Paths graph on the Posture page * **Objects** - This graph represents the trend in the total number of objects in the selected zone. As you add or remove objects from the selected zone, this chart helps you track the changes in the number of objects over time. A view of the Objects graph on the Posture page ## Completeness Graphs For Active Directory environments, the **Group Completeness** and **Session Completeness** graphs represent how much visibility BloodHound Enterprise has into session and local group data across active computers in your environment. BloodHound Enterprise calculates completeness as the percentage of all computers that it successfully scanned for sessions and groups. It includes only enabled computers with at least one login in the past 14 days. A view of the Group Completeness and Session Completeness graphs on the Posture page The total collection completeness significantly impacts the accuracy of the graph available for analysis within BloodHound Enterprise. See [Why perform privileged collection in SharpHound](/collect-data/enterprise-collection/privileged-collection) for more details. # Risk Acceptance Source: https://bloodhound.specterops.io/analyze-data/findings/risk-acceptance Learn how to accept findings as known risks in BloodHound Enterprise, and understand the difference between acceptance and remediation. Applies to BloodHound Enterprise only The **Attack Paths** page in BloodHound Enterprise surfaces findings. Each finding includes prioritization data such as exposure and impact metrics to help you focus remediation efforts where they matter most. Not every finding requires immediate remediation. Risk acceptance lets you acknowledge a finding as a known risk that your organization has reviewed and chosen to retain for a certain amount of time. Use **acceptance** when: * You have reviewed the risk and agreed to tolerate it for a defined period. * You are waiting for a change to complete, such as a retention window. Accepting a finding records that the risk is known and temporarily tolerated. Acceptance is not a fix. To reduce risk, you must remediate the underlying condition. Use **remediation** when: * You are ready to remove the risky condition. * You want posture trends to reflect actual risk reduction. ## Accept a finding Before accepting a finding, you must sign in to BloodHound Enterprise with a [role](/manage-bloodhound/auth/users-and-roles#user-role-definitions) that can accept attack path impacted principals. When you accept a finding principal: * It is still included in posture calculations. * It is hidden from the default principal table view for that finding. * The principal and related edges still appear in other analysis views, including **Explore** and **Posture**. In the left navigation menu, click **Attack Paths**. Expand the finding, open the menu to the left of the principal name (three vertical dots) and click **Accept**. A view of an expanded finding that shows the accept option In the **Accept Attack Path** window, set the number of days for acceptance and click **Accept**. If you are accepting a finding while you wait for [data retention](/collect-data/enterprise-collection/data-retention#data-retention) settings to delete the associated data, set the duration based on that scenario. For example, for **Logons from Tier Zero Users**, set the duration to 7 days. A view of the accept attack path window that shows the duration setting ## Remove acceptance To remove acceptance for a principal: In the left navigation menu, click **Attack Paths**. Expand the finding and enable the **Accepted** toggle. A view of the attack paths page that shows the accepted toggle Open the menu to the left of the accepted principal (three vertical dots), and click **Remove Acceptance**. A view of the menu that appears when clicking the three vertical dots next to an accepted principal In the **Remove Attack Path Acceptance** window, select **Remove Acceptance**. A view of the remove attack path acceptance window ## Outcome After acceptance, the principal is hidden from the default principal table for that finding until you enable the **Accepted** toggle. The principal and related edges remain visible in the **Explore** and **Posture** pages. A view of a finding that shows the accepted toggle # The BloodHound Dashboard Source: https://bloodhound.specterops.io/analyze-data/overview Learn how to use the BloodHound dashboard to analyze your data and identify attack paths. ## Findings and Remediation Understand how the analysis process works to surface findings and prioritize risk. Investigate findings and prioritize remediation efforts based on risk exposure and impact. Temporarily accept findings as known risks while you work on remediation or wait for data retention to remove the underlying data. Track trends over time across Attack Paths, findings, and overall exposure to understand the impact of your remediation efforts. ## Explore Find objects, analyze relationships, and visualize attack paths in a graph view. Use prebuilt or custom Cypher queries to uncover relationships and insights. See supported Cypher components and examples of how to use them. ## Privilege Zones Learn how Privilege Zones segment your environment by sensitivity and access boundaries. Explore how Privilege Zones help you manage attack paths and reduce risk exposure. Create and manage zones that define privileged boundaries in your environment. Apply labels to classify objects and supplement zone assignment logic. Build rules that automatically place objects into the right zones. Review built-in rules used to seed and maintain Privilege Zone structure. Certify zone membership and validate that privileged boundaries remain accurate. Audit Privilege Zone changes over time to understand what changed and when. # Certification Source: https://bloodhound.specterops.io/analyze-data/privilege-zones/certification Understand the certification process for Privilege Zones and how to manage member approvals. Applies to BloodHound Enterprise Certification is an optional process to interrupt automatic inclusion of additional objects in a zone based on [rule](/analyze-data/privilege-zones/rules) expansion behavior by requiring manual certification of the additional objects. It allows administrators or power users to manually review and approve objects before they appear in privilege zones. This process gives you control over zone membership and helps prevent unexpected additions from triggering false findings. A view of the Zone Builder certification tab ## Why use certification? Without certification, BloodHound automatically includes objects in zones as soon as they match a rule's expansion criteria. This can create unexpected findings when objects are inadvertently added to privileged groups. For example, if a new user is added to the Domain Admins group, BloodHound immediately tags them to the **Tier Zero** zone and generates attack path findings for that user. In the preceding example, certification solves this problem by requiring manual approval before objects are fully recognized within a zone. During the certification process, BloodHound still identifies the object's relationship to the zone but generates a "Non-Certified Principal with Tier Zero Privileges" finding instead of standard attack path findings. This gives you time to review whether the object should remain in the zone or if its group membership was a mistake. BloodHound supports certification for zones only. ## How certification works When you enable certification for a zone: 1. Objects that match the zone's rules enter a pending state 2. BloodHound generates findings indicating the objects require certification 3. Administrators or power users review objects in the **Certifications** tab 4. Once certified, objects are fully recognized in the zone and BloodHound generates standard findings 5. Alternatively, you can remove objects from privileged groups to prevent zone membership You can configure certification requirements at the zone level (to affect all rules) or at the individual [rule](/analyze-data/privilege-zones/rules) level, giving you flexibility in managing object approvals. ## Manage certifications The **Certifications** tab on the **Zone Builder** page allows administrators and power users to review certification status and take action on objects in zones where certification has been configured. When you open the **Certifications** tab, BloodHound selects **All Statuses** by default. This view helps you confirm whether a specific object is already present in a zone without running separate searches for each certification status. The table title shows **All Statuses** and the total number of objects currently listed across these statuses: **Pending**, **User Certified**, **Automatic**, and **Rejected**. The certification table includes the following columns: | Column | Description | | --------------- | ----------------------------------------------------------------------------------------------------- | | **Type** | The object type | | **Status** | The certification status for the object | | **Object Name** | The display name of the object | | **Environment** | The environment where the object exists | | **Zone** | The zone that BloodHound Enterprise associates with the object based on the configured zone hierarchy | | **First Seen** | The date and time the object was first seen in the zone | * You can certify or reject certification only for objects in zones where certification is enabled. * Objects appear in the certification queue only when their [rules](/analyze-data/privilege-zones/rules) have **Automatic Certification** turned off. To manage certifications: Navigate to the **Privilege Zones** > **Certifications** tab. Use the status filter, environment filter, and search box to refine the results. Click the status dropdown menu and choose **All Statuses**, **Pending**, **User Certified**, **Automatic**, or **Rejected**. **All Statuses** is selected by default and lists objects across all statuses. The table title updates to match your selection and shows the total number of matching objects. Actions are only available for certifications that require manual approval. You cannot certify or reject objects with the **Automatic** status. An animated view of the Zone Builder certification status filter Click the environment dropdown menu and select the desired environment to view its certifications. An animated view of the Zone Builder certification environment filter Use the search box and additional filters to find specific objects in the current table view. With **All Statuses** selected, you can search across all objects in **Pending**, **User Certified**, **Automatic**, and **Rejected** at the same time. If no object matches your search, BloodHound displays `No results.` A view of the Zone Builder certification search and filter options 1. Use the checkboxes to select one or more objects. 2. Click **Certify** or **Reject** as needed. 3. *(Optional)* Add a note to document the reason for your action. * Click **Skip Note** to complete the certification action without a note * Click **Cancel** to exit without completing the certification action A view of the certification note dialog in the Zone Builder certification tab Notes are visible to all BloodHound users in the [History Log](/analyze-data/privilege-zones/history).A view of a certification note in the Zone Builder history log # Default Rules Source: https://bloodhound.specterops.io/analyze-data/privilege-zones/default-rules Explore and understand the default rules in Privilege Zones. Applies to BloodHound Enterprise and CE BloodHound provides a set of rules by default that place known critical objects into the default Tier Zero zone according to SpecterOps best practices. You can disable some of these rules, but not all of them. | Rule | Disable-able | Type | Reason | | --------------------------------------- | ------------ | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Account Operators | TRUE | DC group | The Account Operators group has GenericAll in the default security descriptor on the AD object classes: User, Group, and Computer. That means all objects of these types will be under full control of Account Operators unless they are AdminSDHolder Protected. Not all Tier Zero objects will be AdminSDHolder Protected typically, as not all Tier Zero objects will be included in Protected Objects. This means Account Operators members have a path to compromise Tier Zero most often.



It is possible to delete all GenericAll ACEs for Account Operators on Tier Zero objects. To protect future Tier Zero objects, one would have to either remove the Account Operators ACE from the default security descriptors or implement a process of removing the ACEs as Tier Zero objects are being created. However, we recommend not using the group and classifying it as Tier Zero instead. | | Administrator | FALSE | AD user | The built-in Administrator account has admin access to DCs by default and is therefore Tier Zero. | | Administrators | FALSE | DC group | The Administrators group has full control over most of AD's essential objects and are inarguably part of Tier Zero. | | AdminSDHolder | FALSE | AD container | The permissions configured on the AdminSDHolder container are a template that will be applied on Protected Objects by the ProtectAdminGroups background task at a default interval of every hour. Control over AdminSDHolder means you have control over the Protected Objects, which include Tier Zero groups such as Domain Admins and their transitive members. The AdminSDHolder container is therefore a Tier Zero object. | | AIA CA (AD object) | TRUE | AD object | The AIA CA objects may represent offline enterprise CAs or cross CAs. In such cases, deleting the AIA CA object would cause certificates, potentially of Tier Zero principals, to lose trust. We therefore recommend to treat AIACAs as Tier Zero. | | Application Administrator | TRUE | Entra ID role | The Application Administrator role can control tenant-resident apps. This includes creating new credentials for apps, which can be used to authenticate the tenant as the app's service principal and abuse the service principal privileges. The role is therefore considered Tier Zero if the tenant contains any Tier Zero service principals. | | Azure tenant object | FALSE | Entra ID role | An attacker with control of the Tenant Root Object has control of all identities, applications, roles, and devices that reside in that tenant. Further, control of the Tenant Root Object enables an attacker to gain control of all Azure Resource Manager subscriptions that trust the tenant. This object is therefore considered Tier Zero. | | AZUREADSSOACC object | TRUE | Entra ID role | Microsoft automatically creates the AZUREADSSOACC account when enabling Seamless SSO. When configured for Seamless SSO, this object can modify any synced object within an Azure environment, granting significant control over the organization. | | Backup Operators | FALSE | DC group | The Backup Operators group has the SeBackupPrivilege and SeRestorePrivilege rights on the domain controllers by default. These privileges allow members to access all files on the domain controllers, regardless of their permission, through backup and restore operations. Additionally, Backup Operators have full remote access to the registry of domain controllers. To compromise the domain, members of Backup Operators can dump the registry hives of a domain controller remotely, extract the domain controller account credentials, and perform a DCSync attack. Alternative ways to compromise the domain exist as well. The group is considered Tier Zero because of these known abuse techniques. | | Cert Publishers | TRUE | AD group | The Cert Publishers group has full control permissions on root CA and AIA CA objects. This enables an attacker to add or remove certificates for these objects, which are trusted throughout the AD forest. As certificate authentication requires the certificate to chain up to a trusted root CA, an attacker could prevent successful authentication for AD accounts and disrupt Tier Zero operations. The group is therefore Tier Zero.

In some environments, the group also has full control over the NTAuth store. In that scenario, the group can take over the forest by adding a forged root certificate, making it trusted for NTAuth. | | Certificate template | TRUE | AD object | Control over a certificate template enables the ADCS ESC4 attack and Tier Zero takeover if the template is published to a CA trusted in the NTAuth store and that chains up to a trusted root CA. There are default templates that meet this requirement; others remain unpublished. A template cannot be used if it is not published, making control over an unpublished object less concerning. However, if it is ever published, it becomes a risk. We, therefore, recommend treating all certificate templates as Tier Zero objects, whether published or not. | | Cryptographic Operators | TRUE | DC group | The Cryptographic Operators group has the local privilege on domain controllers to perform cryptographic operations but no privilege to log in.

There are no known ways to abuse the membership of the group to compromise Tier Zero. The local privilege the group has on the domain controllers is considered security dependencies, and the group is therefore considered Tier Zero. | | Distributed COM Users | TRUE | DC group | The Distributed COM Users group has local privileges on domain controllers to launch, activate, and use Distributed COM objects but no privilege to log in.

There are no known ways to abuse the membership of the group to compromise Tier Zero. The local privileges the group has on the DCs are considered security dependency, and the group is therefore considered Tier Zero. | | DNS Admins | TRUE | AD group | DnsAdmins controls DNS which enables an attacker to trick a privileged victim to authenticate against an attacker-controlled host as it was another host. This enables a Kerberos relay attack. Also, control over DNS enables disruption of Tier Zero since Kerberos depends on DNS by default.

The group could previously use a feature in the Microsoft DNS management protocol to make the DNS service load any DLL and thereby obtain a session as SYSTEM on the DNS server. This vulnerability was patched in Dec 2021. | | Domain Admins | FALSE | AD group | The Domain Admins group has full control over most of AD's essential objects and are inarguably part of Tier Zero. | | Domain Controllers | FALSE | AD group | The Domain Controllers group has the GetChangesAll privilege on the domain. This is not enough to perform DCSync, where the GetChanges privilege is also required.

There are no known ways to abuse membership in this group to compromise Tier Zero. However, the GetChangesAll privilege is considered a security dependency that should only be held by Tier Zero principals. Additionally, control over the group allows one to impact the operability of Tier Zero by removing domain controllers from the group, which breaks AD replication. The group is therefore considered Tier Zero. | | Domain root object | FALSE | AD object | An attacker with control over the domain root object can compromise the domain in multiple ways, for example by a DCSync attack (see reference). The domain root object is therefore Tier Zero. | | Enterprise Admins | FALSE | AD group | The Enterprise Admins group has full control over most of AD's essential objects and are inarguably part of Tier Zero. | | Enterprise CA (AD object) | TRUE | AD object | Control over an enterprise CA object enables an attacker to publish certificate templates. If any templates that allow ADCS domain escalation exist but are unpublished, then control over the enterprise CA object could enable a takeover of Tier Zero. An attacker could potentially also disrupt or takeover Tier Zero by deleting the certificate of the enterprise CA or changing the DNShostName of the enterprise CA to an attacker-controlled host. Enterprise CA objects are therefore Tier Zero.

If the enterprise CA certificate is removed from the NTAuth store, certificates from this CA cannot be used for domain authentication, thus preventing a Tier Zero takeover. | | Enterprise CA Computers | TRUE | AD computer | Enterprise CAs can by default issue certificates that enable authentication as anyone, thereby allowing takeover of Tier Zero. An attacker with admin rights on an enterprise CA can obtain a certificate as any user in different ways. One option is to dump the private key of the CA and craft a 'golden certificate' as a target user. This attack can be prevented by protecting the private key with hardware. Alternatively, the attacker can publish any template, modify pending certificate requests, and issue denied requests, which typically also enable a takeover of Tier Zero. Enterprise CA computer objects are therefore Tier Zero.

If the enterprise CA certificate is removed from the NTAuth store, then certificates from this CA cannot be used for domain authentication, thus preventing a Tier Zero takeover. | | Enterprise Domain Controllers | FALSE | DC object | There are no known ways to abuse membership in this group to compromise Tier Zero. However, the GetChangesAll privilege is considered a security dependency that should only be held by Tier Zero principals. Additionally, control over the group allows one to impact the operability of Tier Zero by removing domain controllers from the group, which breaks AD replication. The group is therefore considered Tier Zero. | | Enterprise Key Admins | FALSE | AD group | The Enterprise Key Admins group has write access to the msds-KeyCredentialLink attribute on all users (not AdminSDHolder Protected when the PDCe runs an OS earlier than Server 2019) and on all computers in the AD forest. This enables the group to compromise all these principals through Shadow Credentials attacks. The group is therefore considered Tier Zero. | | Exchange Trusted Subsystem | TRUE | AD group | The Exchange Trusted Subsystem group has takeover permissions on all users with the default ACL inheritance enabled from the domain, regardless of the permission model Exchange is configured to. The compromising permission is write access to the AltSecurityIdentities attribute, which allows an attacker to add an explicit mapping for the user for domain authentication. Typically, some Tier Zero users inherit permissions from the domain. The group is therefore Tier Zero.



The group can only be treated as non-Tier Zero if all Tier Zero users are protected from this compromising permission. | | Exchange Windows Permissions | TRUE | AD group | The Exchange Windows Permissions group has takeover permissions on all users (WriteDACL and reset password) and all groups (edit membership) with the default ACL inheritance enabled from the domain, if Exchange is configured with the default shared permission model or the RBAC split model. Typically, some Tier Zero users and groups inherit permissions from the domain. The group is therefore Tier Zero.

If Exchange is configured in the AD split model, then this group has no compromising permissions and can be treated as non-Tier Zero. | | Global Administrator | FALSE | Entra ID role | The Global Administrator role is the highest privilege role in Entra ID and inarguably part of Tier Zero. It can do almost anything, and grant permission to do the things it cannot do. | | Intune Administrator | TRUE | Entra ID role | The Intune Administrator role has permission to execute scripts locally on Entra-managed devices. The role has therefore a potential attack path to Tier Zero through Entra-managed devices used by Tier Zero principals. Furthermore, the Intune Administrator role can manage Conditional Access, which can be abused to lower the security of Tier Zero or prevent the operability of Tier Zero. The role is therefore considered Tier Zero. | | Key Admins | FALSE | AD group | The Key Admins group has write access to the msds-KeyCredentialLink attribute on all users (not AdminSDHolder Protected when the PDCe runs an OS earlier than Server 2019 or unless the ProtectAdminGroups task is manually forced) and on all computers in the AD domain. This enables the group to compromise all these principals through Shadow Credentials attacks. The group is therefore considered Tier Zero. | | Knowledge Administrator | TRUE | Entra ID role | The Knowledge Administrator role can control non-role-assignable groups. If any non-role-assignable group has compromising permissions over a Tier Zero asset (e.g. Contributor on a domain controller Azure VM), then the Knowledge Administrator role can add arbitrary principals to the given group and compromise Tier Zero. If no non-role-assignable group has compromising permissions over a Tier Zero asset, then there is no attack path to Tier Zero from the Knowledge Administrator role. It therefore depends on the usage of non-role-assignable groups whether the role should be considered Tier Zero. | | KRBTGT objects | TRUE | AD user | The krbtgt's credentials allow one to create golden ticket and compromise the domain. Therefore, if you obtain the credentials of this account, then you can authenticate as any Tier Zero user. However, there is currently no known privilege on the object to obtain the Kerberos keys or to compromise the account in any other way. When you reset the password of krbtgt, AD will ignore your password input and use a random string instead. So, the reset password privilege does not work for a compromise. An attacker could use the reset password privilege to harm Tier Zero, as a double password reset causes all Kerberos TGTs in the domain to become invalid. So, since control over the account can harm Tier Zero, and there is no reason for delegating control to non-Tier Zero, the krbtgt is Tier Zero. | | NTAuth store | TRUE | AD object | The NTAuth store is a security dependency for Tier Zero. A certificate that impersonates any user in AD must chain up to a trusted root CA and be issued by a CA trusted by the NTAuth store. With control over a root CA and the NTAuth store, an attacker can make an attacker-controlled root CA certificate meet these requirements and issue certificates as anyone, taking over Tier Zero. Control over the NTAuth store alone may be sufficient to disrupt Tier Zero operations, as the attacker can delete CA certificates that Tier Zero principals or systems rely on for authentication. The NTAuth store is therefore Tier Zero. | | Partner Tier2 Support | FALSE | Entra ID role | The Partner Tier2 Support role can reset the password for any principal, including principals with the Global Administrator role. The role is therefore considered Tier Zero. | | Performance Log Users | TRUE | DC group | The Performance Log Users group has local privileges on domain controllers to launch, activate, and use Distributed COM objects but no privilege to log in.

There are no known ways to abuse the membership of the group to compromise Tier Zero. The local privileges the group has on the DCs are considered security dependency, and the group is therefore considered Tier Zero. | | Print Operators | TRUE | DC group | The Print Operators group has the local privilege on the domain controllers to load device drivers and can log on locally on domain controllers by default.

It is feasible to remove the logon privilege from the group on the domain controllers, such that the group has no known abusable path to Tier Zero. However, the local privilege to load device drivers is considered a security dependency for the domain controllers, and the group is therefore considered Tier Zero. | | Privileged Authentication Administrator | FALSE | Entra ID role | The Privileged Authentication Administrator role can set or reset any authentication method (including passwords) for any principal, including principals with the Global Administrator role. The role is therefore considered Tier Zero. | | Privileged Role Administrator | FALSE | Entra ID role | The Privileged Role Administrator role can grant any other admin role to any principal at the tenant level. The role is therefore considered Tier Zero. | | Read-Only Domain Controllers | TRUE | AD computer | An attacker with control over a RODC computer object can compromise Tier Zero principals. The attacker can modify the msDS-RevealOnDemandGroup and msDS-NeverRevealGroup attributes of the RODC computer object such that the RODC can retrieve the credentials of a targeted Tier Zero principal. The attacker can obtain admin access to the OS of the RODC through the managedBy attribute, from where they can obtain the credentials of the RODC krbtgt account. With that, the attacker can create a RODC golden ticket for the target principal. This ticket can be converted to a real golden ticket as the target has been added to the msDS-RevealOnDemandGroup attribute and is not protected by the msDS-NeverRevealGroup attribute. Therefore, the RODC computer object is Tier Zero. | | Root CA object | TRUE | AD object | A root CA is a security dependency for Tier Zero. A certificate that impersonates any user in AD must chain up to a trusted root CA and be issued by a CA trusted by the NTAuth store. With control over a root CA and the NTAuth store, an attacker can make an attacker-controlled root CA certificate meet these requirements and issue certificates as anyone, taking over Tier Zero. Control over a root CA alone may be sufficient to disrupt Tier Zero operations, as the attacker can delete root CA certificates that Tier Zero principals or systems rely on for authentication. Root CA objects are therefore Tier Zero. | | Schema Admins | FALSE | AD group | The Schema Admins group has full control over the AD schema. This allows the group members to create or modify ACEs for future AD objects. An attacker could grant full control to a compromised principal on any object type and wait for the next Tier Zero asset to be created, to then have a path to Tier Zero. This attack could be remediated by removing any unwanted ACEs on objects before they are promoted to Tier Zero, but we recommend considering the group as Tier Zero instead. | | Security Administrator | TRUE | Entra ID role | The Security Administrator role has access to Live Response API (if not disabled) with permission to execute scripts locally on Entra-managed devices. The role has therefore a potential attack path to Tier Zero through Entra-managed devices used by Tier Zero principals. Furthermore, the Security Administrator role can manage Conditional Access, which can be abused to lower the security of Tier Zero or prevent the operability of Tier Zero. The role is therefore considered Tier Zero. | | Server Operators | TRUE | DC group | The Server Operators group has local privileges on the domain controllers and perform administrative operations as creating backups of all files. The group can log on locally on domain controllers by default.

It is feasible to remove the logon privilege from the group on the domain controllers, such that the group has no known abusable path to Tier Zero. However, the local privileges are considered security dependencies for the domain controllers, and the groups are therefore considered Tier Zero. | # History Source: https://bloodhound.specterops.io/analyze-data/privilege-zones/history Review the audit log of changes made to Privilege Zones over time. Applies to BloodHound Enterprise and CE The **History Log** provides a record of changes to your Zones and Labels, including the type of change that occurred, who made it, and when it happened. Use the log to audit and track changes to your Zones and Labels over time. BloodHound retains 90 days of history from the last successful analysis operation. Zone Builder history log ### Search and filter The **History Log** provides a search box and filters to help you identify specific changes quickly. Zone Builder history log filter # Labels Source: https://bloodhound.specterops.io/analyze-data/privilege-zones/labels Learn how to use labels to categorize and manage objects within Privilege Zones for better organization. Applies to BloodHound Enterprise and CE Labels let you tag groups of objects for easier searching and filtering. For example, you can label compromised assets with the default **Owned** label to quickly identify attack paths from non-compromised to compromised assets in your environment. The **Owned** label represents objects that have been compromised in your environment. You can tag objects with the **Owned** label using rules or manually in the graph. Unlike zones, BloodHound does not use labels in risk analysis—they're designed to help you organize and query your data. The tab provides different views depending on which edition of BloodHound you're using. The **Summary View** is available in BloodHound Enterprise only, while the **Details View** is available in both BloodHound Enterprise and BloodHound Community Edition. The **Summary View** shows label names, rule counts, and object counts. A view of the Zone Builder labels summary view The **Details View** displays all rules configured for the selected label and the objects that they pull into the label (organized by node type). Use the dropdown menus to filter the view by specific zones, identity providers, and services in your network environment. Select a rule or object to display more details in the right panel, including: * Rule definition and Cypher query * Object properties and relationships BloodHound displays objects for enabled rules only. To view objects related to a disabled rule, you must re-enable it. A view of the Zone Builder labels detail view ### Create a label Applies to BloodHound Enterprise only You can create custom labels to categorize objects based on any criteria relevant to your environment, such as business function, sensitivity level, or compliance requirements. For example, you might create a label for PCI-scoped systems to quickly identify attack paths from non-PCI to PCI environments. Creating a label involves configuring the label details and defining a rule. In the left menu, click **Privilege Zones** > **Labels** > **Create Label**. Enter all relevant information about the label: | Field | Required? | Description | | ----------- | :-------: | ----------------------------------------------------------------------- | | Name | Yes | A unique name for the label (e.g., PCI) | | Description | No | A brief description of the label's purpose and scope (e.g., PCI assets) | A view of the Zone Builder create label page Click **Define Rule** to save your new label and continue on to define the objects to include in the label. See [Rules](/analyze-data/privilege-zones/rules) for more detailed information about defining rules. The content in this section provides a high-level overview only. When defining a rule during the label creation process, provide the following information: | Field | Required? | Description | | ----------- | :-------: | ---------------------------------------------------------------------- | | Name | Yes | A unique name for the rule (e.g., PCI) | | Description | No | A brief description of the rule's purpose and scope (e.g., PCI assets) | | Rule Type | Yes | The type of rule to use (e.g., Object ID or Cypher) | A view of the Zone Builder define label rule page Click **Save** to finish creating the label. ### Edit a label To edit a label, follow these steps: 1. In the left menu, click **Privilege Zones**. 2. Click the **Labels** tab 3. By default, the **Owned** label is pre-selected. To edit a different label, select the label you want to edit. If you're using BloodHound Enterprise, you can select a label from the **Summary View**. A view of the Zone Builder edit label page in BloodHound Enterprise with the Summary View Alternatively, BloodHound Enterprise and BloodHound Community Edition users can select a label using the dropdown menu on the **Details View**. A view of the Zone Builder edit label page in BloodHound with the Detail View 4. Click **Edit Label**. To edit the label: 1. Modify the label's name or description. 2. Click **Save Edits** to apply your changes. To manage how objects are included in the label, see [Rules](/analyze-data/privilege-zones/rules). ### Delete a label Applies to BloodHound Enterprise only You cannot delete the default **Owned** label, but you can edit its description and rules. Deleting a is irreversible. To delete an existing label, follow these steps: Navigate to the **Labels** tab, select the label you want to delete, and click **Edit Label**. To delete the label: 1. Click **Delete Label** at the top of the page. 2. Confirm your action in the dialog. A view of the Zone Builder confirm label delete dialog 3. Click **Confirm** to delete the label. # Overview Source: https://bloodhound.specterops.io/analyze-data/privilege-zones/overview Define protected boundaries in BloodHound and analyze attack paths that violate your security model. Applies to BloodHound Enterprise and CE Privilege Zones help you define the boundaries that matter most in your environment so you can understand who can reach them, how they can be reached, and where that access violates your security model. For many teams, **Tier Zero** is the starting point because it contains the identities, systems, and permissions that can control the broader environment. BloodHound includes a default Tier Zero zone so you can begin reducing attack paths to those assets. Most organizations do not stop at Tier Zero. Some teams extend a traditional tiering model. Others protect a critical application, regulated environment, business unit boundary, source code system, or endpoint management platform. Privilege Zones give teams a flexible way to model those priorities inside BloodHound and analyze attack paths into them. ## Use cases Use Privilege Zones to turn security priorities into visible attack path boundaries. Some zones become a durable part of your operating model. Others support a specific remediation campaign, audit response, or focused investigation. Start with the use case that matches your goal: Expand beyond Tier Zero by choosing the next protected boundary. Test whether administrative tier boundaries hold up against real attack paths. Model critical applications and regulated environments as protected boundaries. Create bounded remediation work with clear scope, ownership, and progress tracking. Treat high-impact repositories, workflows, and deployment paths as protected boundaries. Analyze endpoint administration paths into sensitive managed Mac environments. BloodHound Community Edition includes the default **Tier Zero** zone. Custom zones for these use cases are available in BloodHound Enterprise. ## Key concepts The **Zone Builder** page provides tools for configuring and managing your Privilege Zones. Review the following key concepts to understand how Privilege Zones work and how to use them effectively: | Concept | Description | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Zone** | A group of objects that represents a hierarchy of control across identity providers and services, based on access level. An object can belong to only one zone at a time (the highest-priority zone that matches). | | **Label** | A flexible way to categorize objects for searching and filtering. An object can belong to multiple labels simultaneously. | | **Certification** | An optional review step in BloodHound Enterprise only that pauses automatic inclusion of newly matched objects in a zone until you certify them. | | **History** | An audit log of changes made to zones, labels, and related rules. | Zones organize objects into a strict hierarchy. BloodHound analyzes how object privileges are assigned and where they can be escalated across your environment. By default, BloodHound includes a **Tier Zero** zone that represents a set of objects with full control over an environment *and* any objects with control over those objects. See [Tier Zero: Members and Modification](/get-started/security-boundaries/tier-zero-members) to learn more. BloodHound Enterprise customers can [create](/analyze-data/privilege-zones/zones) additional zones to match their organization's security model. However, analyzing them requires the **Privilege Zone Analysis** feature (available for purchase). For more information, contact your sales representative. If BloodHound Enterprise detects an object in a lower-privileged zone controlling an object in a higher-privileged zone, it identifies it as a finding in the **Attack Paths** and **[Posture](/analyze-data/findings/posture)** pages. For example, if a Tier One user can control a Tier Zero server, BloodHound flags it as a violation of the privilege model. This analysis helps you identify and remediate privilege escalation paths and misconfigurations that violate your security model. ## Object membership Before working with Privilege Zones, it's important to understand how BloodHound assigns objects to zones and labels through the analysis process. * **Object membership requires rule matching**: Objects are only assigned to a zone or label if they match at least one of the zone's or label's rules * **Zone membership is exclusive to the highest-priority zone**: If multiple zone rules match an object, BloodHound assigns it to the highest-priority zone only * **Objects can have multiple labels**: Unlike zones, an object can match multiple label rules simultaneously Most changes to Privilege Zones affect object membership and require analysis to run before you can validate the results. Understanding when you can expect to see results helps you maintain your configuration and validate remediation efforts. For Cypher-based rules, validation happens in two stages. First, rerun the query in the rule editor after you change it so BloodHound can validate the updated rule definition. After you save the rule, BloodHound runs analysis before updated zone or label membership appears elsewhere in the product. Applies to BloodHound Enterprise only BloodHound Enterprise can complete analysis for Privilege Zone changes faster with [Variable Analysis Mode](/analyze-data/findings/analysis#variable-analysis-mode) because it starts at the **Tagging** stage instead of running the full analysis pipeline. *Scheduled analysis (a SpecterOps-managed feature) always runs a full analysis.* ## Workflow The following steps represent the general workflow for making changes and validating the results: Make any necessary changes to your zones, labels, and rules on the **Zone Builder** page. Save your configuration changes to automatically trigger analysis for any settings that affect object membership. All changes that affect object membership require analysis to run before you can validate the results. Actions that require analysis include: * Creating a new zone or label with a rule definition * Creating, editing, or deleting a rule definition for an existing zone or label * Editing the details of a zone or label that has a rule definition * Deleting a zone or label that has a rule definition Check your [tenant status](/collect-data/enterprise-collection/monitor#tenant-status) to monitor analysis progress. During analysis, BloodHound re-evaluates object membership against the updated configuration. Analysis may take several minutes to complete depending on the size of your environment. BloodHound Enterprise customers can enable [Variable Analysis Mode](/analyze-data/findings/analysis#variable-analysis-mode) to potentially speed up analysis for Privilege Zone changes. Until analysis finishes, the Zone Builder **Details View** and related metrics will not reflect your latest changes. Validate that your configuration changes have the expected results: 1. Navigate to the **Zone Builder** page and verify that the expected objects are now included in (or excluded from) the relevant zones and labels. 2. Review the **Attack Paths** and **Posture** pages for new findings or changes in existing findings related to the updated zones and labels. ## Common issues **I created a new zone rule but don't see any objects in the zone.** * Analysis may still be running. Wait for it to complete before checking zone membership. * The rule may not match any objects. Review the rule definition and test with known objects. * Multiple zone rules may match the same objects. Check if another zone with higher priority is taking precedence for those objects. **I updated a zone rule but don't see any changes.** Analysis is required for rule updates to take effect. Check the analysis status and wait for completion. **I deleted a zone rule and objects are still showing in the zone.** * Other rules in the same zone may still match those objects. To see which rules are tagging a specific object, use the [object-based](/analyze-data/privilege-zones/rules#method-2%3A-search-by-object-name-or-id) search method. * Analysis may still be running. Wait for completion to see the final result. # Rules Source: https://bloodhound.specterops.io/analyze-data/privilege-zones/rules Learn how to create, manage, and optimize rules in Privilege Zones to enhance BloodHound's analysis. Applies to BloodHound Enterprise and CE Rules are instructions that associate objects with zones and labels based on object ID, object relationships (expansion), and Cypher queries. BloodHound applies any rule changes during the next analysis operation. **Zone** rules provide a logical method of ensuring objects appear in the appropriate zone using either a Cypher query or by searching for an object ID. If an object has been added to multiple zones, the most critical zone in your defined hierarchy takes precedence. **Label** rules provide a flexible method of tagging objects in an environment. Objects can have multiple labels and you can use those labels to search and filter using Cypher in the **Explore** page. If your rules don't show expected objects, see [Troubleshoot missing objects](/analyze-data/privilege-zones/rules#troubleshoot-missing-objects). ### Types Rules are instructions that automatically tag objects into zones or labels. Think of them as the "how" behind the tagging process. * **Object rules** target specific objects and their related objects through "expansion" * **Cypher rules** tag objects based on custom query results * [**Default rules**](/analyze-data/privilege-zones/default-rules) are system-managed and tag critical objects automatically ## Rule expansion Rules automatically include related objects based on the type of object that you select, expanding through relationships to tag additional objects ([*some exceptions apply*](#control-of-tagged-object-expansion)). This "expansion" saves you time by tagging entire groups or organizational units at once. The following sections describe how different object types expand during the tagging process. You can interrupt automatic inclusion of additional objects into Privilege Zones by requiring manual certification of the additional objects. See [Certification](/analyze-data/privilege-zones/certification) to learn more. ### Group-like expansion Objects that behave like groups in Active Directory include all contained members within the zone/label. These include the following type (edge) relationships: * Group ([`MemberOf`](/resources/edges/member-of)) * AZRole ([`AZHasRole`](/resources/edges/az-has-role), `AZRoleElligible`) * AZGroup ([`AZMemberOf`](/resources/edges/az-member-of)) ### Structured expansion Objects that provide structural organization include all contained objects within the zone/label. These include the following type (edge) relationships: * Domain ([`Contains`](/resources/edges/contains)) For non-default rules only. * OU ([`Contains`](/resources/edges/contains)) * AZSubscription ([`AZContains`](/resources/edges/az-contains)) * AZManagementGroup ([`AZContains`](/resources/edges/az-contains)) * AZAdministrativeUnit ([`AZContains`](/resources/edges/az-contains)) ### Control of tagged object expansion During the tagging process for zones, the final step involves tagging all objects that contain (or provide external control of) the selected objects. For example, in Active Directory this means that all OUs, Containers, and GPOs that apply to any Tier Zero object are *also* tagged to the Tier Zero zone. If any OUs or Containers are tagged in the last step of the tagging process only (not because you explicitly selected them), the process won't expand to tag other contained objects. ## Define a rule The process and screens for creating and editing rules is nearly the same for zones and labels. The primary difference is that [certification](/analyze-data/privilege-zones/certification) is a BloodHound Enterprise feature available for zones only. Unless you're defining a rule as part of the zone or label creation process, be sure to select a specific zone or label on the **Zone Builder** page first. If you're defining a rule as part of the [zone or label](/analyze-data/privilege-zones/zones) creation process, skip to **Configure rule details** below. 1. In the left menu, click **Privilege Zones**. 2. Click the **Zones** or **Labels** tab and select a specific zone or label. If you don't select a zone or label first, the new rule will be associated with the default zone or label selection when you open the page (top position in the **Zones** or **Labels** summary and detail view). 1. Click **Create Rule**. 2. Enter all relevant information for the rule: Review [rule expansion](/analyze-data/privilege-zones/rules#rule-expansion) for more information about rule behavior. | Field | Required? | Description | | ----------------------- | :-------: | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Name | Yes | A unique name for the rule (e.g., PCI Assets) | | Description | No | A brief description of the rule's purpose and scope (e.g., PCI assets) | | Automatic Certification | No | An option in Enterprise Edition to choose how new objects are [certified](/analyze-data/privilege-zones/certification) (available for zones only) | | Rule Type | Yes | The type of rule to use (e.g., Object ID or Cypher) | **Automatic Certification options** See [Certification](/analyze-data/privilege-zones/certification) to learn more. * **Direct Objects**: Only the objects directly matched by the rule are certified automatically (excludes objects added through [expansion](/analyze-data/privilege-zones/rules#rule-expansion), such as OUs and GPOs). These objects are shown separately in the **Sample Results** panel. * **All Objects**: Every object (including those tied to direct objects through expansion) is certified automatically * **Off**: All certification is manual A view of the Zone Builder define rule page **Rule type configuration details** When you switch between **Object ID** and **Cypher** while working on rules, BloodHound preserves the current state for each rule type until you save the rule or leave the page. If you navigate away before saving, BloodHound clears that temporary state. 1. In the **Object Rule** panel, type to search for an object by name or ID. 2. Click the object to add it to the list of targeted objects. You must select at least one object to create the rule. The **Sample Results** panel displays up to 200 sample results, separating directly selected objects from objects selected through [expansion](/analyze-data/privilege-zones/rules#rule-expansion). This helps you understand why your results may include more objects than initially expected. If objects appear in the **Sample Results** panel during rule creation but don't show in the zone after saving, see [Zone precedence conflicts](/analyze-data/privilege-zones/rules#zone-precedence-conflicts) in the troubleshooting section below—a higher-priority zone may be claiming those objects. A view of the Zone Builder Object ID rule configuration 1. Enter a Cypher query into the **Cypher Search** box. If you need to reference existing Tier Zero membership in a Cypher rule, match on the `Tag_Tier_Zero` label rather than inspecting `system_tags` properties with patterns such as `coalesce(n.system_tags, [])`. For example, to return Tier Zero users, use `MATCH (n:User:Tag_Tier_Zero) RETURN n`. 2. Click **Run** below the Cypher query box. You must run the query before you can create the rule. If you change the query after running it, click **Run** again before you save the rule. The **Sample Results** panel displays up to 200 sample results, separating directly selected objects from objects selected through [expansion](/analyze-data/privilege-zones/rules#rule-expansion). This helps you understand why your results may include more objects than initially expected. If objects appear in the **Sample Results** panel during rule creation but don't show in the zone after saving, see [Zone precedence conflicts](/analyze-data/privilege-zones/rules#zone-precedence-conflicts) in the troubleshooting section below—a higher-priority zone may be claiming those objects. A view of the Zone Builder Cypher rule configuration If the query returns no results, BloodHound asks you to confirm before saving the rule. This is useful when you expect a future data collection or environment change to produce matching objects. *(Optional)* Click **View in Explore** to pivot to the **Explore** page and see results in the graph view. Adding the following object types automatically includes more objects according to the definition below. The **Sample Results** panel displays these expanded objects separately from the directly selected objects. * `OU/Container` → All objects contained in the OU/container * `Group` → All objects with membership in the Group * `AZResourceGroup`/`AZSubscription` → All objects contained in the RG/Sub * `AZGroup` → All objects with membership in the group * `AZRole` → All objects with role assignments (or eligibility) Click **Create Rule** to finish creating the rule. ## Edit or delete a rule To edit or delete a rule, follow these steps: Only users with the appropriate [permissions](/manage-bloodhound/auth/users-and-roles) can make changes. You cannot delete [default rules](/analyze-data/privilege-zones/default-rules). 1. In the left menu, click **Privilege Zones**. 2. Click the **Zones** or **Labels** tab and open the **Detail View**. 3. Select the zone or label that contains the rule that you want to edit or delete and select it. 4. Use one of the following methods to locate the rule you want to edit or delete: 1. Enter the name of the rule in the search bar. 2. Select the rule from the search results. 3. Click **Edit Rule** to open the rule details. 1. Enter the object name or ID. 2. Select the object. 3. In the right panel, click the **Object** tab and expand the **Rule** accordion. 4. Click the ellipsis () beside the relevant rule and select **Edit**. Choose one of the following options: To edit a rule: Only users with the appropriate [permissions](/manage-bloodhound/auth/users-and-roles) can make changes. You cannot disable some [default rules](/analyze-data/privilege-zones/default-rules). 1. Make any necessary changes to the rule configuration. For example, you can modify the rule's name, description, rule type, and certification settings (available for zones only). You can also disable or enable a rule by toggling the **Enabled** switch. If you switch between **Object ID** and **Cypher** while editing the rule, BloodHound preserves the current state for each rule type until you save or leave the page. If you edit a Cypher query, click **Run** again before you click **Save Edits**. If the rerun query returns no results, BloodHound asks you to confirm the save. If you do not rerun the updated query, BloodHound prompts you to run it before saving. A view of the Zone Builder edit rule page 2. Click **Save Edits** to apply your changes. To delete a rule: 1. Click **Delete Rule** at the top of the page. 2. Confirm your action in the dialog. Confirm deletion of a custom rule 3. Click **Confirm** to delete the rule. ## Troubleshoot rules If a rule doesn't tag the objects you expect, or you run into issues creating or saving a rule, use the following sections to identify the cause and resolve the issue. ### Domain filter mismatch The **Domain** selector filters which objects are visible in the zone or label view. If the selected domain doesn't contain objects that match your rule criteria, the zone or label may appear empty or incomplete. **Solution**: Check the domain selector and ensure you've selected the correct domain(s) that contain the expected objects. ### Zone precedence conflicts When an object matches rules in multiple zones, only the highest-priority zone in your [**Zone Order**](/analyze-data/privilege-zones/zones) tags that object. Lower-priority zones won't tag the object, even if their rules match. This is why the **Sample Results** panel during [rule creation](/analyze-data/privilege-zones/rules#define-a-rule) may show objects that don't appear in your lower-priority zones—they're being tagged by a higher-priority zone instead. For example, if an object is tagged by both a Tier Zero rule and a Tier One rule, it will only appear in the Tier Zero zone. The **Sample Results** panel would show the object as a result of your Tier One rule, but the object would only appear in the higher-priority Tier Zero zone, not in Tier One. **Solution**: Review your **Zone Order** and check whether objects are being tagged by higher-priority zones. You can verify this by checking the higher-priority zones for the missing objects. ### Unsaved rule type changes disappeared BloodHound preserves temporary **Object ID** and **Cypher** rule state only while you remain on the current rule page. If you leave the page before saving, BloodHound clears that temporary state. **Solution**: Save the rule before navigating away if you want to keep your current changes. ### Cypher changes do not save If you change a Cypher query after running it, BloodHound requires you to click **Run** again before you can save the rule. This ensures BloodHound validates the updated query and refreshes the sample results for the current rule definition. If the query returns no results, BloodHound asks you to confirm the save. If you do not rerun the updated query, BloodHound prompts you to run it first. **Solution**: Rerun the updated query, review the direct and expanded sample results, and then save the rule. ### Object deleted from graph Applies to BloodHound Enterprise only Objects are automatically deleted from the graph if they haven't been observed within the configured retention period. BloodHound stores a timestamp on every object that updates whenever a collection includes that object or references to it. This ensures your data remains fresh and accurate over time. By default, objects are retained for 7 days after they were last seen. For Active Directory environments with the AD recycle bin enabled, objects are retained in BloodHound until they've been permanently deleted from AD (after the tombstone lifetime, which defaults to 180 days) plus the configured retention period. If an object has been deleted due to retention, it won't appear in any zone or label, even if a rule targets it. **Solution**: Check when the object was last seen by viewing the "Last Seen by BloodHound" attribute in the object's entity panel on the **Explore** page. Review your [data retention](/collect-data/enterprise-collection/data-retention) settings to understand the configured retention period. If the object reappears in the graph during a future data collection, the rule will automatically capture it (assuming the rule is still enabled). # Plan Attack Path Management Source: https://bloodhound.specterops.io/analyze-data/privilege-zones/use-cases/attack-path-management-journey Expand Privilege Zones beyond Tier Zero by connecting Attack Path Management to business priorities. Applies to BloodHound Enterprise only Privilege Zones help organizations expand Identity Attack Path Management beyond the default Tier Zero view. Tier Zero is the natural starting point because it contains the identities, systems, and permissions that can control the broader environment. Once a team begins reducing paths to Tier Zero, the next question is usually straightforward: what should we protect next? That answer varies by organization. For one customer, the next priority may be Tier One administrative systems. For another, it may be a clinical application, a payment environment, a production deployment repository, an endpoint management platform, or a business-unit-owned infrastructure boundary. Privilege Zones give teams a way to define those priorities and analyze attack paths into them. A zone can represent: * A traditional administrative tier * A critical application * A regulated environment * A business unit * A platform team * A source code boundary * An endpoint management boundary * A technology control plane Some zones become durable parts of an organization's operating model. Others support a specific campaign, audit response, or focused investigation. Teams do not need a perfect model before starting. Many organizations begin with known groups, object IDs, OUs, naming conventions, asset metadata, or simple Cypher queries. They use the results to validate assumptions, find unexpected control paths, and refine the zone over time. Treat Privilege Zones as living documents. Revisit and refine them as your environment changes, ownership becomes clearer, and your Attack Path Management program matures. ## When to use this approach * Your organization has started reducing Tier Zero attack paths * Your team wants to expand Attack Path Management to additional critical systems * You need to organize remediation around business priorities, ownership boundaries, or regulatory scope * You want a repeatable model for deciding what to protect next * You are adding OpenGraph-connected technologies and need to define which assets deserve focused analysis ## Example progression 1. Start with Tier Zero and reduce the highest-impact attack paths. 2. Define a Tier One [zone](/analyze-data/privilege-zones/zones) around administrative systems or critical infrastructure. 3. Define a [zone](/analyze-data/privilege-zones/zones) around a specific business-critical application or regulated system. 4. Use [labels](/analyze-data/privilege-zones/labels) or [zones](/analyze-data/privilege-zones/zones) to map ownership across business units or platform teams. 5. Add [OpenGraph-connected technologies](/opengraph/extensions/manage), such as GitHub, Jamf, Okta, or cloud platforms. 6. Use zones to understand inherited risk across identity systems, endpoint management platforms, source code systems, and cloud environments. ## Practical starting questions * What system would create the most business impact if compromised? * What systems are subject to regulatory, audit, or compliance scrutiny? * What administrative boundary do we need to validate after Tier Zero? * Which application owners need visibility into inherited identity risk? * Which business unit or platform team owns remediation? * Which source code repositories, workflows, or endpoint management systems can influence production? * What existing structure can help us build a first version of the zone? ## Starter Cypher queries These example Cypher queries help you identify candidate objects for zones and labels during discovery, trials, and early implementation. Review and tune these queries before using them as production Privilege Zone rules. ### Broad discovery by naming pattern ```cypher theme={null} MATCH (n:Base) WHERE toUpper(COALESCE(n.name, '')) CONTAINS 'ADMIN' OR toUpper(COALESCE(n.name, '')) CONTAINS 'PRIV' OR toUpper(COALESCE(n.name, '')) CONTAINS 'TIER1' OR toUpper(COALESCE(n.name, '')) CONTAINS 'T1' RETURN n LIMIT 1000 ``` ### Critical application keyword search ```cypher theme={null} // Replace APPNAME / APPLICATION-ALIAS with your critical application. MATCH (n) WHERE toUpper(coalesce(n.name, '')) CONTAINS 'APPNAME' OR toUpper(coalesce(n.name, '')) CONTAINS 'APPLICATION-ALIAS' RETURN n LIMIT 1000 ``` ## Key takeaway Privilege Zones give teams a repeatable way to decide what to protect after Tier Zero and to keep expanding Attack Path Management as the environment matures. # Extend a Traditional Tiering Model Source: https://bloodhound.specterops.io/analyze-data/privilege-zones/use-cases/extend-traditional-tiering-model Use Privilege Zones to test whether administrative tier boundaries hold up against real attack paths. Applies to BloodHound Enterprise only Many organizations begin with a traditional administrative tiering model. Tier Zero contains the systems and identities that control the environment. Tier One may contain servers, applications, or administrative systems that support critical operations. Tier Two may contain workstations, users, or broader operational assets. Privilege Zones help teams test whether that model holds up in the environment. ## Scenario A security team has already started reducing attack paths to Tier Zero. The team now wants to understand whether lower-privileged users, groups, or systems can control Tier One assets. The team creates a simple Tier One zone using known administrative groups, server lists, OUs, and a small number of hand-selected objects. The first version is intentionally practical. It gives the team enough structure to start analysis and review findings. Once the zone is created, BloodHound Enterprise analyzes attack paths into that zone. The team can then identify where lower-tier principals have unexpected control over higher-tier systems. ## What this can reveal * Help desk users with control over Tier One systems * Lower-tier administrators with rights that cross tier boundaries * Service accounts that can influence higher-tier assets * Groups that appear operational but have privileged reach * Legacy permissions that violate the intended administrative model ## Why this works The value comes from testing the tiering model against attack paths. A policy document may say that lower-tier users should not control Tier One systems. BloodHound Enterprise can show whether those paths exist. The first version of the zone can be simple. Smaller organizations may start with list-based rules and known objects. More advanced organizations may use Cypher queries, naming conventions, OUs, or other structural indicators. ## Suggested workflow 1. Identify the next administrative tier to model after Tier Zero. 2. Build a first version of the [zone](/analyze-data/privilege-zones/zones) using known groups, servers, OUs, or object IDs. 3. Run [analysis](/analyze-data/findings/analysis) and review [findings](/analyze-data/findings/attack-paths). 4. Validate whether each finding is expected. 5. Refine the zone as ownership and scope become clearer. 6. Use [remediation progress](/analyze-data/findings/posture) to show movement in the Attack Path Management journey. ## Starter Cypher queries These example Cypher queries help you identify candidate objects for zones and labels during discovery, trials, and early implementation. Review and tune these queries before using them as production Privilege Zone rules. ### Candidate Tier One administrative objects ```cypher theme={null} MATCH (n) WHERE toUpper(coalesce(n.name, '')) CONTAINS 'TIER1' OR toUpper(coalesce(n.name, '')) CONTAINS 'T1' OR toUpper(coalesce(n.name, '')) CONTAINS 'SERVER ADMIN' OR toUpper(coalesce(n.name, '')) CONTAINS 'INFRA ADMIN' OR toUpper(coalesce(n.name, '')) CONTAINS 'PLATFORM ADMIN' RETURN n LIMIT 1000 ``` ### Candidate help desk or support groups ```cypher theme={null} MATCH (n) WHERE toUpper(coalesce(n.name, '')) CONTAINS 'HELPDESK' OR toUpper(coalesce(n.name, '')) CONTAINS 'HELP DESK' OR toUpper(coalesce(n.name, '')) CONTAINS 'DESKTOP SUPPORT' OR toUpper(coalesce(n.name, '')) CONTAINS 'WORKSTATION ADMIN' RETURN n LIMIT 1000 ``` ### Direct relationships from lower-tier candidates to Tier One ```cypher theme={null} // Replace Tag_Tier_One with your Tier One Privilege Zone's tag label. MATCH p=(s)-[r]->(t:Tag_Tier_One) WHERE NOT (s:Tag_Tier_One) RETURN p LIMIT 1000 ``` ### Multi-hop attack paths to Tier One ```cypher theme={null} // Replace Tag_Tier_One with your Tier One Privilege Zone's tag label. MATCH p=(s)-[:AD_ATTACK_PATHS*1..]->(t:Tag_Tier_One) WHERE NOT (s:Tag_Tier_One) RETURN p LIMIT 1000 ``` ## Guidance Teams can start before they have perfect tiering documentation. A useful first version of a zone can generate enough visibility to begin productive conversations with infrastructure, IAM, and security teams. ## Key takeaway A traditional tiering model becomes actionable when the team can compare the intended model against attack paths in the environment. # Protect Critical Applications and Environments Source: https://bloodhound.specterops.io/analyze-data/privilege-zones/use-cases/protect-critical-app-or-regulated-environment Model critical applications and regulated environments as Privilege Zones for focused attack path analysis. Applies to BloodHound Enterprise only Some important systems do not fit cleanly into a traditional Tier Zero, Tier One, or Tier Two model. Many applications and environments matter because of the data they process, the business function they support, or the regulatory exposure they create. Examples include: * Clinical systems * Payment systems * Customer data platforms * Production control planes * Trading or settlement systems * Identity infrastructure for a specific business unit * Systems in scope for PCI, HIPAA, SOX, or other regulatory programs Privilege Zones can draw a fence around those assets and analyze attack paths into them. ## Scenario A healthcare organization wants to understand attack paths into a set of critical clinical application servers. These systems may not be Tier Zero in the traditional sense, but compromise would create patient care, regulatory, financial, and reputational impact. The team creates a Privilege Zone containing the application servers and supporting infrastructure. The first version may use known server lists, application owner input, OUs, or naming conventions. Once the zone is analyzed, BloodHound Enterprise identifies the users, groups, systems, and relationships that can create paths into that environment. ## What this can reveal * Broad server groups with access to application servers * Administrative users outside the expected support model * Legacy permissions inherited from prior domain or application structures * Misaligned ownership between application teams and infrastructure teams * Paths from ordinary users or lower-tier systems into regulated environments ## Why this works Critical application zones make Attack Path Management easier to connect to business priorities. The team can ask a specific question: who can reach this application and should they be able to? This approach is useful in complex environments where full tiering may take time. A critical application zone gives the team a practical starting point with clear business relevance. ## Suggested workflow 1. Select one critical application or regulated environment. 2. Identify the systems, groups, service accounts, and administrative roles that support it. 3. Create a [zone](/analyze-data/privilege-zones/zones) using known objects, OUs, naming conventions, or Cypher. 4. Review [attack paths](/analyze-data/findings/attack-paths) into the zone. 5. Validate findings with the application owner and infrastructure owner. 6. Remediate unexpected paths. 7. Keep the zone for ongoing monitoring or use the process to define the next critical application zone. ## Starter Cypher queries These example Cypher queries help you identify candidate objects for zones and labels during discovery, trials, and early implementation. Review and tune these queries before using them as production Privilege Zone rules. ### Healthcare or clinical system discovery ```cypher theme={null} MATCH (n) WHERE toUpper(coalesce(n.name, '')) CONTAINS 'EPIC' OR toUpper(coalesce(n.name, '')) CONTAINS 'EHR' OR toUpper(coalesce(n.name, '')) CONTAINS 'EMR' OR toUpper(coalesce(n.name, '')) CONTAINS 'CLINICAL' OR toUpper(coalesce(n.name, '')) CONTAINS 'PATIENT' RETURN n LIMIT 1000 ``` ### PCI or payment system discovery ```cypher theme={null} MATCH (n) WHERE toUpper(coalesce(n.name, '')) CONTAINS 'PCI' OR toUpper(coalesce(n.name, '')) CONTAINS 'CARD' OR toUpper(coalesce(n.name, '')) CONTAINS 'PAYMENT' OR toUpper(coalesce(n.name, '')) CONTAINS 'POS' OR toUpper(coalesce(n.name, '')) CONTAINS 'CDE' RETURN n LIMIT 1000 ``` ### Production system discovery ```cypher theme={null} MATCH (n) WHERE toUpper(coalesce(n.name, '')) CONTAINS 'PROD' OR toUpper(coalesce(n.name, '')) CONTAINS 'PRODUCTION' OR toUpper(coalesce(n.name, '')) CONTAINS 'PRD' RETURN n LIMIT 1000 ``` ### Paths into a critical application zone ```cypher theme={null} // Replace Tag_Critical_App with your application zone's tag label. MATCH p=(s)-[:AD_ATTACK_PATHS*1..]->(t:Tag_Critical_App) WHERE NOT (s:Tag_Critical_App) RETURN p LIMIT 1000 ``` ## Guidance Start with a system that has a clear business owner and clear consequences if compromised. Broad keyword searches are useful for discovery, but production rules should be reviewed with application owners so the zone remains credible. ## Key takeaway Privilege Zones help organizations apply Attack Path Management to the applications and regulated environments that carry the highest business impact. # Protect Critical GitHub Repositories Source: https://bloodhound.specterops.io/analyze-data/privilege-zones/use-cases/protect-critical-github-repos Model high-impact GitHub repositories, workflows, and deployment paths as protected Privilege Zone boundaries. Applies to BloodHound Enterprise only In source code platforms, a critical asset may be a repository, workflow, deployment environment, secret, app installation, personal access token, or role that can influence production systems. GitHub is a strong example. Some repositories are sensitive because they contain important code. Others are sensitive because a commit, pull request, workflow, app installation, or token can trigger follow-on actions that affect downstream infrastructure. Privilege Zones can help teams isolate and analyze attack paths into high-impact GitHub assets. ## Scenario A software company uses GitHub Actions to build and deploy code into a cloud environment. Several repositories contain workflows that can deploy infrastructure, publish production artifacts, or assume cloud roles through OIDC federation. The security team creates a Privilege Zone around the repositories and GitHub objects that can influence production deployment. The zone may include: * Production application repositories * Infrastructure-as-code repositories * Repositories with workflows that deploy to production * Repositories with privileged GitHub Actions workflows * Repositories that publish production containers or packages * Repositories with access to cloud deployment secrets * Organization or repository secrets * GitHub environments tied to production deployment * GitHub teams or roles with administrative access * GitHub environments tied to production * Personal access tokens with broad write scope * Workflows that can assume cloud roles or access deployment credentials * Organization owner roles * All-repository admin roles * App installations scoped to all repositories with write permissions * Personal access tokens scoped to all repositories with write permissions The objective is to answer a simple question: who or what can influence code or workflows that can affect production? ## What this can reveal * Users or teams with write, maintain, or admin access to critical repositories * Repository administrators with broader access than intended * Workflows that can access secrets or deployment environments * GitHub Apps with write access to all repositories * Personal access tokens scoped broadly across the organization * Paths from identity providers into GitHub roles * GitHub permissions that can lead to cloud access * Repositories that function as indirect control points for production ## Why this works In GitHub, privilege is often contextual. A repository may be critical because it stores sensitive code, but it may also be critical because it can trigger a workflow, deploy infrastructure, publish an artifact, or assume a role in a cloud environment. That makes GitHub a strong candidate for Privilege Zones. The zone can define the repositories, workflows, teams, apps, and secrets that matter most, then BloodHound Enterprise can help identify attack paths that lead to them. ## Suggested workflow 1. Identify repositories that can affect production or critical infrastructure. 2. Identify workflows that deploy, publish, or assume cloud roles. 3. Identify secrets, environments, teams, and roles connected to those repositories. 4. Create a [zone](/analyze-data/privilege-zones/zones) around those GitHub objects. 5. Analyze who or what can control the zone. 6. Validate [findings](/analyze-data/findings/attack-paths) with application, platform, DevOps, and cloud owners. 7. Reduce unnecessary access and monitor the zone over time. ## Starter Cypher queries These example Cypher queries help you identify candidate objects for zones and labels during discovery, trials, and early implementation. Review and tune these queries before using them as production Privilege Zone rules. For queries covering owner roles, all-repository admin access, app installations, personal access tokens, and external identity mappings, see the GitHub extension's [Privilege Zone Rules](/opengraph/extensions/github/privilege-zone-rules) page. ### Production repository discovery ```cypher theme={null} MATCH (n:GH_Repository) WHERE toUpper(COALESCE(n.name, '')) CONTAINS 'PROD' OR toUpper(COALESCE(n.name, '')) CONTAINS 'PRODUCTION' OR toUpper(COALESCE(n.name, '')) CONTAINS 'PRD' RETURN n LIMIT 1000 ``` ### Infrastructure-as-code repository discovery ```cypher theme={null} MATCH (n:GH_Repository) WHERE toUpper(coalesce(n.name, '')) CONTAINS 'TERRAFORM' OR toUpper(coalesce(n.name, '')) CONTAINS 'TF' OR toUpper(coalesce(n.name, '')) CONTAINS 'IAC' OR toUpper(coalesce(n.name, '')) CONTAINS 'INFRA' OR toUpper(coalesce(n.name, '')) CONTAINS 'CLOUD' RETURN n LIMIT 1000 ``` ## Guidance GitHub zones should be defined around impact. A repository named production may be important. A repository with a workflow that can deploy to production may be even more important. Work with platform engineering, DevOps, application owners, and cloud teams to understand which repositories create downstream control. ## Key takeaway In OpenGraph-connected environments, critical assets may be repositories, workflows, secrets, app installations, tokens, or deployment relationships. Privilege Zones help teams define those assets and understand the attack paths that can influence them. # Protect Jamf Administration Source: https://bloodhound.specterops.io/analyze-data/privilege-zones/use-cases/protect-jamf-administration Model Jamf administration and sensitive managed Mac boundaries as Privilege Zones for focused analysis. Applies to BloodHound Enterprise only Endpoint management platforms can create powerful control relationships. In Jamf Pro environments, users, groups, scripts, API integrations, sites, and computers may influence managed macOS endpoints. Some endpoints may also be more sensitive than others because of who uses them, what data they access, or what administrative roles they support. Privilege Zones can help teams organize Jamf-related risk around high-impact administrators, scripts, integrations, sites, or managed device groups. ## Scenario An organization uses Jamf Pro to manage macOS endpoints across executive users, engineers, administrators, and general employees. The security team wants to understand whether lower-privileged Jamf users, groups, scripts, or integrations can influence sensitive endpoints or administrative workflows. The team creates a Privilege Zone around the most important Jamf objects. Depending on the organization, that zone may include: * Jamf administrators * High-impact Jamf groups * Scripts with privileged execution * API integrations * Executive or administrator device groups * Sites or policies tied to sensitive endpoints * Managed devices used by privileged users The objective is to understand which Jamf identities and relationships can influence sensitive managed endpoints. ## What this can reveal * Jamf users or groups with broader administrative reach than expected * Scripts or policies that can influence sensitive devices * API integrations with powerful permissions * Delegated administration paths that cross intended boundaries * Exposure from endpoint management into identity or application administration ## Why this works As organizations add Jamf data to BloodHound Enterprise, endpoint management becomes part of the attack path picture. Privilege may exist in the ability to manage devices, run scripts, modify policies, or influence endpoints used by privileged users. Privilege Zones can help security, endpoint, and infrastructure teams define which Jamf objects deserve closer analysis and ongoing monitoring. ## Suggested workflow 1. Identify the Jamf users, groups, scripts, integrations, and sites that matter most. 2. Identify sensitive managed device groups, such as administrator, developer, or executive endpoints. 3. Create a [zone](/analyze-data/privilege-zones/zones) around the high-impact Jamf objects. 4. Review [attack path](/analyze-data/findings/attack-paths) into the zone. 5. Validate findings with endpoint management and security teams. 6. Reduce unnecessary administrative reach. 7. Use the zone to support ongoing endpoint management governance. ## Starter Cypher queries These example Cypher queries help you identify candidate objects for zones and labels during discovery, trials, and early implementation. Review and tune these queries before using them as production Privilege Zone rules. ### Jamf tenant ```cypher theme={null} MATCH (n:jamf_Tenant) RETURN n LIMIT 1000 ``` ### Jamf Tier Zero principals ```cypher theme={null} MATCH (n) WHERE n.tier = 0 AND n.primarykind STARTS WITH 'jamf' RETURN n LIMIT 1000 ``` ### All Jamf computers ```cypher theme={null} MATCH (n:jamf_Computer) RETURN n LIMIT 1000 ``` ### All Jamf groups ```cypher theme={null} MATCH (n:jamf_Group) RETURN n LIMIT 1000 ``` ### Jamf API client immediate edges ```cypher theme={null} MATCH p=(s:jamf_ApiClient)-[]->(t) RETURN p LIMIT 1000 ``` ### Jamf account paths ```cypher theme={null} MATCH p=(s:jamf_Account)-[*1..4]->(t) RETURN p LIMIT 1000 ``` ### Jamf account to tenant edges ```cypher theme={null} MATCH p=(s:jamf_Account)-[]->(t:jamf_Tenant) RETURN p LIMIT 1000 ``` ### Jamf group edges to accounts ```cypher theme={null} MATCH p=(s:jamf_Group)-[]->(t:jamf_Account) RETURN p LIMIT 1000 ``` ### Tier One to Tier Zero Jamf paths ```cypher theme={null} MATCH p=(s:jamf)-[r*1..5]->(t:jamf) WHERE s.tier = 1 AND t.tier = 0 AND s.primarykind <> 'jamf_Tenant' AND s.primarykind <> 'jamf_Site' RETURN p LIMIT 1000 ``` ### Direct Tier One to Tier Zero Jamf edges ```cypher theme={null} MATCH p=(s)-[]->(t) WHERE s.tier = 1 AND t.tier = 0 AND s.primarykind STARTS WITH 'jamf' AND t.primarykind STARTS WITH 'jamf' RETURN p LIMIT 1000 ``` ### Jamf sensitive endpoint discovery ```cypher theme={null} MATCH (n:jamf_Computer) WHERE toUpper(coalesce(n.name, '')) CONTAINS 'EXEC' OR toUpper(coalesce(n.name, '')) CONTAINS 'ADMIN' OR toUpper(coalesce(n.name, '')) CONTAINS 'ENG' OR toUpper(coalesce(n.name, '')) CONTAINS 'DEVELOPER' OR toUpper(coalesce(n.name, '')) CONTAINS 'SECURITY' RETURN n LIMIT 1000 ``` ## Guidance Jamf zones should be developed with the endpoint management team. The security team may understand the risk, but the endpoint team usually understands which scripts, policies, integrations, and sites are operationally sensitive. ## Key takeaway Privilege Zones can extend Attack Path Management into endpoint administration by helping teams define and monitor the Jamf objects that create control over sensitive devices. # Run Remediation Campaigns Source: https://bloodhound.specterops.io/analyze-data/privilege-zones/use-cases/run-remediation-campaigns Use Privilege Zones to create bounded remediation campaigns with clear scope, ownership, and progress tracking. Applies to BloodHound Enterprise only Privilege Zones can support time-boxed remediation campaigns. A campaign zone is useful when a team needs to focus on a specific risk area, audit concern, executive priority, or application environment. The zone can be used to create focus, drive remediation, and measure progress. After the campaign, the team can decide whether to keep the zone for ongoing monitoring or move to the next priority. ## Scenario A security team receives an audit finding related to privileged access into a sensitive application environment. The organization does not yet have a complete enterprise tiering model, but it needs to show progress quickly. The team creates a Privilege Zone around the systems in scope for the finding. The zone is used to identify unexpected control paths, prioritize remediation, and report progress during the campaign. ## What this can reveal * Unexpected administrative paths into the scoped environment * Overly broad groups that need to be reduced * Service accounts that create hidden control paths * Ownership gaps between application, IAM, endpoint, and infrastructure teams * Progress over time as attack paths are removed ## Why this works A campaign zone gives teams a bounded problem to solve. It reduces the pressure to model the entire enterprise before taking action. It also creates a practical way to communicate progress to leadership, audit teams, or application owners. ## Suggested workflow 1. Define the campaign objective. 2. Select the assets in scope. 3. Create a [zone](/analyze-data/privilege-zones/zones) around those assets. 4. Review [findings](/analyze-data/findings/attack-paths) and identify the highest-value remediation actions. 5. Assign remediation owners. 6. Track [reduction](/analyze-data/findings/posture) in attack paths over the campaign period. 7. Decide whether the zone should remain permanent or be replaced by the next campaign zone. ## Example campaign objectives * Reduce attack paths into a payment environment * Validate access into a clinical application * Clean up administrative access to production infrastructure * Prepare for an audit or remediate audit findings * Reduce exposure before a major application migration * Validate segmentation after a restructuring or acquisition ## Starter Cypher queries These example Cypher queries help you identify candidate objects for zones and labels during discovery, trials, and early implementation. Review and tune these queries before using them as production Privilege Zone rules. ### Migration or acquisition campaign discovery ```cypher theme={null} MATCH (n) WHERE toUpper(coalesce(n.name, '')) CONTAINS 'LEGACY' OR toUpper(coalesce(n.name, '')) CONTAINS 'MIGRATION' OR toUpper(coalesce(n.name, '')) CONTAINS 'ACQUIRED' OR toUpper(coalesce(n.name, '')) CONTAINS 'OLD' RETURN n LIMIT 1000 ``` ### Audit or compliance campaign discovery ```cypher theme={null} MATCH (n) WHERE toUpper(coalesce(n.name, '')) CONTAINS 'SOX' OR toUpper(coalesce(n.name, '')) CONTAINS 'PCI' OR toUpper(coalesce(n.name, '')) CONTAINS 'HIPAA' OR toUpper(coalesce(n.name, '')) CONTAINS 'AUDIT' OR toUpper(coalesce(n.name, '')) CONTAINS 'COMPLIANCE' RETURN n LIMIT 1000 ``` ### Review immediate inbound relationships to campaign assets ```cypher theme={null} // Replace Tag_Campaign with your campaign zone's tag label. MATCH p=(s)-[r]->(t:Tag_Campaign) WHERE NOT (s:Tag_Campaign) RETURN p LIMIT 1000 ``` ### Review multi-hop paths into campaign assets ```cypher theme={null} // Replace Tag_Campaign with your campaign zone's tag label. MATCH p=(s)-[:AD_ATTACK_PATHS*1..]->(t:Tag_Campaign) WHERE NOT (s:Tag_Campaign) RETURN p LIMIT 1000 ``` ## Guidance Campaign zones work best when paired with clear ownership and a defined time horizon. The zone creates visibility, but remediation still depends on the right teams taking action. ## Key takeaway Privilege Zones can turn broad identity risk into a focused remediation campaign with clear scope, ownership, and measurable progress. # Zones Source: https://bloodhound.specterops.io/analyze-data/privilege-zones/zones Organize and categorize objects in your environment using Privilege Zones. Applies to BloodHound Enterprise and CE Zones define hierarchical privilege levels in your environment based on a tiered administration model. The most common tiering model is [Microsoft's Enterprise Access Model](https://learn.microsoft.com/en-us/security/privileged-access-workstations/privileged-access-access-model). BloodHound uses zones to measure risk and detect violations. Each zone has a specific tier level (**Tier Zero** is the default and highest). BloodHound Enterprise customers can [create](/analyze-data/privilege-zones/zones) additional zones to match their organization's security model. However, analyzing them requires the **Privilege Zone Analysis** feature (available for purchase). For more information, contact your sales representative. The tab provides different views depending on which edition of BloodHound you're using. The **Summary View** is available in BloodHound Enterprise only, while the **Details View** is available in both BloodHound Enterprise and BloodHound Community Edition. The **Summary View** shows zone names and their hierarchy relative to other zones (the top zone is most critical), rule counts, and object counts. A view of the Zone Builder summary view The **Details View** displays all rules configured for the selected zone and the objects that they pull into the zone (organized by node type). Use the dropdown menus to filter the view by specific zones, identity providers, and services in your network environment. Select a rule or object to display more details in the right panel, including: * Rule definition and Cypher query * Object properties and relationships BloodHound displays objects for enabled rules only. To view objects related to a disabled rule, you must re-enable it. A view of the Zone Builder detail view ### Create a zone Applies to BloodHound Enterprise only Creating a zone involves configuring the zone details and defining a rule. See [Rules](/analyze-data/privilege-zones/rules) for more detailed information about defining rules. The content in this section provides a high-level overview only. In the left menu, click **Privilege Zones** > **Zones** > **Create Zone**. Enter all relevant information about the zone: | Field | Required? | Description | | -------------------- | :-------: | ------------------------------------------------------------------------------------------------------------------ | | Name | Yes | A unique name for the zone (e.g., Server Tier) | | Description | No | A brief description of the zone's purpose and scope (e.g., PCI assets) | | Enable Certification | No | An option to mandate [certification](/analyze-data/privilege-zones/certification) for all objects within this zone | | Enable Analysis | No | An option to include this zone in risk analysis and Attack Path Findings | | Apply Custom Glyph | No | An option to apply a custom glyph to visually distinguish objects within this zone on the **Explore** page | A view of the Zone Builder create zone page Click **Define Rule** to save your new Privilege Zone and continue on to define the objects to include in the zone. See [Rules](/analyze-data/privilege-zones/rules) for more detailed information about defining rules. The content in this section provides a high-level overview only. When defining a rule during the zone creation process, provide the following information: | Field | Required? | Description | | ----------------------- | :-------: | ------------------------------------------------------------------------------------------------------------------ | | Name | Yes | A unique name for the rule (e.g., PCI Assets) | | Description | No | A brief description of the rule's purpose and scope (e.g., PCI assets) | | Rule Type | Yes | The type of rule to use (e.g., Object ID or Cypher) | | Automatic Certification | No | An option to choose how BloodHound Enterprise [certifies](/analyze-data/privilege-zones/certification) new objects | A view of the Zone Builder define zone rule page Click **Save** to finish creating the zone. ### Edit a zone Editing options depend on which edition of BloodHound you're using. In BloodHound Enterprise, you can edit all zone properties. In BloodHound Community Edition, you can edit the default **Tier Zero** zone description. To edit a zone, follow these steps: 1. In the left menu, click **Privilege Zones**. 2. By default, the **Tier Zero** zone is pre-selected. To edit a different zone in BloodHound Enterprise, select the zone you want to edit. **Tier Zero** is the only available zone for BloodHound Community Edition. A view of the Zone Builder edit zone page in BloodHound Community Edition 3. Click **Edit Zone**. Modify one of the available fields. For example, you can modify the zone's name, description, [certification](/analyze-data/privilege-zones/certification) and analysis settings, and custom glyph. In BloodHound Community Edition, you can edit the default **Tier Zero** zone description only. You can also change the zone's hierarchical position by using the (vertical grip control) in the **Zone Order** panel to reorder it. Zone order is defined by privilege level, with the highest-privileged zone at the top. A view of the Zone Builder zone reorder control Click **Save Edits** to apply your changes. ### Delete a zone Applies to BloodHound Enterprise only You cannot delete the default **Tier Zero** zone, but you can edit its properties. Deleting a is irreversible. To delete an existing zone, follow these steps: Navigate to the **Zones** tab, select the zone you want to delete, and click **Edit Zone**. To delete the zone: 1. Click **Delete Zone** at the top of the page. 2. Confirm your action in the dialog. A view of the Zone Builder confirm zone delete dialog 3. Click **Confirm** to delete the zone. Zone deletion is not available in BloodHound Community Edition. # AzureHound Data Collection and Permissions Source: https://bloodhound.specterops.io/collect-data/azurehound-data-permissions Learn how AzureHound collects data and the permissions required. Applies to BloodHound Enterprise and CE AzureHound CE and AzureHound Enterprise collect the same data by utilizing the [AzureHound CE](https://github.com/SpecterOps/AzureHound) collection code, maintained by the BloodHound Enterprise Engineering team. AzureHound collects two categories of data: * **Entra ID** — users, groups, applications, service principals, devices, and role assignments via the [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/overview) * **Azure Resource Manager (ARM)** — subscriptions, management groups, resource groups, VMs, key vaults, and other Azure resources via the [ARM REST API](https://learn.microsoft.com/en-us/rest/api/azure/) This article details the least-privilege permissions required for each data type. The AzureHound service principal does not require the `Directory.Read.All` Microsoft Graph permission, the `Reader` Azure role on all subscriptions, and the `Directory Readers` Entra ID role. These built-in permissions and roles grant more access than AzureHound needs. You can follow the least-privilege guidance below instead. ## Entra ID Information about Entra ID objects and their relationships is necessary to identify attack paths within your Azure tenant. This information includes: * Tenant/organization information. * Users and their properties (account status, sign-in activity, on-premises sync status). * Groups, group memberships, and group owners. * Applications, application owners, and federated identity credentials. * Service principals, service principal owners, and app role assignments. * Devices and device registered owners. * Entra ID role definitions, active role assignments, and PIM-eligible role assignments. * Role management policy assignments. **Collection Method:** AzureHound collects this information via GET requests to the [Microsoft Graph API](https://learn.microsoft.com/en-us/graph/overview) (`https://graph.microsoft.com`), using both `v1.0` and `beta` endpoints. **Default Permissions:** A service principal with no explicit Microsoft Graph application permissions cannot read any Entra ID objects. Application permissions must be explicitly granted on the app registration and admin-consented before the service principal can access directory data. The default `User.Read` delegated permission granted to all new app registrations does not apply to AzureHound and can be removed. **Least-Privileged Option:** Grant the following granular Microsoft Graph **application** permissions: | Permission | Purpose | | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `User.Read.All` | Enumerate users and user properties | | `GroupMember.Read.All` | Enumerate groups, group owners, and group members | | `Application.Read.All` | Enumerate applications, app owners, federated identity credentials, service principals, service principal owners, and app role assignments | | `Device.Read.All` | Enumerate devices and device registered owners | | `Organization.Read.All` | Read tenant/organization information | | `RoleManagement.Read.Directory` | Enumerate Entra ID role definitions, role assignments, PIM eligible role assignments, and role management policy assignments | | `AdministrativeUnit.Read.All` | Populate Administrative Unit properties on AU-scoped role assignments. Collected by AzureHound, not processed by BloodHound yet. | | `AuditLog.Read.All` | (Optional) Collect `signInActivity` (last sign-in timestamps) on user objects. AzureHound gracefully degrades if this permission is missing. Requires a Microsoft Entra ID P1 or P2 license. | All permissions must be granted as **application** permissions (not delegated) and require admin consent. The `Directory Readers` Entra ID role is **not required** when using application-based authentication, as access is controlled entirely by Graph application permissions. Granting broad permissions (`Directory.Read.All`, `RoleManagement.Read.All`) instead of their granular equivalents can automatically cover any new permissions AzureHound requires in the future — for example, if AzureHound adds support for Defender role management, `RoleManagement.Read.All` will include that scope. The trade-off is between least-privilege and least-maintenance. The hierarchy below shows which granular permissions are covered by a broader one (✓ = least-privilege permission from the table above): * `Directory.Read.All` * `User.Read.All` ✓ * `Group.Read.All` * `GroupMember.Read.All` ✓ * `Application.Read.All` ✓ * `Device.Read.All` ✓ * `Organization.Read.All` ✓ * `AdministrativeUnit.Read.All` ✓ * `RoleManagement.Read.All` * `RoleManagement.Read.Directory` ✓ * `AuditLog.Read.All` ✓ ## Azure Resource Manager Information about Azure resources and their RBAC role assignments is necessary to identify attack paths that cross from Entra ID into Azure infrastructure. This information includes: * Subscriptions and tenants. * Management groups and their hierarchy (descendants). * Resource groups. * Virtual machines and VM scale sets. * Key vaults and key vault access policies. * Web apps and function apps. * Automation accounts. * Logic apps. * Container registries. * Managed clusters (AKS). * RBAC role assignments at every resource scope (management group, subscription, resource group, and individual resources). **Collection Method:** AzureHound collects this information via GET requests to the [ARM REST API](https://learn.microsoft.com/en-us/rest/api/azure/) (`https://management.azure.com`). **Default Permissions:** Azure RBAC is deny-by-default. A service principal with no explicit role assignments cannot read any Azure resources. All resource reads (resource groups, VMs, key vaults, role assignments, etc.) require an explicit RBAC role assignment. **Least-Privileged Option:** Create a custom Azure RBAC role with only the specific actions AzureHound requires, and assign it at the **Tenant Root Management Group** so that all actions propagate to every scope where AzureHound collects — management groups, subscriptions, resource groups, and individual resources. If assigned at a lower scope (e.g., a single subscription), AzureHound will not be able to read resources outside that scope and no error will be logged as the resources are invisible to the service principal. For step-by-step instructions on creating and assigning this as a custom role — either manually in the Azure portal or via script, see [Create and assign custom AzureHound Reader role in Azure Resource Manager](/install-data-collector/install-azurehound/azure-configuration#create-and-assign-custom-azurehound-reader-role-in-azure-resource-manager). Replace `` in the JSON definition with your [Tenant Root Management Group ID](https://learn.microsoft.com/en-us/azure/governance/management-groups/overview#root-management-group-for-each-directory) (this is your Entra ID tenant ID). Download
azurehound-reader-role.json Key vault access policies are extracted from the vault properties returned by `Microsoft.KeyVault/vaults/read` — this is a management plane operation only. AzureHound does not access the Key Vault data plane and cannot read secrets, keys, or certificates. It only reads vault metadata to determine *who has access* to the vault, not the vault contents themselves. # AzureHound Community Edition Source: https://bloodhound.specterops.io/collect-data/ce-collection/azurehound Applies to BloodHound Enterprise and CE AzureHound Community Edition is a Go binary that collects data from Entra ID (formerly known as AzureAD) and AzureRM via the Microsoft Graph and Azure REST APIs. It does not use any external dependencies and will run on any operating system. AzureHound CE can be obtained in a few ways: * From the BloodHound CE interface as a pre-compiled binary * ⚙️ → **Download Collectors**, and click the button **Download AzureHound** * From the [AzureHound releases](https://github.com/SpecterOps/AzureHound/releases/latest) as precompiled binaries for your OS/arch * Build it from source with the code on the [AzureHound repository](https://github.com/SpecterOps/AzureHound) 1. Clone the repository and \`cd\` into the directory 2. Run `go build .` 3. When built, you will have a binary called \`azurehound\` in the directory ## Collecting Data with AzureHound AzureHound supports several authentication flows for collecting information from Azure. You can supply a username/password combo, a JWT, a refresh token, a service principal secret, or service principal certificate. You can combine these various authentication methods with several collection scoping options. For example, to authenticate with a username/password and list all groups in a tenant: ```bash theme={null} ./azurehound -u "MattNelson@contoso.onmicrosoft.com" -p "MyVeryStrongPassword" list groups --tenant "contoso.onmicrosoft.com" ``` AzureHound will authenticate as that user and print all groups in the “Contoso” tenant. Or, you may want to supply a JWT and collect all users from the tenant instead. You do not need to supply a username or password when supplying a JWT: ```bash theme={null} ./azurehound -j "ey..." list users --tenant "contoso.onmicrosoft.com" ``` When collecting data for import into BloodHound, you must use the -o switch to instruct AzureHound to output to a file. For example, to list all available data in both Entra ID and AzureRM, you can do this: ```bash theme={null} ./azurehound -u "MattNelson@contoso.onmicrosoft.com" -p "MyVeryStrongPassword" list --tenant "contoso.onmicrosoft.com" -o output.json ``` ## Dealing with Multi-Factor Auth and Conditional Access Policies If a user has MFA or CAP restrictions applied to them, you will not be able to authenticate with just a username and password with AzureHound. In this situation, you can acquire a refresh token for the user and supply the refresh token to AzureHound. The most straight-forward way to accomplish this is to use the device code flow. In this example I will show you how to perform this flow using PowerShell, but this example can be very easily ported to any language, as we are simply making calls to Azure APIs. Open a PowerShell window on any system and paste the following: ```powershell theme={null} $body = @{ "client_id" = "1950a258-227b-4e31-a9cf-717495945fc2" "resource" = "https://graph.microsoft.com" } $UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36" $Headers=@{} $Headers["User-Agent"] = $UserAgent $authResponse = Invoke-RestMethod ` -UseBasicParsing ` -Method Post ` -Uri "https://login.microsoftonline.com/common/oauth2/devicecode?api-version=1.0" ` -Headers $Headers ` -Body $body $authResponse ``` The output will contain a \`user\_code\` and \`device\_code\`. Now, open a browser where your Entra ID user either already logged on or can log on to Azure. In this browser, navigate to [https://microsoft.com/devicelogin](https://microsoft.com/devicelogin) Enter the code you generated from the above PowerShell script. Follow the steps in the browser to authenticate as the Entra ID user and approve the device code flow request. When done, the browser page should display a message similar to “You have signed in to the Microsoft Azure PowerShell application on your device. You may now close this window.” Now go back to your original PowerShell window and paste this: ```powershell theme={null} $body=@{ "client_id" = "1950a258-227b-4e31-a9cf-717495945fc2" "grant_type" = "urn:ietf:params:oauth:grant-type:device_code" "code" = $authResponse.device_code } $Tokens = Invoke-RestMethod ` -UseBasicParsing ` -Method Post ` -Uri "https://login.microsoftonline.com/Common/oauth2/token?api-version=1.0" ` -Headers $Headers ` -Body $body $Tokens ``` The output will include several tokens including a \`refresh\_token\`. It will start with characters similar to “0.ARwA6Wg…”. Now you are ready to run AzureHound! Take the refresh token and supply it to AzureHound using the `-r` switch: ```bash theme={null} ./azurehound -r "0.ARwA6Wg..." list --tenant "contoso.onmicrosoft.com" -o output.json ``` This will attempt to list all possible data from that particular tenant, but you can ALSO use that same refresh token to target any other tenant your user has access to! # AzureHound Community Edition Flags Source: https://bloodhound.specterops.io/collect-data/ce-collection/azurehound-flags Applies to BloodHound Enterprise and CE AzureHound Community Edition has several optional flags that let you control scan scope, performance, output, and other behaviors. ## Enumeration Commands and Options The `list` command collects all supported Entra ID and Azure Resource Manager data. Add a collection option directly after `list` to limit the scope. For example, use `azurehound list az-ad` to collect all supported Entra ID data and `azurehound list apps` to collect only application registrations within Entra ID. Run `azurehound list -h` to see all available collection commands. The following tree logically groups every collection option by resource and abuse type. This grouping does not mean an aggregate command emits every option shown beneath it. All options are direct children of `list`; for example, run `azurehound list apps`, not `azurehound list az-ad apps`. ```text expandable theme={null} list # Collects both Entra ID and Azure Resource Manager data ├── az-ad # Collects all supported Entra ID data │ ├── apps # Collects application registrations │ │ ├── app-owners # Collects explicitly assigned owners of application registrations │ │ └── appfics # Collects federated identity credentials for application registrations │ ├── devices # Collects devices │ ├── groups # Collects security-enabled groups │ │ ├── group-members # Collects group memberships │ │ └── group-owners # Collects explicitly assigned owners of groups │ ├── roles # Collects Entra ID directory role definitions │ │ └── role-assignments # Collects Entra ID directory role assignments │ ├── service-principals # Collects service principals │ │ ├── app-role-assignments # Collects principals assigned application roles exposed by service principals │ │ └── service-principal-owners # Collects explicitly assigned owners of service principals │ ├── tenants # Collects the target tenant and other accessible tenants │ ├── unified-role-assignment-policies # Collects tenant-wide Entra PIM directory role policy assignments and activation requirements │ ├── unified-role-eligibility-schedule-instances # Collects Entra PIM directory role eligibility instances │ └── users # Collects users, including guest users in the target tenant └── az-rm # Runs the aggregate Azure Resource Manager collection ├── management-groups # Collects management groups │ ├── management-group-descendants # Collects descendant management groups and subscriptions │ └── management-group-role-assignments # Collects role assignments at management group scope │ ├── management-group-contributors # Collects principals with the Contributor role on management groups │ ├── management-group-owners # Collects principals with the Owner role on management groups │ └── management-group-user-access-admins # Collects principals with the User Access Administrator role on management groups └── subscriptions # Collects subscriptions ├── subscription-role-assignments # Collects role assignments at subscription scope │ ├── subscription-contributors # Collects principals with the Contributor role on subscriptions │ ├── subscription-owners # Collects principals with the Owner role on subscriptions │ └── subscription-user-access-admins # Collects principals with the User Access Administrator role on subscriptions ├── resource-groups # Collects resource groups │ └── resource-group-role-assignments # Collects role assignments that apply to resource groups │ ├── resource-group-contributors # Collects principals with the Contributor role on resource groups │ ├── resource-group-owners # Collects principals with the Owner role on resource groups │ └── resource-group-user-access-admins # Collects principals with the User Access Administrator role on resource groups ├── automation-accounts # Collects Automation accounts │ └── automation-account-role-assignments # Collects role assignments that apply to Automation accounts ├── container-registries # Collects container registries │ └── container-registry-role-assignments # Collects role assignments that apply to container registries ├── function-apps # Collects function apps │ └── function-app-role-assignments # Collects role assignments that apply to function apps ├── key-vaults # Collects key vaults │ ├── key-vault-access-policies # Collects legacy access policies for key vaults │ └── key-vault-role-assignments # Collects role assignments that apply to key vaults │ ├── key-vault-contributors # Collects principals with the Contributor role on key vaults │ ├── key-vault-kvcontributors # Collects principals with the Key Vault Contributor role on key vaults │ ├── key-vault-owners # Collects principals with the Owner role on key vaults │ └── key-vault-user-access-admins # Collects principals with the User Access Administrator role on key vaults ├── logic-apps # Collects logic apps │ └── logic-app-role-assignments # Collects role assignments that apply to logic apps ├── managed-clusters # Collects Azure Kubernetes Service managed clusters │ └── managed-cluster-role-assignments # Collects role assignments that apply to managed clusters ├── storage-accounts # Collects storage accounts │ ├── storage-account-role-assignments # Collects role assignments that apply to storage accounts │ └── storage-containers # Collects blob containers in storage accounts ├── virtual-machines # Collects virtual machines │ └── virtual-machine-role-assignments # Collects role assignments that apply to virtual machines │ ├── virtual-machine-admin-logins # Collects principals with the Virtual Machine Administrator Login role │ ├── virtual-machine-avere-contributors # Collects principals with the Avere Contributor role on virtual machines │ ├── virtual-machine-contributors # Collects principals with the Contributor role on virtual machines │ ├── virtual-machine-owners # Collects principals with the Owner role on virtual machines │ ├── virtual-machine-user-access-admins # Collects principals with the User Access Administrator role on virtual machines │ └── virtual-machine-vmcontributors # Collects principals with the Virtual Machine Contributor role ├── vm-scale-sets # Collects virtual machine scale sets │ └── vm-scale-set-role-assignments # Collects role assignments that apply to virtual machine scale sets └── web-apps # Collects web apps └── web-app-role-assignments # Collects role assignments that apply to web apps ``` ## Authentication Flags AzureHound supports several authentication options. You can control how AzureHound authenticates by using command-line flags or the configuration file. Some flags should always be used together and are presented here in the context of their authentication use cases. For authentication flags, AzureHound reads file contents only from paths passed to `--cert` and `--key`. Every other authentication flag takes a literal value and does not interpret that value as a file path. ### Authenticating with Username and Password * `-u` or `--username` — The Entra ID user's user principal name (UPN), in `username@domain.com` format. * `-p` or `--password` — The user's clear-text password value. * `-t` or `--tenant` — The directory tenant value, in GUID or friendly-name format. Example: ```bash theme={null} ./azurehound -u "MattNelson@contoso.onmicrosoft.com" -p "MyVerySecurePassword123" --tenant "contoso.onmicrosoft.com" list ``` You can omit the password from the command line, and AzureHound will interactively prompt you for it instead. ### Authenticating with Service Principal Secret * `-a` or `--app` — The application (client) ID value assigned when the app was registered. * `-s` or `--secret` — The client secret value generated for the app registration. * `-t` or `--tenant` — The directory tenant value, in GUID or friendly-name format. Example: ```bash theme={null} ./azurehound -a "6b5adee8-0d36-45b6-b393-8f29ae8a8cc8" -s "MyVerySecureClientSecret123" --tenant "contoso.onmicrosoft.com" list ``` ### Authenticating with Service Principal Certificate * `-a` or `--app` — The application (client) ID value assigned when the app was registered. * `--cert` — The path to the certificate uploaded for the app registration, in PEM format. AzureHound reads the certificate from this file. * `-k` or `--key` — The path to the certificate's private key file, in PEM format. AzureHound reads the private key from this file. * `--keypass` (optional) — The literal passphrase value to use if the private key is encrypted. * `-t` or `--tenant` — The directory tenant value, in GUID or friendly-name format. Example: ```bash theme={null} ./azurehound -a "6b5adee8-0d36-45b6-b393-8f29ae8a8cc8" --cert ./certificate.pem --key ./key.pem --tenant "contoso.onmicrosoft.com" list ``` ### Authenticating with Azure Managed Identity * `--managed-identity` — Use Azure Managed Identity to authenticate. Use this when running AzureHound on an Azure resource, such as a virtual machine or App Service, with a managed identity assigned. * `--managed-identity-client-id` (optional) — The client ID value of a user-assigned managed identity. If not provided, AzureHound uses the system-assigned identity. * `-t` or `--tenant` — The directory tenant value, in GUID or friendly-name format. Example (system-assigned): ```bash theme={null} ./azurehound --managed-identity --tenant "contoso.onmicrosoft.com" list ``` Example (user-assigned): ```bash theme={null} ./azurehound --managed-identity --managed-identity-client-id "6b5adee8-0d36-45b6-b393-8f29ae8a8cc8" --tenant "contoso.onmicrosoft.com" list ``` ### Authenticating with a JWT * `-j` or `--jwt` — The literal value of a Microsoft Graph or Azure Resource Manager scoped JWT. Example: ```bash theme={null} ./azurehound -j "ey..." list az-ad ``` ### Authenticating with a Refresh Token * `-r` or `--refresh-token` — The literal refresh token value. AzureHound exchanges it for an appropriately scoped JWT when accessing the Microsoft Graph and Azure Resource Manager APIs. * `-t` or `--tenant` — The directory tenant value, in GUID or friendly-name format. Example: ```bash theme={null} ./azurehound -r "0.ARwA6Wg..." --tenant "contoso.onmicrosoft.com" list ``` ## Additional Scoping and Output Flags * `-b` - Filter by one or more subscription IDs. AzureHound will automatically dedupe this list for you. * `-m` - Filter by one or more management group IDs. AzureHound will automatically dedupe all descendant management groups and subscriptions for you. * `-o` or `--output` - Instructs AzureHound to write its output to a specified file name. Accepts either a bare filename (`azurehound.json`, written to the current working directory) or an absolute path (`~/azurehound.json`). * `--log-file` - Write logs to the specified file. Accepts either a bare filename (`log.txt`, written to the current working directory) or an absolute path (`~/azurehound.log`). * `--json` - Emit logs as structured JSON instead of the default line-based format. Requires `--log-file` to be set. * `-v` or `--verbosity` - AzureHound verbosity level (defaults to 0), a higher value gives more verbosity \[Min: -1, Max: 2] * `--version` - Print the AzureHound version and exit. ## Custom User-Agent `-U` or `--user-agent` - Set a custom User-Agent header for all HTTP requests. This can be useful for evasion purposes or for debugging and identification. If not specified, AzureHound uses the default User-Agent value. Example: ```bash theme={null} ./azurehound list --tenant "contoso.onmicrosoft.com" -u "MattNelson@contoso.onmicrosoft.com" -p "MyVerySecurePassword123" --user-agent "MyCustomAgent/1.0" ``` # Create a gMSA for Use With SharpHound Community Edition Source: https://bloodhound.specterops.io/collect-data/ce-collection/create-gmsa-community-edition Applies to BloodHound Enterprise and CE This page describes how to configure and run the SharpHound Community Edition collection tool using an Active Directory gMSA. ## Overview of gMSAs Group Managed Service Accounts (gMSA) are managed domain accounts that provide automatic password management, simplified service principal name (SPN) management, and the ability to delegate the management to other objects. Detailed software requirements from Microsoft are available [here](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/hh831782\(v=ws.11\)#software-requirements). Microsoft gMSA documentation is available [here](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/hh831782\(v=ws.11\)). ## Create a gMSA account To create a gMSA account, start by preparing the domain. 1. Log into a domain controller within the domain you want to create a gMSA. 2. To validate whether the domain has a KDS Root Key configured, run: ``` Get-KdsRootKey ``` If there's no result returned, the KDS Root Key has not been configured in the domain. Continue on to step 3. If there is a result returned, the KDS Root Key has already been configured in the domain. Skip step 3 and move on to [Create the gMSA and password read group](#create-the-gmsa-and-password-read-group). 3. Create the KDS Root Key. For a production environment, run: ``` Add-KdsRootKey -EffectiveImmediately ``` For a test environment, make the key available for immediate use by running: ``` Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10)) ``` ## Create the gMSA and password read group Perform these steps from/against a writeable Domain Controller. 1. Create a gMSA password read group for computers that should have access to the gMSA password. Browse to the desired location in Users and Computers and create the group. Alternatively, use this template to create the group using PowerShell: ```json theme={null} $gmsaName = "t0_gMSA_SHS" # Name of the gMSA $pwdReadOUDN = "<DISGINGUISHED_NAME>" # Distinguished Name of OU to create the password read group in New-ADGroup ` -Name "$($gmsaName)_pwdRead" ` -GroupScope Global ` -GroupCategory Security ` -Path $pwdReadOUDN ` -Description "This group grants the rights to retrieve the password of the BloodHound data collector (SharpHound) gMSA '$gmsaName'." ` -PassThru ``` 2. Add the SharpHound server that performs the Sharphound collections as a member of the gMSA password read group. This allows it to access the password of the gMSA and run the service. Add the computer to the group in Users and Computers. Alternatively, use this template to add group membership using PowerShell: ```json theme={null} $gmsaName = "t0_gMSA_SHS" # Name of the gMSA $shServerDN = "<DISGINGUISHED_NAME>" # Distinguished Name of the SharpHound Enterprise server Add-ADGroupMember ` -Identity "$($gmsaName)_pwdRead" ` -Members $shServerDN ` -PassThru ``` When viewing the changes on a Windows server with the GUI enabled, you can see the OUs and the t0\_gMSA\_SHS\_pwdRead group you created. 3. Create the gMSA and allow the password read group to retrieve its password. On a Domain Controller, use this template to create the gMSA and set the retrieve right using PowerShell: ```json theme={null} $gmsaName = "t0_gMSA_SHS" # Name of the gMSA $gmsaOUDN = "<DISGINGUISHED_NAME>" # Distinguished Name of OU to create the gMSA in New-ADServiceAccount -Name $gmsaName ` -Description "SharpHound service account for BloodHound" ` -DNSHostName "$($gmsaName).$((Get-ADDomain).DNSRoot)" ` -ManagedPasswordIntervalInDays 32 ` -PrincipalsAllowedToRetrieveManagedPassword "$($gmsaName)_pwdRead" ` -Enabled $True ` -AccountNotDelegated $True ` -KerberosEncryptionType AES128,AES256 ` -Path $gmsaOUDN ` -PassThru ``` If you receive the error `"_New-ADServiceAccount : Key does not exist_"`, try again in 10 hours. This allows all Domain Controllers to converge AD replication of the KDS root key. ## Prepare the SharpHound server 1. Restart the SharpHound Enterprise server so that the server's membership of the \`pwdRead\` group takes effect. 2. Grant the gMSA the "Log on as a service" User Rights Assignment on the SharpHound server. This can be done through \`secpol.msc\` or policy deployment methods like a GPO. 3. (Optional) Test that the SharpHound server can retrieve the gMSA password. See [Test the gMSA](#test-the-gmsa). ## Test the gMSA Optionally test the gMSA server to make sure that the gMSA is working. 1. Check the status of the RSAT PowerShell module. On the SharpHound Enterprise server, open a PowerShell as an Administrator and run: ```json theme={null} Get-WindowsCapability -Name RSAT* ``` If the Install State shows "Installed", skip to step 2, otherwise run: ``` Get-WindowsCapability -Name RSAT* -Online | Add-WindowsCapability -Online ``` 2. In the elevated PowerShell, test that the SharpHound server can retrieve the gMSA password by running: ```json theme={null} $gmsaName = "t0_gMSA_SHS" # Name of the gMSA Test-ADServiceAccount -Identity $gmsaName ``` The test is successful if the command responds with `True`. ## Configure User Rights As SharpHound is launched with a PowerShell script instead of running as a service, you need to grant the gMSA account the **Log on as a batch job** User Right instead of the **Log on as Service** User Right. Do this with the Local Security Policy or Group Policy. ### Configure user permissions The Active Directory details collected by SharpHound depend on the permissions that the user running SharpHound has within the Domain. A regular, non-privileged user can run SharpHound and collect a significant amount of information from Active Directory, but some local system data requires additional permissions on the in-scope computers. See [SharpHound Data and Permissions](/collect-data/sharphound-data-permissions) for an overview. Grant collection permissions directly to the SharpHound gMSA account. There are two recommended paths: * **Method 1**: Explicitly make the SharpHound gMSA account a member of the local `Administrators` group on in-scope computers. * **Method 2**: Grant the SharpHound gMSA account the least-privilege permission set described in [Least-Privileged Collection](/collect-data/enterprise-collection/least-privileged-collection). The SharpHound collection service account does not require `Domain Admin` membership. `Domain Admins` receives local administrator access on domain-joined computers by default, but that is implicit access and the `Domain Admins` membership gives many other unnecessary permissions. Use explicit local administrator assignment or least-privilege delegation instead. #### Implement gMSA account protections After granting collection permissions, consider additional protections for the gMSA account. We highly recommend membership in the `Protected Users` group. See [SharpHound Enterprise Service Hardening](/manage-bloodhound/securing-bloodhound-and-collectors/sharphound-hardening) to learn about protections you can implement to prevent the SharpHound gMSA account from being compromised and leveraged by an attacker. ## Create a SharpHound PowerShell script to run as a scheduled task Create a PowerShell script to run SharpHound with a scheduled task. ### Create a script To create the script on the server where the scheduled task should run: 1. Create these three directories: * `C:\Program Files\SharpHound` * `C:\Program Files\SharpHound\Results`, and grant the gMSA service account Modify permissions on files within. * `C:\Program Files\SharpHound\Logs`, and grant the gMSA service account Modify permissions on files within. Run the code block below, or create the directories and ACLs manually. The code block creates `C:\Program Files\SharpHound` implicitly as a parent of the children. ```powershell theme={null} $account = "$($env:USERDOMAIN)\t0_gMSA_SHS$" $paths = @( "C:\Program Files\SharpHound\Results", "C:\Program Files\SharpHound\Logs" ) foreach ($path in $paths) { New-Item $path -ItemType Directory -Force $acl = Get-Acl -Path $path $rule = New-Object System.Security.AccessControl.FileSystemAccessRule( $account, [System.Security.AccessControl.FileSystemRights]::Modify, [System.Security.AccessControl.InheritanceFlags]"ContainerInherit,ObjectInherit", [System.Security.AccessControl.PropagationFlags]::None, [System.Security.AccessControl.AccessControlType]::Allow ) $acl.AddAccessRule($rule) Set-Acl -Path $path -AclObject $acl } ``` 2. Copy the SharpHound CE executable to the `C:\Program Files\SharpHound` directory. The SharpHound CE executable may need to be allowlisted or transferred as a password-protected zip file so that Microsoft Defender doesn't block it during the file transfer. 3. Create the PowerShell script `C:\Program Files\SharpHound\SharpHound_Collection.ps1` with the contents from the code block below. The PowerShell script contains: * The relative path to the SharpHound executable * The command line arguments that you want to pass to SharpHound * Redirection of the console output to a log file * Deletion of results that are older than 2 months ```powershell theme={null} $Results = ".\Results" $Logs = ".\Logs" $FileDateStamp = Get-Date -Format FileDateTime & ".\SharpHound.exe" --collectionmethods all --outputdirectory $Results *> "$Logs\$FileDateStamp.log" #Delete results older than 2 months Get-ChildItem $Results -Recurse -Force -ea 0 | ? {!$_.PsIsContainer -and $_.LastWriteTime -lt (Get-Date).AddDays(-60)} | ForEach-Object { $_ | Remove-Item -Force $_.FullName | Out-File "$Logs\$FileDateStamp-deletedlog.txt" } ``` The resulting directory structure should resemble: ### Create a scheduled task To create the scheduled task, choose the PowerShell method or the GUI method. The PowerShell method is the easiest way to create the scheduled task. To create the scheduled task with PowerShell: 1. Run these PowerShell commands, modifying files names and domain names as necessary: ```powershell theme={null} $arg = '-ExecutionPolicy ByPass -NoProfile -File ".\SharpHound_Collection.ps1"' $ta = New-ScheduledTaskAction -Execute C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Argument $arg -WorkingDirectory "C:\Program Files\SharpHound" $tt = New-ScheduledTaskTrigger -At 9:00 -Weekly -DaysOfWeek Monday $ap = New-ScheduledTaskPrincipal -UserID "$($env:USERDOMAIN)\t0_gMSA_SHS$" -LogonType Password -RunLevel Highest Register-ScheduledTask SharpHoundCollection -Action $ta -Trigger $tt -Principal $ap ``` 2. After refreshing the Scheduled Tasks MMC, you should see the newly created scheduled task that runs as the gMSA account. To create the scheduled task with the GUI: 1. Open the Scheduled Tasks MSC on the SharpHound server and create a scheduled task. In the example shown below, the scheduled task name is SharpHound. 2. Configure the scheduled task to run on the desired schedule. 3. Configure the scheduled task with this **Action**: * **Program / script**: `C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe` * **Arguments**: `-ExecutionPolicy ByPass -NoProfile -File ".\SharpHound_Collection.ps1"` * **Start in**: `C:\Program Files\SharpHound` 4. Click **OK** on the **Edit Action**. 5. Leave the default run-account selection for now — you'll change it to the gMSA in a later step. 6. Click **OK** on the scheduled task to complete the creation. 7. Provide the password for the standard user account when prompted and click **OK**. At this point you should see the scheduled task setup to run using the user account you previously configured. 8. Modify the scheduled task to run as the gMSA account with PowerShell: * Option A: `schtasks` ```powershell theme={null} schtasks /change /TN \SharpHound /RU "$($env:USERDOMAIN)\t0_gMSA_SHS$" /RP ``` * Option B: `New-ScheduledTaskPrincipal` ```powershell theme={null} $principal = New-ScheduledTaskPrincipal -UserID "$($env:USERDOMAIN)\t0_gMSA_SHS$" -LogonType Password -RunLevel Highest Set-ScheduledTask -TaskName SharpHound -Principal $principal ``` 9. You should see a message stating that command was successful. If you open the scheduled task you should see that the account it will run as is the gMSA account. ### Test the SharpHound scheduled task To test the scheduled task running as the gMSA account, right click on the scheduled task and click **Run**. When completed, a zip file should appear in `C:\Program Files\SharpHound\Results\`, and a log file in `C:\Program Files\SharpHound\Logs\`. *** Thank you to [@robertstrom](https://github.com/robertstrom) for your contribution and permission in modifying your [original docs](https://github.com/robertstrom/SharphoundgMSA/tree/main) for inclusion in BloodHound's documentation for other users! # BloodHound CE Collection Source: https://bloodhound.specterops.io/collect-data/ce-collection/overview Learn about attack path data collection in BloodHound Community Edition. # SharpHound Community Edition Source: https://bloodhound.specterops.io/collect-data/ce-collection/sharphound SharpHound Community Edition (CE) is the official data collector for BloodHound CE. It is written in C# and uses native Windows API functions and LDAP namespace functions to collect data from domain controllers and domain-joined Windows systems. Applies to BloodHound Enterprise and CE SharpHound CE can be obtained in a few ways: * From the BloodHound CE interface as a pre-compiled binary * ⚙️ → **Download Collectors**, and click the button **Download SharpHound** * From the [SharpHound releases](https://github.com/SpecterOps/SharpHound/releases/latest) as precompiled binaries * Build it from source with the code on the [SharpHound repository](https://github.com/SpecterOps/SharpHound) ## Basic Usage You can collect plenty of data with SharpHound CE by simply running the binary itself with no flags set: ``` C:\> SharpHound.exe ``` SharpHound CE will automatically determine what domain your current user belongs to, find a domain controller for that domain, and start the “default” collection method. The default collection method will collect the following pieces of information from the domain controller: * Security group memberships * Domain trusts * Abusable rights on Active Directory objects * Group Policy links * OU tree structure * Several properties from computer, group and user objects * SQL admin links Additionally, SharpHound CE will attempt to collect the following information from each domain-joined Windows computer: * The members of the local administrators, remote desktop, distributed COM, and remote management groups * Active sessions, which SharpHound CE will attempt to correlate to systems where users are interactively logged on When finished, SharpHound CE will create several JSON files and place them into a zip file. Drag and drop that zip file into the BloodHound GUI, and the interface will merge the data into the database. ## The Session Loop Collection Method BloodHound uses graph theory to find attack paths in Active Directory, and the more data you have, the more likely you are to find and execute attack paths successfully. Much of the data you initially collect with SharpHound CE will not likely change or require updating over the course of a typical red team assessment - security group memberships, Active Directory permissions, and Group Policy links change relatively rarely. That data can be collected one time, and not again. User sessions are a bit different because users, especially privileged users, log on and off different systems daily. How many systems does a typical help desk user or server admin log into on any given day? SharpHound CE’s Session Loop collection method makes this very easy: ``` C:\> SharpHound.exe --CollectionMethods Session --Loop ``` This will run SharpHound CE’s session collection method for 2 hours, generating a zip file after each loop ends. When done, collect all the zip files and drag and drop them into the BloodHound GUI. If you want to specify a different loop time, use the --Loopduration flag with the HH:MM:SS format to specify how long you want SharpHound CE to perform looped session collection. For example, if you want SharpHound CE to perform looped session collection for 3 hours, 9 minutes, and 41 seconds: ``` C:\> SharpHound.exe --CollectionMethods Session --Loop --Loopduration 03:09:41 ``` ## Running SharpHound CE from a Non Domain-Joined System While not an officially supported collection method, and not a collection method we recommend you do, it is possible to collect data for a domain from a system that is not joined to that domain. To do so, carefully follow these steps: 1. Configure your system DNS server to be the IP address of a domain controller in the target domain. 2. Spawn a CMD shell as a user in that domain using \`runas\` its `/netonly` flag. You will be prompted to enter a password. Enter the password and hit enter. ``` C:\> runas /netonly /user:CONTOSO\Jeff.Dimmock cmd.exe ``` 3. A new CMD window will appear. If you type \`whoami\`, you will not see the name of the user you’re impersonating. This is because of the `/netonly` flag: the instance of CMD will only authenticate as that user when you authenticate to other systems over the network, but you are still the same user you were before when authenticating locally. 4. Verify you’ve got valid domain authentiation by using the \`net\` binary. If you can see the SYSVOL and NETLOGON folders, you’re good. ``` C:\> net view \\\contoso\ ``` 5. Run SharpHound CE, using the `-d` flag to specify the AD domain you want to collect information from. You can also use any other flags you wish. ``` C:\> SharpHound.exe -d contoso.local ``` ## Building SharpHound CE from Source SharpHound CE is written using C# 9.0 features. To easily compile this project, use Visual Studio 2019. If you want to compile on previous versions of Visual Studio, you can install the Microsoft.Net.Compilers nuget package. Building the project will generate an executable and a PowerShell script that encapsulates the executable. All dependencies are rolled into the binary. ## SharpHound CE vs. Antivirus Many anti-virus engines have signatures for SharpHound CE. You may even find that Chrome or other browsers will warn you against downloading SharpHound CE, saying the binary is malicious. This isn’t completely unexpected, as BloodHound is primarily a tool used by penetration testers and red teamers to find attack paths in Active Directory. While BloodHound has plenty of defensive value, antivirus and browser vendors continue to flag SharpHound CE as malicious. If you are on the red team side, you can employ AV bypass strategies to avoid getting caught by AV. One of the best things you can do is stay completely off-disk when running SharpHound CE. Many command-and-control tools have in-memory .net assembly execution capabilities, such as Cobalt Strike’s **execute-assembly** and Covenant’s **assembly** commands. Using these commands will keep SharpHound CE totally off-disk when run on your target, which will go a very long way toward evading basic AV signatures. If you are on the blue team side, we recommend you use BloodHound Enterprise which is built for defenders and has a trusted and signed SharpHound Enterprise binary that does not get flagged by antivirus. You can also use the same AV bypass techniques used by the red team, or you can request an exception for the SharpHound CE binary itself or possibly a folder that you run SharpHound CE out of. Be aware, though, that AV-excluded folders and files can commonly be enumerated by low-privilege users running on the same system, so try to be as specific as possible with your allowlist exceptions. Finally, remember that SharpHound CE is free and *open source*. You can build SharpHound CE from the source and apply your own obfuscation techniques to the source code itself during that build process. Several resources are available to help get started here: * [https://docs.microsoft.com/en-us/visualstudio/ide/dotfuscator/?view=vs-2019](https://docs.microsoft.com/en-us/visualstudio/ide/dotfuscator/?view=vs-2019) * [https://github.com/TheWover/donut](https://github.com/TheWover/donut) * [https://blog.xpnsec.com/building-modifying-packing-devops/](https://blog.xpnsec.com/building-modifying-packing-devops/) # SharpHound Community Edition Flags Source: https://bloodhound.specterops.io/collect-data/ce-collection/sharphound-flags Applies to BloodHound Enterprise and CE SharpHound Community Edition has several optional flags that let you control scan scope, performance, output, and other behaviors. ## Enumeration Options ### CollectionMethods or 'c' This tells SharpHound what kind of data you want to collect. These are the most common options you’ll likely use: * **Default:** You can specify default collection, or don’t use the CollectionMethods option and this is what SharpHound will do. Default collection includes Active Directory security group membership, domain trusts, abusable permissions on AD objects (incl. ADCS objects), OU tree structure, Group Policy links, the most relevant AD object properties, local groups from domain-joined Windows systems, and user sessions. * **All:** Performs all collection methods. * **DCOnly:** Collects data ONLY from the domain controller, will not touch other domain-joined Windows systems. Collects AD security group memberships, domain trusts, abusable permissions on AD objects (incl. ADCS objects), OU tree structure, Group Policy links, the most relevant AD object properties, and will attempt to correlate Group Policy-enforced local groups to affected computers. * **ComputerOnly:** Collects user sessions (*Session*), local groups (*LocalGroup*), and User Rights Assignment (*UserRights*) from domain-joined Windows systems. Additionally, CA registry (*CARegistry*) data and DC registry (*DCRegistry*) data is collected. Will NOT collect the data collected with the DCOnly collection method. * **Session:** Just does user session collection. You will likely couple this with the `--Loop` option. See SharpHound examples below for more info on that. * **LoggedOn:** Does session collection using the privileged collection method. Use this if you are running as a user with local admin rights on lots of systems for the best user session data. Here are the less common CollectionMethods and what they do: * **Group:** Just collect security group memberships from Active Directory * **ACL:** Just collect abusable permissions on objects in Active Directory * **GPOLocalGroup:** Just attempt GPO to computer correlation to determine members of the relevant local groups on each computer in the domain. Doesn’t actually touch domain-joined systems, just gets info from domain controllers * **Trusts:** Just collect domain trusts * **Container:** Just collect the OU tree structure and Group Policy links * **LocalGroup:** Just collect the members of all interesting local groups on each domain-joined computer. Equivalent for *LocalAdmin* + *RDP* + *DCOM* + *PSRemote* * **LocalAdmin:** Just collect the members of the local Administrators group on each domain-joined computer * **RDP:** Just collect the members of the Remote Desktop Users group on each domain-joined computer * **DCOM:** Just collect the members of the Distributed COM Users group on each domain-joined computer * **PSRemote:** Just collect the members of the Remote Management group on each domain-joined computer * **ObjectProps** - Performs Object Properties collection for properties such as LastLogon or PwdLastSet * **SPNTargets** - Just collect Service Principal Name (SPN) target information from Active Directory objects. This helps identify service relationships such as MSSQL service accounts and related target systems. * **UserRights** - Just collect User Rights Assignment from domain computers (needs admin) * **CARegistry** - Just collect ADCS properties from registry of Certificate Authority servers * **DCRegistry** - Just collect properties from registry of Domain Controller servers * **CertServices** - Just collect ADCS objects from Certificate Services * **WebClientService** - Just check whether the WebClient service is running on domain-joined Windows systems. This data supports NTLM relay-related attack paths. * **LdapServices** - Just collect LDAP service configuration information from domain controllers, including LDAP/LDAPS authentication behavior. This data supports NTLM relay-related attack paths. * **SmbInfo** - Just collect SMB configuration information from domain-joined Windows systems, such as SMB service settings. This data supports NTLM relay-related attack paths. * **NTLMRegistry** - Just collect NTLM-related registry values from domain-joined Windows systems. This data supports NTLM relay-related attack paths. For example, to collect sessions: ``` C:\> SharpHound.exe --CollectionMethods session ``` Collection methods visualization: Image credit: [https://twitter.com/SadProcessor](https://twitter.com/SadProcessor) ### Domain or 'd' Tell SharpHound which Active Directory domain you want to gather information from. Importantly, you must be able to resolve DNS in that domain for SharpHound to work correctly. For example, to collect data from the \`contoso.local\` domain: ``` C:\> SharpHound.exe --Domain contoso.local ``` ### SearchForest or 's' This flag would instruct SharpHound to automatically collect data from all domains in your current forest. ### Stealth Perform “stealth” data collection. This switch modifies your data collection method. For example, if you want to perform user session collection, but only touch systems that are the most likely to have user session data: ``` C:\> SharpHound.exe --CollectionMethods Session --Stealth ``` ### ComputerFile Load a list of computer names or IP addresses for SharpHound to collect information from. The file should be line-separated. ### DistinguishedName Base DistinguishedName to start search at. Use this to limit your search. Equivalent to the old `--OU` and '--SearchBase' option. ``` C:\> SharpHound.exe --DistinguishedName "OU=New York,DC=Contoso,DC=Local" ``` ### LDAPFilter or 'f' Instruct SharpHound to only collect information from principals that match a given LDAP filter. For example, to only gather abusable ACEs on a user with a certain display name, run this: ``` C:\> SharpHound.exe --LDAPFilter "(displayName=John Smith)" ``` ### ExcludeDCs Instruct SharpHound to not touch domain controllers. By not touching domain controllers, you will not be able to collect anything specified in the `DCOnly_`\_ collection method, but you will also likely avoid detection by e.g., Microsoft ATA/ATP. ``` C:\> SharpHound.exe -d contoso.local --ExcludeDCs ``` ### RealDNSName In some networks, DNS is not controlled by Active Directory, or is otherwise not synchronized to Active Directory. This causes issues when a computer joined to AD has an AD FQDN of COMPUTER.CONTOSO.LOCAL, but also has a DNS FQDN of, for example, COMPUTER.COMPANY.COM. You can help SharpHound find systems in DNS by providing the latter DNS suffix, like this: ``` C:\> SharpHound.exe --RealDNSName COMPANY.COM ``` ### OverrideUserName When running SharpHound from a \`runas /netonly\`-spawned command shell, you may need to let SharpHound know what username you are authenticating to other systems as. ### CollectAllProperties Collect every LDAP property where the value is a string from each enumerated Active Directory object. ## Output Options ### OutputDirectory By default, SharpHound will output zipped JSON files to the directory SharpHound was launched from. You can specify a different folder for SharpHound to write files to. For example, to instruct SharpHound to write output to C:temp: ``` C:\> SharpHound.exe --OutputDirectory C:\temp\ ``` ### OutputPrefix Add a prefix to your JSON and ZIP files. For example, to have the JSON and ZIP file names start with “Financial Audit”: ``` C:\> SharpHound.exe --OutputPrefix "Financial Audit" ``` ### NoZip Instruct SharpHound to **not** zip the JSON files when collection finishes ### ZipPassword Specify the password to be used for encrypting zip file, by default the zip file is not encrypted ### ZipFileName Specify the name of the zip file ### RandomFileNames Randomize output file names ### PrettyPrint Outputs JSON with indentation on multiple lines to improve readability. Tradeoff is increased file size. ### TrackComputerCalls Adds a CSV tracking requests to computers by dumping error codes from attempted connections to computers ## Loop Options ### Loop or 'l' Instruct SharpHound to loop computer-based collection methods. For example, attempt to collect local group memberships across all systems in a loop: ``` C:\> SharpHound.exe --CollectionMethods LocalGroup --Loop ``` ### LoopDuration By default, SharpHound will loop for 2 hours. You can specify whatever duration you like using the HH:MM:SS format. For example, to loop session collection for 12 hours, 30 minutes and 12 seconds: ``` C:\> SharpHound.exe --CollectionMethods Session --Loop --LoopDuration 12:30:12 ``` ### LoopInterval How long to pause for between loops, also given in HH:MM:SS format. For example, to loop session collection for 12 hours, 30 minutes and 12 seconds, with a 15 minute interval between loops: ``` C:\> SharpHound.exe --CollectionMethods Session --Loop --Loopduration 12:30:12 --LoopInterval 00:15:00 ``` ## Connection Options ### DomainController Target a specific domain controller by its IP address or name for LDAP collection ### LdapPort Specify an alternate port for LDAP if necessary ### SecureLdap Connect to the domain controller using LDAPS (secure LDAP) vs plain text LDAP. This will use port 636 instead of 389. Recommended. ### LdapUsername Use with the LdapPassword parameter to provide alternate credentials to the domain controller when performing LDAP collection. ### LdapPassword Use with the LdapUsername parameter to provide alternate credentials to the domain controller when performing LDAP collection. ### DisableSigning Disables Kerberos Signing/Sealing. Not recommended. ### DisableCertVerification Disables certificate verification when using LDAPS. Not recommended. ### OverrideUserName Override the username to filter for NetSessionEnum. ### DoLocalAdminSessionEnum Do the session enumeration with local admin credentials instead of domain credentials. ### LocalAdminUsername Username for local Administrator to be used if DoLocalAdminSessionEnum is set. ### LocalAdminPassword Password for local Administrator to be used if DoLocalAdminSessionEnum is set. ## Performance Options ### PortCheckTimeout When SharpHound is scanning a remote system to collect user sessions and local group memberships, it first checks to see if port 445 is open on that system. This helps speed up SharpHound collection by not attempting unnecessary function calls when systems aren’t even online. By default, SharpHound will wait 2000 milliseconds (2 seconds) to get a response when scanning 445 on the remote system. You can decrease this if you’re on a fast LAN, or increase it if you need to. For example, to tell SharpHound to wait just 1000 milliseconds (1 second) before skipping to the next host: ``` C:\> SharpHound.exe --PortCheckTimeout 1000 ``` ### SkipPortCheck Instruct SharpHound to not perform the port 445 check before attempting to enumerate information from a remote host. This can result in significantly slower collection periods. ### SkipPasswordCheck Skip check for PwdLastSet when enumerating computers. ### SkipRegistryLoggedOn Skip registry session enumeration ### Throttle Adds a delay after each request to a computer. Value is in milliseconds (Default: 0) ### Jitter Adds a percentage jitter to throttle. (Default: 0) ### Threads Number of threads to run enumeration with ## Cache Options ### CacheName SharpHound will create a local cache file to dramatically speed up data collection. It does this primarily by storing a map of principal names to SIDs and IPs to computer names. By default, SharpHound will auto-generate a name for the file, but you can use this flag to control what that name will be. For example, to name the cache file \`Accounting.bin\`: ``` C:\> SharpHound.exe --CacheName Accounting.bin ``` ### MemCache This will instruct SharpHound to NOT create the local cache file. Future enumeration will be slower than they would be with a cache file, but this will prevent SharpHound from putting the cache file on disk, which can help with AV and EDR evasion. ### RebuildCache Invalidate the cache file and build a new cache ## Miscellaneous ### StatusInterval Interval in which to display status in milliseconds ### Verbosity or 'v' Enable verbose output # Review Data Quality Source: https://bloodhound.specterops.io/collect-data/data-quality Use the Data Quality page to validate collected object counts, trends, and coverage after ingest. Applies to BloodHound Enterprise and CE The **Data Quality** page helps you validate what data exists in your BloodHound database after collection and ingest. Use it to confirm that expected data sources appear, object counts change as expected, and collection coverage supports the analysis workflows you plan to use. It helps answer questions about the current shape and recent history of your database before you investigate findings, explore the graph, or troubleshoot collector output. ## What the Data Quality page is for Use the **Data Quality** page after you upload or refresh data to: * Confirm that BloodHound ingested new or updated data. * Review object counts across supported data sources. * Compare current counts with historical collection trends. * Verify that expected object types appear for a directory, platform, or extension. * Check Active Directory data completeness for privileged collection coverage, including local groups and sessions. Data quality does not replace collector logs or Cypher queries. If the page shows unexpected counts or coverage gaps, use it to identify where to investigate next. ## Before you begin Before you review data quality, complete the collection or upload workflow for the data source you want to validate: * For Active Directory, collect and ingest SharpHound data. * For Azure and Entra ID, collect and ingest AzureHound data. * For structured OpenGraph data, verify or install the matching [OpenGraph extension definition schema](/opengraph/extensions/manage#verify-or-install-an-extension), then upload a conforming [data payload](/opengraph/developer/graph-data). The **Data Quality** page does not support generic OpenGraph data. This feature is available under early access. Enable on the **Administration** > **Early Access Features** page to access it. Wait for ingest and analysis processing to complete before you compare counts or trends. ## Review data quality In BloodHound, go to **Administration** > **Data Quality**. The page opens with an aggregate view of included object data. Use the total object count at the top of the page to confirm the current amount of included object data in the database. The total helps you verify that a recent upload or collection run changed the database in the expected direction. Review the historical graph to understand how object counts changed over time. Use this trend to spot unexpected drops after a collector configuration change or large increases after onboarding a new data source. Use the environment selector to focus on a specific data source, environment, or extension. Compare the displayed object types and counts with the data you expected the collector or payload to produce. For Active Directory privileged collection, review **Local Group Completeness Over Time** and **Session Completeness Over Time**. Use these charts to understand how much visibility BloodHound has into local group membership and session data across active computers. ## Data quality views The available views depend on the data in your BloodHound database. The page can include aggregate object counts, source-specific breakdowns, environment-specific views, and completeness charts. | View or metric | What it tells you | Use it to | | --------------------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------- | | **Object type and relationship counts** | Current object counts (by type) and relationships in the database. | Confirm that ingest added, removed, or retained the expected amount of data. | | **Historical object count graph** | Object-type count changes over time. | Identify unexpected drops, spikes, or missing collection activity. | | **Source or extension breakdown** | Filter by built-in source or OpenGraph extension. | Confirm which data sources contribute objects to the database. | | **Completeness charts** | Active Directory privileged collection coverage over time. | Evaluate local group and session visibility across active computers. | ## Count behavior Data quality counts show user-facing object data. They do not include internal labels or metadata objects that BloodHound uses to support platform features. For example, internal labels such as `Base` and `AZBase` can support calculations for Active Directory and Azure totals. They do not appear as separate object types in the breakdown because showing them separately can double count the same objects. ## Completeness charts Completeness charts apply to Active Directory data collected with SharpHound [privileged collection](/collect-data/enterprise-collection/privileged-collection). They help you understand collection coverage for data types that depend on host availability and permissions. | Chart | What it measures | | -------------------------------------- | -------------------------------------------------------------------------- | | **Local Group Completeness Over Time** | Visibility into local group membership collection across active computers. | | **Session Completeness Over Time** | Visibility into session collection across active computers. | Completeness below 100% is common. Workstations and servers can be offline, unavailable, or inaccessible during a collection run. If completeness is lower than expected, review collector permissions, collector logs, and collection scheduling. ## Structured OpenGraph data In BloodHound v9.5.1 and later, the **Data Quality** page includes [structured](/opengraph/overview#graph-structure) OpenGraph object counts and historical trends. This enhancement helps you validate OpenGraph ingest without writing custom Cypher queries. This feature is available under early access. Enable on the **Administration** > **Early Access Features** page to access it. For structured OpenGraph data, BloodHound organizes counts by extension and registered [`source_kind`](/opengraph/developer/graph-data#data-source). The `source_kind` identifies the OpenGraph data source and can come from payload metadata or from `environments.source_kind` in an extension definition schema. Select a specific extension or source kind to review object type counts and historical trends for that OpenGraph source. # Ad-hoc BHE Data Collection with SharpHound CE Source: https://bloodhound.specterops.io/collect-data/enterprise-collection/ad-hoc-collection Learn how to do ad-hoc data collection for BloodHound Enterprise using SharpHound Community Edition. Applies to BloodHound Enterprise only ## Purpose This article explains how to perform ad-hoc data collection for BloodHound Enterprise (BHE) using the SharpHound Community Edition (CE) collector. Use SharpHound CE when you cannot deploy [SharpHound Enterprise](/install-data-collector/install-sharphound/system-requirements). Examples include: * Environments with no internet access (such as SCADA or OT environments) * Merger and acquisition scenarios to assess risk before integration or consolidation of IT infrastructure * Quick deployment scenarios to do an initial assessment before a full SharpHound Enterprise deployment SharpHound CE may require allow-listing in endpoint protection solutions, as it is unsigned and will likely be flagged as malicious. SharpHound CE uses the same collection library as SharpHound Enterprise and therefore collects the same data. However, CE does not integrate with the SaaS portal, so it cannot provide portal status monitoring or configurable scheduled automatic collection and upload. ## Prerequisites The following prerequisites are required to perform ad-hoc data collection with SharpHound CE: * Logged in as a user with the **Administrator**, **Power User**, or **Upload-only** [role](/manage-bloodhound/auth/users-and-roles) * Access to an account and computer in the in-scope domain or a domain trusted by the in-scope domain ## Process The ad-hoc data collection process consists of two main steps: performing the data collection with SharpHound CE and uploading the collected data to BloodHound Enterprise. ### Perform SharpHound CE data collection This section outlines how to use SharpHound CE to collect data from the target environment. Get the latest version of SharpHound CE using one of the following methods: * Download from your [BloodHound Enterprise](/get-started/quickstart/community-edition-quickstart#download-collectors) tenant * Download from [GitHub](https://github.com/SpecterOps/SharpHound/releases/latest) You can also compile SharpHound CE from the [source code](https://github.com/SpecterOps/SharpHound). Extract the contents of the downloaded ZIP archive to a working directory on the system where you plan to collect data. 1. Choose a [collection](/collect-data/ce-collection/sharphound-flags#collectionmethods-or-'c') method that meets your requirements. DCOnly is the recommended starting method and is equivalent to BHE's Active Directory + Certificate Services. 2. Open a PowerShell or Command Prompt window. 3. Navigate to the directory where you extracted SharpHound CE. 4. Start collection with the chosen method. For example, to perform a **DCOnly** collection: ```ps theme={null} C:\> SharpHound.exe --CollectionMethods DCOnly ``` After the collection completes, locate the output .zip file in the same directory where you ran SharpHound CE. The file name is in the format `SharpHound-.zip`. ### Upload data to BloodHound Enterprise This section outlines how to upload the collected data to BloodHound Enterprise for analysis. In the left menu, click **Administration** > **File Ingest**. 1. Click **Upload File(s)**. File Ingest screen showing the Upload File(s) button 2. Click the modal or drag and drop the output .zip file onto it and click **Upload**. File Ingest screen showing the upload modal After the upload completes, you can verify the status on the [**File Ingest**](/collect-data/enterprise-collection/monitor#file-ingest-logs) page. ### Analyze Data and Use BloodHound Enterprise Features * **Dashboard and Visualization:** Review key insights and summaries. * **Running Queries:** Explore specific security aspects and visualize attack paths. * **Posture Reporting:** Visualize and track exposure within your Enterprise ### Best Practices for Secure Environments * **Minimize Data Collection Scope:** Focus on necessary data to limit exposure. * **Secure Data Handling:** Ensure secure storage and handling of collected data. * **Regular Updates and Maintenance:** Keep SharpHound CE updated. ## Outcome After ingest and analysis is complete, BloodHound Enterprise presents a comprehensive report with actionable recommendations on the **Attack Paths** page. # Create a Data Collection Schedule Source: https://bloodhound.specterops.io/collect-data/enterprise-collection/collection-schedule Learn how to configure an Enterprise collector client to run data collection on a schedule. Applies to BloodHound Enterprise only ## Purpose This article explains how to configure a data collector client to run on a schedule. Administrators should use it when deploying a new client or adding an additional schedule to an existing client. Azure and Active Directory objects typically change slowly, so daily collection is usually sufficient. To capture activity across different times of day, schedule Local Groups and Sessions more frequently (for example, every 7 hours). ## Prerequisites The following prerequisites are required to create a data collection schedule: * An existing SharpHound Enterprise or AzureHound Enterprise [collector client](/collect-data/enterprise-collection/create-collector) * Logged in as a user assigned a [role](/manage-bloodhound/auth/users-and-roles) authorized to modify clients ## Process The process to create a data collection schedule consists of the following steps: In the left menu, click **Administration** > **Manage Clients**. On the client that you want to schedule, click the hamburger menu in the *Actions* column and select **Edit Client**. Navigate to the Edit Client button on the Clients page 1. Click under **Collection Schedule** to add a new schedule. Click the plus icon to add a new collection schedule 2. Configure the following details in the **Schedule** window: * **Start Date**: The time at which the first collection should run * **Frequency**: The frequency at which the collection should run * **Data**: The type of data that the schedule collects, see: * [SharpHound Data and Permissions](/collect-data/sharphound-data-permissions) * [AzureHound Data and Permissions](/collect-data/azurehound-data-permissions) * **Advanced Options**: If you need SharpHound Enterprise to collect outside the SharpHound service account domain, expand **Advanced Options** and configure **Scope Collection to Multiple Domains**. To collect from every domain that trusts the SharpHound service account domain, enable **Collect from all domains trusting the SharpHound service account domain, including transitively**. Configure the schedule details | **Option** | **Description** | | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Data (Required) | Multi-select option for the different types of collection available. See [SharpHound Data Collection and Permissions](/collect-data/permissions) for details on the data collected and permissions necessary for each. | | Domain controller | By default, SharpHound automatically selects a Domain Controller for LDAP queries. Specifying a Domain Controller hostname or FQDN here will define the default value used for this scan or schedule.

If not set, SharpHound will utilize the value set in the client configuration.

We recommend not configuring a Domain Controller manually. | | Target Local Group and/or User Session Collection by Organizational Unit | Define one or more OUs within a domain to only collect Local Group and Session data from computers contained within the specified OUs and their descendants.

If left empty, *SharpHound will collect from all OUs.*

If defined, the schedule or On Demand Scan will not collect AD structure data. A dedicated schedule or On Demand Scan must therefore be created for AD structure collection.

Not supported with multi-domain collections. | | Scope Collection to Multiple Domains | Utilize trust relationships in your environment to collect data from multiple domains.

If left empty, SharpHound collects only from the domain where the SharpHound service account belongs.

SharpHound supports two options:

  • Define a specific list of domains from which to collect data.
  • Enable **Collect from all domains trusting the SharpHound service account domain, including transitively** to collect from every trusting domain, including trusted domains in other forests.


For setup details, see [SharpHound Enterprise Cross-Trust Collection](/collect-data/enterprise-collection/cross-trust).

Multi-domain collections cannot be scoped by OU. | 3. Click **Save** in the **Schedule** window. 4. Click **Save** in the **Edit SharpHound Client** window.
## Outcome The client is now configured for continuous data collection with one schedule. You can add multiple schedules to a single client for more granular control. A summary of a client's schedule displays in the *Collection Schedule* column on the **Clients** page. View of the Collection Schedule column on the Clients page After the next schedule, see the job's status on the [**Finished Jobs Log**](/collect-data/enterprise-collection/monitor#finished-jobs-log) page. # Create a Collector Client Source: https://bloodhound.specterops.io/collect-data/enterprise-collection/create-collector Learn how to create a BloodHound Enterprise collector client. Applies to BloodHound Enterprise only ## Purpose This guide explains how to create a BloodHound Enterprise collector client. It is intended for Administrators who are deploying SharpHound Enterprise or AzureHound Enterprise for data collection. Collector clients connect your BloodHound Enterprise tenant to your collector applications. They provide the necessary authentication and configuration information for your SharpHound Enterprise or AzureHound Enterprise collector applications to securely upload collected data to your BloodHound Enterprise instance for processing and analysis. BloodHound Enterprise supports two types of collector clients: * **SharpHound Enterprise** - Collects data from Active Directory environments * **AzureHound Enterprise** - Collects data from Entra ID environments ## Prerequisites * A BloodHound Enterprise tenant * Logged in as a user assigned a [role](/manage-bloodhound/auth/users-and-roles) authorized to create a collector client See [SharpHound Enterprise System Requirements](/install-data-collector/install-sharphound/system-requirements) or [AzureHound Enterprise System Requirements](/install-data-collector/install-azurehound/system-requirements) for more information on the requirements for each collector type. ## Process This guide covers the required steps to create a collector client in your BloodHound Enterprise tenant. Optional configuration settings are also explained, but can be skipped during initial setup and configured later if necessary. ### AzureHound Enterprise AzureHound collector clients use API token-based authentication. When creating an AzureHound collector client, you must save the generated token information and use it to [configure](/install-data-collector/install-azurehound/create-configuration) the AzureHound collector application. In the left menu, click **Administration** > **Manage Clients**. 1. On the right side of the page, click **Create Client**. 2. Select **Create AzureHound Client** from the dropdown menu. 3. Complete the required fields: | Field | Required | Description | | ----------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- | | **Client Name** | Yes | A descriptive name for the collector client (e.g., the name of the domain it collects from or system it runs on) | | **Collection Schedule** | No | Optional configuration options for [scheduling](/collect-data/enterprise-collection/collection-schedule) data collection jobs | A view of the Create Client screen for AzureHound Enterprise 4. Click **Create**. A *Client Token Info* window will appear with authentication credentials. Copy and save the token information before closing. The token information is required to [configure](/install-data-collector/install-azurehound/create-configuration) the AzureHound collector application. A view of the client token info screen for AzureHound Enterprise ### SharpHound Enterprise SharpHound Enterprise collector clients support both API token-based authentication and Integrated Windows Authentication (IWA) via Active Directory Federation Services (ADFS). When creating a SharpHound Enterprise collector client, you must select the authentication method and provide the required information based on that method. Be sure to save the generated token or configuration information and use it to [configure](/install-data-collector/install-sharphound/local-configuration) the SharpHound Enterprise collector application. In the left menu, click **Administration** > **Manage Clients**. 1. On the right side of the page, click **Create Client**. 2. Select **Create SharpHound Enterprise Client** from the dropdown menu. 3. Complete the required fields: | Field | Required | Description | | --------------------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Client Name** | Yes | A descriptive name for the collector client (e.g., the name of the domain it collects from or system it runs on) | | **Collection Schedule** | No | Optional configuration options for [scheduling](/collect-data/enterprise-collection/collection-schedule) data collection jobs | | **Advanced Options** | No | Optional domain controller targeting

By default, SharpHound automatically selects a Domain Controller for LDAP queries.

Specifying a target will prevent cross-trust collection from working unless the targeted LDAP server can respond for all desired domains | | **Authentication** | Yes | Authentication method the client will use:
  • **BHE Authentication**: Traditional API token-based authentication (default)
  • **Integrated Windows Authentication**: Windows-based authentication via ADFS
| | **Issuer ID** | Yes

(*IWA only*) | The ADFS well-known endpoint URL, typically: `https://adfs.example.com/.well-known/openid-configuration` | | **Issuer Address Override** | No

(*IWA only*) | An optional override for the token issuer address if your ADFS configuration uses a different issuer URL for token validation | The following screenshot shows the client creation form when **BHE Authentication** is selected. A view of the Create Client screen for SharpHound Enterprise The following screenshot shows the client creation form when **Integrated Windows Authentication** is selected. Note the additional required **Issuer ID** field and optional **Issuer Address Override**. A view of the Create Client screen for SharpHound Enterprise IWA 4. Click **Create**.
A pop-up window will appear and display the client token (for BHE Authentication) or client ID information (for Integrated Windows Authentication). Follow the instructions in it before clicking **Close**. **Switching Authentication Methods** If you are switching an existing SharpHound Enterprise collector client to a different authentication method, this step replaces the current credentials. * Switching to **Integrated Windows Authentication** invalidates existing API tokens and requires you to update the `settings.json` file and remove the `auth.json` file. * Switching to **BHE Authentication** generates a new token and requires you to update the `auth.json` file and disable IWA in the `settings.json` file. A *Client Token Info* window will appear with authentication credentials. Copy and save the token information before closing. The token information is required to [configure](/install-data-collector/install-sharphound/local-configuration#auth-json) the SharpHound Enterprise collector application in the `auth.json` file. A view of the client token info screen for SharpHound Enterprise A *Client Configuration Info* window will appear with the Client ID required to set up ADFS. The Client ID and configuration details are required to [configure ADFS](/install-data-collector/install-sharphound/configure-adfs-iwa) and to [configure](/install-data-collector/install-sharphound/local-configuration) the SharpHound Enterprise collector application in the `settings.json` file. A view of the client configuration info screen for SharpHound Enterprise IWA
## Outcome BloodHound Enterprise displays collector clients in the table on the **Manage Clients** page with a **Status** of **Unconfigured**. A view of the clients table showing a newly created AzureHound and SharpHound Enterprise collector clients with a status of Unconfigured ## Next Steps * SharpHound Enterprise clients: * **BHE Authentication**: Use the token information to [configure](/install-data-collector/install-sharphound/local-configuration#auth-json) the SharpHound Enterprise collector application in the `auth.json` file. * **Integrated Windows Authentication**: Follow the [ADFS configuration guide](/install-data-collector/install-sharphound/configure-adfs-iwa) to set up ADFS, then [configure](/install-data-collector/install-sharphound/local-configuration#settings-json) the SharpHound Enterprise collector application in the `settings.json` file. * AzureHound Enterprise clients: * Use the token information to [configure](/install-data-collector/install-azurehound/create-configuration) the AzureHound collector application. # SharpHound Enterprise Cross-Trust Collection Source: https://bloodhound.specterops.io/collect-data/enterprise-collection/cross-trust Learn how to configure SharpHound Enterprise to collect data across trusted Active Directory domains and forests. Applies to BloodHound Enterprise only By default, SharpHound Enterprise collects only from the domain where the SharpHound service account belongs. If you need to collect from trusted domains or other forests, configure **Scope Collection to Multiple Domains** in the client or schedule **Advanced Options**. You can either specify a list of domains or enable **Collect from all domains trusting the SharpHound service account domain, including transitively**. ## Configure Cross-Trust Collection In **Scope Collection to Multiple Domains**, you can either specify a list of domains to collect from or enable **Collect from all domains trusting the SharpHound service account domain, including transitively**. This option also collects from trusting domains in other forests. If selective authentication is enabled on a trust, the SharpHound Enterprise service account must explicitly be granted read permissions on all AD objects in all domains of the targeted forest to perform collection. ## Collect Across External Trust Kerberos authentication works by default for all Active Directory trust types except external trusts. SharpHound Enterprise supports collection across external trusts via two mechanisms. ### Forest Search Order (preferred) Administrators can enable Kerberos authentication across external trusts by adding the name of the other domain to the [Use forest search order](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-r2-and-2008/hh920181\(v=ws.10\)) policy setting on Domain Controllers. We recommend deploying this setting to all Domain Controllers in domains with external trusts to avoid using the older and less secure NTLM authentication. ### LDAP Auto-Negotiation By default, SharpHound Enterprise only supports Kerberos authentication for the LDAP connections to Domain Controllers for Active Directory Structure Data collection. This will cause the collection across the external trust to fail without modifying SharpHound's default behavior in the `settings.json` configuration file. The default configuration file path is described in [SharpHound Enterprise Local Configuration](/install-data-collector/install-sharphound/local-configuration). To enable support for auto-negotiation in LDAP connections: 1. Stop the SharpHound Delegator service. 2. Open the `settings.json` configuration file as an Administrator (right click -> Run as Administrator on notepad.exe). 3. Change the `ForceLDAPKerberosAuth` setting to `False` (no quotes). 4. Save the `settings.json` configuration file. 5. Start the SharpHound Delegator service. If NTLM fallback is enabled, deny outbound NTLM authentication from the SharpHound server to all servers except Domain Controllers in domains with external trust relationships. For hardening guidance, see [NTLM cracking (and relaying) remediation](/manage-bloodhound/securing-bloodhound-and-collectors/sharphound-hardening#attack-4-ntlm-cracking). ## Troubleshoot Cross-Trust Collection The collection across a trust will fail if: * The Kerberos-supported encryption types between domains/forests do not match. * Authentication has been restricted, e.g., using authentication policy silos or IPSec. * There is no network access from the SharpHound server to the trusting domain's DCs and domain-joined Windows systems in scope for privileged collection. # Data Reconciliation and Retention Source: https://bloodhound.specterops.io/collect-data/enterprise-collection/data-retention Applies to BloodHound Enterprise only ## Data reconciliation BloodHound Enterprise (BHE) will perform data reconciliation. That is, BHE will automatically update changes identified during subsequent data collections, such as the removal of group membership, role assignments, access control list changes, etc. ### HasSession edge reconciliation [HasSession](/resources/edges/has-session) edges are generated to indicate patterns of behavior rather than session active at any exact moment. For this reason, HasSession edges are only reconciled based on their retention/time-to-live expiring, rather than reconciling upon follow-on collections no longer seeing the active session. If you need to refresh session data immediately, an administrator can delete **HasSession** edges from **Database Management** before recollecting session data instead of waiting for the retention window to expire. Manual **HasSession** edge deletion is generally only necessary in BloodHound Community. In BloodHound Enterprise, **HasSession** edges reconcile automatically based on the configured retention period. Delete them manually only when you need to refresh session data immediately instead of waiting for that retention window to expire. ## Data retention BloodHound Enterprise (BHE) implements data retention, i.e., a time-to-live where data that has not been collected and ingested for a certain period will get deleted from BHE. This retention period is configurable and is by default: * Session Data, i.e. [HasSession](/resources/edges/has-session) edges: 3 days * General Data, i.e. objects/nodes and relationships/edges, excluding HasSession edges: 7 days Tier Zero tags on deleted nodes will remain, and the Group Management page will show the deleted node as an object ID. Data retention periods can be changed at ⚙️ > Administration > BloodHound Configuration: Retention means BHE does not assume that lack of visibility during a single collection means that an object or edge no longer exists; it's possible that the most recent collection, for example, if BHE doesn't see a user object for some reason (operational issue, collection scoped to another domain, etc.). On objects, this timestamp is updated for both visibility of the object itself and visibility to references of the object. For example, if an object is deleted, but the SID remains present in an ACE applied to some other remaining object, this timestamp will be updated, and the object will appear present in BHE. To implement this, BHE stores a timestamp on every data point, updated whenever a new collection includes the same data point. The timestamp on nodes can be seen as the "Last Seen by BloodHound" attribute in every node's entity panel on the "Explore" page. In cases where retention maintains visibility into an already resolved finding, the "Accept" feature may be used to hide nodes/principals in the "Attack Paths" page, see [Accept attack path finding](/analyze-data/bloodhound-gui/accept-findings). ### Active Directory recycle bin BHE's data retention period starts once an object has been permanently deleted from Active Directory, that is, after the object has left retention in the AD recycle bin. By default, the AD recycle bin has a retention (tombstone lifetime) of 180 days; thus, the default total retention for nodes will be 180 days + 7 days = 187 days. Check if the AD recycle bin has been enabled for the forest: ```powershell theme={null} # Returns 'True' if the AD recycle bin has been enabled [bool](Get-ADOptionalFeature -Identity 766ddcd8-acd0-445e-f3b9-a7f9b6744f2a | select -ExpandProperty EnabledScopes) ``` Check the AD recycle bin's retention period (tombstone lifetime): ```powershell theme={null} # Returns the number of days the AD recycle bin retains deleted objects (tombstone lifetime) $ForestConfigurationNC = (Get-ADRootDSE).configurationNamingContext Get-ADObject -Identity "CN=Directory Service,CN=Windows NT,CN=Services,$ForestConfigurationNC" -Partition $ForestConfigurationNC -Properties tombstoneLifetime | select tombstoneLifetime ``` # SharpHound Collection FAQ Source: https://bloodhound.specterops.io/collect-data/enterprise-collection/faq The following are common questions about the data collection capabilities provided by the SharpHound Enterprise service. Applies to BloodHound Enterprise only Collection time can vary from minutes to hours depending on the size of the environment (but other complicating factors can contribute to longer durations). Example full scan and upload durations with privileged collection: * 15,000 users + groups, 4,000 computers, and AD CS: 45 minutes * > 500,000 computers , and AD DS: 3 hours SharpHound automatically selects the best Domain Controller based on information returned from Active Directory. If you see an error in run.log that looks something like this: 2022-08-05T09:18:13.6406652-04:00|WARNING|\[CommonLib LDAPUtils]LDAP Exception in Loop: 52. (null). The LDAP server returned an unknown error.. You may reference this link to understand the meaning of the exception code by number: [https://ldap.com/ldap-result-code-reference-core-ldapv3-result-codes](https://ldap.com/ldap-result-code-reference-core-ldapv3-result-codes) SharpHound Enterprise installs as a signed Windows service. For this reason, antivirus products tend not to alert on the service. Notable exceptions include: * Behavioral analytics tools: Any security tool that performs behavioral identification of scanners will flag SharpHound Enterprise as a scanner during local privileged collection. Typically these cannot block activity but will generate alerts to the SOC. * Cisco Umbrella: As each customer is deployed utilizing their own domain, Umbrella commonly flags the domain as new and will quarantine it until excluded by an administrator. # Least-Privileged Collection in SharpHound Source: https://bloodhound.specterops.io/collect-data/enterprise-collection/least-privileged-collection Learn how to collect more than AD Structure data without Domain Admin. Applies to BloodHound Enterprise and CE Privileged collection allows BloodHound to analyze Attack Paths based on non-centralized configurations using privileged administrative credentials, similar to performing a privileged vulnerability scan. Least-privileged collection accomplishes these goals without using default administrative privileges to perform the collection. With some additional configuration SharpHound can collect the local groups, active sessions, and registry keys without adding any SharpHound collection service accounts to `Domain Admins`. We also recommend following the article [SharpHound Enterprise Service Hardening](/manage-bloodhound/securing-bloodhound-and-collectors/sharphound-hardening). ## AD Structure Data By default, all `Authenticated Users` may query almost all data from Active Directory utilized by BloodHound via LDAP. Additional privileges are required for the sections below. ### Restricted Read Permissions If modifications exist that restrict the default read permissions, the SharpHound collector service account must be a member of an audit role group which is granted `Read Property` and `Read Permissions` on all collected AD objects. ### Delegated Managed Service Account The Active Directory Schema restricts `Authenticated Users` read permissions on Delegated Managed Service Account (dMSA) objects by default. Grant the SharpHound account `Read` and `Read Permissions` on dMSA objects in the `Managed Service Accounts` container by running the code below after changing the SID to the SharpHound account. Repeat for all other OUs containing dMSA objects. ```powershell theme={null} Import-Module ActiveDirectory # SharpHound account SID $SharpHoundSID = "S-1-5-21-1273778777-4208638582-2921056243-16306" # Container/OU to grant permissions on $MSAContainerDN = "CN=Managed Service Accounts," + $(Get-ADDomain).DistinguishedName # Static configuration $DMSAObjectClassGUID = [GUID]"0feb936f-47b3-49f2-9386-1dedc2c23765" # msDS-DelegatedManagedServiceAccount $Trustee = New-Object System.Security.Principal.SecurityIdentifier($SharpHoundSID) # Get current ACL and trustee SID $ACL = Get-Acl "AD:\$MSAContainerDN" # Add Read Property and Read Permissions ACEs (inherit-only on dMSA objects) $ACL.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule($Trustee, "ReadProperty", "Allow", [GUID]::Empty, "Descendents", $DMSAObjectClassGUID))) $ACL.AddAccessRule((New-Object System.DirectoryServices.ActiveDirectoryAccessRule($Trustee, "ReadControl", "Allow", [GUID]::Empty, "Descendents", $DMSAObjectClassGUID))) # Apply the modified ACL Set-Acl "AD:\$MSAContainerDN" -AclObject $ACL ``` ### Deleted Objects Container (Optional) SharpHound can read the content of the Deleted Objects container (also known as the AD Recycle Bin). Collecting deleted objects affects data retention behavior in BloodHound Enterprise, see [Active Directory Recycle Bin](/collect-data/enterprise-collection/data-retention#active-directory-recycle-bin) for details on how this impacts retention periods. You can delegate permissions to a group for [read access to the "Deleted Objects" container](https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/non-administrators-view-deleted-object-container), and then add the SharpHound collector service account to that group. The Deleted Objects container locations are: * Domain NC Deleted Objects DistinguishedName (DN): `CN=Deleted Objects,` * Configuration NC Deleted Objects DistinguishedName (DN): `CN=Deleted Objects,CN=Configuration,` SharpHound can read these containers even if the AD Recycle Bin feature is not enabled. ## Local Group Membership SharpHound collects local group membership via [Remote SAM Enumeration](https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-access-restrict-clients-allowed-to-make-remote-sam-calls). By default, on currently supported Windows operating systems, only `Administrators` on the device(s) being collected have this right on Windows clients and member servers. For compatibility purposes, `Everyone` is granted this right on domain controllers by default. Microsoft supports delegating this permission via a properly scoped Group Policy Object with the [Network access: Restrict clients allowed to make remote calls to SAM](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/security-policy-settings/network-access-restrict-clients-allowed-to-make-remote-sam-calls) setting. For example, if our Tier Zero SharpHound collector gMSA is a member of the Allow\_SamConnect\_T0 group, a GPO configured like this and linked to the Tier Zero OU where all of the non-DC Tier Zero assets are located, SharpHound will be able to collect Local Group data from those hosts. ## User Rights Assignments Only `Administrators` can perform the `LsaOpenPolicy` and `LsaEnumerateAccountsWithUserRights` function calls necessary to collect User Rights Assignments (URAs) directly from a remote host. There is no known way around this limitation. Currently, not collecting User Rights Assignments may cause inaccurate [CanRDP](/resources/edges/can-rdp) edges. In the future, SharpHound may collect additional user rights to identify more attack paths. ## Sessions By default, local `Administrators` have the rights necessary to perform the [NetWkstaUserEnum](https://learn.microsoft.com/en-us/windows/win32/api/lmwksta/nf-lmwksta-netwkstauserenum) function calls to collect session data. While not ideal, local `Print Operators` also have the rights necessary to collect session data from Windows Server hosts. Unfortunately, this option does not exist on Windows desktop operating systems. An alternate collection method, such as event log parsing, is required to collect session data from all domain-joined devices. To collect session data from domain controllers, the collector service account can be added to the local builtin [Print Operators](https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/understand-security-groups#print-operators) group for the domain. When doing this it is also important to lessen the capabilities of the Print Operators group by removing the default User Rights Assignments created by the Default Domain Controllers Policy linked to the Domain Controllers OU. The Print Operators group should no longer be granted rights to Allow log on locally: `SeInteractiveLogonRight`, Load and unload device drivers: `SeLoadDriverPrivilege`, and Shut down the system: `SeShutdownPrivilege`. Domain controllers should not be used as print servers and the builtin Print Operators group for the domain should be unused, except perhaps for this purpose. To collect session data from Windows member servers, the collector service account can be added to the local `Print Operators` group on each device. The best way to accomplish this is likely via Group Policy Preferences. For example, if our Tier One SharpHound collector gMSA is a member of the Allow\_NetwkstaUserEnum\_T1 group, a GPO configured like this and linked to any OUs where Tier One assets are located will grant the SharpHound collector session enumeration rights. Local group membership can also be managed via the `Restricted Groups` GPO setting category. This is a legacy setting. Group Policy Preferences is more robust and less likely to create security or denial of service (DoS) issues. Collecting session data from domain-joined Windows desktops will require membership in the local `Administrators` group on each endpoint, which is best handled via Group Policy Preferences. Utilizing a service account that is a member of `Domain Admins` is strongly discouraged. There may be other options for collecting session data from the environment, such as parsing Windows event logs from all forest domain controllers, specifically Event ID 4624. ## Certificate Services All `Authenticated Users`, by default, may collect certificate services data from Active Directory via LDAP. The majority of certificate services data is in the Configuration NC for the forest and collected with the AD Structure data. The remainder of Certificate Services data may be collected from the Windows Registry. ## Registry SharpHound collects registry data for both certificate services and NTLM relay edges. The certificate services registry paths are on certificate authorities and domain controllers. NTLM relay paths are located on all Windows hosts. By default, only `Administrators` may read the registry remotely. There are two methods to delegate remote registry access for least-privileged collection: 1. **AllowedPaths / AllowedExactPaths exceptions**: Create exceptions using the GPO setting [Network access: Remotely accessible registry paths](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/security-policy-settings/network-access-remotely-accessible-registry-paths) to allow `Authenticated Users` to connect to the `Remote Registry` named pipe at specific registry paths. Effective access is still governed by permissions on individual registry keys. 2. **Modify Remote Registry named pipe security descriptor**: Alternatively, modify the default security descriptor on the `Remote Registry` named pipe to grant explicit security principals read permissions to the entire registry. Permissions on individual registry keys still govern effective access. ### CA Registry When the AD CS role is installed in Windows, the `HKLM\SYSTEM\CurrentControlSet\Services\CertSvc` registry path is automatically added to `HKLM\SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg\AllowedPaths`. This creates a remote registry exception that allows `Authenticated Users` to query any keys and subkeys in this path, as long as they also are granted rights to read the key. Therefore, CA registry data is accessible to `Authenticated Users` by default when AD CS is installed. ### DC Registry For domain controllers, you can create exceptions by adding the required DC registry paths to `HKLM\SYSTEM\CurrentControlSet\Control\SecurePipeServers\winreg\AllowedExactPaths` using the GPO setting mentioned above. This will create an exception to the Remote Registry named pipe on the DC allowing `Authenticated Users` to read those exact key paths, as long as they also are granted permissions on the registry key DACL. ### NTLM Relay Registry Paths Registry paths for NTLM relay edges exist on all Windows hosts. By default, only `Administrators` can access these paths remotely. To enable least-privileged collection, use Group Policy to add these specific paths to `AllowedExactPaths`. This grants `Authenticated Users` remote read access, following the same method described above. # Monitor Data Collection Source: https://bloodhound.specterops.io/collect-data/enterprise-collection/monitor Learn how to interpret the status of collector jobs and file uploads. Applies to BloodHound Enterprise only Monitor collection activity and processing status to confirm uploads, understand analysis timing, and troubleshoot failures. The status concepts in this guide apply to both collector client jobs on the [Finished Jobs Log](#finished-jobs-log) page and manual uploads on the [File Ingest](#file-ingest) page. BloodHound Enterprise uses separate status indicators for tenants and jobs. These statuses operate independently and are not directly synchronized. This means you may observe scenarios where job status and tenant status appear out of sync, which is expected behavior. * **Tenant** status reflects datapipe ingestion and analysis progress. The datapipe performs its own processing and reflects status independent of job status. * **Job** status reflects the collector client and file ingest workflows. Jobs notify the datapipe when certain actions are complete. ## Tenant Status Tenant status (also known as [datapipe status](/reference/datapipe/get-datapipe-status)) displays in the top-right corner of the BloodHound Enterprise user interface. This status reflects the current state of data processing for your tenant. | Status | Description | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Idle** | The tenant is waiting for new data. | | **Ingesting** | Data is actively being ingested from one or more collection jobs or file uploads. | | **Analyzing** | Analysis is actively running on ingested data. | | **Pruning** | Analysis is almost complete; stale objects, edges, and disconnected nodes are being removed based on [data reconciliation](/collect-data/enterprise-collection/data-retention) settings. | | **Purging** | Data is actively being deleted from the database (for example, you used the **Database Management** page to delete data). | With [Variable Analysis Mode](/analyze-data/findings/analysis#variable-analysis-mode), analysis triggered by Privilege Zone changes still appears as **Analyzing** in tenant status. The tenant status does not distinguish between a full analysis run and a variable analysis run that starts at **Tagging**. *Scheduled analysis (a SpecterOps-managed feature) always runs a full analysis.* ## Job Status The following statuses apply to both the **Finished Jobs Log** and **File Ingest** pages. | Status | Description | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Ready** | Job is queued and waiting to start. | | **Running** | Job is actively executing data collection or file processing. | | **Complete** | Job finished successfully; all data has been ingested and analyzed. | | **Partially Completed** | Job finished with some data processed successfully, but one or more files or operations encountered issues. Check the job details for specific warnings. | | **Canceled** | Job was manually canceled before completion. | | **Timed Out** | Job exceeded the maximum allowed execution time and was terminated. | | **Failed** | Job encountered an error and could not complete. Check the job details for specific error messages. | | **Ingesting** | Data is actively being written to the database. | | **Analyzing** | Data collection is complete; awaiting completion of analysis. | | **Invalid** | Data failed validation (for example, schema errors, corrupted files, or unsupported formats). | The following state diagram illustrates the possible transitions between job statuses: ```mermaid theme={null} stateDiagram-v2 [*] --> Ready Ready --> Running: Job starts Running --> Ingesting: Data upload begins Running --> Failed: Error occurs Running --> TimedOut: Timeout exceeded Running --> Canceled: User cancels Ingesting --> Analyzing: Ingestion complete Ingesting --> Invalid: Validation fails Analyzing --> Complete: Analysis complete Analyzing --> PartiallyCompleted: Partial success state "Timed Out" as TimedOut state "Partially Completed" as PartiallyCompleted Complete Failed Canceled Invalid ``` ## Finished Jobs Log Data collection clients log completed jobs to the **Finished Jobs Log**, which provides details about all collection activities that a client performs. This log is essential for monitoring and troubleshooting data collection jobs. The **Finished Jobs Log** page provides a detailed log of each collection job, including: | Field | Value | | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **ID** | A unique identifier for the client collection job | | **Client** | The client that initiated the collection job | | **Status** | The [status](#job-status) of the collection job (for example, complete, failed) | | **Message** | A brief message providing additional context about the collection job | | **Start Time** | The time when the collection job started | | **Duration** | The time taken to process the collection job | | **Data Collected** | The type of data that the client is configured to collect, see [SharpHound Data and Permissions](/collect-data/sharphound-data-permissions) and [AzureHound Data and Permissions](/collect-data/azurehound-data-permissions) | For clients on [scheduled collection](/collect-data/enterprise-collection/collection-schedule), jobs can display an *Analyzing* status while the tenant status remains *Idle*. When the scheduled collection time arrives, the tenant status changes to *Ingesting*, and analysis begins automatically after the collection completes successfully. Finished Jobs Log screen showing the details panel In the left menu, click **Administration** > **Finished Jobs Log**. Click the specific job **ID** in the table to open the **Details** panel. You can also click the icon to filter job IDs by status, data collected, data range, and client. In the **Details** panel, review the job details for the selected ID. Look for any errors or warnings that may indicate issues during the collection process. The **Details** panel displays the following information for each job ID: | Field | Description | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | **Job ID** | A unique identifier for the client collection operation | | **Client Name** | The name of the client that performed the collection | | **Domains attempted** | The number of domains that were attempted to be collected | | **Domain Controller** | The domain controller used for collection | | **OUs** | The organizational units (OUs) that were collected | | **Domains** | The domains that were collected, including the number of objects collected from each domain and status messages (if available) | Finished Jobs Log Details panel showing job details ## File Ingest When you perform an [ad-hoc data collection](/collect-data/enterprise-collection/ad-hoc-collection) by uploading a SharpHound output .zip file, ingestion process details are logged on the **File Ingest** page. You can use this page to monitor ingestion and ensure successful data processing. It shows the status of each file ingest operation, which can be helpful for viewing data upload history or troubleshooting data ingestion issues. The **File Ingest** page provides a detailed log of each ingestion attempt, including: | Field | Description | | -------------------- | ---------------------------------------------------------------------------------------------- | | **ID** | A unique identifier for the file ingest operation | | **User** | The user who initiated the file ingest operation | | **Status** | The current [status](#job-status) of the file ingest operation (for example, complete, failed) | | **Message** | A brief message providing additional context about the file ingest operation | | **Start Time** | The time when the file ingest was initiated | | **Duration** | The time taken to process the file ingest operation | | **File Information** | Details about the ingested file(s), such as file count and file name(s) | In the left menu, click **Administration** > **File Ingest**. Click the specific file ingest **ID** in the table to open the **Details** panel. You can also click the icon to filter ingest IDs by status, data range, and user. File Ingest screen showing the upload details panel In the **Details** panel, review the log entries for the selected ID. Look for any errors or warnings that may indicate issues during the ingestion process. If a file failed to ingest due to format issues or data corruption, the log provides specific error messages to help you diagnose the problem. For example, the following log indicates a failed ingestion due to a schema validation error: File Ingest screen showing details about a failed file ingest # Run an On Demand Scan Source: https://bloodhound.specterops.io/collect-data/enterprise-collection/on-demand-scan Learn how to run an on demand scan with a collector client in BloodHound Enterprise. Applies to BloodHound Enterprise only ## Purpose This article describes how to run an unscheduled scan to perform a one-time, immediate data collection with a collector client. Administrators may use it during collector client deployment, one-time collections, or troubleshooting. ## Prerequisites The following prerequisites are required to run an on demand scan: * An existing SharpHound Enterprise or AzureHound Enterprise [collector client](/collect-data/enterprise-collection/create-collector) * Logged in as a user assigned a [role](/manage-bloodhound/auth/users-and-roles) authorized to run a collector client on demand scan ## Process The process to run an on demand scan consists of the following steps: In the left menu, click **Administration** > **Manage Clients**. On the client that you want to schedule, click the icon in the *Action* column and select **On Demand Scan**. Verify the client is online by validating **Status** is **Ready** A collector client with the Action menu open and On Demand Scan selected Configure the following details in the **On Demand Scan** window: * **Data**: The [type of data](/collect-data/permissions) that the scan collects, see: * [SharpHound Data and Permissions](/collect-data/sharphound-data-permissions) * [AzureHound Data and Permissions](/collect-data/azurehound-data-permissions) * **Advanced Options**: If you need SharpHound Enterprise to collect outside the SharpHound service account domain, expand **Advanced Options** and configure **Scope Collection to Multiple Domains**. To collect from every domain that trusts the SharpHound service account domain, enable **Collect from all domains trusting the SharpHound service account domain, including transitively**. A collector client On Demand Scan configuration window | **Option** | **Description** | | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Data (Required) | Multi-select option for the different types of collection available. See [SharpHound Data Collection and Permissions](/collect-data/permissions) for details on the data collected and permissions necessary for each. | | Domain controller | By default, SharpHound automatically selects a Domain Controller for LDAP queries. Specifying a Domain Controller hostname or FQDN here will define the default value used for this scan or schedule.

If not set, SharpHound will utilize the value set in the client configuration.

We recommend not configuring a Domain Controller manually. | | Target Local Group and/or User Session Collection by Organizational Unit | Define one or more OUs within a domain to only collect Local Group and Session data from computers contained within the specified OUs and their descendants.

If left empty, *SharpHound will collect from all OUs.*

If defined, the schedule or On Demand Scan will not collect AD structure data. A dedicated schedule or On Demand Scan must therefore be created for AD structure collection.

Not supported with multi-domain collections. | | Scope Collection to Multiple Domains | Utilize trust relationships in your environment to collect data from multiple domains.

If left empty, SharpHound collects only from the domain where the SharpHound service account belongs.

SharpHound supports two options:

  • Define a specific list of domains from which to collect data.
  • Enable **Collect from all domains trusting the SharpHound service account domain, including transitively** to collect from every trusting domain, including trusted domains in other forests.


For setup details, see [SharpHound Enterprise Cross-Trust Collection](/collect-data/enterprise-collection/cross-trust).

Multi-domain collections cannot be scoped by OU. |
Click **Run** to begin the on demand scan.
## Outcome The client starts the on demand scan after the next client check-in (usually within one minute). After it starts, the client status shows **Running a Job**: A collector client summary showing Running a Job status After the next schedule, see the job's status on the [**Finished Jobs Log**](/collect-data/enterprise-collection/monitor#finished-jobs-log) page. # BloodHound Enterprise Collection Source: https://bloodhound.specterops.io/collect-data/enterprise-collection/overview Learn about attack path data collection in BloodHound Enterprise. Promoted article # Privileged Collection in SharpHound Source: https://bloodhound.specterops.io/collect-data/enterprise-collection/privileged-collection Applies to BloodHound Enterprise and CE Privileged collection allows BloodHound to analyze Attack Paths based on non-centralized configurations, the local groups, active sessions, registry, and user rights assignments configured on each domain-joined system in your environment. Without this data, BloodHound Enterprise will be limited in its ability to accurately assess the true risk each Attack Path poses to your environment. Privileged collection is similar to performing a privileged vulnerability scan - without it, you will gain a lot of previously unknown information about your environment, however that presents you with a limited and less accurate picture of the risks present in your environment. As an example, if BloodHound Enterprise identified the following set of Attack Paths in a given environment based on AD Structure alone: Based on this view, the tree of Attack Paths on the left would present the greatest risk to this environment, now lets collect Local Group membership information from the domain: BloodHound Enterprise has identified that a computer at the bottom of the right Attack Path tree has `Authenticated Users` (all users and computers contained within the current domains, and all domains trusted by the current domain) added as a local `Administrator` to a system at the beginning of one Attack Path. After updating the exposure presented by this new information, BloodHound Enterprise would identify that the actual largest risk to this environment as the path on the right. For details on what data types can be collected, see: * [SharpHound Data and Permissions](/collect-data/sharphound-data-permissions) * [AzureHound Data and Permissions](/collect-data/azurehound-data-permissions) # Data Collection Source: https://bloodhound.specterops.io/collect-data/overview Learn how to run attack path data collection and ingestion. ## BloodHound Enterprise Collection ## BloodHound CE Collection ## Validate Data # SharpHound Data Collection and Permissions Source: https://bloodhound.specterops.io/collect-data/sharphound-data-permissions Learn how SharpHound collects data and the permissions required. Applies to BloodHound Enterprise and CE SharpHound data collection utilizes the open-source [SharpHound Common](https://github.com/SpecterOps/SharpHoundCommon) library, maintained by the BloodHound Enterprise Engineering team. The scan types in SharpHound Enterprise and SharpHound Community Edition are named differently but effectively collect the same data. * BloodHound Enterprise scan types can be started with a [collection schedule](/collect-data/enterprise-collection/collection-schedule) or an [on-demand scan](/collect-data/enterprise-collection/on-demand-scan) * BloodHound Community Edition, you run scans with the [CollectionMethods flag](https://bloodhound.specterops.io/collect-data/ce-collection/sharphound-flags#enumeration-options) This article details all BloodHound Enterprise scan types and the required service account permissions. SpecterOps recommends collecting all data types because it provides maximum visibility into your environment. Local Group Memberships and Sessions are especially important, as they reveal Attack Paths to individual systems based on non-centralized configurations, see [Why perform privileged collection in SharpHound](/collect-data/enterprise-collection/privileged-collection). The SharpHound collection service account does not require `Domain Admin` membership. While adding the account to local `Administrators` groups on domain computers will work, we recommend following the articles [Least-Privileged Collection](/collect-data/enterprise-collection/least-privileged-collection) (referenced below for each scan type) and [SharpHound Enterprise Service Hardening](/manage-bloodhound/securing-bloodhound-and-collectors/sharphound-hardening). ## Active Directory Structure Data Information about the objects and relationships within your Active Directory environment makes up the basic information necessary to identify attack paths within your environment. This information includes: * Domain trusts. * Object properties of users, groups, computers, GPOs, OUs containers, and Domain objects. * ACLs related to users, groups, computers, GPOs, OUs, containers, and Domain objects. * Enumerated objects contained in every OU, container, and Domain. * Enumerated memberships of all Groups. Reference: [Current Object Properties collected by SharpHound](https://github.com/SpecterOps/SharpHoundCommon/blob/68a68c6eab5375b46f975274b16ff1acdc35dc48/src/CommonLib/LdapQueries/CommonProperties.cs). **Collection Method:** SharpHound collects this information utilizing signed LDAP queries against a domain controller in the domain. **Default Permissions:** By default, all `Authenticated Users` may query almost all data from Active Directory utilized by BloodHound via LDAP. Higher privileges are required for other objects, see **Least-Privileged Option**. **Least-Privileged Option:** For information on how to collect all objects with least privilege, see [Least-Privileged Collection - AD Structure Data](/collect-data/enterprise-collection/least-privileged-collection#ad-structure-data). **Additional Data Sources:** (Optional) Deleted Objects Container: SharpHound can read the contents of the Deleted Objects container (also known as the AD Recycle Bin). By default, SharpHound cannot read the Deleted Objects container, but read access can be delegated. Collecting deleted objects affects data retention behavior in BloodHound Enterprise, see [Active Directory Recycle Bin](/collect-data/enterprise-collection/data-retention#active-directory-recycle-bin) for details. For delegation configuration, see [Least-Privileged Collection - AD Structure Data](/collect-data/enterprise-collection/least-privileged-collection#ad-structure-data). ## Local Groups / NTLM ### Local Group Membership Members of the following groups are enumerated: * Administrators * Remote Desktop Users * Distributed COM Users * Remote Management Users **Collection Method:** SharpHound collects this information utilizing [Remote SAM Enumeration](https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-access-restrict-clients-allowed-to-make-remote-sam-calls). **Default Permissions:** By default, computers beginning with Windows 10 version 1607 and Windows Server 2016 require `Administrator` access to perform `Remote SAM` operations. **Least-Privileged Option:** This setting may be overridden with Group Policy to allow non-administrative collection. For detailed configuration steps using the "Network access: Restrict clients allowed to make remote calls to SAM" setting, see [Least-Privileged Collection - Local Group Membership](/collect-data/enterprise-collection/least-privileged-collection#local-group-membership). ### User Rights Assignments User Rights Assignments (URAs) in Windows define what privileges and capabilities security principals have on a system, independent of group membership. Collecting User Rights Assignments allows BloodHound to accurately determine the [CanRDP](/resources/edges/can-rdp) edge. Before SharpHound Common v3, BloodHound made assumptions based solely on group membership—assuming that membership in the `Remote Desktop Users` group alone gives users the ability to RDP to a system. However, to successfully use Remote Desktop, a user needs **both** membership in the `Remote Desktop Users` group **and** the User Rights Assignment `SeRemoteInteractiveLogonRight`. **Collection Method:** SharpHound collects this information utilizing the [LsaOpenPolicy](https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaopenpolicy) function. **Required Permissions:** Only local `Administrators` may call the `LsaOpenPolicy` function. **Least-Privileged Option:** There is currently no known method to delegate this permission for least-privileged collection, see [Least-Privileged Collection - User Rights Assignments](/collect-data/enterprise-collection/least-privileged-collection#user-rights-assignments) for more details on the implications. ### NTLM SharpHound collects various registry values related to NTLM from the registry paths `SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0`, `SYSTEM\CurrentControlSet\Control\Lsa\`, and `SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters` to create the `CoerceAndRelayNTLMTo*` edges. **Collection Method:** SharpHound collects these registry key values first via WMI access, and remote registry as a failover. **Default Permissions:** Collecting these registry key values requires membership of `Administrators` on the systems by default. **Least-Privileged Option:** Delegation is possible via Group Policy or registry modifications, see [Least-Privileged Collection - NTLM Relay Registry Paths](/collect-data/enterprise-collection/least-privileged-collection#ntlm-relay-registry-paths). ## Sessions SharpHound collects active session information to identify abusable sessions on domain-joined systems. These sessions are vulnerable to [OS Credential Dumping](https://attack.mitre.org/techniques/T1003/001/) from tools such as [Mimikatz](https://github.com/ParrotSec/mimikatz). **Collection Method:** SharpHound collects this information utilizing the [NetWkstaUserEnum](https://learn.microsoft.com/en-us/windows/win32/api/lmwksta/nf-lmwksta-netwkstauserenum) function. **Default Permissions:** Members of the local `Administrators` group may call this function. **Least-Privileged Options:** * On Windows Server operating systems, members of the local `Print Operators` group may also collect session data * Windows desktop operating systems do not have a local `Print Operators` group and require alternate approaches When using `Print Operators` for collection, you should remove dangerous default User Rights Assignments (`SeInteractiveLogonRight`, `SeLoadDriverPrivilege`, `SeShutdownPrivilege`) from this group to prevent privilege escalation on DCs. For detailed configuration instructions, including how to safely configure `Print Operators`, see [Least-Privileged Collection - Sessions](/collect-data/enterprise-collection/least-privileged-collection#sessions). ## Certificate Services Information about the Active Directory Certificate Service hierarchy within your Active Directory environment makes up the basic information necessary to identify ADCS attack paths within your environment. This information includes: * Certificate Templates * Root CAs * Enterprise CAs **Collection Method:** SharpHound collects this information utilizing signed LDAP queries against a domain controller in the domain. **Default Permissions:** By default, `Authenticated Users` can enumerate almost all Certificate Services data utilized by BloodHound Enterprise. **Additional Data Sources:** Two additional types of data can enhance the findings - [DC Registry](/collect-data/sharphound-data-permissions#dc-registry) and [CA Registry](/collect-data/sharphound-data-permissions#ca-registry). ## CA Registry SharpHound collects the following registry key values on enterprise CAs stored under `SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\`: * **EnrollmentAgentRights**: Contains restrictions for enrollment agents. BloodHound will take the restrictions into account when calculating ADCS ESC3 edges, and assume no restrictions if not collected, as no restrictions are configured by default. * **Security**: Contains the security descriptor for the enterprise CA (i.e., the permissions for Enroll, ManageCA, and ManageCertificates edges against the enterprise CA). This security descriptor is also stored in the AD object of the enterprise CA. SharpHound collects both. The CA registry security descriptor holds the effective permissions. Changes in the CA registry security descriptor are replicated to the AD copy, however, not the other way. Therefore, collecting the CA registry security descriptor may reveal permissions of the enterprise CA that are not present if only collecting the AD object. * **PolicyModules\\\\EditFlags**: SharpHound checks if the `EDITF_ATTRIBUTESUBJECTALTNAME2` flag is present, which is required to calculate ADCS ESC6 edges. * **RoleSeparationEnabled**: SharpHound checks whether role separation is enforced (a user cannot be both CA Administrator and Certificate Manager). The registry key values are described in detail in the [Certified Pre-Owned whitepaper](https://specterops.io/wp-content/uploads/sites/3/2022/06/Certified_Pre-Owned.pdf). **Collection Method:** SharpHound collects these registry key values via remote registry access on enterprise CAs. **Default Permissions:** `Authenticated Users` may collect these registry key values by default, see [Least-Privileged Collection - CA Registry](/collect-data/enterprise-collection/least-privileged-collection#ca-registry) for an explanation of why this is accessible. ## DC Registry/LDAP Services ### DC Registry SharpHound collects the registry values `Kdc\StrongCertificateBindingEnforcement` and `Schannel\CertificateMappingMethods` (described [here](https://support.microsoft.com/en-us/topic/kb5014754-certificate-based-authentication-changes-on-windows-domain-controllers-ad2c23b0-15d8-4340-a468-4d4f3b188f16)) to determine the allowed certificate mapping methods on domain controllers (DCs). The BloodHound ADCS edges ESC6, ESC9, and ESC10 require this data to be collected. SharpHound Enterprise additionally collects the `VulnerableChannelAllowList` value under `SYSTEM\CurrentControlSet\Services\Netlogon\Parameters` (described [here](https://support.microsoft.com/en-us/topic/how-to-manage-the-changes-in-netlogon-secure-channel-connections-associated-with-cve-2020-1472-f7e8cc17-0309-1d6a-304e-5ba73cd1a11e#theGroupPolicy)) to determine which accounts are allowed to use Netlogon secure channel connections without secure RPC. **Collection Method:** SharpHound collects these registry key values via remote registry access. **Default Permissions:** Collecting these registry key values requires membership of `Administrators` on the DCs by default. **Least-Privileged Option:** Delegation is possible via Group Policy or registry modifications, see [Least-Privileged Collection - DC Registry](/collect-data/enterprise-collection/least-privileged-collection#dc-registry). ### LDAP Services SharpHound collects LDAP service configuration information from domain controllers. **Collection Method:** SharpHound collects this information by performing NTLM authentication tests against all domain controllers by default on ports 389 (LDAP) and 636 (LDAPS). **Default Permissions:** No special directory permissions are required beyond valid domain credentials and network access. # BloodHound Community Edition Custom Installation Source: https://bloodhound.specterops.io/get-started/custom-installation Learn how to install and customize BloodHound Community Edition (BHCE). Applies to BloodHound CE only The recommended installation method for most users is the [BloodHound CLI](/get-started/quickstart/community-edition-quickstart). However, if you need more control over the installation process or want to customize specific aspects of your BHCE environment, this guide provides alternative installation methods and customization options. Use cases for custom installation include: * Switch between PostgreSQL and Neo4j backends * Run multiple BHCE instances on a single machine ## Prerequisites BloodHound CE deploys in a traditional multi-tier container architecture consisting of database, application, and UI layers. To complete installation, ensure your system meets the following requirements: | Minimum specifications | For large environments (>50K users) | | ---------------------- | ----------------------------------- | | 8GB of RAM | 96GB of RAM | | 4 processor cores | 12 processor cores | | 10GB hard disk space | 50GB hard disk space | During startup, BloodHound CE runs initial graph analysis that may continue for about the first minute after launch. On low-memory hosts, sending API requests immediately after container startup can cause the `bloodhound` container to terminate with exit code `137` (out of memory). To avoid this behavior, allocate at least **8GB of RAM** or wait for startup processing to complete before running API automation tasks such as user creation. ## Install with Docker Compose BloodHound Community Edition is a security auditing tool that was written to test the resilience of networks against attackers. Because this tool can equally be used for evil, some anti-malware and endpoint detection and response (EDR) solutions flag BloodHound and its components as **malware**. If you encounter issues with downloads being blocked and files being prohibited from execution, create targeted allow-list entries for the specific BloodHound binaries, supporting libraries, and installation paths required for your deployment. We recommend that you set up BloodHound on a dedicated machine so that your regular work environment remains protected. If you are planning to use BloodHound on a corporate network, notify your Security Operations Center (SOC) or Chief Information Security Officer (CISO) ahead of time and ensure you have the required permissions to audit the network. For legal and ethical reasons, you must never use BloodHound on systems you do not own or lack explicit permission to audit. This installation method provides more control over each configuration file and works well for running multiple BHCE instances on a single machine. Follow the instructions in the [Docker documentation](https://docs.docker.com/get-started/get-docker/) to install Docker Desktop for your operating system. Docker Desktop must be running to build and test BHCE. Start Docker Desktop on your machine. Create a new directory on your machine to hold the BHCE configuration files. ```bash theme={null} mkdir bhce cd bhce ``` Download the following configuration files: * [`docker-compose.yml`](https://github.com/SpecterOps/BloodHound/blob/main/examples/docker-compose/docker-compose.yml) * [`bloodhound.config.json`](https://github.com/SpecterOps/BloodHound/blob/main/examples/docker-compose/bloodhound.config.json) Move the configuration files into the directory you created. ```bash theme={null} mv /path/to/downloaded/files/* . ``` Continue to the [Customizations](/get-started/custom-installation#customizations) section below to modify your installation as needed. Now that your files are ready, you can bring the containers up using the following command: ```bash theme={null} docker compose up ``` You only need to perform this step once. In the future, you can use the Docker Desktop application to start and turn off BloodHound. You can still use the CLI to bring the containers `up` or `down` if you prefer. ## Build from source You can also build the BHCE code from source if you plan on contributing to the project or customizing the application beyond what is possible with configuration files. ### Prerequisite The following table lists the minimum requirements to build BHCE from source: These requirements are higher than the minimum specifications needed to *run* BHCE. | Requirement | Version/Specification | | ----------------------------------------------------------------- | --------------------- | | RAM | 16GB | | Processor cores | 8 | | [Just](https://github.com/casey/just) | --- | | [Python](https://www.python.org/downloads/) | 3.10 | | [Go](https://go.dev/dl/) | 1.24 | | [Node.js](https://nodejs.dev/en/download/) | 22 | | [Yarn](https://yarnpkg.com/getting-started/install) | 3.6 | | [Docker Desktop](https://www.docker.com/products/docker-desktop/) | --- | ### Set up your environment The code repository contains all the necessary files to build BHCE. Follow these steps to set up your development environment: Clone the BloodHound repository from GitHub: ```bash theme={null} git clone https://github.com/SpecterOps/BloodHound.git ``` Docker Desktop must be running to build and test BHCE. Start Docker Desktop on your machine. Change to the cloned BloodHound repository directory: ```bash theme={null} cd BloodHound ``` Use the following command to install project dependencies and initialize the environment: ```bash theme={null} just init ``` Use the following command to start the development environment: ```bash theme={null} just bh-dev ``` The BloodHound team maintains a Python tool called [`stbernard`](https://github.com/SpecterOps/BloodHound/wiki/packages/go/stbernard/README.md) for building and testing the project. To build locally, run the following command: ```bash theme={null} just build ``` The build process generates all artifacts in the `dist/` directory. See the following resources for next steps: * [Source code wiki](https://github.com/SpecterOps/BloodHound/wiki/Development#quick-start) on GitHub for testing and debugging details. * [Customizations](/get-started/custom-installation#customizations) section below to modify your installation as needed. ## Customizations This section describes common customizations you can make to your BHCE installation. ### Change backend database PostgreSQL provides significant advantages over Neo4j as a backend database, particularly in terms of query performance and speed. For information on supported Cypher syntax in PostgreSQL, see [Supported Cypher Syntax](/analyze-data/cypher-supported). #### PostgreSQL If you are currently using a Neo4j backend database and want to change to PostgreSQL, follow these steps. We recommend using PostgreSQL 18 for new installations. To upgrade an existing installation from PostgreSQL 16 to 18, see [Upgrade PostgreSQL](/get-started/upgrade-postgres). Modify the `bloodhound.config.json` file and add a line in the main section to use PostgreSQL as the graph driver: ```json theme={null} ... "graph_driver": "pg", ... ``` This is an example of adding the line between `default_password` and `log_level`, but it can be anywhere at the top level. ```json theme={null} { "bind_addr": "127.0.0.1:8080", "collectors_base_path": "/etc/bloodhound/collectors", "default_admin": { "password": "", "principal_name": "admin" }, "default_password": "", "graph_driver": "pg", // [!code ++] "log_level": "INFO", "log_path": "bloodhound.log", "metrics_port": ":2112", "recreatedefaultadmin": "false", "root_url": "http://127.0.0.1:8080", "tls": { "cert_file": "", "key_file": "" }, "version": 1, "work_dir": "/opt/bloodhound/work" } ``` Modify the `docker-compose.yml` file to remove the Neo4j service and its dependencies. * Delete the `graph_db` section: ```yaml theme={null} graph-db: # [!code --:23] labels: name: "bhce_neo4j" image: docker.io/library/neo4j:4.4 environment: - NEO4J_AUTH=${NEO4J_USER:-neo4j}/${NEO4J_SECRET:-bloodhoundcommunityedition} - NEO4J_dbms_allow__upgrade=${NEO4J_ALLOW_UPGRADE:-true} # Database ports are disabled by default. Please change your database password to something secure before uncommenting ports: - 127.0.0.1:${NEO4J_DB_PORT:-7687}:7687 - 127.0.0.1:${NEO4J_WEB_PORT:-7474}:7474 volumes: - ${NEO4J_DATA_MOUNT:-neo4j-data}:/data healthcheck: test: [ "CMD-SHELL", "wget -O /dev/null -q http://localhost:7474 || exit 1" ] interval: 10s timeout: 5s retries: 5 start_period: 30s ``` * Delete the two lines at the end of the `bloodhound` section: ```yaml theme={null} graph-db: # [!code --:2] condition: service_healthy ``` * Delete the following line from the `volumes` section: ```yaml theme={null} neo4j-data: # [!code --] ``` The following is the complete modified `docker-compose.yml` file for PostgreSQL: ```yaml theme={null} # Copyright 2023 Specter Ops, Inc. # # Licensed under the Apache License, Version 2.0 # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 services: app-db: image: docker.io/library/postgres:18 environment: - PGUSER=${POSTGRES_USER:-bloodhound} - POSTGRES_USER=${POSTGRES_USER:-bloodhound} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-bloodhoundcommunityedition} - POSTGRES_DB=${POSTGRES_DB:-bloodhound} # Database ports are disabled by default. Please change your database password to something secure before uncommenting # ports: # - 127.0.0.1:${POSTGRES_PORT:-5432}:5432 volumes: - postgres-data:/var/lib/postgresql healthcheck: test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-bloodhound} -d ${POSTGRES_DB:-bloodhound} -h 127.0.0.1 -p 5432" ] interval: 10s timeout: 5s retries: 5 start_period: 30s bloodhound: image: docker.io/specterops/bloodhound:${BLOODHOUND_TAG:-latest} environment: - bhe_disable_cypher_complexity_limit=${bhe_disable_cypher_complexity_limit:-false} - bhe_enable_cypher_mutations=${bhe_enable_cypher_mutations:-false} - bhe_graph_query_memory_limit=${bhe_graph_query_memory_limit:-2} - bhe_database_connection=user=${POSTGRES_USER:-bloodhound} password=${POSTGRES_PASSWORD:-bloodhoundcommunityedition} dbname=${POSTGRES_DB:-bloodhound} host=app-db - bhe_neo4j_connection=neo4j://${NEO4J_USER:-neo4j}:${NEO4J_SECRET:-bloodhoundcommunityedition}@graph-db:7687/ - bhe_recreate_default_admin=${bhe_recreate_default_admin:-false} - bhe_graph_driver=${GRAPH_DRIVER:-neo4j} ### Add additional environment variables you wish to use here. ### For common configuration options that you might want to use environment variables for, see `.env.example` ### example: bhe_database_connection=${bhe_database_connection} ### The left side is the environment variable you're setting for bloodhound, the variable on the right in `${}` ### is the variable available outside of Docker ports: ### Default to localhost to prevent accidental publishing of the service to your outer networks ### These can be modified by your .env file or by setting the environment variables in your Docker host OS - ${BLOODHOUND_HOST:-127.0.0.1}:${BLOODHOUND_PORT:-8080}:8080 ### Uncomment to use your own bloodhound.config.json to configure the application # volumes: # - ./bloodhound.config.json:/bloodhound.config.json:ro depends_on: app-db: condition: service_healthy volumes: postgres-data: ``` #### Neo4j If you are currently using a PostgreSQL backend database and want to change to Neo4j, follow these steps. Replace your `docker-compose.yml` file with the following: ```yaml theme={null} # Copyright 2023 Specter Ops, Inc. # # Licensed under the Apache License, Version 2.0 # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 services: app-db: labels: name: "bhce_postgres" image: docker.io/library/postgres:18 environment: - PGUSER=${POSTGRES_USER:-bloodhound} - POSTGRES_USER=${POSTGRES_USER:-bloodhound} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-bloodhoundcommunityedition} - POSTGRES_DB=${POSTGRES_DB:-bloodhound} # Database ports are disabled by default. Please change your database password to something secure before uncommenting # ports: # - 127.0.0.1:${POSTGRES_PORT:-5432}:5432 volumes: - postgres-data:/var/lib/postgresql healthcheck: test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-bloodhound} -d ${POSTGRES_DB:-bloodhound} -h 127.0.0.1 -p 5432" ] interval: 10s timeout: 5s retries: 5 start_period: 30s graph-db: labels: name: "bhce_neo4j" image: docker.io/library/neo4j:4.4 environment: - NEO4J_AUTH=${NEO4J_USER:-neo4j}/${NEO4J_SECRET:-bloodhoundcommunityedition} - NEO4J_dbms_allow__upgrade=${NEO4J_ALLOW_UPGRADE:-true} # Database ports are disabled by default. Please change your database password to something secure before uncommenting ports: - 127.0.0.1:${NEO4J_DB_PORT:-7687}:7687 - 127.0.0.1:${NEO4J_WEB_PORT:-7474}:7474 volumes: - ${NEO4J_DATA_MOUNT:-neo4j-data}:/data healthcheck: test: [ "CMD-SHELL", "wget -O /dev/null -q http://localhost:7474 || exit 1" ] interval: 10s timeout: 5s retries: 5 start_period: 30s bloodhound: labels: name: bhce_bloodhound image: docker.io/specterops/bloodhound:${BLOODHOUND_TAG:-latest} environment: - bhe_disable_cypher_complexity_limit=${bhe_disable_cypher_complexity_limit:-false} - bhe_enable_cypher_mutations=${bhe_enable_cypher_mutations:-true} - bhe_graph_query_memory_limit=${bhe_graph_query_memory_limit:-2} - bhe_database_connection=user=${POSTGRES_USER:-bloodhound} password=${POSTGRES_PASSWORD:-bloodhoundcommunityedition} dbname=${POSTGRES_DB:-bloodhound} host=app-db - bhe_neo4j_connection=neo4j://${NEO4J_USER:-neo4j}:${NEO4J_SECRET:-bloodhoundcommunityedition}@graph-db:7687/ - bhe_recreate_default_admin=${bhe_recreate_default_admin:-false} - bhe_enable_text_logger=${bhe_enable_text_logger:-true} ### Add additional environment variables you wish to use here. ### For common configuration options that you might want to use environment variables for, see `.env.example` ### example: bhe_database_connection=${bhe_database_connection} ### The left side is the environment variable you're setting for bloodhound, the variable on the right in `${}` ### is the variable available outside of Docker ports: ### Default to localhost to prevent accidental publishing of the service to your outer networks ### These can be modified by your .env file or by setting the environment variables in your Docker host OS - ${BLOODHOUND_HOST:-127.0.0.1}:${BLOODHOUND_PORT:-8080}:8080 ### Uncomment to use your own bloodhound.config.json to configure the application volumes: - ./bloodhound.config.json:/bloodhound.config.json:ro depends_on: app-db: condition: service_healthy graph-db: condition: service_healthy volumes: neo4j-data: postgres-data: ``` Delete the following line from the `bloodhound.config.json` file: ```json theme={null} "graph_driver": "pg", // [!code --] ``` ### Enable Transport Layer Security (TLS) To secure BHCE with HTTPS, add your certificate information to the `tls` block in the [`bloodhound.config.json`](https://github.com/SpecterOps/BloodHound/blob/main/examples/docker-compose/bloodhound.config.json) file. Then, make the certificate files available in the BloodHound container with volume mounts in the [`docker-compose.yml`](https://github.com/SpecterOps/BloodHound/blob/main/examples/docker-compose/docker-compose.yml) file. After you set both `cert_file` and `key_file`, BloodHound uses HTTPS on that port instead of HTTP. For a local or testing deployment, generate a self-signed certificate and key with `openssl`: ```bash theme={null} openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:4096 \ -keyout private_key.pem -out certificate.pem \ -subj "/CN=your_domain.com" ``` Self-signed certificates are appropriate for local testing only. For any shared or production deployment, use a certificate issued by a trusted certificate authority (CA). Create a directory next to your `docker-compose.yml` and `bloodhound.config.json` files and move the certificate and key into it. The examples on this page use a directory named `cert`: ```bash theme={null} mkdir cert mv certificate.pem private_key.pem cert/ ``` In the `bloodhound.config.json` file, set `cert_file` and `key_file` to the paths where the certificate and key will be available **inside the container**. These values must match the container-side destination of the volume mount you add in the next step, not the location of the files on your host: ```json title="bloodhound.config.json" theme={null} "tls": { "cert_file": "/cert/certificate.pem", // [!code ++] "key_file": "/cert/private_key.pem" // [!code ++] }, ``` In the `docker-compose.yml` file, mount both the `bloodhound.config.json` file and the `cert` directory into the `bloodhound` service as read-only volumes: ```yaml title="docker-compose.yml" theme={null} bloodhound: # ... volumes: # [!code ++] - ./bloodhound.config.json:/bloodhound.config.json:ro # [!code ++] - ./cert:/cert:ro # [!code ++] ``` By default, the `volume` mount for the `bloodhound` service is commented out. Make sure to uncomment it before you continue. Otherwise, the container ignores your custom configuration and continues to serve plain HTTP. Bring up (or recreate) the `bloodhound` container so the new volume mounts and configuration take effect: ```bash theme={null} docker compose up -d --force-recreate bloodhound ``` The `--force-recreate` option ensures the container picks up changes to volumes and configuration. A plain `docker compose restart` does not reapply volume mounts. Browse to `https://127.0.0.1:8080` and confirm that BloodHound loads over HTTPS. The default URL is `http://127.0.0.1:8080`, so make sure to change the protocol to `https://` in the address bar. The listening port is unchanged. TLS is negotiated on the same port that the BloodHound service binds to by default (`8080`). Browsers display a certificate warning when you use a self-signed certificate; this is expected and does not occur with a CA-issued certificate. ### Run multiple instances simultaneously You might want to run multiple BHCE instances to: * Test a Neo4j backend alongside a PostgreSQL backend * Evaluate a new version of BHCE without affecting your current installation * Isolate data for different engagements You must bind each instance to a different port on your machine. The steps below show you which settings to edit. Follow these steps to run multiple BHCE instances on a single machine. In the `docker-compose.yml` file, update the BloodHound service port binding to use a different port. The following example uses port `8585` instead of the default `8080` port: ```yaml theme={null} - ${BLOODHOUND_HOST:-127.0.0.1}:${BLOODHOUND_PORT:-8585}:8080 ``` If you are running multiple Neo4j databases, update the Neo4j web console port binding in the `docker-compose.yml` file to use a different port. The following example uses port `7575` instead of the default `7474` port: ```yaml theme={null} - 127.0.0.1:${NEO4J_WEB_PORT:-7575}:7474 ``` You must also update the health check to use the new port: ```yaml theme={null} "wget -O /dev/null -q http://localhost:7575 || exit 1" ``` The Neo4j database port `7687` runs only inside the Docker container, so you do not need to change it. ### Expose BHCE outside of `localhost` You might need to access your BHCE instance from another computer than the one it is installed on. The default installation does not expose the port outside of `localhost`. To do it, you will need to change the IP address that the BloodHound UI binds to. In the `docker-compose.yml` file, update the BloodHound service port binding to use a different IP Address. The following example uses IP `0.0.0.0` to bind the port `8080` to *all* interfaces and IP Addresses on the machine. If the machine has multiple IP Addresses, you can set that specific IP Address. ```yaml theme={null} - ${BLOODHOUND_HOST:-0.0.0.0}:${BLOODHOUND_PORT:-8080}:8080 ``` Binding to `0.0.0.0` can expose BloodHound to untrusted networks. Only do this behind appropriate network controls (for example, firewall rules, VPN, or a restricted reverse proxy), and ensure HTTPS and strong non-default credentials are in place. # Introduction to BloodHound Source: https://bloodhound.specterops.io/get-started/introduction BloodHound uses graph theory to reveal hidden and often unintended relationships within Active Directory and Entra ID (formerly Azure Active Directory). Defenders (blue teams) and attackers (red teams) use BloodHound for a deeper understanding of privileged relationships in an environment. With the introduction of [OpenGraph](/opengraph/overview) in BloodHound v8.0, you can extend BloodHound's capabilities beyond Active Directory and Entra ID to visualize attack paths across hybrid environments. OpenGraph is a powerful framework that enables you to: * **Extend coverage** to identity services across other platforms (for example, GitHub, Okta, Jamf, and more) * **Build custom collectors** that ingest data using a standardized JSON schema There are two BloodHound products: BloodHound Enterprise and BloodHound Community Edition (BloodHound CE). This site documents both products. New to BloodHound? Take the on-demand [BloodHound Basics](https://academy.specterops.io/bloodhound-basics) course to build a practical, ground-up understanding of attack path management with BloodHound Community Edition and BloodHound Enterprise. ## BloodHound Enterprise vs BloodHound Community Edition
BloodHound Enterprise logo BloodHound Community Edition logo
With BloodHound Enterprise, continuously monitor, prioritize, and eliminate attack path risk across your environment. With BloodHound Community Edition, identify, test, and validate attack path risk.
A fully-managed SaaS application for blue teams focused on risk management. An offering for red teams focused on security testing.
Leverage features like scheduled data collection and data reconciliation.  
Manage attack paths over time with exposure measurement and remediation guidance.  
24/7/365 enterprise support. No dedicated support.
Get started Get started
See the [full feature comparison](https://specterops.io/bloodhound-feature-comparison/). ## BloodHound Enterprise BloodHound Enterprise is a fully deployed and secured SaaS offering by SpecterOps to address the need for Attack Path Management. It requires requires no additional installation or maintenance. [Attack Path Management](https://specterops.io/what-is-attack-path-management/) is a framework designed to help organizations measure and remediate the risk created by attack paths. With BloodHound Enterprise, leverage ongoing collection, data reconciliation, and analysis to manage risk following the Attack Path Management framework. It's the only tool available that helps defenders easily identify and eliminate highly complex attack paths that would otherwise be impossible to manage. [Get started](/get-started/quickstart/enterprise-quickstart) [Request a demo](https://specterops.io/get-a-demo/) ## BloodHound Community Edition BloodHound CE is free, open-source, and focused on enabling penetration testers and red teams to rapidly evaluate attack paths within Active Directory and Entra ID (formerly Azure AD). [Get started](/get-started/quickstart/community-edition-quickstart) ## Need help? [**Submit a request**](https://support.bloodhoundenterprise.io/requests/new) for dedicated support or ask in the [**BloodHound user Slack community**](https://slack.specterops.io). Ask in the [**BloodHound user Slack community**](https://slack.specterops.io). # BloodHound Community Edition Sample Data Source: https://bloodhound.specterops.io/get-started/quickstart/ce-ingest-sample-data Applies to BloodHound CE only # Ingest Sample Data Download sample data for [Active Directory](https://raw.githubusercontent.com/SpecterOps/BloodHound-Docs/main/docs/assets/sample-data/ad_sampledata.zip). **Active Directory sample data** generated with SharpHound includes: * 3 collected domains with trusts between them * Additional, visible, trusted domains without collections * Coverage for local permissions * Multiple ADCS escalation paths Download sample data for [Azure](https://raw.githubusercontent.com/SpecterOps/BloodHound-Docs/main/docs/assets/sample-data/entra_sampledata.zip). **Azure sample data** generated with AzureHound includes: * Full collection of an Azure environment * Support for user-sync hybrid paths when ingested alongside the example AD data 1. Log in to the BloodHound CE UI. 2. In the left menu, click **Quick Upload** 3. Click the **Upload Files** modal to open a file system dialog or drag and drop the downloaded sample data ZIP file. 4. Click **Upload** to begin the data ingest process. The default admin email is `admin@example.com` and will appear as the user ingesting the data. # BloodHound Community Edition Quickstart Source: https://bloodhound.specterops.io/get-started/quickstart/community-edition-quickstart Set up a local instance of BloodHound Community Edition and start identifying and visualizing security risks in your environment. Applies to BloodHound CE only This quickstart guide walks you through installing BloodHound Community Edition (BloodHound CE) using the BloodHound CLI (BH-CLI), which is a wrapper around Docker Compose. After installation, you'll learn how to ingest data into BloodHound CE and explore attack paths in your environment. Are you a blue team member looking to remediate identity risks? [Request a demo of BloodHound Enterprise](https://specterops.io/get-a-demo/). ## Prerequisites BloodHound CE deploys in a traditional multi-tier container architecture consisting of database, application, and UI layers. To complete installation, ensure your system meets the following requirements: | Minimum specifications | For large environments (>50K users) | | ---------------------- | ----------------------------------- | | 8GB of RAM | 96GB of RAM | | 4 processor cores | 12 processor cores | | 10GB hard disk space | 50GB hard disk space | During startup, BloodHound CE runs initial graph analysis that may continue for about the first minute after launch. On low-memory hosts, sending API requests immediately after container startup can cause the `bloodhound` container to terminate with exit code `137` (out of memory). To avoid this behavior, allocate at least **8GB of RAM** or wait for startup processing to complete before running API automation tasks such as user creation. ## Install BloodHound CE BloodHound Community Edition is a security auditing tool that was written to test the resilience of networks against attackers. Because this tool can equally be used for evil, some anti-malware and endpoint detection and response (EDR) solutions flag BloodHound and its components as **malware**. If you encounter issues with downloads being blocked and files being prohibited from execution, create targeted allow-list entries for the specific BloodHound binaries, supporting libraries, and installation paths required for your deployment. We recommend that you set up BloodHound on a dedicated machine so that your regular work environment remains protected. If you are planning to use BloodHound on a corporate network, notify your Security Operations Center (SOC) or Chief Information Security Officer (CISO) ahead of time and ensure you have the required permissions to audit the network. For legal and ethical reasons, you must never use BloodHound on systems you do not own or lack explicit permission to audit. Installing BloodHound CE with the BloodHound CLI is the easiest way to get started. The CLI handles downloading the necessary Docker images and creating the `docker-compose.yml` file with standard defaults. For ease and convenience, we recommend installing [Docker Desktop](https://www.docker.com/get-started) to run BloodHound CE containers on your local machine. Download the latest release of **[BloodHound CLI](https://github.com/SpecterOps/bloodhound-cli/releases)** for your operating system and architecture (AMD or ARM). BloodHound CLI is a utility that makes it easy to install BloodHound CE in containers on your machine. To avoid the software getting blocked as malware in the browser, we recommend downloading it using the command line. ```bash Linux theme={null} wget https://github.com/SpecterOps/bloodhound-cli/releases/latest/download/bloodhound-cli-linux-amd64.tar.gz ``` ```powershell Windows PowerShell theme={null} curl.exe -L -o "$env:USERPROFILE\Downloads\bloodhound-cli-windows-amd64.zip" https://github.com/SpecterOps/bloodhound-cli/releases/latest/download/bloodhound-cli-windows-amd64.zip ``` ```bash Mac theme={null} curl -L -o bloodhound-cli-darwin-arm64.tar.gz https://github.com/SpecterOps/bloodhound-cli/releases/latest/download/bloodhound-cli-darwin-arm64.tar.gz ``` Change to the directory where you downloaded the file and unpack it. ```bash Linux theme={null} tar -xvzf bloodhound-cli-linux-amd64.tar.gz ``` ```powershell Windows PowerShell theme={null} cd "$env:USERPROFILE\Downloads"; tar -xf bloodhound-cli-windows-amd64.zip ``` ```bash Mac theme={null} tar -xvzf bloodhound-cli-darwin-arm64.tar.gz ``` In your terminal or PowerShell, navigate to the directory where you unpacked the BloodHound CLI and install BloodHound CE: ```bash Linux theme={null} ./bloodhound-cli install ``` ```powershell Windows PowerShell theme={null} .\bloodhound-cli install ``` ```bash Mac theme={null} ./bloodhound-cli install ``` Encountering issues? See [troubleshooting](#troubleshooting). Keep your terminal open until you see the randomly generated password displayed. Save this password for the next step. ```bash theme={null} [+] BloodHound is ready to go! [+] You can log in as `admin` with this password: ``` If you lose the password, reset it with: ```bash Linux theme={null} ./bloodhound-cli resetpwd ``` ```powershell Windows PowerShell theme={null} .\bloodhound-cli resetpwd ``` ```bash Mac theme={null} ./bloodhound-cli resetpwd ``` In a browser, go to [http://localhost:8080/ui/login](http://localhost:8080/ui/login) and log in with the `admin` username and the randomly generated password. The default `docker-compose.yml` file binds only to localhost (127.0.0.1). To access BloodHound outside localhost, follow the instructions in [examples/docker-compose/README.md](https://github.com/SpecterOps/BloodHound/blob/main/examples/docker-compose/README.md). Reset your password as prompted on first login. ## Get data into BloodHound To get data into BloodHound, ingest sample data or collect data from your environment with either standalone collectors or OpenHound. Use sample data to quickly explore BloodHound CE without setting up a SharpHound or AzureHound data collector. Download sample data for [Active Directory](https://raw.githubusercontent.com/SpecterOps/BloodHound-Docs/main/docs/assets/sample-data/ad_sampledata.zip). **Active Directory sample data** generated with SharpHound includes: * 3 collected domains with trusts between them * Additional, visible, trusted domains without collections * Coverage for local permissions * Multiple ADCS escalation paths Download sample data for [Azure](https://raw.githubusercontent.com/SpecterOps/BloodHound-Docs/main/docs/assets/sample-data/entra_sampledata.zip). **Azure sample data** generated with AzureHound includes: * Full collection of an Azure environment * Support for user-sync hybrid paths when ingested alongside the example AD data 1. Log in to the BloodHound CE UI. 2. In the left menu, click **Quick Upload** 3. Click the **Upload Files** modal to open a file system dialog or drag and drop the downloaded sample data ZIP file. 4. Click **Upload** to begin the data ingest process. The default admin email is `admin@example.com` and will appear as the user ingesting the data. There are two main ways to collect data for BloodHound CE: * Active Directory, collected by SharpHound CE * Entra ID (formerly Azure AD) and Azure IaaS, collected by AzureHound CE * Github, Jamf, and Okta, collected by OpenHound CLI for BloodHound CE Use [OpenHound](/openhound/overview) for built-in collectors and workflows for platforms like GitHub, Jamf, and Okta. For additional community-built collectors, explore the [OpenGraph Library](/opengraph/library). Pick the collection method that best fits your environment: * Use **SharpHound CE** for Active Directory and **AzureHound CE** for Entra ID. * Use **OpenHound CLI** for supported SaaS and platform data sources, such as Github, Jamf, and Okta. Each collector is a standalone binary. You can download SharpHound CE and AzureHound CE using one of the following methods: To install the OpenHound CLI and configure supported collectors, see [OpenHound Community](/openhound/community). Download the collectors directly from the BloodHound CE UI: 1. Log in to the BloodHound CE UI. 2. In the left menu, click **Download Collectors**. 3. Click one of the links in the **Community Collectors** section to download the SharpHound or AzureHound binary. Download the latest releases from GitHub: * [SharpHound releases](https://github.com/SpecterOps/SharpHound/releases/latest) * [AzureHound releases](https://github.com/SpecterOps/AzureHound/releases/latest) Build the collector from the data collector source code * [SharpHound](/collect-data/ce-collection/sharphound) * [AzureHound](/collect-data/ce-collection/azurehound) Start the collector to generate and compress JSON files into a ZIP archive. ```powershell SharpHound CE theme={null} # Run SharpHound CE from a domain-joined Windows system C:\> SharpHound.exe ``` ```powershell AzureHound CE theme={null} # Provide your credentials and tenant information to run AzureHound CE C:\> AzureHound.exe --username "MattNelson@contoso.onmicrosoft.com" --password "MyVeryStrongPassword" --tenant "contoso.onmicrosoft.com" list ``` ```bash OpenHound CLI theme={null} # Collect data with OpenHound CLI for BloodHound CE openhound collect okta ./output # Convert collected data to OpenGraph format for BloodHound CE openhound convert okta ./output/okta ./graph/okta ``` For command options, see: * [SharpHound Community Edition Flags](/collect-data/ce-collection/sharphound-flags) * [AzureHound Community Edition Flags](/collect-data/ce-collection/azurehound-flags) * [OpenHound CLI](/openhound/community#cli-commands) Use the BloodHound CE API or the BloodHound CE UI to ingest collected data into BloodHound. To ingest collected data with the API, use the BloodHound CE endpoint `/api/v2/file-upload/`. See the [BloodHound API documentation](/integrations/bloodhound-api/working-with-api) for details. To ingest collected data with the BloodHound CE UI, go to **Administration** > **Data Collection** > **File Ingest** and click **Upload File(s)** to upload your files. BloodHound CE accepts .zip archives or JSON files, with no size limit. Your browser's ability to package the uploaded file is a limiting factor in uploading large datasets directly through the UI. ## Explore attack paths To look at identified attack paths in the graph, go to the **Explore** page in the BloodHound CE UI. 1. In the **Search** bar, search nodes for a user object. * For Active Directory sample data, enter `user:bob`. * For Azure sample data, enter `azuser:bob`. 2. Select the user and click on the node that appears. 3. Explore information about the user's sessions and memberships. Review the path from one user to another on the **Pathfinding** tab. For example, pathfind from `BOB@PHANTOM.CORP` to `ADMINISTRATORS@PHANTOM.CORP`. Explore the pre-saved Cypher queries on the **Cypher** tab. Learn more in [Explore → Search for Objects](/analyze-data/explore/search). ## Update BloodHound CE The easiest way to update your instance of BloodHound Community Edition is via `bloodhound-cli`. ```bash Linux theme={null} ./bloodhound-cli update ``` ```powershell Windows PowerShell theme={null} .\bloodhound-cli update ``` ```bash Mac theme={null} ./bloodhound-cli update ``` ## Next steps * [Learn how to work with the BloodHound API](/integrations/bloodhound-api/working-with-api) * [Configure BloodHound integrations](/integrations/overview) ## Troubleshooting If you encounter issues during installation, refer to the following common problems and solutions. When running `./bloodhound-cli install`, you may see an error stating that Apple could not verify the binary is free of malware. This is a standard macOS security check for unsigned or unnotarized applications. **Terminal (Quick Fix)** 1. Clear the quarantine flag by running: ```bash theme={null} xattr -d com.apple.quarantine ./bloodhound-cli ``` 2. Repeat the CLI command: `./bloodhound-cli install` **System Settings (GUI)** 1. Go to **System Settings** (or **System Preferences** on older macOS versions) 2. Navigate to **Privacy & Security** 3. Scroll down to the **Security** section 4. You should see a message stating that bloodhound-cli was blocked 5. Click **Allow Anyway** 6. Repeat the CLI command: `./bloodhound-cli install` 7. Click **Open Anyway** when prompted 8. Enter your password or use your fingerprint to confirm This error occurs when macOS blocks Docker's networking component. Resolve it by reinstalling Docker: 1. Follow the [Docker uninstall instructions](https://docs.docker.com/desktop/uninstall/) (select your operating system tab) 2. Re-install [Docker Desktop](https://www.docker.com/get-started/) 3. Repeat the CLI command: `./bloodhound-cli install` If you see an error stating "Docker is installed on this system, but the daemon is not running": 1. Simply launch **Docker Desktop** from your Applications folder 2. Wait for Docker to fully start 3. Repeat the CLI command: `./bloodhound-cli install` # BloodHound Enterprise Quickstart Source: https://bloodhound.specterops.io/get-started/quickstart/enterprise-quickstart Applies to BloodHound Enterprise only Get started with your BloodHound Enterprise instance and start identifying and remediating security risks. # Prerequisites To complete this quickstart, you must have a BloodHound Enterprise instance. To connect with the SpecterOps team and receive an instance, [request a demo of BloodHound Enterprise](https://specterops.io/get-a-demo/). # Get data into BloodHound BloodHound Enterprise supports multiple data collection paths. Use the path that matches the directories and platforms you want to analyze: * Active Directory, collected by SharpHound Enterprise * Entra ID (formerly Azure AD) and Azure IaaS, collected by AzureHound Enterprise * Github, Jamf, and Okta, collected by OpenHound for BloodHound Enterprise You can run SharpHound Enterprise and AzureHound Enterprise from the same Windows system. AzureHound Enterprise also supports Docker and Kubernetes deployments. Use [OpenHound](/openhound/overview) for built-in collectors and workflows for platforms like GitHub, Jamf, and Okta. For additional community-built collectors, explore the [OpenGraph Library](/opengraph/library). ## Ingest with SharpHound Enterprise (Active Directory) SharpHound Enterprise collects [multiple data types](/collect-data/enterprise-collection/data-retention) from Active Directory and its domain-joined systems. We recommend collecting all types for full risk identification and accurate risk assessment calculation. Install the SharpHound Enterprise collector service on a domain-joined Windows system and run it as an Active Directory account. 1. Review the [SharpHound Enterprise System Requirements](/install-data-collector/install-sharphound/system-requirements) and [SharpHound Service Hardening Guidelines](/manage-bloodhound/securing-bloodhound-and-collectors/sharphound-hardening). 2. [Install and Upgrade SharpHound Enterprise](/install-data-collector/install-sharphound/installation-upgrade). 3. To fully secure a domain, collect data from all other domains with a trust relationship to it (in- and outgoing trust). Configure SharpHound Enterprise for [Cross-Trust Collection](/collect-data/enterprise-collection/cross-trust). ## Ingest with AzureHound Enterprise (Entra ID and Azure) Install and run AzureHound Enterprise on Windows, Docker, or Kubernetes. When you deploy AzureHound Enterprise on Windows, it runs as a Windows service. 1. Review the [AzureHound Enterprise System Requirements and Deployment Process](/install-data-collector/install-azurehound/system-requirements). 2. [Configure Azure](/install-data-collector/install-azurehound/azure-configuration). 3. [Create your AzureHound configuration](/install-data-collector/install-azurehound/create-configuration). 4. [Deploy and maintain AzureHound](/install-data-collector/install-azurehound/installation-options). ## Ingest with OpenHound (Github, Jamf, and Okta) OpenHound for BloodHound Enterprise runs as a containerized service and is complementary to SharpHound Enterprise and AzureHound Enterprise. This is a SpecterOps-managed feature. If it is not enabled in your environment, contact your account team for assistance. 1. Review the OpenHound for BloodHound Enterprise [configuration requirements](/openhound/enterprise). 2. Create an OpenHound [collector client](/collect-data/enterprise-collection/create-collector) to get API credentials. 3. Configure the OpenHound collector you want to run: * [Github](/openhound/collectors/github/collect-data#configure-openhound) * [Jamf](/openhound/collectors/jamf/collect-data#configure-openhound) * [Okta](/openhound/collectors/okta/collect-data#configure-openhound) 4. Deploy OpenHound in your environment and run an on-demand scan or scheduled collection. # Verify data quality After collecting data, to verify data quality: 1. Go to settings (⚙️) → **Administration** and select **Data Quality**. 2. Verify that each collector has collected the expected amount of data and that principal types match your expected coverage for each directory and platform. For more information, see [Review Data Quality](/collect-data/data-quality). 3. If using privileged collection, verify that the charts **Local Group Completeness Over Time** and **Session Completeness Over Time** report higher than 0%. Obtaining 100% completeness is not possible in most environments due to things like workstations being offline during collection. 4. If you see lower-than-expected data quality examine the data collection logs and contact your SpecterOps representative if you need assistance. # Scope Tier Zero objects BloodHound Enterprise identifies and prioritizes attack paths. To get the most accurate assessment scope your Tier Zero objects. 1. [Scope Tier Zero for your environment](/get-started/security-boundaries/tier-zero-members). 2. [Mark your environment's Tier Zero objects in BloodHound](/analyze-data/privilege-zones/overview). # Grant users access Your BloodHound Enterprise instance has a few administrative users by default. To bring your team into your instance, grant your team access with dedicated users and roles. To grant users access to your instance, [create users and set access control roles](/manage-bloodhound/auth/users-and-roles). BloodHound Enterprise supports two authentication methods for users: * Built-in authentication via username and password, supporting TOTP-based multi-factor authentication * [SAML 2.0-based Single-Sign-On](/manage-bloodhound/auth/saml) Your default users are configured with built-in authentication. For your team, you can also configure SAML authentication. Enable multi-factor authentication for all users, no matter the authentication method and user role. If using SAML authentication, your connected identity provider will handle multi-factor authentication. # Explore and remediate attack paths Go to the **Attack Paths**, **Explore**, and **Posture** pages to see identified attack paths, prioritization, and recommended mitigations. # Next steps * Learn how to work with the [BloodHound Enterprise API](/integrations/bloodhound-api/working-with-api) * [Configure BloodHound integrations](/integrations/overview) # BloodHound Enterprise Security Overview Source: https://bloodhound.specterops.io/get-started/security-boundaries/enterprise-security-overview Applies to BloodHound Enterprise only ## Introduction and Architecture Attack Paths are chains of abusable privileges and user behaviors that create direct and indirect connections between computers and users. They exist due to years of misconfigurations and a lack of visibility into how privileges are applied. Attack Paths cannot be patched through traditional methods because they are misconfigurations, not vulnerabilities. SpecterOps built BloodHound Enterprise following the principles of [Attack Path Management](https://specterops.io/what-is-attack-path-management/) (APM). The primary goal of APM is to solve the Attack Path problem directly. APM is a fundamentally different, unique methodology designed to help organizations understand, empirically quantify the impact of, and eliminate Attack Path risks. ## Single-Tenant Architecture Diagram Customer Data Residency and Subprocessors Customers may request that their tenant reside within one of the supported AWS regions below. Existing customers may request migration to an alternate region should residency needs demand. * **United States**: US-EAST-1 (Northern Virginia) * **Canada**: CA-CENTRAL-1 (Montreal) * **Europe**: EU-CENTRAL-1 (Frankfurt) * **United Kingdom**: EU-WEST-2 (London) * **Australia**: AP-SOUTHEAST-2 (Sydney) * **Middle East**: ME-CENTRAL-1 (UAE) Additionally, BloodHound Enterprise utilizes [Pendo](https://pendo.io) to provide in-product tours and behavior monitoring. Customer data is not sent to Pendo. More information on Pendo's data privacy, security policies, and certifications is available [here](https://www.pendo.io/data-privacy-security/). ## AWS Datacenter Security BloodHound Enterprise is hosted within AWS, which touts a litany of security certifications and is subject to regular audits and certifications. Certifications for AWS include ISO 27001, SOC 1 and 2, etc. * [AWS Cloud Security Overview](https://aws.amazon.com/security/) * [AWS Compliance Programs](https://aws.amazon.com/compliance/programs/) * Quick links to frequently requested compliance documentation: * [ISO 27001 Certification](https://d1.awsstatic.com/certifications/iso_27001_global_certification.pdf) * [ISO 27017 Certification](https://d1.awsstatic.com/certifications/iso_27017_certification.pdf) * [SOC 1 / 2 /3 Artifacts](https://aws.amazon.com/compliance/soc-faqs/) ## Customer Data Storage and Separation ### Separation of Customer Data BloodHound Enterprise is deployed in a single-tenancy model within AWS. Each customer environment is configured with its own database, API, and UI servers, and data is not commingled between customers. Each tenant has unique authentication keys defined for authentication between services within the overall system. ### Data Backup and Retention Data backup occurs via [Amazon EBS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/snapshot-lifecycle.html) and [Amazon RDS](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_CommonTasks.BackupRestore.html) backup functionality. All backups are encrypted using the methods listed below in the Customer Data Security section. All backups are retained for seven (7) days. ### Customer Data Security BloodHound Enterprise uses available Amazon encryption functionalities to encrypt all data using AES-256 with an Amazon-managed key. This applies to all servers in the BloodHound Enterprise infrastructure. Backup snapshots utilize the same encryption mechanisms. AWS security groups isolate all BHE installations. They do not have permission to reach other customer assets. More information on EBS volume encryption [can be found here](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSEncryption.html). Information specific to RDS volume encryption [can be found here](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Overview.Encryption.html). ### Bring Your Own Key Encryption BloodHound Enterprise supports a "Bring Your Own Key" (BYOK) model for encryption of data at rest, allowing you to maintain control over your encryption keys to support internal security policies and compliance requirements. With BYOK, you provide your own encryption key through AWS Key Management Service (KMS), and BloodHound Enterprise uses that key to encrypt your tenant data instead of SpecterOps-managed keys. BYOK encryption applies only to data at rest. It does not impact TLS certificates used for encrypting data in transit. #### Important Considerations To enable BYOK encryption for your BloodHound Enterprise tenant, you must contact your account team. You should also be aware of the following considerations: * **Key availability**: Your BloodHound Enterprise tenant requires continuous access to your KMS key. If the key becomes unavailable (deleted, access revoked, or expired), your tenant will experience service disruptions. * **Key rotation**: You are responsible for managing key rotation according to your security policies. Coordinate any key changes with your SpecterOps account team. * **Cost**: There may be additional AWS KMS costs associated with key usage. Consult AWS pricing documentation for details. * **Migration**: Migrating an existing tenant to BYOK requires coordination and may involve scheduled maintenance windows. #### Prerequisites To use BYOK encryption, you need: * An AWS account with permissions to create and manage KMS keys * Ability to configure IAM policies for cross-account access * A BloodHound Enterprise tenant #### Setup Process BYOK setup requires coordination with your SpecterOps account team. The general process involves the following steps: Create a KMS key in your AWS account. See the [AWS documentation](https://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html) for details. Attach an IAM policy to your KMS key that allows the SpecterOps AWS account ID (provided by your account team) to use the key. See the [AWS documentation](https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-modifying-external-accounts.html) for details. Contact your account team and provide the Amazon Resource Name (ARN) of your KMS key. Work with your account team to provision a new tenant or migrate your existing tenant to use your key. ## Network Communications ### Web Browser and SharpHound/AzureHound connections BloodHound Enterprise uses a shared AWS Application Load Balancer. The load balancer policy has been set to ELBSecurityPolicy-TLS13-1-2-2021-06. With this policy, BloodHound Enterprise: * Does not support SSL renegotiation for client or target connections. * Does not support custom TLS/cipher policies. * Enforces TLS v1.2 minimum. #### Supported protocols * TLS v1.3 * TLS v1.2 #### Supported ciphers * TLS\_AES\_128\_GCM\_SHA256 * TLS\_AES\_256\_GCM\_SHA384 * TLS\_CHACHA20\_POLY1305\_SHA256 * ECDHE-ECDSA-AES128-GCM-SHA256 * ECDHE-RSA-AES128-GCM-SHA256 * ECDHE-ECDSA-AES128-SHA256 * ECDHE-RSA-AES128-SHA256 * ECDHE-ECDSA-AES256-GCM-SHA384 * ECDHE-RSA-AES256-GCM-SHA384 * ECDHE-ECDSA-AES256-SHA384 * ECDHE-RSA-AES256-SHA384 More information on supported TLS policies [can be found here](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html). ### Certificates The Amazon certificate authority provides customer-facing TLS certificates. This CA is trusted by all major browsers and operating systems and includes: * RSA 2048 public key. * Automatic renewal on an annual basis. * Private keys are never exposed to SpecterOps. Amazon retains full control. More information about the TLS certificates we use [can be found here](https://docs.aws.amazon.com/acm/latest/userguide/acm-overview.html). ## Authentication and Role-Based Access ### Authentication Details BloodHound Enterprise provides built-in authentication via username and password, with the option to enable TOTP-based multi-factor authentication. Customers may additionally choose to enable SAML 2.0-based Single Sign-On to control authentication through an external, third-party provider such as Azure AD SAML, ADFS, or Okta. User access and role assignment are controlled within the BloodHound Enterprise product, and both SP- and IDP-initiated authentication flows are supported. ### Local Authentication Specifications #### Password Expiration and Complexity All passwords configured locally expire every 90 days. * Passwords configured via local authentication require: * At least 12 characters in length * At least one lowercase letter * At least one uppercase letter * At least one number * At least one symbol (One of: !@#\$%^&\*) #### Brute Force Resistance Although BloodHound Enterprise does not lock a user out from attempted brute forcing, API calls against the BloodHound Enterprise authentication API are limited to one call per second, making a successful brute force attack impossible before a password rotation occurs. All other API endpoints are limited to 55 calls per second. #### Password Hashing All passwords are hashed utilizing the Argon2id key derivation function with a unique 16-byte salt length per Argon2id recommendations. For more information on Argon2, see [here](https://datatracker.ietf.org/doc/html/rfc9106). #### Session Expiration User-interface sessions expire after eight (8) hours. #### Role-Based Access Control [BloodHound Enterprise User Roles](/manage-bloodhound/auth/users-and-roles) ## Third-Party Assessments and Certifications ### Penetration Testing BloodHound Enterprise undergoes annual penetration tests at a minimum. Results from these tests can be made available upon request under NDA. All critical-, high-, and medium-risk findings have been remediated. ### Certification BloodHound Enterprise is included in the scope for the following certifications held by SpecterOps: * ISO/IEC 27001:2022 * ISO/IEC 27017:2015 * SOC 2 Type II Visit [trust.specterops.io](https://trust.specterops.io) for more information. ## Patching Operating system security patches are fetched daily and applied automatically. The rest of the BloodHound Enterprise infrastructure utilizes Amazon-provided services, and Amazon performs security patching automatically. We follow security mailing lists, RSS feeds, etc., and periodically review for CVEs in supporting software we use in BHE environments. ## Access to Data Only the BloodHound Enterprise infrastructure engineers maintain persistent access to BHE environments. All SpecterOps employees must pass criminal background checks as a condition of employment. In some instances, select developers are provided temporary access to systems for debugging purposes. This activity is monitored and logged. Access is revoked at the end of the event. ## Logging and Monitoring BloodHound Enterprise captures login and administrative actions within an Audit log that includes the following information: * Who attempted the action (user ID, user name, email) * What action was attempted * The status of the action (intent/success/failure) * When it occurred * Source IP(s) associated with the request This information is available from the API of a running BloodHound Enterprise environment. ## Data Collection Overview For SharpHound, see [SharpHound Data Collection and Permissions](/collect-data/sharphound-data-permissions). For AzureHound, see [AzureHound Data and Permissions](/collect-data/azurehound-data-permissions). # Tier Zero: Members and Modification Source: https://bloodhound.specterops.io/get-started/security-boundaries/tier-zero-members Applies to BloodHound Enterprise and CE BloodHound borrows from [Microsoft's Enhanced Security Administration Environment (ESAE - Retired)](https://learn.microsoft.com/en-us/security/compass/esae-retirement) model in using the term "Tier Zero." In this model, Tier Zero is the set of objects with full control over the environment AND *any objects with control over those objects*. You may also be familiar with [Microsoft's Enterprise Access Model (EAM)](https://learn.microsoft.com/en-us/security/compass/privileged-access-access-model), which later replaced ESAE; however, they recommend effectively the same advice. Although implementing a tiered model remains the best path toward securing your overall environment, BloodHound enables your teams to identify and remediate the paths towards control of Tier Zero assets without necessarily implementing a strict tiering model. Jonas Bülow Knudsen, one of our BloodHound Enterprise team members, has written this fantastic blog on implementing a tiering model in your environment: [Establish security boundaries in your on-prem AD and Azure environment](https://specterops.io/blog/2022/06/20/establish-security-boundaries-in-your-on-prem-ad-and-azure-environment/). ## How BloodHound Identifies Tier Zero Objects The following sections will cover the default objects in Tier Zero in BloodHound and what to include in the group manually. Still, it's important to understand how the identification process works. Loosely, the steps look like this in BloodHound: 1. Identify all starting members of Tier Zero (default objects and any objects tagged manually within the environment). 2. Unroll group memberships and role assignments: 1. For Active Directory, all group objects will be unrolled to identify any nested objects and tag them as members of Tier Zero. This process is recursive, so any nested memberships will also be identified and tagged. 2. In Azure, the same process occurs with Roles and Groups, identifying objects with those roles assigned to them. This process only identifies direct group members, as Azure does not grant nested group members permissions of the parent group. 3. After identifying these objects, BloodHound will identify objects which maintain structural control over those things already tagged. This includes things like OUs, Containers, and GPOs in Active Directory and Subscriptions and Management Groups in Azure. At this point, the "Tier Zero Boundary" is drawn, and any remaining control over those objects would be identified as a Tier Zero path within BloodHound. ## Default Members of Tier Zero BloodHound will automatically identify many Tier Zero objects within your environment to get you started on protecting the most critical assets within your environment. ### Active Directory In Active Directory, BloodHound starts primarily with the default groups that maintain full control of a domain or have the (effectively) irrevocable ability to gain access to those groups. These include (default RIDs are provided for reference): * Domain head object * AdminSDHolder object * Built-in Administrator account (-500) * Domain Admins (-512) * Domain Controllers (-516) * Schema Admins (-518) * Enterprise Admins (-519) * Enterprise Domain Controllers (1-5-9) * Key Admins (-526) * Enterprise Key Admins (-527) * Administrators (-544) *Please see the following sections about modifying Tier Zero in an Active Directory environment for information on the inclusion of other default groups in Tier Zero.* ### Azure In Azure, BloodHound starts primarily with the default roles that maintain full control of an Azure tenant or have the (effectively) irrevocable ability to gain access to those roles. These include: * Tenant object * Global Administrator * Privileged Role Administrator * Privileged Authentication Administrator * Partner Tier2 Support ## Modifying Tier Zero Although BloodHound can identify much of Tier Zero by default, many things included in Tier Zero do not have a consistent means to identify them across multiple environments. We recommend splitting this effort into two phases for organizations just starting to develop the internal concept of Tier Zero. If your organization has already begun the process, you may decide to go through both phases more rapidly. ### Phase One Phase One consists of those systems within an AD environment which are almost always Tier Zero, regardless of your implementation. You will want to identify both the computers and the service accounts associated with the following services: * PKI (Public Key Infrastructure) / ADCS (Active Directory Certificate Services) * Root CA (Certificate Authority) server * Subordinate CAs * ADFS (Active Directory Federation Services) * Note: The Web Application Proxy (WAP) servers should be in a separate AD forest (DMZ or extranet network) and are not considered Tier Zero. * Azure AD Connect servers and accounts * Incl. servers with PTA agents if Pass-Through Authentication (PTA) is enabled. * Privileged Access Management systems (such as Delinea or CyberArk) * GPO Administration tools (such as Quest GPO Admin or AGPM) * Read-Only Domain Controller (RODC) computer objects * Read about why the RODC computer objects are Tier Zero and how RODCs should be configured to protect Tier Zero here: [What is Tier Zero - Part 2](https://specterops.io/blog/2023/09/14/what-is-tier-zero-part-2/). * Anything else your organization already classifies as Tier Zero, such as Privileged Access Workstations. ### Phase Two In Phase 2, you will include systems in Tier Zero that have code execution ability on Tier Zero systems (or other privileged control). For example, if DCs are managed from SCCM, compromising SCCM allows the attacker to execute code on DCs. SCCM is, in this case, an indirect path to full control over the environment and, therefore, a Tier Zero system. Compromising the environment through these systems requires a more sophisticated attack than the systems covered in Phase 1. There are often many systems in this category, and it will usually require some effort to execute this part of the Tier Zero classifications. We recommend examining the [TierZeroTable](https://github.com/SpecterOps/TierZeroTable) and adding assets classified as Tier Zero in this table to Tier Zero in BloodHound as part of this phase as well. # Upgrade PostgreSQL Source: https://bloodhound.specterops.io/get-started/upgrade-postgres Migrate BloodHound Community Edition from PostgreSQL 16 to 18. Applies to BloodHound CE only BloodHound Community Edition is already compatible with PostgreSQL 18. Use this guide to get a head start on the latest PostgreSQL release before it becomes the default bundled version, and to benefit from its new capabilities and performance improvements. However, the upgrade introduces a **breaking change** to the Docker volume mount path that prevents a simple image tag update. The PostgreSQL 18 Docker image uses a different volume mount path than version 16. Starting a PostgreSQL 18 container against an existing PostgreSQL 16 volume will fail. You must migrate your data using the scripts provided on this page. | PostgreSQL version | Volume mount path | | ------------------ | -------------------------- | | 16 (and earlier) | `/var/lib/postgresql/data` | | 18 (and later) | `/var/lib/postgresql` | The upgrade scripts on this page automate the migration by dumping your existing data, backing up the Docker volume, updating your `docker-compose.yml`, and restoring the data into a fresh PostgreSQL 18 container. ## Prerequisites * Docker with the Compose V2 plugin (`docker compose`, not `docker-compose`) * Sufficient free disk space for the database dump file and a volume backup * PowerShell 5.1 or later (all platforms), or bash (Linux/macOS) * Your BloodHound CE installation must be accessible and running before you begin ## Before you begin Back up your data before running the upgrade script. The scripts automatically create a copy of your PostgreSQL Docker volume, but you should verify that your data is intact independently before and after the migration. The upgrade scripts perform the following steps in order: | Step | What it does | | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | 1. Stop app, keep database running | Stops the BloodHound application container while leaving PostgreSQL 16 running so the database can be dumped cleanly | | 2. Dump the database | Exports the PostgreSQL 16 database to a compressed dump file on your host | | 3. Stop all containers | Brings down all containers before modifying the volume | | 4. Back up the data volume | Copies the PostgreSQL 16 Docker volume to a backup volume for safety | | 5. Update `docker-compose.yml` | Changes the image tag to `postgres:18` and updates the volume mount path; saves a backup of the original compose file | | 6. Start PostgreSQL 18 and restore | Removes the old volume, starts PostgreSQL 18, and restores the database from the dump file | | 7. Start the full stack | Brings up all BloodHound CE containers | ## Run the upgrade script Save the following script to a file and run it. Save the script as `upgrade-pg.ps1` and run it with `.\upgrade-pg.ps1`. ```powershell theme={null} #Requires -Version 5.1 <# .SYNOPSIS Migrates BloodHound Community Edition PostgreSQL from 16 to 18. .DESCRIPTION Walks through dumping the PG 16 database, backing up the Docker volume, upgrading to PG 18, and restoring the data. Requires Docker with the Compose V2 plugin. #> $ErrorActionPreference = "Stop" function Write-Step { param([string]$Msg) Write-Host "`n=== $Msg ===" -ForegroundColor Cyan } function Write-Ok { param([string]$Msg) Write-Host $Msg -ForegroundColor Green } function Write-Warn { param([string]$Msg) Write-Host $Msg -ForegroundColor Yellow } # ── 1. Gather inputs ──────────────────────────────────────────────────────── Write-Step "BloodHound PostgreSQL 16 -> 18 Migration" $defaultDir = Join-Path $HOME ".config" "bloodhound" $composeDir = Read-Host "Enter the directory containing docker-compose.yml (default: $defaultDir)" if ([string]::IsNullOrWhiteSpace($composeDir)) { $composeDir = $defaultDir } $composeDir = [System.IO.Path]::GetFullPath($composeDir.Replace("~", $HOME)) $composeFile = Join-Path $composeDir "docker-compose.yml" if (-not (Test-Path $composeFile)) { Write-Error "docker-compose.yml not found at $composeFile"; exit 1 } $defaultDump = Join-Path $composeDir "pg16_backup.dump" $dumpPath = Read-Host "Enter path for the database dump file (default: $defaultDump)" if ([string]::IsNullOrWhiteSpace($dumpPath)) { $dumpPath = $defaultDump } $dumpPath = [System.IO.Path]::GetFullPath($dumpPath.Replace("~", $HOME)) # ── 2. Read credentials (.env then defaults) ──────────────────────────────── $pgUser = "bloodhound"; $pgDb = "bloodhound" $envFile = Join-Path $composeDir ".env" if (Test-Path $envFile) { Write-Host "Reading overrides from $envFile ..." Get-Content $envFile | ForEach-Object { if ($_ -match '^\s*POSTGRES_USER\s*=\s*(.+)$') { $pgUser = $Matches[1].Trim() } if ($_ -match '^\s*POSTGRES_DB\s*=\s*(.+)$') { $pgDb = $Matches[1].Trim() } } } Write-Host "Credentials: user=$pgUser db=$pgDb" # ── 3. Resolve project and volume names via Docker Compose ─────────────────── $dcBase = @("-f", $composeFile, "--project-directory", $composeDir) $projectName = (docker compose @dcBase config --format json | ConvertFrom-Json).name if ([string]::IsNullOrWhiteSpace($projectName)) { Write-Error "Could not determine Compose project name."; exit 1 } $declaredVols = @(docker compose @dcBase config --volumes) $pgVolDecl = $declaredVols | Where-Object { $_ -match 'postgres-data' } | Select-Object -First 1 if (-not $pgVolDecl) { Write-Error "No 'postgres-data' volume declared in compose config."; exit 1 } $volumeName = "${projectName}_${pgVolDecl}" $volExists = docker volume ls --format "{{.Name}}" | Where-Object { $_ -eq $volumeName } if (-not $volExists) { Write-Error "Docker volume '$volumeName' not found. Is BloodHound installed?" exit 1 } Write-Host "Project: $projectName Volume: $volumeName" # ── 4. Confirm ────────────────────────────────────────────────────────────── Write-Warn "`nThis script will:" Write-Host " 1. Stop the BloodHound app (keep PG 16 running)" Write-Host " 2. Dump the PG 16 database to: $dumpPath" Write-Host " 3. Stop all containers" Write-Host " 4. Create a backup copy of the PostgreSQL data volume" Write-Host " 5. Update docker-compose.yml to use PostgreSQL 18" Write-Host " 6. Start PG 18 and restore the database" Write-Host " 7. Start the full BloodHound stack" $confirm = Read-Host "`nProceed? (y/N)" if ($confirm -notin @('y','Y')) { Write-Host "Cancelled."; exit 0 } # ── Helper: run docker compose with project context ────────────────────────── function Invoke-DC { $dcArgs = @("-f", $composeFile, "--project-directory", $composeDir) + $args & docker compose @dcArgs if ($LASTEXITCODE -ne 0) { throw "docker compose failed (exit $LASTEXITCODE)" } } # ── 5. Stop app, keep PG 16 alive ─────────────────────────────────────────── Write-Step "Stopping BloodHound application container" Invoke-DC stop bloodhound Write-Ok "BloodHound app stopped." Write-Step "Ensuring PostgreSQL 16 is running" Invoke-DC start app-db Start-Sleep -Seconds 5 # ── 6. Dump database ──────────────────────────────────────────────────────── Write-Step "Dumping PostgreSQL 16 database" Invoke-DC exec app-db pg_dump -U $pgUser -d $pgDb -Fc -Z 9 -f /tmp/pg16_backup.dump Invoke-DC cp app-db:/tmp/pg16_backup.dump $dumpPath $dumpSize = (Get-Item $dumpPath).Length Write-Ok "Database dumped ($dumpSize bytes) -> $dumpPath" # ── 7. Stop everything ────────────────────────────────────────────────────── Write-Step "Stopping all containers" Invoke-DC down Write-Ok "All containers stopped." # ── 8. Copy the volume ────────────────────────────────────────────────────── Write-Step "Backing up PostgreSQL data volume" $backupVol = "${volumeName}-pg16-backup" docker volume create $backupVol | Out-Null docker run --rm -v "${volumeName}:/source:ro" -v "${backupVol}:/backup" ` alpine sh -c "cp -a /source/. /backup/" Write-Ok "Volume copied to: $backupVol" # ── 9. Update docker-compose.yml ──────────────────────────────────────────── Write-Step "Updating docker-compose.yml to PostgreSQL 18" $bakFile = Join-Path $composeDir "docker-compose.yml.pg16.bak" Copy-Item $composeFile $bakFile Write-Host "Compose file backed up to: $bakFile" $content = Get-Content $composeFile -Raw $updated = $content -replace 'image:\s*docker\.io/library/postgres:16', 'image: docker.io/library/postgres:18' if ($updated -eq $content) { Write-Error "Could not find postgres:16 image reference in compose file"; exit 1 } # PG 18+ expects the mount at /var/lib/postgresql (not /var/lib/postgresql/data). # It manages version-specific subdirectories under that path automatically. $beforeMount = $updated $updated = $updated -replace 'postgres-data:/var/lib/postgresql/data', 'postgres-data:/var/lib/postgresql' if ($updated -eq $beforeMount) { Write-Error "Could not find postgres-data:/var/lib/postgresql/data volume mount in compose file"; exit 1 } [System.IO.File]::WriteAllText($composeFile, $updated) Write-Ok "docker-compose.yml updated (image + volume mount path)." # ── 10. Remove old volume, start PG 18 ────────────────────────────────────── Write-Step "Removing old PostgreSQL data volume" docker volume rm $volumeName | Out-Null Write-Ok "Old volume removed (backup preserved at $backupVol)." Write-Step "Starting PostgreSQL 18" Invoke-DC up -d app-db # Resolve the actual container name for app-db from Compose $appDbContainer = (docker compose @dcBase ps --format "{{.Name}}" app-db).Trim() if ([string]::IsNullOrWhiteSpace($appDbContainer)) { Write-Error "Could not determine container name for app-db service."; exit 1 } Write-Host "Waiting for PostgreSQL 18 to become healthy ($appDbContainer)..." for ($i = 0; $i -lt 30; $i++) { Start-Sleep -Seconds 2 $health = docker inspect --format "{{.State.Health.Status}}" $appDbContainer 2>$null if ($health -eq "healthy") { break } } if ($health -ne "healthy") { Write-Error "Container '$appDbContainer' is not healthy (status: $health). Aborting migration."; exit 1 } # ── 11. Restore database ──────────────────────────────────────────────────── Write-Step "Restoring database into PostgreSQL 18" Invoke-DC cp $dumpPath app-db:/tmp/pg16_backup.dump Invoke-DC exec app-db pg_restore -U $pgUser -d $pgDb --clean --if-exists /tmp/pg16_backup.dump Write-Ok "Database restored." # ── 12. Start full stack ───────────────────────────────────────────────────── Write-Step "Starting full BloodHound stack" Invoke-DC up -d Write-Ok "BloodHound stack is starting." # ── Done ───────────────────────────────────────────────────────────────────── Write-Step "Migration Complete" Write-Ok "PostgreSQL upgraded from 16 to 18." Write-Host " Database dump : $dumpPath" Write-Host " Volume backup : $backupVol" Write-Host " Compose backup: $bakFile" Write-Warn "`nOnce verified, you can clean up with:" Write-Host " docker volume rm $backupVol" Write-Host " Remove-Item '$bakFile'" Write-Host " Remove-Item '$dumpPath'" ``` Save the script as `upgrade-pg.sh`, make it executable with `chmod +x upgrade-pg.sh`, and run it with `./upgrade-pg.sh`. ```bash theme={null} #!/usr/bin/env bash # Migrates BloodHound Community Edition PostgreSQL from 16 to 18. # Requires Docker with the Compose V2 plugin. set -euo pipefail # ── Helpers ────────────────────────────────────────────────────────────────── step() { printf '\n\033[36m=== %s ===\033[0m\n' "$1"; } ok() { printf '\033[32m%s\033[0m\n' "$1"; } warn() { printf '\033[33m%s\033[0m\n' "$1"; } die() { printf '\033[31mError: %s\033[0m\n' "$1" >&2; exit 1; } dc() { docker compose -f "$COMPOSE_FILE" --project-directory "$COMPOSE_DIR" "$@"; } # ── 1. Gather inputs ──────────────────────────────────────────────────────── step "BloodHound PostgreSQL 16 -> 18 Migration" DEFAULT_DIR="$HOME/.config/bloodhound" read -rp "Enter the directory containing docker-compose.yml (default: $DEFAULT_DIR): " COMPOSE_DIR COMPOSE_DIR="${COMPOSE_DIR:-$DEFAULT_DIR}" COMPOSE_DIR="${COMPOSE_DIR/#\~/$HOME}" COMPOSE_DIR="$(cd "$COMPOSE_DIR" && pwd)" COMPOSE_FILE="$COMPOSE_DIR/docker-compose.yml" [ -f "$COMPOSE_FILE" ] || die "docker-compose.yml not found at $COMPOSE_FILE" DEFAULT_DUMP="$COMPOSE_DIR/pg16_backup.dump" read -rp "Enter path for the database dump file (default: $DEFAULT_DUMP): " DUMP_PATH DUMP_PATH="${DUMP_PATH:-$DEFAULT_DUMP}" DUMP_PATH="${DUMP_PATH/#\~/$HOME}" # ── 2. Read credentials (.env then defaults) ──────────────────────────────── PG_USER="bloodhound" PG_DB="bloodhound" ENV_FILE="$COMPOSE_DIR/.env" if [ -f "$ENV_FILE" ]; then echo "Reading overrides from $ENV_FILE ..." _val="$(grep -E '^\s*POSTGRES_USER\s*=' "$ENV_FILE" | tail -n1 | cut -d= -f2- | xargs || true)" && [ -n "$_val" ] && PG_USER="$_val" _val="$(grep -E '^\s*POSTGRES_DB\s*=' "$ENV_FILE" | tail -n1 | cut -d= -f2- | xargs || true)" && [ -n "$_val" ] && PG_DB="$_val" fi echo "Credentials: user=$PG_USER db=$PG_DB" # ── 3. Resolve project and volume names via Docker Compose ─────────────────── PROJECT_NAME="$(docker compose -f "$COMPOSE_FILE" --project-directory "$COMPOSE_DIR" \ config --format json | python3 -c "import sys,json; print(json.load(sys.stdin)['name'])")" [ -n "$PROJECT_NAME" ] || die "Could not determine Compose project name." PG_VOL_DECL="$(docker compose -f "$COMPOSE_FILE" --project-directory "$COMPOSE_DIR" \ config --volumes | grep 'postgres-data' | head -n1)" [ -n "$PG_VOL_DECL" ] || die "No 'postgres-data' volume declared in compose config." VOLUME_NAME="${PROJECT_NAME}_${PG_VOL_DECL}" docker volume ls --format '{{.Name}}' | grep -qx "$VOLUME_NAME" \ || die "Docker volume '$VOLUME_NAME' not found. Is BloodHound installed?" echo "Project: $PROJECT_NAME Volume: $VOLUME_NAME" # ── 4. Confirm ────────────────────────────────────────────────────────────── warn "" warn "This script will:" echo " 1. Stop the BloodHound app (keep PG 16 running)" echo " 2. Dump the PG 16 database to: $DUMP_PATH" echo " 3. Stop all containers" echo " 4. Create a backup copy of the PostgreSQL data volume" echo " 5. Update docker-compose.yml to use PostgreSQL 18" echo " 6. Start PG 18 and restore the database" echo " 7. Start the full BloodHound stack" read -rp $'\nProceed? (y/N): ' CONFIRM [[ "$CONFIRM" =~ ^[yY]$ ]] || { echo "Cancelled."; exit 0; } # ── 5. Stop app, keep PG 16 alive ─────────────────────────────────────────── step "Stopping BloodHound application container" dc stop bloodhound ok "BloodHound app stopped." step "Ensuring PostgreSQL 16 is running" dc start app-db sleep 5 # ── 6. Dump database ──────────────────────────────────────────────────────── step "Dumping PostgreSQL 16 database" dc exec app-db pg_dump -U "$PG_USER" -d "$PG_DB" -Fc -Z 9 -f /tmp/pg16_backup.dump dc cp app-db:/tmp/pg16_backup.dump "$DUMP_PATH" DUMP_SIZE="$(stat -f%z "$DUMP_PATH" 2>/dev/null || stat -c%s "$DUMP_PATH")" ok "Database dumped ($DUMP_SIZE bytes) -> $DUMP_PATH" # ── 7. Stop everything ────────────────────────────────────────────────────── step "Stopping all containers" dc down ok "All containers stopped." # ── 8. Copy the volume ────────────────────────────────────────────────────── step "Backing up PostgreSQL data volume" BACKUP_VOL="${VOLUME_NAME}-pg16-backup" docker volume create "$BACKUP_VOL" > /dev/null docker run --rm -v "${VOLUME_NAME}:/source:ro" -v "${BACKUP_VOL}:/backup" \ alpine sh -c "cp -a /source/. /backup/" ok "Volume copied to: $BACKUP_VOL" # ── 9. Update docker-compose.yml ──────────────────────────────────────────── step "Updating docker-compose.yml to PostgreSQL 18" BAK_FILE="$COMPOSE_DIR/docker-compose.yml.pg16.bak" cp "$COMPOSE_FILE" "$BAK_FILE" echo "Compose file backed up to: $BAK_FILE" # Replace image tag if ! grep -q 'docker.io/library/postgres:16' "$COMPOSE_FILE"; then die "Could not find postgres:16 image reference in compose file" fi sed -i.tmp 's|image: *docker\.io/library/postgres:16|image: docker.io/library/postgres:18|' "$COMPOSE_FILE" # PG 18+ expects the mount at /var/lib/postgresql (not /var/lib/postgresql/data). if ! grep -q 'postgres-data:/var/lib/postgresql/data' "$BAK_FILE"; then die "Could not find postgres-data:/var/lib/postgresql/data volume mount in compose file" fi sed -i.tmp 's|postgres-data:/var/lib/postgresql/data|postgres-data:/var/lib/postgresql|' "$COMPOSE_FILE" rm -f "${COMPOSE_FILE}.tmp" ok "docker-compose.yml updated (image + volume mount path)." # ── 10. Remove old volume, start PG 18 ────────────────────────────────────── step "Removing old PostgreSQL data volume" docker volume rm "$VOLUME_NAME" > /dev/null ok "Old volume removed (backup preserved at $BACKUP_VOL)." step "Starting PostgreSQL 18" dc up -d app-db # Resolve the actual container name for app-db from Compose APP_DB_CONTAINER="$(docker compose -f "$COMPOSE_FILE" --project-directory "$COMPOSE_DIR" \ ps --format '{{.Name}}' app-db | head -n1 | xargs)" [ -n "$APP_DB_CONTAINER" ] || die "Could not determine container name for app-db service." echo "Waiting for PostgreSQL 18 to become healthy ($APP_DB_CONTAINER)..." for i in $(seq 1 30); do sleep 2 HEALTH="$(docker inspect --format '{{.State.Health.Status}}' "$APP_DB_CONTAINER" 2>/dev/null || true)" [ "$HEALTH" = "healthy" ] && break done [ "$HEALTH" = "healthy" ] || die "Container '$APP_DB_CONTAINER' is not healthy (status: $HEALTH). Aborting migration." # ── 11. Restore database ──────────────────────────────────────────────────── step "Restoring database into PostgreSQL 18" dc cp "$DUMP_PATH" app-db:/tmp/pg16_backup.dump dc exec app-db pg_restore -U "$PG_USER" -d "$PG_DB" --clean --if-exists /tmp/pg16_backup.dump ok "Database restored." # ── 12. Start full stack ───────────────────────────────────────────────────── step "Starting full BloodHound stack" dc up -d ok "BloodHound stack is starting." # ── Done ───────────────────────────────────────────────────────────────────── step "Migration Complete" ok "PostgreSQL upgraded from 16 to 18." echo " Database dump : $DUMP_PATH" echo " Volume backup : $BACKUP_VOL" echo " Compose backup: $BAK_FILE" warn "" warn "Once verified, you can clean up with:" echo " docker volume rm $BACKUP_VOL" echo " rm '$BAK_FILE'" echo " rm '$DUMP_PATH'" ``` ## Verify the upgrade After the script finishes running, confirm that the database is healthy and your data is intact. Confirm all containers are running: ```bash theme={null} docker compose ps ``` The `app-db` container should show a status of `healthy`. Confirm that PostgreSQL 18 is running: ```bash theme={null} docker compose exec app-db psql -U [POSTGRES_USER] -d [POSTGRES_DB] -c "SELECT version();" ``` The output should include `PostgreSQL 18`. Open your browser and navigate to BloodHound CE (default: `http://127.0.0.1:8080`). Log in and confirm that your data is accessible. ## Clean up After you have verified that the upgrade was successful and your data is intact, remove the temporary files and backup volume created during the migration. Replace the placeholder values with the actual paths and volume name printed by the script. ```powershell theme={null} docker volume rm Remove-Item 'C:\path\to\docker-compose.yml.pg16.bak' Remove-Item 'C:\path\to\pg16_backup.dump' ``` ```bash theme={null} docker volume rm rm '/path/to/docker-compose.yml.pg16.bak' rm '/path/to/pg16_backup.dump' ``` # Home Source: https://bloodhound.specterops.io/home
Attack Paths For All

(b:BloodHoundUsers) - \[h:Think\_In] -> (e:Graphs)

Get started
Learn about BloodHound, how to get started, and its security boundaries. Deploy SharpHound Enterprise or AzureHound Enterprise to collect and upload data for processing and analysis. Learn how attack path data collection and ingestion works, and how to run attack path data collections. Analyzing ingested BloodHound data, and identify and remediate attack paths and risks. Ingest any data source and map Attack Paths. Learn about the schema, custom icons and more. Manage a BloodHound instance and its related components, such as users, roles, authentication, collector status, and general security. Build with BloodHound through its REST API and integrations.
# AzureHound Enterprise Azure Configuration Source: https://bloodhound.specterops.io/install-data-collector/install-azurehound/azure-configuration This section details creating and configuring an Enterprise Application for AzureHound within Microsoft Entra ID, including API permissions, roles, and authentication certificate. Applies to BloodHound Enterprise only You can complete the steps on this page manually following the instructions below, or use the PowerShell script in the [Scripted Configuration](#scripted-configuration) section to automate the entire process (except certificate upload). ## Create the AzureHound Enterprise app 1. Log into the [Microsoft Entra admin center](https://entra.microsoft.com/) as a user with the [Global Administrator] role, or the following less privileged roles: * [Privileged Role Administrator] AND * [Application Administrator] OR [Cloud Application Administrator] [Global Administrator]: https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference#global-administrator [Privileged Role Administrator]: https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference#privileged-role-administrator [Cloud Application Administrator]: https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference#cloud-application-administrator [Application Administrator]: https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference#application-administrator 2. In the left menu, select **App registrations**. 3. Click **New registration**. 4. In the **Name** field, give the application an identifying name in your organization. Make sure the supported account type is set to the "Accounts in this organizational directory only (Single tenant)" option. A URI is not required. Then click **Register**. 5. In the **Overview** menu, copy the **Application (client) ID** and **Directory (tenant) ID** to be used later in [AzureHound Enterprise Local Configuration](/install-data-collector/install-azurehound/create-configuration). 6. Continue to the next section: "Grant Microsoft Graph Permissions". ## Grant Microsoft Graph Permissions 1. In the AzureHound application, select **API Permissions**. 2. Remove the default **User.Read** delegated permission from the application, as it is not needed for data collection. Confirm the operation in the dialog window that appears after selecting the **Remove permission** option from the context menu. 3. Select **Add a permission**. 4. Click on **Microsoft Graph**. 5. Select **Application permissions**. 6. Search for and check the box next to each of the following Microsoft Graph **application** permissions. See [AzureHound Data and Permissions: Entra ID](/collect-data/azurehound-data-permissions#entra-id) for details on the least-privilege approach: | Permission | Purpose | | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `User.Read.All` | Enumerate users and user properties | | `GroupMember.Read.All` | Enumerate groups, group owners, and group members | | `Application.Read.All` | Enumerate applications, app owners, federated identity credentials, service principals, service principal owners, and app role assignments | | `Device.Read.All` | Enumerate devices and device registered owners | | `Organization.Read.All` | Read tenant/organization information | | `RoleManagement.Read.Directory` | Enumerate Entra ID role definitions, role assignments, PIM eligible role assignments, and role management policy assignments | | `AdministrativeUnit.Read.All` | Populate Administrative Unit properties on AU-scoped role assignments. Collected by AzureHound, not processed by BloodHound yet. | | `AuditLog.Read.All` | (Optional) Collect `signInActivity` (last sign-in timestamps) on user objects. AzureHound gracefully degrades if this permission is missing. Requires a Microsoft Entra ID P1 or P2 license. | 7. In the bottom of the window, select **Add permissions**. 8. Click on **Grant admin consent for \**. 9. Click **Yes** on the confirmation dialog. 10. After being redirected to API Permissions again, you should see the **Status** column listing all permissions as **Granted**. 11. Continue to the next section: "Add application authentication certificate". ## Add application authentication certificate This section requires you have authentication material. We highly recommend using certificate-based authentication. If you do not already have a certificate created, follow the article [AzureHound Enterprise Local Configuration](/install-data-collector/install-azurehound/create-configuration) and then return back here. 1. Select the **Certificates & secrets** section on the left. 2. Click on **Certificates**. 3. Click **Upload certificate**. 4. Locate the **cert.pem** file created during AzureHound setup (either on your own, or utilizing the instructions at [AzureHound Enterprise Local Configuration](/install-data-collector/install-azurehound/create-configuration)). 5. Click the folder icon and locate the "cert.pem" file. Add a description if desired. 6. In the bottom of the window, select **Add**. 7. Continue to the next section to optionally configure application branding. ## Configure application branding (optional) *Note: All steps in this section are optional and do not affect the functionality of the collector.* 1. Download the [AzureHound Enterprise icon](/assets/icons/entra-bhe-app-icon.png) to your computer. 2. Still on the AzureHound application in the Entra ID admin center, open the **Branding & properties** section. 3. Click the **Select a file** button and browse to the file you previously downloaded. 4. Provide a human-readable **Name** for the application, e.g., **BloodHound Enterprise Collector (AzureHound)**. 5. Type your BloodHound Enterprise tenant URL into the **Home page URL** field. This is for record keeping only. 6. Click the **Save** button and review the results. 7. Continue to the next section to create and assign a custom role in Azure Resource Manager ## Create and assign custom AzureHound Reader role in Azure Resource Manager If you don't have any management groups, you can skip this section. However, AzureHound will log a warning during each collection indicating it cannot collect management group data. Alternatively, you can create your Tenant Root Group by following the prompts in the Azure portal. This ensures visibility if another administrator begins using subscriptions in the future. 1. Log into the [Azure portal](https://portal.azure.com/) as a user with the [User Access Administrator](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/privileged#user-access-administrator) role. 2. Search for and select the **Management groups** item in the top search bar. 3. Select **Tenant Root Group**. 4. Select **Access control (IAM)**. 5. Click **Add**, then **Add custom role**. 6. Download azurehound-reader-role.json, open it in a text editor, and replace `` with your [Tenant Root Management Group ID](https://learn.microsoft.com/en-us/azure/governance/management-groups/overview#root-management-group-for-each-directory) (this is your Entra ID tenant ID). 7. In **Basics** > **File**, upload the edited file and click **Review + create**. See [AzureHound Data Collection and Permissions: Azure Resource Manager](/collect-data/azurehound-data-permissions#azure-resource-manager) for details on each least-privilege permission in the AzureHound Reader role. 8. Review the role and click **Create** at the bottom of the page. 9. Back in **Access control (IAM)**, click **Add**, then **Add role assignment**. 10. Search for the **AzureHound Reader** role and select it. 11. Click **Members**. 12. Click **Select members**. 13. Search for and click on your previously created service principal. 14. Validate the principal selected, then click **Select**. 15. Click the tab **Review + Assign**. 16. Click **Review + Assign** at the bottom of the page. 17. Confirm the role is present by refreshing this view. You may need to alter the filter to see this role. 18. Continue to [Run and Upgrade AzureHound (Windows, Docker, or Kubernetes)](/install-data-collector/install-azurehound/installation-options) ## Scripted configuration As an alternative to the manual steps described in this document, you can use **PowerShell** to automate the entire configuration process. The script below registers AzureHound in Entra ID, creates the least-privilege Azure Reader role, and assigns it to the service principal. The script is **idempotent**—it safely handles re-runs by detecting existing resources and updating them as needed, preventing duplicate role definitions or assignments. After running this script, follow the [Add application authentication certificate](#add-application-authentication-certificate) section to complete the setup. ```powershell theme={null} <# .SYNOPSIS Registers the Azure Data Exporter for BloodHound Enterprise (AzureHound) as an application in Entra ID. .DESCRIPTION This script registers the Azure Data Exporter for BloodHound Enterprise (AzureHound) as an application in Microsoft Entra ID and Azure, including all necessary read permissions and a least-privilege custom Azure Reader role. The script is idempotent: on subsequent runs, it detects existing role definitions and assignments, updates them if needed, and skips creation to prevent duplicates. The required PowerShell modules can be installed from the PowerShell Gallery using the following command: Install-Module -Scope AllUsers -Repository PSGallery -Force -Name @( Microsoft.Graph.Applications, Microsoft.Graph.Authentication, Az.Resources, Az.Accounts ) .NOTES Version: 1.1 #> #Requires -Version 5 #Requires -Modules Microsoft.Graph.Applications,Microsoft.Graph.Authentication #Requires -Modules Az.Resources,Az.Accounts #region Entra ID # Connect to Microsoft Entra ID through the Microsoft Graph API # Note: The -TenantId parameter is also required when using an External ID. Connect-MgGraph -NoWelcome -ContextScope Process -Scopes @( 'User.Read', 'Application.ReadWrite.All', 'AppRoleAssignment.ReadWrite.All' ) # Register the AzureHound application [string] $appName = 'BloodHound Enterprise Collector' [string] $appDescription = 'Azure Data Exporter for BloodHound Enterprise (AzureHound)' # TODO: Optionally provide the actual URL your BloodHound Enterprise tenant [string] $homePage = 'https://specterops.io/bloodhound-enterprise' [hashtable] $infoUrls = @{ MarketingUrl = 'https://specterops.io/bloodhound-enterprise' TermsOfServiceUrl = 'https://specterops.io/terms-of-service' PrivacyStatementUrl = 'https://specterops.io/privacy-policy' SupportUrl = 'https://bloodhound.specterops.io/' } [hashtable] $webUrls = @{ HomePageUrl = $homePage } [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphApplication] $registeredApp = New-MgApplication -DisplayName $appName ` -Description $appDescription ` -Info $infoUrls ` -Web $webUrls ` -SignInAudience 'AzureADMyOrg' # Configure the application logo [string] $logoUrl = 'https://bloodhound.specterops.io/assets/icons/entra-bhe-app-icon.png' [string] $tempLogoPath = New-TemporaryFile Invoke-WebRequest -Uri $logoUrl ` -OutFile $tempLogoPath ` -UseBasicParsing ` -ErrorAction Stop try { Set-MgApplicationLogo -ApplicationId $registeredApp.Id ` -ContentType 'image/png' ` -InFile $tempLogoPath } finally { # Delete the local copy of the logo from temp Remove-Item -Path $tempLogoPath } # Make sure the app instance property lock is enabled Update-MgApplication ` -ApplicationId $registeredApp.Id ` -ServicePrincipalLockConfiguration @{ IsEnabled = $true AllProperties = $true } # Create the associated service principal object [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphServicePrincipal] $servicePrincipal = New-MgServicePrincipal -DisplayName $appName ` -AppId $registeredApp.AppId ` -AccountEnabled ` -ServicePrincipalType Application ` -Notes $appDescription ` -Homepage $homePage ` -Tags 'WindowsAzureActiveDirectoryIntegratedApp','HideApp' # Fetch the Microsoft Graph applicaton ID, # which should be 00000003-0000-0000-c000-000000000000 [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphServicePrincipal] $microsoftGraph = Get-MgServicePrincipal -Filter "DisplayName eq 'Microsoft Graph'" # Define the least-privilege Microsoft Graph application permissions # See: /collect-data/azurehound-data-permissions#entra-id [string[]] $requiredPermissions = @( 'User.Read.All' 'GroupMember.Read.All' 'Application.Read.All' 'Device.Read.All' 'Organization.Read.All' 'RoleManagement.Read.Directory' 'AdministrativeUnit.Read.All' 'AuditLog.Read.All' # Optional: for signInActivity on user objects. Requires a Microsoft Entra ID P1 or P2 license ) # Resolve each permission name to its AppRole definition [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphAppRole[]] $appRoles = $requiredPermissions | ForEach-Object { [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphAppRole] $role = $microsoftGraph.AppRoles | Where-Object Value -eq $PSItem if (-not $role) { throw "Permission '$PSItem' not found in Microsoft Graph app roles" } $role } # Transform the app roles to the format required by Update-MgApplication [hashtable[]] $resourceAccess = $appRoles | ForEach-Object { @{ id = $PSItem.Id; type = 'Role' } } # Delegate the required API permissions Update-MgApplication -ApplicationId $registeredApp.Id -RequiredResourceAccess @{ ResourceAppId = $microsoftGraph.AppId # 00000003-0000-0000-c000-000000000000 ResourceAccess = $resourceAccess } # Admin-consent each permission foreach ($access in $resourceAccess) { New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $servicePrincipal.Id ` -PrincipalId $servicePrincipal.Id ` -ResourceId $microsoftGraph.Id ` -AppRoleId $access.id } # Get the environment-specific Microsoft Graph API endpoint # Azure Global: https://graph.microsoft.com # Azure USGov: https://graph.microsoft.us [string] $graphEndpoint = (Get-MgEnvironment -Name (Get-MgContext).Environment).GraphEndpoint # Fetch the info about the current user [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphDirectoryObject] $currentUser = Invoke-MgGraphRequest -Method GET -Uri '/v1.0/me' # OData IDs need to be used when assigning application ownership, # e.g., https://graph.microsoft.com/v1.0/users/{bca3617a-4c54-45eb-9a32-744c1938242e} [string] $currentUserOdataId = "$graphEndpoint/v1.0/users/{$($currentUser.Id)}" # Assign the current user as the application object owner New-MgApplicationOwnerByRef -ApplicationId $registeredApp.Id ` -OdataId $currentUserOdataId # Assign the current user as the service principal owner New-MgServicePrincipalOwnerByRef -ServicePrincipalId $servicePrincipal.Id ` -OdataId $currentUserOdataId # Sign out from Microsoft Graph Disconnect-MgGraph | Out-Null #endregion Entra ID #region Azure # Optionally enable browser-based login on Windows 10 and later Update-AzConfig -EnableLoginByWam $false # Authenticate against Azure Resource Manager Connect-AzAccount -Environment AzureCloud -Scope Process # Load the AzureHound Reader custom role definition [string] $roleDefinitionJson = @' { "properties": { "roleName": "AzureHound Reader", "description": "Least-privilege read-only role for AzureHound data collection from Azure Resource Manager.", "assignableScopes": [ "/providers/Microsoft.Management/managementGroups/" ], "permissions": [ { "actions": [ "Microsoft.Resources/tenants/read", "Microsoft.Resources/subscriptions/read", "Microsoft.Resources/subscriptions/resourceGroups/read", "Microsoft.Authorization/roleAssignments/read", "Microsoft.Management/managementGroups/read", "Microsoft.Management/managementGroups/descendants/read", "Microsoft.Compute/virtualMachines/read", "Microsoft.Compute/virtualMachineScaleSets/read", "Microsoft.KeyVault/vaults/read", "Microsoft.Web/sites/read", "Microsoft.ContainerRegistry/registries/read", "Microsoft.ContainerService/managedClusters/read", "Microsoft.Automation/automationAccounts/read", "Microsoft.Logic/workflows/read", "Microsoft.Storage/storageAccounts/read", "Microsoft.Storage/storageAccounts/blobServices/containers/read" ], "notActions": [], "dataActions": [], "notDataActions": [] } ] } } '@ if (-not (Test-Path -Path 'variable:servicePrincipal')) { # Fetch the service principal if the Azure part of the script is executed independently [Microsoft.Azure.PowerShell.Cmdlets.Resources.MSGraph.Models.ApiV10.IMicrosoftGraphServicePrincipal] $servicePrincipal = Get-AzADServicePrincipal -DisplayName 'BloodHound Enterprise Collector' } # Fetch the Tenant Root Group [guid] $currentTenantId = (Get-AzContext).Tenant.Id [Microsoft.Azure.Commands.Resources.Models.ManagementGroups.PSManagementGroup] $rootManagementGroup = Get-AzManagementGroup -GroupName $currentTenantId # New-AzRoleDefinition expects a flat structure, not the nested `properties` wrapper # used by the ARM portal upload format, so we must flatten it before passing it in. [PSCustomObject] $roleDefinition = $roleDefinitionJson | ConvertFrom-Json $flatRole = [PSCustomObject]@{ Name = $roleDefinition.properties.roleName Description = $roleDefinition.properties.description Actions = $roleDefinition.properties.permissions[0].actions NotActions = $roleDefinition.properties.permissions[0].notActions DataActions = $roleDefinition.properties.permissions[0].dataActions NotDataActions = $roleDefinition.properties.permissions[0].notDataActions AssignableScopes = @($roleDefinition.properties.assignableScopes | ForEach-Object { $_ -replace '', $currentTenantId }) } [string] $flatRolePath = New-TemporaryFile $flatRole | ConvertTo-Json -Depth 10 | Set-Content -Path $flatRolePath # Create the custom AzureHound Reader role try { $existingRole = Get-AzRoleDefinition -Name 'AzureHound Reader' -ErrorAction SilentlyContinue if ($existingRole) { $existingRole.Actions = $flatRole.Actions $existingRole.NotActions = $flatRole.NotActions $existingRole.DataActions = $flatRole.DataActions $existingRole.NotDataActions = $flatRole.NotDataActions $existingRole.Description = $flatRole.Description $existingRole.AssignableScopes = $flatRole.AssignableScopes [Microsoft.Azure.Commands.Resources.Models.Authorization.PSRoleDefinition] $azureHoundReaderRole = Set-AzRoleDefinition -Role $existingRole } else { [Microsoft.Azure.Commands.Resources.Models.Authorization.PSRoleDefinition] $azureHoundReaderRole = New-AzRoleDefinition -InputFile $flatRolePath } } finally { Remove-Item -Path $flatRolePath } # Assign the AzureHound Reader role at the Tenant Root Group $existingRoleAssignment = Get-AzRoleAssignment -ObjectId $servicePrincipal.Id ` -Scope $rootManagementGroup.Id ` -RoleDefinitionId $azureHoundReaderRole.Id ` -ErrorAction SilentlyContinue if ($null -ne $existingRoleAssignment) { [Microsoft.Azure.Commands.Resources.Models.Authorization.PSRoleAssignment] $readerRoleAssignment = $existingRoleAssignment } else { [Microsoft.Azure.Commands.Resources.Models.Authorization.PSRoleAssignment] $readerRoleAssignment = New-AzRoleAssignment -ObjectId $servicePrincipal.Id ` -Scope $rootManagementGroup.Id ` -RoleDefinitionId $azureHoundReaderRole.Id } # Sign out from Azure Resource Manager Disconnect-AzAccount -Scope Process #endregion Azure #region Summary Write-Host '' Write-Host '=== Configuration Summary ===' -ForegroundColor Cyan Write-Host '' Write-Host 'Entra ID app registration:' -ForegroundColor Green Write-Host " Application Name: $appName" Write-Host " Application ID: $($registeredApp.AppId)" Write-Host " Object ID: $($registeredApp.Id)" Write-Host " Tenant ID: $currentTenantId" Write-Host " Owner: $($currentUser.UserPrincipalName)" Write-Host '' Write-Host 'Microsoft Graph permissions (admin-consented):' -ForegroundColor Green $requiredPermissions | ForEach-Object { Write-Host " - $_" } Write-Host '' Write-Host 'Azure Resource Manager:' -ForegroundColor Green Write-Host " Custom Role: $($azureHoundReaderRole.Name)" Write-Host " Role ID: $($azureHoundReaderRole.Id)" Write-Host " Assigned Scope: $($rootManagementGroup.Id)" Write-Host '' Write-Host 'Remaining manual step:' -ForegroundColor Yellow Write-Host ' Upload an authentication certificate to the app registration.' Write-Host ' See: https://bloodhound.specterops.io/install-data-collector/install-azurehound/azure-configuration#add-application-authentication-certificate' Write-Host '' #endregion Summary ``` # Create an AzureHound Configuration Source: https://bloodhound.specterops.io/install-data-collector/install-azurehound/create-configuration Learn how to create a configuration file for AzureHound Enterprise data collection. Applies to BloodHound Enterprise only To complete the configuration process, you must have the following information: | Item | Description | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Directory (tenant) ID | Identifies the Microsoft Entra ID instance where you must [register](/install-data-collector/install-azurehound/azure-configuration) the AzureHound Enterprise application. | | Application (client) ID | Identifies the AzureHound Enterprise [app registration](/install-data-collector/install-azurehound/azure-configuration) that you must create in the Microsoft Entra admin center. | | AzureHound token ID | Identifies the AzureHound Enterprise [collector client](/collect-data/enterprise-collection/create-collector) that you must create in BloodHound Enterprise. | | AzureHound token | Provides the authentication key for the AzureHound Enterprise [collector client](/collect-data/enterprise-collection/create-collector) that you must create in BloodHound Enterprise. | Configuring AzureHound Enterprise involves the following steps: ```mermaid theme={null} flowchart LR subgraph Review f(Configuration Summary) end subgraph Configure d-->e(AzureHound logging) c-->d(AzureHound collector client) b(Azure connection)-->c(AzureHound authentication) end subgraph Download a(AzureHound Enterprise) end Download-->Configure-->Review ``` Follow the steps below to create your AzureHound Enterprise configuration file using the AzureHound Enterprise CLI tool. 1. Login to your BloodHound Enterprise tenant. 2. In the left menu, click **Download Collectors**. 3. Download the AzureHound Enterprise ZIP archive. Choose the option suitable for your system's architecture (ARM64 or AMD64). 4. Extract the contents of the ZIP archive to a working directory on the system where you plan to run the AzureHound Enterprise binary. 1. Start the AzureHound Enterprise CLI tool with the `configure` command. ```text theme={null} C:\Users\Administrator.ROOT\Downloads\azurehound-v2.8.2\azurehound-windows-amd64>azurehound.exe configure ``` To see all available options, run `azurehound.exe -h`. 2. Select the Azure region where your organization's tenant is hosted. Most organizations use the `cloud` region. ```text theme={null} AzureHound v2.8.2 Created by the BloodHound Enterprise team - https://bloodhoundenterprise.io Use the arrow keys to navigate: ↓ ↑ ← → ? Azure Region: china > cloud usgov14 usgov15 ``` 3. Enter the Azure **Directory (tenant) ID**. ```text theme={null} Directory (tenant) ID: b82887fc-338d-44ab-97d6-ac32d060ad7e ``` 4. Enter the Azure **Application (client) ID** that you created when [registering](/install-data-collector/install-azurehound/azure-configuration) the AzureHound Enterprise application. ```text theme={null} Application (client) ID: 18a7b927-9905-484e-8b17-c09630ce8ff2 ``` 1. Select a method for authenticating AzureHound Enterprise to BloodHound Enterprise. We **highly** recommend certificate-based authentication. ```text theme={null} Use the arrow keys to navigate: ↓ ↑ ← → ? Authentication Method: > Certificate Client Secret Username and Password ``` 2. If using Certificate authentication, press **Enter** or type `Y` to create a new certificate and key. ```text theme={null} Authentication Method: Certificate ? Generate Certificate and Key? [Y/n] ``` * The certificate generated by AzureHound expires after one year. * If using a certificate issued by another authority, AzureHound Enterprise supports certificates with the following characteristics: * PEM encoded * RSA 256 * PKCS#8 or PKCS#5 3. If using Certificate authentication, enter an optional passphrase for the private key. ```text theme={null} Authentication Method: Certificate v Private Key Passphrase (optional): ``` 4. Press **Enter** (or enter `Y`) to connect to BloodHound Enterprise. ```text theme={null} ? Setup connection to BloodHound Enterprise? [Y/n] ``` 5. Enter the URL of your BloodHound Enterprise tenant. ```text theme={null} v BloodHound Enterprise URL: https://enterprise.bloodhoundenterprise.io/ ``` 1. Create an AzureHound [collector client](/collect-data/enterprise-collection/create-collector). Continue to the next step when you have the **Token ID** and **Token**. 2. Enter the collector client's **Token ID**. ```text theme={null} v BloodHound Enterprise Token ID: bb7b957f-2508-400b-971e-6a1857cc0101 ``` 3. Enter the collector client's **Token**. ```text theme={null} v BloodHound Enterprise Token: **************************************** ``` 4. (Optional) Enter `y` if you want to use a proxy URL. Most organizations do not use a proxy. ```text theme={null} ? Set proxy URL? [y/N] ``` 1. Press **Enter** (or type `y`) to set up local logging. ```text theme={null} ? Setup AzureHound logging? [Y/n] ``` 2. Select the logging verbosity, as a start we recommend **Default**. ```text theme={null} Use the arrow keys to navigate: ↓ ↑ ← → ? Verbosity: Disabled > Default Debug Trace ``` 3. Enter a name for the log file. You can also enter a full path as a file name. If you do not specify a full path, AzureHound Enterprise writes logs to the specified file name and stores it in the same directory as the AzureHound binary. ```text theme={null} v Log file (optional): azurehound.log ``` 4. If you want AzureHound Enterprise to generate JSON-structured logs, press **Enter** or type `y`. ```text theme={null} ? Enable Structured Logs? [y/N] ``` When configuration is complete, the AzureHound Enterprise CLI tool displays a configuration summary. ```text theme={null} Configuration written to C:\Users\Administrator.ROOT\.config\azurehound\config.json Key written to C:\Users\Administrator.ROOT\.config\azurehound\key.pem Certificate written to C:\Users\Administrator.ROOT\.config\azurehound\cert.pem Ensure certificate is uploaded to your application's client credentials ``` If you are using Certificate authentication, the summary also includes the location of the certificate to complete the configuration in Azure. # Install and Upgrade AzureHound (Windows, Docker, or Kubernetes) Source: https://bloodhound.specterops.io/install-data-collector/install-azurehound/installation-options Applies to BloodHound Enterprise only You will need your AzureHound Enterprise configuration file from [Create an AzureHound Configuration](/install-data-collector/install-azurehound/create-configuration) prior to beginning this process. ## Windows ### Install AzureHound Enterprise on Windows This shows how to install AzureHound Enterprise as a service. Many organizations choose to run AzureHound Enterprise alongside SharpHound Enterprise from the same system. These services can live alongside each other and will not conflict. Organizations who wish to run multiple AzureHound Enterprise collectors on the same server, for example, because of multiple Azure tenants, must install AzureHound Enterprise as Scheduled Tasks instead of Windows Services. See [Setting up multiple AzureHound collectors on the same server with scheduled tasks](/install-data-collector/install-azurehound/multiple-collectors). 1. Follow the article [Create an AzureHound Configuration](/install-data-collector/install-azurehound/create-configuration) to create a configuration file. 2. Create a directory for the AzureHound service binary. We recommend using "C:\Program Files\AzureHound Enterprise" as the Program Files directory is write-protected from non-administrative users. ``` New-Item 'C:\\Program Files\\AzureHound Enterprise' -ItemType Directory ``` 3. Move "azurehound.exe" into the created directory 4. Open a command line as a local administrator, navigate to the created directory, and run: ``` azurehound.exe install ``` 5. Hit Enter, or type 'y', to use the previously created configuration file. * AzureHound will copy the configuration settings in your user profile to "C:\ProgramData\AzureHound\config.json", this is a hard-coded configuration file location 6. **If using certificate authentication:** * Copy the certificate and key file created in your user profile to a more central location, for example next to the configuration settings in "C:\ProgramData\azurehound" ``` Move-Item "$env:USERPROFILE\\.config\\azurehound\\*.pem" "C:\\ProgramData\\azurehound\\" ``` * Edit the configuration file in "C:\ProgramData\AzureHound\config.json" and change the "cert" and "key" values to the new certificate and key file locations. 7. Start the "AzureHound" service: 8. If configured correctly, the collector client in BloodHound Enterprise will show "Status: Ready", and "Last Checkin: a few seconds ago" ### Upgrade AzureHound Enterprise on Windows Upgrading AzureHound Enterprise is done by replacing the previous service binary. 1. Log into your BloodHound Enterprise tenant. 2. Click ⚙️ → **Download Collectors** 3. Download AzureHound Enterprise by clicking the button **DOWNLOAD AZUREHOUND vX.X.X (.ZIP)** 4. Extract the contents of the zip archive and locate the binary suitable for your system's architecture 5. Log into the server running your AzureHound service 6. Click "Start" and then locate the "Services" application or run "services.msc" 7. Locate the AzureHound service and open its properties 8. From the service properties window, stop AzureHound by clicking **Stop** 9. Replace the existing "azurehound.exe" binary seen in "Path to executable" with the newly downloaded one 10. From the service properties window, start AzureHound by clicking **Start** ### Single run of AzureHound Enterprise on Windows Instead of installing AzureHound as a service, it is also possible to run AzureHound a single time which runs until the command line is closed or the user logs off. This is often used in troubleshooting scenarios. 1. Follow the article [Create an AzureHound Configuration](/install-data-collector/install-azurehound/create-configuration) to create a configuration file. 2. Open a command line as a local administrator, navigate to the directory containing AzureHound Enterprise, and run: ``` azurehound.exe start ``` 3. If the connection to BloodHound Enterprise is successful, the program will output "*Waiting for jobs...*". * In BloodHound Enterprise, the AzureHound collector client will now show "Status: Ready", and "Last Checkin: a few seconds ago" 4. If needing to test AzureHound's connectivity to Azure; keep the command prompt open and follow [Run an On Demand Scan](/collect-data/enterprise-collection/on-demand-scan) 5. When started and AzureHound has fetched the job, the command line will output data while the collection is running. * If successful, the final message will be "*Collection completed successfully*" * If unsuccessful, check the log for errors, or check the AzureHound log generated next to the binary, or contact the BloodHound Enterprise team for support. ## Docker ### Run AzureHound Enterprise on Docker 1. Use the attached sample file: [docker-compose.yaml](https://raw.githubusercontent.com/SpecterOps/BloodHound-docs/main/docs/assets/deploy/docker-compose.yaml) (397 Bytes) 2. Integrate the appropriate structure into your existing configuration or utilize it as a new configuration in Docker, moving the associated config.json, cert.pem, and key.pem files to the appropriate location, and updating config.json according to your assigned values. 3. In your docker directory, run: ``` docker-compose pull && docker-compose up -d ``` 4. Review the container logs and BloodHound Enterprise user interface to verify that AzureHound has successfully connected. ### Upgrade AzureHound Enterprise on Docker 1. In your docker directory, run: ``` docker-compose pull && docker-compose up -d ``` 2. Review the container logs and BloodHound Enterprise user interface to verify that AzureHound has successfully connected. ## Kubernetes ### Run AzureHound Enterprise on Kubernetes 1. Create TLS secret for certificate and key using ``` kubectl create secret tls azurehound-tls --cert=<path to cert> --key=<path to key>” ``` 2. Create a generic secret. Choose between: 1. No passphrase: ``` kubectl create secret generic azurehound-secret --from-literal tokenId=<bloodhound enterprise token id> --from-literal token=<bloodhound enterprise token> ``` 2. Private key has passphrase: ``` kubectl create secret generic azurehound-secret --from-literal tokenId=<bloodhound enterprise token id> --from-literal token=<bloodhound enterprise token> --from-literal keypass=<private key passphrase> ``` 3. A sample [deployment.yaml](https://raw.githubusercontent.com/SpecterOps/bloodhound-docs/main/docs/assets/deploy/deployment.yaml) file is attached to this article. 4. Edit the provided deployment.yaml file. Read comments and replace instances of \[ INSERT HERE ] with appropriate values 5. Deploy AzureHound on k8s: ``` kubectl apply -f deployment.yaml ``` 6. Review the container logs and BloodHound Enterprise user interface to verify that AzureHound has successfully connected. ### Upgrade AzureHound Enterprise on Kubernetes 1. On your Kubernetes cluster, run: kubectl rollout restart deployment/azurehound-deployment 2. Review the container logs and BloodHound Enterprise user interface to verify that AzureHound has successfully connected. * [docker-compose.yaml](https://raw.githubusercontent.com/SpecterOps/bloodhound-docs/main/docs/assets/deploy/docker-compose.yaml) (397 Bytes) * [deployment.yaml](https://raw.githubusercontent.com/SpecterOps/bloodhound-docs/main/docs/assets/deploy/deployment.yaml) (2 KB) # Run Multiple AzureHound Enterprise Collectors on One Server With Scheduled Tasks Source: https://bloodhound.specterops.io/install-data-collector/install-azurehound/multiple-collectors Applies to BloodHound Enterprise only ## Purpose This article outlines how to set up multiple AzureHound Enterprise collectors on the same server using scheduled tasks. Authorized Active Directory administrators should use this when experiencing issues while running multiple AzureHound collectors simultaneously on the same server. ## Process ### A. Create the AzureHound files for all Azure tenants 1. Follow the [AzureHound Enterprise System Requirements and Deployment Process](/install-data-collector/install-azurehound/system-requirements) through deployment in step 3. Deploy and maintain AzureHound: [Run and Upgrade AzureHound (Windows, Docker, or Kubernetes).](/install-data-collector/install-azurehound/installation-options) 2. Organize the files created in a directory structure with a single AzureHound binary and a directory at the same level for each desired tenant configuration. 3. Update associated paths in each **config.json** file to reflect the new file locations. The example below shows the **default-tenant** directory pictured in step 2. ### B. Setup the Scheduled Task 1. Log in to an Administrator account on a computer with access to the desired collection server. Then, from the **Windows S\*\*\*\*tart** menu, open the **Task Scheduler** application. 2. From the **Action** menu, select **Create task…** This will open a window to name and configure a new task. 3. Best practices recommend naming tasks after their collector service and tenant to ensure clarity when reviewing multiple tasks across multiple collectors and/or tenants. In this example, **AzureHound Enterprise** is the collector, and the tenant referenced is **Dumpster**. In the Security options in the lower portion of the **General** tab, select the **Change User or Group...** button to run this task as **SYSTEM**. This ensures the scheduled task remains independent from user activity. 4. On the **Trigger** tab, click **New**. 5. On the **New Trigger** screen, set the task to run **Daily**, **repeating every 5 minutes** for a **duration of 1 day**. This ensures the task restarts if an issue arises. 5. On the **Action** tab, **New**. 6. On the **New Action** window, select **Start a program**, then **Browse...** to the location of AzureHound with the config file for that Azure tenant. Modify the following argument to match the location of the appropriate **config.json**, then fill in the **Add arguments (optional)** field, and click **OK**. ``` start -c "C:\\AzureHound\\dumpster-tenant\\config.json" ``` 6. On the bottom of the **Conditions** tab, check the **Start only if the following network connection is** **available:** checkbox, and select **Any connection**. 7. On the **Settings** tab, enable the **Stop the task if it runs longer than 1 day** setting, then select **OK**. 8. Right-click on the new **AzureHound** task in the **Task Scheduler** window, and choose **Run**. 9. Navigate to your BloodHound Enterprise tenant, click on the **Gear icon**, then **Administration,** and scroll down in the **Manage Clients** view to confirm AzureHound is executing collections appropriately. If the task is set up correctly, there will be a green dot next to **Ready**. ### C. Setup remaining Scheduled Tasks Repeat **Section B** for any additionally required scheduled tasks for other Azure tenants. ## Outcome When this process is executed successfully, scheduled tasks automatically direct multiple AzureHound collections to run on the same server simultaneously and in a way that is clearly distinguishable from discrete user activity. # Deploying AzureHound Enterprise Source: https://bloodhound.specterops.io/install-data-collector/install-azurehound/overview Deploy and maintain AzureHound Enterprise for continuous, automated collection of Microsoft Entra ID (formerly Azure Active Directory) and Azure attack path data. Promoted article # AzureHound Enterprise System Requirements and Deployment Process Source: https://bloodhound.specterops.io/install-data-collector/install-azurehound/system-requirements Applies to BloodHound Enterprise only AzureHound Enterprise is a critical element in your deployment that collects and uploads data about your Microsoft Entra ID and Azure environments to your BloodHound Enterprise tenant for processing and analysis. AzureHound Enterprise supports Windows, Docker, and Kubernetes deployments. Many organizations deploy one AzureHound Enterprise instance per Entra ID tenant. If you deploy AzureHound Enterprise on Windows, it typically runs as a Windows service. You need at least one AzureHound deployment for the tenants in scope and one Entra ID Enterprise Application service instance for each tenant. Running multiple AzureHound collector instances on a single server requires the collectors to be installed as Scheduled Tasks instead of Windows Services. Installation instructions for such a configuration can be found at: [Setting up multiple AzureHound collectors on the same server with scheduled tasks](/install-data-collector/install-azurehound/multiple-collectors). While it is possible to run both AzureHound and SharpHound on the same machine, the hardware recommendations for each application persist. ## Deployment Process Overview To deploy a new AzureHound collector: 1. Configure Entra ID and Azure: [AzureHound Enterprise Azure Configuration](/install-data-collector/install-azurehound/azure-configuration) 2. Create your AzureHound configuration: [Create an AzureHound Configuration](/install-data-collector/install-azurehound/create-configuration) 3. Deploy and maintain AzureHound: [Run and Upgrade AzureHound (Windows, Docker, or Kubernetes)](/install-data-collector/install-azurehound/installation-options) ## Server Requirements ### Hardware | Resource | Minimum | Recommended | Large enterprise | | ------------------- | ---------------- | ---------------- | ---------------- | | **Processor Cores** | 2 physical cores | 4 physical cores | 6 physical cores | | **Memory** | 4GB RAM | 16GB RAM | 32GB RAM | | **Hard disk space** | 1GB for logging | 5GB for logging | 20GB for logging | These recommendations should be considered a baseline and may need to be increased depending on the size and complexity of your environments. Minimums apply to test or development deployments. Where multiple collectors are deployed on a single host, scaling will be necessary to maintain performance. ### Software AzureHound Enterprise supports several deployment options: * Windows Server 2019+ * .NET 4.7.2+ OR * Docker OR * Kubernetes ### Network * TLS on 443/TCP to your BloodHound Enterprise tenant URL (provided by your account team) * TLS on 443/TCP to your Azure environment. Required domains are: * login.microsoftonline.com * Required for authentication to Entra ID and Azure. * msidentity.com (CNAME of login.microsoftonline.com) * Required for authentication to Entra ID and Azure. * graph.microsoft.com * Required for collection of attack path data from Microsoft Entra ID. * management.azure.com * Required for collection of attack path data from Microsoft Azure Resource Manager. ## Service Principal Requirements The AzureHound Enterprise service runs as an Entra ID registered application with a corresponding service principal (Enterprise application). The application and service principal need permissions to collect data from your tenant as detailed in [AzureHound Data Collection and Permissions](/collect-data/azurehound-data-permissions). # Configure ADFS for Integrated Windows Authentication Source: https://bloodhound.specterops.io/install-data-collector/install-sharphound/configure-adfs-iwa Learn how to enable Integrated Windows Authentication for SharpHound Enterprise on your Active Directory Federation Services (ADFS) server. Applies to BloodHound Enterprise only When using Integrated Windows Authentication (IWA) to authenticate SharpHound Enterprise with your BloodHound Enterprise tenant, you must configure your Active Directory Federation Services (ADFS) server to support this authentication method. ## Purpose This guide explains the BloodHound-specific ADFS configuration steps. For general ADFS setup and administration, refer to [Microsoft's ADFS documentation](https://learn.microsoft.com/en-us/windows-server/identity/active-directory-federation-services). ## Prerequisites * An ADFS server deployed and operational in your network * Administrative access to your ADFS server * The **Client ID** generated after [creating a SharpHound Enterprise collector client](/collect-data/enterprise-collection/create-collector#integrated-windows-authentication) ## Process The ADFS configuration process involves creating a server application for SharpHound Enterprise, configuring authentication, and ensuring that the necessary claims are included in the issued tokens for proper validation by BloodHound Enterprise. Follow the steps below to complete the configuration. In ADFS Management, create a server application using the Web API template. 1. Open **ADFS Management** on your ADFS server. 2. Right-click **Application Groups**. 3. Select **Add Application Group...** to launch the *Add Application Group Wizard*. 4. Enter a name for the application group (e.g., "sharphound-bloodhound"). 5. Under **Template** > **Client Server applications**, select **Server application accessing a Web API**. A view of the Add Application Group Wizard in ADFS Management 6. Click **Next**. 1. On the **Server Application** page enter the **Client ID** provided after [creating](/collect-data/enterprise-collection/create-collector#integrated-windows-authentication-2) the SharpHound Enterprise collector client. A **Redirect URI** is not necessary for SharpHound's authentication flow, but is required for application creation. You can enter any valid URI (e.g., `http://localhost`) and it will not impact SharpHound's ability to authenticate. A view of the Server Application configuration page in the Add Application Group Wizard 2. Click **Next**. 1. On the **Configure Application Credentials** page, click the **Integrated Windows Authentication** checkbox. 2. Click **Select...** and enter the service account user that SharpHound Enterprise will use to authenticate with ADFS. A view of the Configure Application Credentials page in the Add Application Group Wizard 3. Click **OK**. 4. Click **Next**. 1. On the **Configure Web API** page, enter a descriptive name for the Web API (e.g., `bloodhound-api`). 2. In the **Identifier** field, enter your BloodHound Enterprise tenant URL (e.g., `https://your-tenant/bloodhoundenterprise.io`). 3. Click **Add**. 4. Click **OK** to add the Web API. A view of the Configure Web API page in the Add Application Group Wizard 5. Click **Next**. On the **Apply Access Control Policy** page, select the appropriate access control policy for your environment and click **Next**. The default **Permit everyone** policy allows any authenticated user to obtain a token for SharpHound Enterprise, but you may choose a more restrictive policy if necessary. 1. On the **Configure Application Permissions** page, review the permitted scopes assigned to the application. At minimum, you must select **openid** to ensure that the necessary claims are included in the token for BloodHound authentication. No additional permissions are required for SharpHound Enterprise. A view of the Configure Application Permissions page in the Add Application Group Wizard 2. Click **Next**. Review the application group configuration and click **Next** to create the application group and associated server application. The issuance transform rule is critical for token validation in BloodHound Enterprise. This rule ensures that the **Client ID** is properly included as the "sub" (subject) claim in the token. 1. On the **Application Groups** page, open the application group you created for SharpHound Enterprise (e.g., `sharphound-bloodhound`). 2. Open the **Web API** you created for BloodHound Enterprise (e.g., `bloodhound`). 3. Click the **Issuance Transform Rules** tab and click **Add Rule...**. 4. On the **Choose Rule Type** page, select the **Send Claims Using a Custom Rule** template from the dropdown menu and click **Next**. A view of the Add Transform Claim Rule Wizard in ADFS Management 5. On the **Configure Claim Rule** page, enter a name for the rule (e.g., `Issue Sub`). 6. Add the following custom rule to ensure the Client ID is included as the "sub" claim in the token: ```text theme={null} c:[type == "http://schemas.microsoft.com/2014/01/clientcontext/claims/appid"] => issue(Type = "sub", Value = c.Value); ``` A view of the Configure Claim Rule page in the Add Transform Claim Rule Wizard The exact claim transformation rules required may vary based on your ADFS version and configuration. The critical requirement is that the token issued by ADFS must include the **Client ID** as the "sub" claim so that BloodHound Enterprise can validate that the token is intended for SharpHound Enterprise. 7. Click **Finish**. 8. Click **Apply**. ## Next Steps After ADFS is configured: 1. [Configure](/install-data-collector/install-sharphound/local-configuration#integrated-windows-authentication-iwa) the SharpHound Enterprise collector application with the required IWA properties in the `settings.json` file. 2. [Run an on-demand scan](/collect-data/enterprise-collection/on-demand-scan) to test the configuration. ## Troubleshooting If SharpHound does not authenticate successfully with ADFS, use the following accordion to troubleshoot common issues: Review the ADFS event log for authentication failures or token issuance errors. The event log often contains detailed information about why authentication or token issuance failed, including specific error codes and messages. Ensure that the SharpHound service account can reach the ADFS server. From the server running SharpHound, verify connectivity to the ADFS server over HTTPS on port 443. You can test this using tools like `Test-NetConnection` (PowerShell) or `curl` to confirm the well-known endpoint is accessible. Verify that the service account running SharpHound has Windows authentication rights on the ADFS-protected Web API. Check the ADFS application configuration and ensure Windows authentication is enabled for the Web API. Review the SharpHound logs for detailed error messages during authentication attempts. Logs are typically located in `%APPDATA%\BloodHoundEnterprise`. Look for errors related to token acquisition, ADFS connectivity, or authentication failures. # Create a gMSA for Use With SharpHound Enterprise Source: https://bloodhound.specterops.io/install-data-collector/install-sharphound/create-gmsa Applies to BloodHound Enterprise only This page describes how to configure and run the SharpHound Enterprise collection tool using an Active Directory gMSA. To learn how to do this with SharpHound Community Edition, see [Create a gMSA for Use With SharpHound Community Edition](/collect-data/ce-collection/create-gmsa-community-edition). ## Overview of gMSAs Group Managed Service Accounts (gMSA) are managed domain accounts that provide automatic password management, simplified service principal name (SPN) management, and the ability to delegate the management to other objects. Detailed software requirements from Microsoft are available [here](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/hh831782\(v=ws.11\)#software-requirements). Microsoft gMSA documentation is available [here](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/hh831782\(v=ws.11\)). ## Create a gMSA account To create a gMSA account, start by preparing the domain. 1. Log into a domain controller within the domain you want to create a gMSA. 2. To validate whether the domain has a KDS Root Key configured, run: ``` Get-KdsRootKey ``` If there's no result returned, the KDS Root Key has not been configured in the domain. Continue on to step 3. If there is a result returned, the KDS Root Key has already been configured in the domain. Skip step 3 and move on to [Create the gMSA and password read group](#create-the-gmsa-and-password-read-group). 3. Create the KDS Root Key. For a production environment, run: ``` Add-KdsRootKey -EffectiveImmediately ``` For a test environment, make the key available for immediate use by running: ``` Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10)) ``` ## Create the gMSA and password read group Perform these steps from/against a writeable Domain Controller. 1. Create a gMSA password read group for computers that should have access to the gMSA password. Browse to the desired location in Users and Computers and create the group. Alternatively, use this template to create the group using PowerShell: ```json theme={null} $gmsaName = "t0_gMSA_SHS" # Name of the gMSA $pwdReadOUDN = "<DISGINGUISHED_NAME>" # Distinguished Name of OU to create the password read group in New-ADGroup ` -Name "$($gmsaName)_pwdRead" ` -GroupScope Global ` -GroupCategory Security ` -Path $pwdReadOUDN ` -Description "This group grants the rights to retrieve the password of the BloodHound data collector (SharpHound) gMSA '$gmsaName'." ` -PassThru ``` 2. Add the SharpHound server that performs the Sharphound collections as a member of the gMSA password read group. This allows it to access the password of the gMSA and run the service. Add the computer to the group in Users and Computers. Alternatively, use this template to add group membership using PowerShell: ```json theme={null} $gmsaName = "t0_gMSA_SHS" # Name of the gMSA $shServerDN = "<DISGINGUISHED_NAME>" # Distinguished Name of the SharpHound Enterprise server Add-ADGroupMember ` -Identity "$($gmsaName)_pwdRead" ` -Members $shServerDN ` -PassThru ``` When viewing the changes on a Windows server with the GUI enabled, you can see the OUs and the t0\_gMSA\_SHS\_pwdRead group you created. 3. Create the gMSA and allow the password read group to retrieve its password. On a Domain Controller, use this template to create the gMSA and set the retrieve right using PowerShell: ```json theme={null} $gmsaName = "t0_gMSA_SHS" # Name of the gMSA $gmsaOUDN = "<DISGINGUISHED_NAME>" # Distinguished Name of OU to create the gMSA in New-ADServiceAccount -Name $gmsaName ` -Description "SharpHound service account for BloodHound" ` -DNSHostName "$($gmsaName).$((Get-ADDomain).DNSRoot)" ` -ManagedPasswordIntervalInDays 32 ` -PrincipalsAllowedToRetrieveManagedPassword "$($gmsaName)_pwdRead" ` -Enabled $True ` -AccountNotDelegated $True ` -KerberosEncryptionType AES128,AES256 ` -Path $gmsaOUDN ` -PassThru ``` If you receive the error `"_New-ADServiceAccount : Key does not exist_"`, try again in 10 hours. This allows all Domain Controllers to converge AD replication of the KDS root key. ## Prepare the SharpHound server 1. Restart the SharpHound Enterprise server so that the server's membership of the \`pwdRead\` group takes effect. 2. Grant the gMSA the "Log on as a service" User Rights Assignment on the SharpHound server. This can be done through \`secpol.msc\` or policy deployment methods like a GPO. 3. (Optional) Test that the SharpHound server can retrieve the gMSA password. See [Test the gMSA](#test-the-gmsa). ## Test the gMSA Optionally test the gMSA server to make sure that the gMSA is working. 1. Check the status of the RSAT PowerShell module. On the SharpHound Enterprise server, open a PowerShell as an Administrator and run: ```json theme={null} Get-WindowsCapability -Name RSAT* ``` If the Install State shows "Installed", skip to step 2, otherwise run: ``` Get-WindowsCapability -Name RSAT* -Online | Add-WindowsCapability -Online ``` 2. In the elevated PowerShell, test that the SharpHound server can retrieve the gMSA password by running: ```json theme={null} $gmsaName = "t0_gMSA_SHS" # Name of the gMSA Test-ADServiceAccount -Identity $gmsaName ``` The test is successful if the command responds with `True`. The gMSA is now ready to be used on the SharpHound Enterprise server. Follow [Install and Upgrade SharpHound Enterprise](/install-data-collector/install-sharphound/installation-upgrade) to complete the installation of the SharpHound Enterprise service. ## Add the gMSA to the SharpHound Enterprise service Change the SharpHound Enterprise service to be run by the created gMSA. This can be done in two ways: ### Using Services GUI / 'services.msc' 1. Open the Services application / 'services.msc' as a local administrator. 2. Open properties of the service: **SharpHoundDelegator**. 3. In the **Log On** tab, set **This account** to be the gMSA. 4. Delete the contents of the password fields if present. 5. Save by clicking **OK**. ### Using command line / 'sc.exe' 1. Open the command prompt/PowerShell as a local administrator. 2. Run the following command, replacing the 'DOMAIN' and gMSA name to match your environment. ``` sc.exe config SHDelegator obj= "DOMAIN\\t0_gMSA_SHS$" ``` # Install or upgrade SharpHound Enterprise Source: https://bloodhound.specterops.io/install-data-collector/install-sharphound/installation-upgrade Install SharpHound Enterprise on a Windows server or upgrade an existing deployment. Applies to BloodHound Enterprise only Use this guide to install SharpHound Enterprise on a domain-joined Windows server, upgrade an existing installation, or perform a headless installation for automated deployment workflows. ## Before you begin * Deploy a domain-joined Windows server for the service. For requirements and deployment guidance, see [SharpHound Enterprise System Requirements and Deployment Process](/install-data-collector/install-sharphound/system-requirements). * Sign in to BloodHound Enterprise with a user role that is authorized to download SharpHound Enterprise installation binaries. For role details, see [User Role Definitions](/manage-bloodhound/auth/users-and-roles#user-role-definitions). ## Install SharpHound Enterprise Sign in to your BloodHound Enterprise tenant. In the left menu, click **Download Collectors**. Click the latest SharpHound Enterprise version to begin downloading the `.zip` archive. SharpHound download option marked Latest Connect to the server where you want to install the SharpHound Enterprise service. Confirm that your SharpHound service account is a member of the local **Administrators** group. Copy the downloaded `.zip` archive to the target server and extract it. Choose one of the following installation methods: Use this method for standard installations where you can interact with the installer UI. 1. Run **SHSetup-v#.#.#.exe** as an administrator. 2. If Microsoft Defender SmartScreen displays the following warning, click **More info**. Microsoft Defender SmartScreen warning Confirm that the publisher is **Specter Ops, Inc.**, then click **Run anyway**. SmartScreen prompt showing publisher details 3. Select the installation path, then click **Next**. We recommend a path that low-privileged users cannot write to, such as the default `C:\Program Files (x86)\SHService`. SharpHound Enterprise installer path selection 4. Click **Install**. SharpHound Enterprise installer ready to install 5. Enter the SharpHound service account credentials in the format `DOMAIN\username`. If you use a gMSA, follow the guidance in [SharpHound hardening](/manage-bloodhound/securing-bloodhound-and-collectors/sharphound-hardening): enter credentials for a standard user account that has local administrator access during installation, then switch the service to the gMSA after installation as described in [Add the gMSA to the SharpHound Enterprise service](/install-data-collector/install-sharphound/create-gmsa). SharpHound Enterprise installer service account credentials screen 6. Click **Finish**. SharpHound Enterprise installer completion screen Use this method for automated deployments where interactive desktop sessions are unavailable or restricted. We recommend using a [Group Managed Service Account (gMSA)](/install-data-collector/install-sharphound/create-gmsa) for the SharpHound Enterprise service account instead of a standard Active Directory user account. Run the installer silently, then register the service. Replace the placeholder values to match your environment and SharpHound Enterprise version. ```powershell theme={null} # Run the silent installer SHSetup-.exe /VERYSILENT /SUPPRESSMSGBOXES /FORCECLOSEAPPLICATIONS /LOG="BloodHound.log" # Register the service $ServiceName = "SHDelegator" $DisplayName = "SharpHoundDelegator" $ExecutablePath = "C:\Program Files (x86)\SHService\SHDelegator.exe" $Username = "DOMAIN\service-account" # For a gMSA, use the format DOMAIN\$ (include the trailing $) $Password = "" # Leave blank ("") for a gMSA if (-not (Get-Service -Name $ServiceName -ErrorAction SilentlyContinue)) { New-Service -Name $ServiceName ` -BinaryPathName "`"$ExecutablePath`"" ` -DisplayName $DisplayName ` -StartupType Automatic } $svc = Get-WmiObject -Class Win32_Service -Filter "Name='$ServiceName'" $null = $svc.Change( $null, # StartMode $null, # DesktopInteract $null, # ErrorControl $null, # BinaryPathName $null, # LoadOrderGroup $null, # LoadOrderGroupDependencies $Username, # ServiceAccountName $Password # Password ) ``` Confirm that the **SharpHoundDelegator** service is present. **Interactive installation** Services console showing the SharpHoundDelegator service **Headless installation** ```powershell theme={null} Get-Service -Name "SHDelegator" ``` If you do not see the service, see [I don't see the SharpHoundDelegator service](/install-data-collector/install-sharphound/installation-upgrade#no-service). If you use a gMSA, change the service to run as the gMSA as described in [Add the gMSA to the SharpHound Enterprise service](/install-data-collector/install-sharphound/create-gmsa). Start the service once. For headless installations, run the following command: ```powershell theme={null} Start-Service -Name $ServiceName ``` This initial startup fails, but it creates the configuration and log directory in the service account's user profile: `%AppData%\BloodHoundEnterprise`. Update `settings.json` and `auth.json` as described in [SharpHound Enterprise Local Configuration](/install-data-collector/install-sharphound/local-configuration). In most environments, update the following values: * In `settings.json`, set `RestEndpoint` to your BloodHound Enterprise tenant domain in the format `CODENAME.bloodhoundenterprise.io`. * In `settings.json`, if you use a proxy, set `Proxy` in the format `proxy.acme.com:8080`. * [Create a BloodHound Enterprise collector client](/collect-data/enterprise-collection/create-collector), then set `Token` and `TokenID` in `auth.json` to the generated values. Start the service. For headless installations, run the following command: ```powershell theme={null} Start-Service -Name "SHDelegator" ``` If the service does not start, see [The SharpHoundDelegator service won't start](/install-data-collector/install-sharphound/installation-upgrade#no-start). Return to BloodHound Enterprise and confirm that the client reports **🟢 Ready**. Collector client status showing Ready Start data collection by doing one of the following: * [Create a data collection schedule](/collect-data/enterprise-collection/collection-schedule) * [Run an on-demand scan](/collect-data/enterprise-collection/on-demand-scan) ## Upgrade SharpHound Enterprise Sign in to your BloodHound Enterprise tenant. In the left menu, click **Download Collectors**. Click the latest SharpHound Enterprise version to begin downloading the `.zip` archive. Latest SharpHound download for upgrade Connect to the SharpHound Enterprise server. Extract the `.zip` archive, then run **SHSetup-v#.#.#.exe** as an administrator. Click **Finish**. SharpHound Enterprise installer finish screen after upgrade Start the **SharpHoundDelegator** service. If the service does not start, see [The SharpHoundDelegator service won't start](/install-data-collector/install-sharphound/installation-upgrade#no-start). Confirm that the service starts successfully and resumes normal communication with BloodHound Enterprise. ## Common installation issues

I don't see the SharpHoundDelegator service

This issue most often has one of the following causes: 1. The service account was not added to the local **Administrators** group before installation. 2. The credentials entered during installation were incorrect. The installation log can help with troubleshooting. The file is `InstallUtil.Install.Log` in the installation directory. The default path is `C:\Program Files (x86)\SHService\InstallUtil.Install.Log`. InstallUtil installation log in the SharpHound Enterprise installation directory

The SharpHoundDelegator service won't start

Check the `TempDirectory` value in `settings.json` as described in [SharpHound Enterprise Local Configuration](/install-data-collector/install-sharphound/local-configuration). If `TempDirectory` is `null`, the service account does not have local administrator privileges. Add the service account to the local **Administrators** group, then restart the service. If `TempDirectory` is set to a directory, open that directory, locate `service.log`, and review the logged errors. Common issues include: * `RestEndpoint` cannot be resolved. * Confirm that `RestEndpoint` matches your BloodHound Enterprise tenant domain in the format `CODENAME.bloodhoundenterprise.io`. * Confirm that the host can resolve the domain from a command prompt. * `RestEndpoint` cannot be reached, such as by timeout or connection refusal. * Confirm that firewall exclusions for your BloodHound Enterprise tenant are configured correctly. * If you need an explicit proxy, configure it in `settings.json`. * Confirm TLS 1.2 connectivity to your BloodHound Enterprise tenant over port `443`. # SharpHound Enterprise Local Configuration Source: https://bloodhound.specterops.io/install-data-collector/install-sharphound/local-configuration Applies to BloodHound Enterprise only SharpHound Enterprise stores its local configuration in two files: | **File** | **Purpose** | | --------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `settings.json` | Defines how the service behaves, including settings for connecting to the BloodHound Enterprise tenant, Active Directory, and logs. | | `auth.json` | Defines the credentials the service uses to authenticate to the BloodHound Enterprise API. | When using Integrated Windows Authentication (IWA), the `auth.json` file is not used; all authentication information is provided in `settings.json` instead. You can find the file paths for each configuration file and logs in the table below. `%AppData%` is the directory of the service account: `C:\\Users\\SERVICE_ACCOUNT$\\AppData\\Roaming`. | **SharpHound File** | **Default Path (v2.5.8+)
** | **Default Path (\<= v2.5.8)
** | | ------------------- | ----------------------------------------------------- | ----------------------------------------------------- | | Configuration File | `%AppData%\\BloodHoundEnterprise\\settings.json` | `C:\\Program Files (x86)\\SHService\\settings.json` | | Authentication File | `%AppData%\\BloodHoundEnterprise\\auth.json` | `C:\\Program Files (x86)\\SHService\\auth.json` | | Active Logs | `%AppData%\\BloodHoundEnterprise\\*.log` | `%AppData%\\BloodHoundEnterprise\\*.log` | | Archived Logs | `%AppData%\\BloodHoundEnterprise\\log_archive\\*.zip` | `%AppData%\\BloodHoundEnterprise\\log_archive\\*.zip` | ## Configure SharpHound Settings To modify any settings in your SharpHound configuration, you must stop the SharpHound service. The process to modify SharpHound's configuration files is as follows: 1. Stop the SharpHound Enterprise service: "SharpHound Delegator" 2. Edit and save one of the configuration files as an Administrator: * `settings.json` * `auth.json` 3. Start the SharpHound Enterprise service: "SharpHound Delegator" ## settings.json The `settings.json` file is a plaintext JSON file that defines information about how the service behaves, such as settings for connecting to the BloodHound Enterprise tenant, connecting to Active Directory, and writing logs. ```json title="settings.json" icon="file-brackets-curly" theme={null} { "RestEndpoint": "CODENAME.bloodhoundenterprise.io", "RestPort": 443, "SSL": true, "CurrentJob": null, "LogLevel": "Information", "EnumerationLogLevel": "Information", "TempDirectory": "C:\\Users\\gmsa_SHS$\\AppData\\Roaming\\BloodHoundEnterprise", "Proxy": null, "ComputerPasswordResetWindow": 60, "ForceLDAPKerberosAuth": true, "PortCheckTimeout": 10000, "LDAPSSLPort": 636, "LDAPPort": 389, "ForceLDAPSSL": false, "NumWorkers": 50, "PartitionLDAPQueries": true, "Version": "2.5.9.0" } ``` ### Integrated Windows Authentication (IWA) If using IWA instead of API tokens, you must include IWA-specific configuration fields in your `settings.json` file. These fields are required to enable SharpHound to authenticate using the service account's Windows credentials via Active Directory Federation Services (ADFS). ```json title="settings.json" icon="file-brackets-curly" theme={null} { ... (other settings) ... "ProviderWellKnown": "https://adfs.example.com/.well-known/openid-configuration", "ClientId": "12345678-1234-1234-1234-123456789012", "Resource": "https://your-tenant.bloodhound.com", "UseIntegratedWindowsAuthForADFS": true, } ``` When using this configuration, do not include an `auth.json` file. The authentication credentials will be handled through ADFS using the service account's Windows identity. See the following reference to learn more about the supported fields in the `settings.json` file: Your tenant domain, as provided by your account team. Enter the domain only and do not include URI information such as `https://`. **Default value:** `CODENAME.bloodhoundenterprise.io` **Example value:** `demo.bloodhoundenterprise.io` TCP port on which the BloodHound Enterprise API runs. **Default value:** `443` **Example value:** `443` Specifies whether SSL is enabled for the API connection. **Default value:** `True` **Example value:** `True` HTTP proxy URL, if your environment requires one. **Default value:** `null` **Example value:** `proxy.acme.com:8080` SharpHound uses this field to track the currently running task. When no task is running, the value is `null`. **Default value:** Do not modify this value. **Example value:** Do not modify this value. Logging verbosity level for the service itself. These logs appear in `service.log` within the configured `TempDirectory` location. Supported values, from most to least verbose: * `Trace` * `Debug` * `Information` * `Warning` * `Error` * `Critical` * `None` **Default value:** `Information` **Example value:** `Trace` Logging verbosity level used during collection jobs. Supported values, from most to least verbose: * `Trace` * `Debug` * `Information` * `Warning` * `Error` * `Critical` * `None` **Default value:** `Information` **Example value:** `Trace` Directory in which SharpHound stores logs and temporary files. If this value is `null` when the service starts, SharpHound uses the `%APPDATA%\\BloodHoundEnterprise\\` directory for the service user. Logs are retained for 14 days. Escape backslashes for valid JSON formatting by using double backslashes. **Default value:** `null` **Example value:** `C:\\Users\\SERVICE_USER$\\AppData\\Roaming\\BloodHoundEnterprise\\` Current SharpHound Enterprise version. **Default value:** Do not modify this value. **Example value:** Do not modify this value. Excludes computer objects from local collections if they have not rotated their password with the domain within this many days. By default, Active Directory computers rotate their passwords every 30 days. Minimum value: `7` This Windows setting controls how often the computer [rotates its password](https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/domain-member-maximum-machine-account-password-age). You can also [prevent password rotation](https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/domain-member-disable-machine-account-password-changes) entirely through Windows policy. **Default value:** `60` **Example value:** `365` Time, in milliseconds, that SharpHound waits for a response on TCP port `445` before considering the system unavailable. Minimum value: `200` Requires SharpHound Enterprise `v2.2.1+`. **Default value:** `10000` **Example value:** `15000` Number of concurrent threads that perform privileged collection. Minimum value: `10` Maximum value: `100` Requires SharpHound Enterprise `v2.2.1+`. **Default value:** `50` **Example value:** `50` Specifies whether SharpHound splits LDAP queries into multiple parts. Use this setting when you query very large domains. **Default value:** `True` **Example value:** `True` Enforces Kerberos authentication when SharpHound queries LDAP servers. Disabling this setting may be required to collect across an External trust type. For more information, see [Cross-Trust Collection](/collect-data/enterprise-collection/cross-trust). When set to `False`, SharpHound auto-negotiates authentication to domain controllers and prefers Kerberos when it is available. **Default value:** `False` **Example value:** `True` TCP port used for LDAP over SSL collection. Requires SharpHound Enterprise `v2.2.1+`. **Default value:** `636` **Example value:** `636` TCP port used for LDAP collection. **Default value:** `389` **Example value:** `389` Controls SharpHound's primary LDAP connection behavior. When set to `False`, SharpHound attempts LDAP over SSL first and can fall back to signed and sealed LDAP. When set to `True`, SharpHound is configured to use LDAPS for its base LDAP configuration where possible, but some ancillary directory-related operations may still use other negotiation or resolution paths. Do not use this setting as a guarantee that no TCP `389` traffic will occur in all scenarios. **Default value:** `False` **Example value:** `False` ADFS well-known endpoint URL for Integrated Windows Authentication. This is typically the ADFS server address with `/.well-known/openid-configuration` appended. Required when `UseIntegratedWindowsAuthForADFS` is enabled. **Default value:** `null` **Example value:** `https://adfs.example.com/.well-known/openid-configuration` Client ID generated by BloodHound during collector client creation. SharpHound uses this value to identify itself to the ADFS server. Required when `UseIntegratedWindowsAuthForADFS` is enabled. **Default value:** `null` **Example value:** `12345678-1234-1234-1234-123456789012` BloodHound Enterprise tenant URL used as the resource identifier for the ADFS application. Required when `UseIntegratedWindowsAuthForADFS` is enabled. **Default value:** `null` **Example value:** `https://your-tenant.bloodhoundenterprise.io` Enables Integrated Windows Authentication through ADFS. When set to `true`, SharpHound authenticates with the service account's Windows credentials against the configured ADFS server. When this setting is enabled, SharpHound does not use `auth.json`. You must also configure `ProviderWellKnown`, `ClientId`, and `Resource`. **Default value:** `False` **Example value:** `True` ## auth.json The `auth.json` file is a plaintext JSON file that defines the credentials the service uses to authenticate to the BloodHound Enterprise API. Creating a new client or rotating the credentials of an existing one will provide you with the complete JSON structure used for a SharpHound Enterprise client. ```json title="auth.json" icon="file-brackets-curly" theme={null} { "Token": "w4Tc+heVmaMTWgodlw0YlztaEGG53J/mwogiEZLvKE6WtylfYuoVEA==", "TokenID": "0c6120ee-2fbe-478f-a864-2e264f9c16d2" } ``` See the following reference to learn more about the supported fields in the `auth.json` file: Token SharpHound Enterprise uses to authenticate with the BloodHound Enterprise tenant. **Default value:** `null` **Example value:** `w4Tc+heVmaMTWgodlw0YlztaEGG53J/mwogiEZLvKE6WtylfYuoVEA==` Unique identifier for the token. **Default value:** `null` **Example value:** `0c6120ee-2fbe-478f-a864-2e264f9c16d2` # Modify the Service Account Used By SharpHound Enterprise Source: https://bloodhound.specterops.io/install-data-collector/install-sharphound/modify-service-account Applies to BloodHound Enterprise only SharpHound Enterprise utilizes the Windows Service manager to handle authentication. In certain cases, you may need to modify the user account utilized by the SharpHound Enterprise service. The steps to perform that modification are as follows. 1. Log into your SharpHound Enterprise server. 2. Open the Computer Management window. 3. Under Local Users and Groups, make sure the new account is a member of the local Administrators group (Note: this is the most commonly missed step that prevents successful migration) 4. Stop the SharpHound Delegator service. 5. Open the properties on the SharpHound Delegator service. 6. Navigate to the Logon tab. 7. Change the account in this tab to the newly desired account. If the account configuration is greyed out, run the following command within a terminal window, then re-open the SharpHound Delegator service properties. ``` sc.exe config "SHDelegator" obj= "localsystem" ``` 8. \[Optional] By default, the service will assign the logging directory to the %APPDATA% folder of the initial user that started the directory. You may desire to change that using the nested steps. 1. Run notepad.exe as an Administrator 2. Open the configuration file "settings.json". The default path is described in [SharpHound Enterprise Local Configuration](/install-data-collector/install-sharphound/local-configuration). 3. Modify the "TempDirectory" value (you may either set this to null, or make sure to utilize double backslashes as seen in the current setting) 4. Save the file and exit Notepad 9. Start the SharpHound Delegator service and confirm that it has started checking into your BloodHound Enterprise tenant utilizing the new account. # Deploy SharpHound Enterprise Source: https://bloodhound.specterops.io/install-data-collector/install-sharphound/overview Deploy and maintain SharpHound Enterprise for continuous automatic collection of Active Directory attack path data. # SharpHound Enterprise System Requirements and Deployment Process Source: https://bloodhound.specterops.io/install-data-collector/install-sharphound/system-requirements Applies to BloodHound Enterprise only The SharpHound Enterprise service is a critical element in your deployment that collects and uploads data about your environment to your BloodHound Enterprise instance for processing and analysis. SharpHound Enterprise is deployed as a signed Windows service, runs under the context of a domain account, and collects from one or more domains utilizing the configured service account. ## Deployment Process Overview To collect Active Directory data with SharpHound and ingest it into BloodHound for analysis: 1. Provision a Server that meets or exceeds the recommended Hardware, Software, and Network requirements below. 2. Create a Service Account or [gMSA](/install-data-collector/install-sharphound/create-gmsa) that SharpHound will run as, meeting the service account requirements below. 3. [Install and Upgrade SharpHound Enterprise](/install-data-collector/install-sharphound/installation-upgrade) 4. [Create a BloodHound Enterprise collector client](/collect-data/enterprise-collection/create-collector) 5. [Run an On Demand Scan](/collect-data/enterprise-collection/on-demand-scan) or [Create a data collection schedule](/collect-data/enterprise-collection/collection-schedule) ## Server Requirements ### Hardware | Resource | Minimum | Recommended | Large enterprise | | ------------------- | ---------------- | ---------------- | ---------------- | | **Processor Cores** | 2 physical cores | 4 physical cores | 6 physical cores | | **Memory** | 4GB RAM | 16GB RAM | 32GB RAM | | **Hard disk space** | 1GB for logging | 5GB for logging | 20GB for logging | These recommendations should be considered a baseline and may need to be increased depending on the size and complexity of your environments. Minimums apply to test or development deployments. Where multiple collectors are deployed on a single host, scaling will be necessary to maintain performance. ### Software * Windows Server 2019+ * .NET 4.7.2+ ### Network SharpHound Enterprise needs outbound access to your BloodHound Enterprise tenant and to at least one domain controller in each domain you collect. Additional ports are required only for specific collection methods. #### Baseline Connectivity | Destination | Protocol / port | Applies to | Notes | | ------------------------------------------------------- | --------------- | ------------------------------------ | ------------------------------- | | BloodHound Enterprise SaaS tenant URL | TLS `443/TCP` | All deployments | Proxy is supported | | At least one domain controller in each collected domain | LDAPS `636/TCP` | Active Directory collection | Configurable with `LDAPSSLPort` | | At least one domain controller in each collected domain | LDAP `389/TCP` | Active Directory collection fallback | Configurable with `LDAPPort` | SharpHound attempts LDAP over SSL first. If LDAPS is unavailable and `ForceLDAPSSL` is disabled, SharpHound falls back to signed and sealed LDAP on the configured LDAP port. SharpHound uses [LDAP channel signing](https://www.hub.trimarcsecurity.com/post/ldap-channel-binding-and-signing) for all queries. `ForceLDAPSSL` is a best-effort control for SharpHound's primary LDAP collection path, not a blanket guarantee that every directory-related operation during collection avoids non-LDAPS resolution paths. Some LDAP negotiation or AD structure resolution may still occur through existing .NET APIs. Do not assume that blocking TCP `389` is safe in every deployment without validating collector behavior in your deployed version. #### Optional Connectivity | Collection method | Protocol / port | Destination | Notes | | ----------------------------------------------------------------------------------- | ----------------- | -------------------------------------------- | --------------------------------------------- | | [Privileged collection](/collect-data/enterprise-collection/privileged-collection) | SMB/RPC `445/TCP` | All in-scope domain-joined Windows systems | Required for host-based privileged collection | | [Privileged collection](/collect-data/enterprise-collection/privileged-collection) | SMB/RPC `135/TCP` | All in-scope domain-joined Windows systems | Required for NTLM relay-based collection | | [DC Registry and CA Registry collection](/collect-data/sharphound-data-permissions) | SMB/RPC `445/TCP` | All domain controllers and domain-joined CAs | Required for registry-based collection | Expected network bandwidth for privileged collection is approximately `60-100 kB` per collection. ## Service Account Requirements Run the SharpHound Enterprise service under a domain-joined account that has the **Log on as a service** User Rights Assignment on the SharpHound Enterprise server. This account can be a traditional user account or a [Group Managed Service Account (gMSA)](/install-data-collector/install-sharphound/create-gmsa). The service account needs permissions to collect data from your target domains and domain-joined systems as detailed in [SharpHound Data Collection and Permissions](/collect-data/sharphound-data-permissions). We recommend following [SharpHound Enterprise Service Hardening](/manage-bloodhound/securing-bloodhound-and-collectors/sharphound-hardening). The SharpHound collection service account does not require `Domain Admin` membership. | Data type | Default permissions | Least-privileged option | | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | [Active Directory Structure](/collect-data/sharphound-data-permissions#active-directory-structure-data) | `Authenticated Users` can read most required data via LDAP | Delegate additional read permissions where needed (for example, restricted AD objects and dMSA) | | [Local Group Membership](/collect-data/sharphound-data-permissions#local-group-membership) | Local `Administrators` | Delegate Remote SAM access with Group Policy configuration | | [User Rights Assignments](/collect-data/sharphound-data-permissions#user-rights-assignments) | Local `Administrators` | No known delegation path today | | [NTLM](/collect-data/sharphound-data-permissions#ntlm) | Local `Administrators` | Delegate registry access with Group Policy or registry configuration | | [Sessions](/collect-data/sharphound-data-permissions#sessions) | Local `Administrators` | On Windows Server, `Print Operators` can be used; Windows desktops still require local `Administrators` | | [Certificate Services](/collect-data/sharphound-data-permissions#certificate-services) | `Authenticated Users` can collect most ADCS LDAP data | Already least-privileged by default for LDAP-collected certificate services data | | [CA Registry](/collect-data/sharphound-data-permissions#ca-registry) | `Authenticated Users` can collect CA registry data when AD CS is installed | No additional delegation is typically required | | [DC Registry](/collect-data/sharphound-data-permissions#dc-registry) | Local `Administrators` on domain controllers | Delegate access via Group Policy or registry configuration for required paths | If Active Directory tombstoning is enabled, the service account must also have [read permissions](https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/non-administrators-view-deleted-object-container) on the deleted objects container. ### Integrated Windows Authentication (IWA) If you want to use IWA for SharpHound, the following additional requirements apply: * Active Directory Federation Services (ADFS) server must be accessible in your network environment Both the system running SharpHound and the BloodHound Enterprise tenant require network connectivity to the ADFS server. * Service account must be [configured](/install-data-collector/install-sharphound/configure-adfs-iwa) in ADFS to support Windows authentication for SharpHound * Client ID property must be registered in ADFS (provided during [collector client creation](/collect-data/enterprise-collection/create-collector)) * Local SharpHound configuration must include [IWA-specific properties](/install-data-collector/install-sharphound/local-configuration#integrated-windows-authentication-iwa) in the `settings.json` file # Deploy a Tiered SharpHound Enterprise Collector Strategy Source: https://bloodhound.specterops.io/install-data-collector/install-sharphound/tiered-collector-strategy Applies to BloodHound Enterprise only ## Purpose This guide provides instructions on how to implement a tiered SharpHound Enterprise collector strategy, which is the recommended approach for collecting local data (i.e. Local Groups or Sessions) using SharpHound Enterprise. The recommendation seeks to remove the risk of credential caching, delegation, and relaying by following the principle that "[elevated user accounts should not be used to log on to lower Tier assets](https://techcommunity.microsoft.com/t5/core-infrastructure-and-security/protecting-domain-administrative-credentials/ba-p/259210)" as recommended for domains with the [Active Directory Tier Model](https://learn.microsoft.com/en-us/microsoft-identity-manager/pam/tier-model-for-partitioning-administrative-privileges) or [Enterprise access model](https://learn.microsoft.com/en-us/security/privileged-access-workstations/privileged-access-access-model). Without a tiered strategy, an organization may violate this principle if a Tier Zero SharpHound Enterprise service account authenticates to all hosts/computer objects in the domain. This is essentially the same as a Domain Admin logging onto a workstation. Be advised that this risk is considered lower because: * SharpHound Enterprise collects data through [network logons](https://learn.microsoft.com/en-us/windows-server/identity/securing-privileged-access/reference-tools-logon-types), which will not cache credentials on target systems. * SharpHound Enterprise does not use NTLM authentication by default, it uses Kerberos, which is less likely to be relayed. * SharpHound Enterprise **can** be hardened to mitigate the risk of Kerberos Delegation and NTLM authentication, see [SharpHound Enterprise Service Hardening](/manage-bloodhound/securing-bloodhound-and-collectors/sharphound-hardening). Although this article uses the term "*tier*", it is mostly interchangeable with the term "'*plane*" from the Enterprise access model. ## Prerequisites * Having defined/created one or more Organizational Units (OUs) for computers of each tier. * Each tier's collector will be configured to only process computers stored in the tier's OU(s). * Creation of one BloodHound collector server for each tier, see [SharpHound Enterprise System Requirements](/install-data-collector/install-sharphound/system-requirements) * Tip: In BloodHound, mark the Tier Zero collector server as Tier Zero, see [Privilege Zones](/analyze-data/privilege-zones/overview) * Creation of one SharpHound Enterprise service account for each tier, see [Create a gMSA for use with SharpHound Enterprise](/install-data-collector/install-sharphound/create-gmsa) * Each service account must have collection permission on all systems in the service account's tier - local `Administrators` group membership or [Least-Privileged Collection](/collect-data/enterprise-collection/least-privileged-collection) permissions. * Each service account is recommended to be hardened, see [SharpHound Enterprise Service Hardening](/manage-bloodhound/securing-bloodhound-and-collectors/sharphound-hardening). * Tip: In BloodHound, mark the Tier Zero service account as Tier Zero, see [Privilege Zones](/analyze-data/privilege-zones/overview) ## Process ### Create a tiered SharpHound Enterprise collector client This section outlines how to create a collector client that will be dedicated to local collection on computers in a single tier. One client should be created for each tier, for example: * Tier Zero * Tier One * Tier Two For organizations without an implemented tier model, we recommend creating a Tier Zero collector, and only a single collector for the other tiers. In this example, a collector client for Tier Zero will be created. 1. Follow the article [Create a SharpHound Enterprise collector client](/collect-data/enterprise-collection/create-collector) * Tip: Include an indicator for the client's tier in the **Client Name** field, for example, appending it with "t0" 2. Install the collector client on the dedicated Tier Zero BloodHound collector server, using the dedicated Tier Zero SharpHound Enterprise service account. [See Install and Upgrade SharpHound Enterprise](/install-data-collector/install-sharphound/installation-upgrade). ### Create tiered collector clients' schedules Two types of data collection schedules can be deployed for each of the tiered collector clients. For three tiers, the recommended schedule configuration is: * Tier Zero * Schedule 1 * **Active Directory Structure Data**, frequency: 1 day * Schedule 2 * **Local Groups** and **Sessions**, frequency: 3-6 hours * Tier One * Schedule 1 * **Local Groups** and **Sessions**, frequency: 3-6 hours * Tier Two * Schedule 1 * **Local Groups** and **Sessions**, frequency: 3-6 hours ### Active Directory Structure Data schedule Only one AD Structure Data schedule is needed, even though multiple tiers exist. It is recommended to be collected by the Tier Zero collector, as the clients of other tiers may be denied read access to Active Directory structure data. 1. On the Tier Zero collector client, create a new collection schedule, see [Create a data collection schedule](/collect-data/enterprise-collection/collection-schedule) 2. Set the frequency to be **Daily** and **Every 1 day(s)**. 3. Set the schedule to only collect **Active Directory Structure Data** 4. The completed schedule should look like so:\*\* ### Local Groups and Sessions schedule In this example, a schedule is configured on a Tier Zero collector client. Other tiers must follow the same procedure with different OUs selected. 1. On the Tier Zero collector client, create a new collection schedule, see [Create a data collection schedule](/collect-data/enterprise-collection/collection-schedule) 2. Set the frequency to be **Hourly** and **Every 3-6 hours**. 3. Set the schedule to collect **Local Groups** and **Sessions** 4. In **Advanced Options** in the setting **Target Local Group and/or User Session Collection by Organizational Unit**, search for the Tier Zero OU(s) containing the domain's Tier Zero computer objects. * Tip: Remember to add your Domain Controllers OU to the Tier Zero schedule. 5. The completed schedule should look like so: # Troubleshoot Local Collection Coverage Source: https://bloodhound.specterops.io/install-data-collector/install-sharphound/troubleshooting Applies to BloodHound Enterprise and CE SharpHound collects data from domain-joined systems utilizing SMB/RPC on port 445/TCP and requires the account running SharpHound (e.g., the SharpHound Enterprise gMSA) to have local administrator membership on each system in scope, see [SharpHound Data Collection and Permissions](/collect-data/sharphound-data-permissions). This article can assist in troubleshooting why a local collection is not successful for all systems in scope. ## Computer status logfile The computer status logfile, named `compstatus.csv` contains information about the collection results for each system in the collection's scope. * SharpHound Enterprise: Generates one `compstatus.csv` per local collection job (Sessions and/or Local Groups) and stores it within the `log_archive` directory on the SharpHound Enterprise server. The default location for this is `%APPDATA%\\Roaming\\BloodHound Enterprise` - that is, App Data for the service account running the SharpHound Enterprise service. However, you may override this location within `settings.json`, see [SharpHound Enterprise Local Configuration](/install-data-collector/install-sharphound/local-configuration). * SharpHound Community Edition: Will generate `compstatus.csv` when run with the `DumpComputerStatus` flag. ## Analyzing compstatus.csv The first step in troubleshooting local collection issues is by identifying and understanding errors in `compstatus.csv`. BloodHound Enterprise customers can reach out to their Technical Account Manager (TAM) for support in this analysis. Alternatively, you may do your own troubleshooting by utilizing the below example PowerShell commands and the process described below the code block. ```json theme={null} ### Import data and get uniques without sorting them $stats_file = Import-Csv -Path 'FILE_PATH_HERE' | Group-Object ComputerName, Task, Status, IPAddress | ForEach-Object { $_.Group[0] } ### Status Pivot Table - Exclude GetMembersInAlias as it's irrelevant for troubleshooting $stats_file | Where-Object {$_.Task -NotLike 'GetMembersInAlias -*'} | Group-Object Task, Status -NoElement | Format-Table -Autosize ### Pivot table for failures only $stats_file | Where-Object {$_.Status -ne "Success"} | Group-Object Task,Status -NoElement | Format-Table -Autosize ### Which systems were unreachable on 445/TCP $stats_file | Where-Object {$_.Task -eq "ComputerAvailability" -and $_.Status -eq "PortNotOpen"} ### IPv4 /24 subnets unreachable on 445/TCP $stats_file | Where-Object {$_.Task -eq "ComputerAvailability" -and $_.Status -eq "PortNotOpen" -and $_.IPAddress -match '^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$'} | Group-Object {$_.IPAddress.Remove($_.IPAddress.LastIndexOf('.'))+'.0/24'} -NoElement | Sort-Object -property Count | Format-Table -Autosize ### IPv4 /16 subnets unreachable on 445/TCP $stats_file | Where-Object {$_.Task -eq "ComputerAvailability" -and $_.Status -eq "PortNotOpen" -and $_.IPAddress -match '^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$'} | Group-Object {($_.IPAddress.split(".")[0..1] -join ".") + ".0.0/16"} -NoElement | Sort-Object -property Count | Format-Table -Autosize ### Which systems are missing permissions $stats_file | Where-Object {$_.Status -eq "ERROR_ACCESS_DENIED" -or $_.Status -eq "StatusAccessDenied"} ``` To understand and resolve the errors outputted by the commands, you must understand the process involved in SharpHound's local collection, described below. ### Domain computer enumeration First, SharpHound queries a Domain Controller to list every enabled computer object in the domain. Every enumerated system will be represented by one or more lines in `compstatus.csv`. ### ComputerAvailability Next, SharpHound performs the `ComputerAvailability` check, which filters out inactive computers, so that SharpHound only connects to active computers to collect Local Groups and Sessions later on in the process. Each active computer object is checked to see whether it is a Windows OS. Local collection is not supported for any OS besides Windows. If the system is not a Windows OS, SharpHound will not perform additional checks on the system. * If a system fails this check, `compstatus.csv` will contain a line for the system with the result `Task = ComputerAvailability` and `Status = NonWindowsOS`. * If a Windows system is incorrectly marked with `NonWindowsOS`; ensure that the system's AD computer object attribute `[operatingSystem](https://learn.microsoft.com/en-us/windows/win32/adschema/a-operatingsystem)` is set to a string representing a Windows OS. If the system is a Windows OS, SharpHound proceeds with the next set of checks: 1. Check if the system has changed it's password within the duration set for `ComputerPasswordResetWindow` in SharpHound's `settings.json`. * If a system fails this check, `compstatus.csv` will contain a line for the system with the result `Task = ComputerAvailability` and `Status = PwdLastSetOutOfRange`. * If an active system is incorrectly marked with `PwdLastSetOutOfRange`, try one of the following: * Ensure that the `ComputerPasswordResetWindow` key in SharpHound's `settings.json` has a value corresponding to the computer's security policy `[Domain member: Maximum machine account password age](https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/domain-member-maximum-machine-account-password-age)`. By default this value is 60 days. A description of the `ComputerPasswordResetWindow` value is found the article [SharpHound Enterprise Local Configuration](/install-data-collector/install-sharphound/local-configuration). * Ensure that the system's password is changing as expected: Confirm that the system's AD computer object attribute `[pwdLastSet](https://learn.microsoft.com/en-us/windows/win32/adschema/a-pwdlastset)` has been changed within the period defined in the computer's security policy `Domain member: Maximum machine account password age` 2. Check if the system has TCP port 445 (SMB) open. * If a system fails this check, `compstatus.csv` will contain a line for the system with the result `Task = ComputerAvailability` and `Status = PortNotOpen`. * If an active system is incorrectly marked with `PortNotOpen`, try one of the following: * Ensure that the system running SharpHound can reach the system checked on TCP port 445. From the SharpHound system, run: ```json theme={null} Test-NetConnection -ComputerName -Port 445 ``` (replace `` with the system's DNS name as seen in `compstatus.csv`). * Ensure that the system running SharpHound can reach the system checked on TCP port 445 **within 500ms**. From the SharpHound system, run: ```json theme={null} Measure-Command { Test-NetConnection -ComputerName -Port 445 } ``` (replace `` with the system's DNS name as seen in `compstatus.csv`). * Ensure that the system's DNS name found in `compstatus.csv` can be resolved, and matches the system's DNS name in Active Directory. From the SharpHound system, check the name can be resolved by running: ```json theme={null} Resolve-DnsName -Name ``` (replace `` with the system's DNS name as seen in `compstatus.csv`). * Ensure that a network layer above TCP (e.g., SMB) is not being blocked by a security solution, such as an IDPS. After these steps, if the system is not available, no further collection attempts are made. If a system *is* found available, `compstatus.csv` will contain a line for it with the result `Task = ComputerAvailability` and `Status = Success`. Next, SharpHound will start the actual collection of Sessions and/or Local Groups. ### Local Groups This collection gathers two types of data points: 1. Local group memberships 2. User Rights Assignment **Local group memberships** First, SharpHound connects via RPC with `SamConnect`. * If unsuccessful, `compstatus.csv` will contain a line for the system with the result `Task = SamConnect` and a status depending on the error type. * `Status = -1073610725` means SharpHound account is not in the system's local administrators group. * `Status = StatusRpcServerUnavailable` means SharpHound cannot access RPC or SMB on the system. Ensure that the system running SharpHound can reach the system checked on SMB. * If successful, SharpHound continues with the method `GetMembersInAlias` as detailed below. Next, SharpHound connects via RPC with `GetMembersInAlias`. * If unsuccessful, `compstatus.csv` will contain a line for the system with the result `Task = GetMembersInAlias` and a status depending on the error type. * If successful `compstatus.csv` will contain one line per computer in system with the result `Task = GetMembersInAlias - ` and "Status = Success' **User Rights Assignment** First, SharpHound connects via RPC with `LSAOpenPolicy`. * If unsuccessful, `compstatus.csv` will contain a line for the system with the result `Task = LSAOpenPolicy` and a status depending on the error type. * `Status = StatusRpcServerUnavailable` means SharpHound cannot access RPC or SMB on the system. Ensure that the system running SharpHound can reach the system via SMB. * `Status = StatusAccessDenied` means SharpHound account is not in the system's local administrators group. * If successful, SharpHound continues with the method `LSAEnumerateAccountsWithUserRight` as detailed below. Next, sharpHound connects via RPC with `LSAEnumerateAccountsWithUserRight`. * If unsuccessful, `compstatus.csv` will contain a line for the system with the result `Task = LSAEnumerateAccountsWithUserRight` and a status depending on the error type. * `Status = StatusAccessDenied` means SharpHound account is not in the Local Administrators group. * If successful, `compstatus.csv` will contain one line per local group in system with the result `Task = LSAEnumerateAccountsWithUserRight` and "Status = Success' ### Sessions This collection gathers logon sessions via RPC with `NetWkstaUserEnum`. * If unsuccessful, `compstatus.csv` will contain a line for the system with the result `Task = NetWkstaUserEnum` and a status depending on the error type. * `Status = ErrorAccessDenied` means SharpHound account is not in the Local Administrators group. * `Status = 53` means SharpHound cannot access RPC or SMB on the system. Ensure that the system running SharpHound can reach the system via SMB. * If successful `compstatus.csv` will contain one line per local group with the result `Task = NetWkstaUserEnum` and "Status = Success' # Install Data Collectors Source: https://bloodhound.specterops.io/install-data-collector/overview Get started with SharpHound Enterprise or AzureHound Enterprise for continuous, automatic collection of attack path data. ## Install SharpHound Enterprise System requirements and deployment process for SharpHound Enterprise Guide for installing and upgrading SharpHound Enterprise Learn about tiered collector strategy deployment Instructions for creating a group Managed Service Account Configure SharpHound Enterprise locally How to change the service account for SharpHound Enterprise [See all 8 articles](/install-data-collector/install-sharphound/overview) ## Install AzureHound Enterprise System requirements and deployment process for AzureHound Enterprise Configure AzureHound Enterprise for Azure Steps to create an AzureHound configuration Guide for installing and upgrading AzureHound on various platforms How to run multiple AzureHound Enterprise collectors using Scheduled Tasks # Integrate BloodHound Enterprise with Jira Source: https://bloodhound.specterops.io/integrations/atlassian/jira/configure Learn how to install and configure the Jira integration for BloodHound Enterprise. Applies to BloodHound Enterprise only The BloodHound Enterprise Jira integration is a Jira Cloud app built on Atlassian Forge. It synchronizes BloodHound Enterprise attack path findings to Jira issues for remediation tracking. This page shows you how to install the integration, connect a Jira project to BloodHound Enterprise, and configure synchronization behavior for Jira Software or Jira Service Management. Use this integration to: * Automatically synchronize BloodHound Enterprise findings with Jira every five minutes * Map BloodHound zones to Jira priorities and due dates * Automatically close Jira issues when findings are remediated ## Prerequisites Before you configure the integration, confirm that you have the following: | Platform | Requirements | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Jira** |
  • A Jira Cloud instance (Data Center and Server are not supported)
  • A Jira Software or Jira Service Management project where you want the integration to create issues
  • Project Administrator or Site Administrator access in Jira
  • Atlassian Forge CLI installed (for self-hosted installations)
  • Network connectivity between Jira and BloodHound Enterprise
| | **BloodHound Enterprise** |
  • A BloodHound Enterprise tenant
  • A BloodHound Enterprise [non-personal API key/ID pair](/integrations/bloodhound-api/working-with-api#create-a-non-personal-api-key%2Fid-pair) with the **Auditor** role
| ## Install the integration Install the integration in your Jira Cloud instance. Atlassian requires **Organization Administrator** or **Site Administrator** privileges to install Marketplace apps in Jira Cloud. 1. Go to the [SpecterOps BloodHound Enterprise](https://marketplace.atlassian.com/apps/2043886069) listing on the Atlassian Marketplace. 2. Click **Get it now**. 3. Select the Jira Cloud site where you want to install the integration. 4. Click **Install app**. Review the installation details, then start the installation. ## Configure connection settings Configure the connection between Jira and BloodHound Enterprise using your BloodHound Enterprise non-personal API key/ID pair. Navigate to the project where you want the integration to create issues. 1. Go to **Space Settings**. 2. In the **Apps** section, select **BloodHound Enterprise Integration**. 3. Click the **Connection Settings** tab. Enter the BloodHound Enterprise connection details. | Field | Description | | -------------------------------- | ----------------------------------------------------- | | **BloodHound Enterprise Domain** | The URL of your BloodHound Enterprise tenant | | **Token ID** | The API token ID used to authenticate requests | | **Token Key** | The API token key used to sign and authorize requests | Before you can access the **Configuration** tab, you must successfully test the connection. Click **Test Connection** and wait for Jira to confirm that it can reach your BloodHound Enterprise tenant. ## Configure synchronization settings After successfully testing the connection, you can configure how the integration should synchronize BloodHound Enterprise findings with your Jira project. The **Configuration** tab provides the following settings: | Setting | Purpose | | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | **Issue Type / Request Type** | Chooses the Jira issue type for Jira Software projects or the request type for Jira Service Management projects | | **BHE Domains** | Limits synchronization to the selected BloodHound Enterprise environments | | **BHE Zones** | Limits synchronization to the selected BloodHound Enterprise zones, such as Tier Zero, Tier One, and Hygiene | | **Priority Mapping** | Maps BloodHound Enterprise zones to Jira priorities | | **Due Days** | Sets the due date window for each Jira priority | | **Enable Auto-Closure** | Enables automatic closure of Jira issues when the corresponding BloodHound Enterprise finding is no longer detected (remediated) | | **Cleanup Interval** | Defines how often the integration checks for orphaned issues | For Jira Service Management projects, the integration detects the project type automatically and replaces the **Issue Type** selector with a **Request Type** selector filtered to incident request types. When the integration detects a Jira Service Management project, it automatically manages the following fields: * Service Desk ID * Request Type ID * Request Type Field ID * Incident Request Type Name Select the Jira issue type that should represent BloodHound Enterprise findings (for example, Task, Bug, or Story). The integration dynamically fetches the available domains and zones from your BloodHound Enterprise tenant. Select the domains and zones that you want to include in the synchronization. 1. Choose one or more **BHE Domains**. 2. Choose the **BHE Zones** that you want to synchronize with Jira. * **Tier Zero**: Critical attack paths to high-value targets * **Tier One**: Significant attack paths that require attention * **Hygiene**: General security hygiene and best practices issues Customize the priority and due date settings for each BloodHound Enterprise zone. 1. Map each BloodHound Enterprise zone to the Jira priority you want to use based on your organization's policies. The default priorities are: | Zone | Default Jira Priority | | ------------- | --------------------- | | **Tier Zero** | Highest | | **Tier One** | Low | | **Hygiene** | Lowest | 2. Set the number of due days for each priority. The default due days are: | Jira Priority | Default Due Days | | ------------- | ---------------- | | **Highest** | 3 days | | **High** | 7 days | | **Medium** | 14 days | | **Low** | 30 days | | **Lowest** | 90 days | Use **None** to disable due dates for specific priorities. The integration can automatically close Jira issues when the corresponding BloodHound Enterprise finding is no longer detected (remediated). | Setting | Description | Options | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- | --------- | | **Enable Auto-Closure** | Enables automatic closure of Jira issues when the corresponding BloodHound Enterprise finding is no longer detected (remediated) | Yes / No | | **Cleanup Interval** | Defines how often the integration checks for (and closes) orphaned issues | 1-10 days | When **Auto-Closure** is enabled: * The cleanup interval runs hourly to check for orphaned issues. * An issue is considered orphaned if the corresponding BloodHound Enterprise finding is no longer detected. * The integration transitions orphaned issues to a **Done** status and adds a comment indicating that the issue has been automatically closed. The integration closes remediated issues according to the cleanup interval that you set. For example, if you set the interval to 3 days, the integration closes eligible issues no more than once every 3 days. After you have configured the integration, you can save the configuration and run the first synchronization from the **Configuration** tab. 1. Click **Save Configuration**. 2. Click **Run Sync Now** to trigger the first synchronization immediately. ## Verify the configuration After you save the configuration and run the first synchronization, confirm the following: * Jira starts the synchronization successfully * The configured project receives issues for findings that match the selected domains and zones * The created issues use the expected priority and due date values * Jira adds zone and domain labels that you can use for filtering ## Next steps * [Use Jira with BloodHound Enterprise](/integrations/atlassian/jira/use) * [Troubleshoot the Jira integration](/integrations/atlassian/jira/troubleshoot) # Jira integration design reference Source: https://bloodhound.specterops.io/integrations/atlassian/jira/reference Technical reference for the Jira integration, including its Atlassian Forge architecture, schedules, and API usage. Applies to BloodHound Enterprise only This page explains how the BloodHound Enterprise Jira integration works. It covers the Atlassian Forge app architecture, authentication model, configuration inputs, scheduler behavior, and BloodHound API dependencies. For setup instructions, see [Install and configure the integration](/integrations/atlassian/jira/configure). ## Integration type The BloodHound Enterprise Jira integration is a **vulnerability management and remediation tracking** integration. It synchronizes BloodHound Enterprise attack path findings into Jira so security and operations teams can manage remediation work in Jira Software or Jira Service Management. ## Use cases * Create Jira issues automatically for BloodHound Enterprise findings * Route findings into Jira Software or Jira Service Management workflows * Map BloodHound Enterprise zones to Jira priorities and due dates * Keep Jira aligned with BloodHound Enterprise by closing remediated findings automatically ## Core design The integration is built on the Atlassian Forge platform and uses a combination of scheduled tasks and API calls to synchronize data between BloodHound Enterprise and Jira. | Component | Purpose | | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | **Configuration UI** | Provides the Forge project settings page where Jira administrators enter BloodHound Enterprise connection details and synchronization settings | | **Sync scheduler** | Pulls findings from BloodHound Enterprise every five minutes and creates Jira issues or incidents | | **Cleanup scheduler** | Runs hourly and closes orphaned Jira issues after the corresponding BloodHound Enterprise findings no longer exist and auto-closure is enabled | | **Jira project** | Hosts the created issues or incidents and provides the remediation workflow | The integration follows the following workflow: 1. A Jira administrator installs the app from the Atlassian Marketplace. 2. A project administrator opens the Forge project settings page in Jira and enters the BloodHound Enterprise connection values. 3. The integration validates the credentials with **Test Connection**. 4. The sync scheduler polls BloodHound Enterprise every five minutes and creates one Jira issue or incident per matching finding. 5. The cleanup scheduler runs hourly and closes orphaned issues when the corresponding finding no longer exists in BloodHound Enterprise. ## Authentication and secrets The integration uses the following security measures: | Control | Implementation | | ---------------------------------------- | -------------------------------------------------------------- | | **BloodHound Enterprise authentication** | HMAC-SHA256 signed requests | | **Jira authorization** | Atlassian Forge OAuth 2.0 app scopes | | **Secret storage** | Atlassian Forge encrypted Key-Value Storage (KVS) | | **Data in transit** | HTTPS/TLS | | **Data flow** | Direct API calls from Atlassian Forge to BloodHound Enterprise | Each BloodHound Enterprise request includes: 1. `Authorization: bhesignature {token_id}` 2. `RequestDate: {rfc3339_timestamp}` 3. `Signature: {base64_hmac_signature}` ## Configuration inputs The integration exposes the following user-configurable inputs: | Input | Required | Description | | -------------------------------- | -------- | ------------------------------------------------------------------------------------- | | **BloodHound Enterprise Domain** | Yes | The URL of your BloodHound Enterprise tenant | | **Token ID** | Yes | The API token ID used to authenticate requests | | **Token Key** | Yes | The API token key used to sign requests | | **Issue Type / Request Type** | Yes | The Jira issue type for Jira Software or the request type for Jira Service Management | | **BHE Domains** | Yes | The BloodHound Enterprise environments to synchronize | | **BHE Zones** | Yes | The BloodHound Enterprise zones to synchronize | | **Priority Mapping** | Yes | The mapping between BloodHound Enterprise zones and Jira priorities | | **Due Days** | Yes | The number of due days to assign for each mapped priority | | **Enable Auto-Closure** | Yes | Enables or disables automatic closure of orphaned Jira issues | | **Cleanup Interval** | Yes | The number of days between orphaned-issue closure checks | The integration does not allow the Jira project key to be configured by the user because the app derives it from the Forge extension context. ## Jira project behavior The integration adjusts its configuration model based on the Jira project type: | Capability | Jira Software | Jira Service Management | | ----------------------- | -------------------------------------------- | -------------------------------------------------------------------------- | | **Issue model** | Standard Jira issue type | Incident issue type with a selected request type | | **Configuration field** | **Issue Type** | **Request Type** | | **Project detection** | Uses the configured Jira Software issue type | Detects the JSM project automatically and populates incident request types | ## Sync model The integration follows the following synchronization behavior: | Setting | Value | | ------------------------- | ------------------------------------------------------ | | **Sync interval** | Every 5 minutes | | **Cleanup scheduler** | Every hour | | **Cleanup interval** | 1 to 10 days, based on configuration | | **Synchronization scope** | Selected BloodHound Enterprise domains and zones | | **Manual action** | **Run Sync Now** triggers an immediate synchronization | The integration creates one Jira issue or incident for each finding that matches the selected domains and zones. It prevents duplicate issue creation by storing Jira Entity Properties in `sync_metadata`, including `findingId` and `updatedAt`. The provided Jira source documents also describe the following operational values for the synchronization workflow: | Setting | Value | | ------------------------ | ---------------------- | | **Batch size** | 500 findings per batch | | **Bulk Jira operations** | 50 issues per API call | | **Invocation timeout** | 15 minutes | ## Synced Jira issue content The integration requires each Jira issue or incident to store the attack path data needed for remediation tracking. | Field | Contents | | ------------------------- | ------------------------------------------------------------------------ | | **Issue correspondence** | One unique Jira issue or incident for each BloodHound Enterprise finding | | **Attack path title** | The issue corresponds to the attack path title | | **Graph View link** | A deep link to the finding in BloodHound Enterprise | | **Attack path data** | Relevant finding data retrieved from BloodHound Enterprise | | **Remediation guidance** | Remediation steps associated with the finding | | **Priority and due date** | Values derived from the configured zone-to-priority and due-day mappings | ## API endpoints The integration depends on the following BloodHound Enterprise API endpoints: | Endpoint | Purpose | | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | [`GET /api/v2/available-domains`](/reference/search/get-available-domains) | Lists the environments available to the configured API token | | [`GET /api/v2/asset-group-tags`](/reference/asset-isolation/get-asset-group-tags) | Lists the available BloodHound Enterprise zones used for synchronization filtering | | [`GET /api/v2/domains/{domain_id}/available-types`](/reference/attack-paths/list-available-attack-paths) | Lists the attack path finding types for a selected environment | | [`GET /api/v2/domains/{domain_id}/details`](/reference/attack-paths/list-domain-attack-paths-details) | Returns detailed attack path records for a selected environment and finding type | | `GET /api/v2/assets/findings/{finding_type}/title.md` | Returns the title for a finding type | | `GET /api/v2/assets/findings/{finding_type}/short_description.md` | Returns the short description for a finding type | | `GET /api/v2/assets/findings/{finding_type}/short_remediation.md` | Returns the short remediation text for a finding type | | `GET /api/v2/assets/findings/{finding_type}/long_remediation.md` | Returns the long remediation text for a finding type | ## Error handling The integration calls for centralized API error handling with logging and graceful recovery when possible. | Status | Meaning | Expected behavior | | ------ | ----------------- | -------------------------------------------------------------------- | | `400` | Bad Request | Log the error and stop the current request gracefully | | `401` | Unauthorized | Log the authentication failure and skip the request | | `403` | Forbidden | Log the authorization failure and skip the request | | `404` | Not Found | Log the missing endpoint or resource and skip retry | | `429` | Too Many Requests | Log the rate-limit condition and apply a cooldown before a later run | | `5xx` | Server Error | Log the failure and treat it as a critical synchronization error | If an API request fails, the integration skips the failed operation, logs the error, and retries during a later synchronization interval. ## Platform dependencies The integration relies on the following external dependencies: | Dependency | Role | | -------------------------------------- | ---------------------------------------------------------------- | | **BloodHound Enterprise API** | Source of attack path findings and finding metadata | | **Atlassian Forge and Jira Cloud API** | Hosts the app runtime, configuration UI, and Jira issue handling | # Troubleshoot the Jira integration Source: https://bloodhound.specterops.io/integrations/atlassian/jira/troubleshoot Learn how to diagnose and resolve common Jira integration issues with BloodHound Enterprise. Applies to BloodHound Enterprise only Use this page to troubleshoot common issues with the BloodHound Enterprise Jira integration. Start with the connection test, then validate the project configuration and synchronization scope. ## Test Connection fails If **Test Connection** fails, the most common causes are: * The **Domain**, **Token ID**, or **Token Key** value is incorrect * The BloodHound Enterprise token is expired or revoked * The BloodHound Enterprise URL does not include `https://` * Jira Cloud cannot reach the BloodHound Enterprise tenant To resolve the issue: 1. Verify the **Domain**, **Token ID**, and **Token Key** values in the **Connection Settings** tab. 2. Confirm that the API token is still active in BloodHound Enterprise. 3. Generate a new token if the current token is invalid or compromised. 4. Confirm that the BloodHound Enterprise tenant is reachable from the internet. ## Configuration tab is unavailable The **Configuration** tab stays unavailable until the connection test succeeds. To unlock the configuration options: 1. Re-enter the Jira connection values. 2. Run **Test Connection** again. 3. Wait for Jira to confirm that it retrieved the available BloodHound Enterprise domains. ## Jira does not create issues If the integration does not create issues after a synchronization, check the following: * At least one **BHE Domain** is selected * At least one **BHE Zone** is selected * BloodHound Enterprise currently has active findings in the selected domains and zones * The issues were not already created by an earlier synchronization To verify the configuration: 1. Open the **Configuration** tab. 2. Confirm the selected domains and zones. 3. Click **Run Sync Now**. 4. Check the target Jira project again after the synchronization starts. ## Jira uses the wrong priority or due date The integration applies the priority and due date values from your configured mappings. To correct the issue: 1. Review the **Priority Mapping** values for each BloodHound Enterprise zone. 2. Review the **Due Days** values for the Jira priorities that the integration assigns. 3. Save the updated configuration. 4. Run a manual synchronization to apply the new mapping to future updates and new issues. ## Auto-closure does not close remediated issues If remediated findings do not close in Jira, the most common causes are: * **Enable Auto-Closure** is disabled * The configured cleanup interval has not elapsed yet * The Jira workflow does not provide a transition to **Done** * The integration has not completed a recent synchronization for the configured scope To resolve the issue: 1. Confirm that **Enable Auto-Closure** is enabled. 2. Review the configured **Cleanup Interval**. 3. Confirm that the Jira project workflow supports the transition to **Done**. 4. Run a manual synchronization, then wait for the hourly cleanup scheduler to evaluate orphaned issues. ## Jira Service Management request types do not appear If the request type selector is empty in Jira Service Management: 1. Confirm that the target project is a Jira Service Management project. 2. Confirm that the project has incident request types available. 3. Refresh the configuration page and rerun **Test Connection** if needed. ## Common configuration messages The following error messages may appear in the configuration screen when performing actions: | Message | Cause | Solution | | -------------------------- | --------------------------------------------- | -------------------------------------------------------------------------------------------- | | `Sync already in progress` | Another synchronization is still running | Wait for the current synchronization to finish before you run another manual synchronization | | `No domains configured` | No BloodHound Enterprise domains are selected | Select one or more domains in the **Configuration** tab and save the change | | `Invalid domain format` | The BloodHound Enterprise URL is malformed | Enter the full tenant URL and include `https://` | # Use Jira with BloodHound Enterprise Source: https://bloodhound.specterops.io/integrations/atlassian/jira/use Learn how to review and manage Jira issues created from BloodHound Enterprise findings. Applies to BloodHound Enterprise only After you complete the [installation and configuration](/integrations/atlassian/jira/configure), the integration synchronizes BloodHound Enterprise findings into Jira every five minutes. This page explains how the synchronized issues are structured and how to work with them in Jira. ## Jira issue model The integration creates one Jira issue or incident for each synchronized BloodHound Enterprise finding. | Jira object | Purpose | | ------------------------ | --------------------------------------------------------------------------------- | | **Issue or incident** | Represents one BloodHound Enterprise finding | | **Priority** | Reflects the zone-to-priority mapping that you configured | | **Due date** | Uses the due-day rule for the mapped Jira priority | | **Labels** | Adds zone and domain labels so you can filter and report on the findings | | **Auto-closure comment** | Explains that the finding was remediated when Jira closes the issue automatically | If you connect a Jira Service Management project, the integration uses the configured request type and incident workflow instead of a standard Jira Software issue type. ## Jira issue format When the integration creates a Jira issue, it follows a specific format to ensure that all relevant information is included: The issue summary includes the zone, domain, affected principal, and finding ID. **Example**: \[Tier-Zero] CORP.LOCAL - Administrator (12345) The issue description includes the finding title, Graph View link, finding description, impact details, context, and remediation guidance. | Field | Contents | | ------------------------ | ------------------------------------------------------------------------------------------------------------ | | **Finding Title** | The name of the attack path finding, such as *GenericWrite Privileges on Tier Zero Objects* | | **BHE Link** | A direct link to the finding in the BloodHound Enterprise graph view | | **Description** | An explanation of what the privilege allows and why it is a concern | | **Impact** | The number of affected assets and exposure paths | | **Context** | The severity level, finding type, environment details, and finding IDs | | **Affected Entities** | Details about the source and target principals involved, including name, type, domain, object ID, and status | | **Relationship** | Information about the relationship, including whether it is ACL-based and inherited | | **Status** | Whether the finding is accepted plus its creation and update timestamps | | **Remediation Guidance** | Both the quick fix and the detailed, step-by-step remediation steps | Labels include zone and domain identifiers, which allow you to filter and report on the findings in Jira. * **Example (zone)**: tier-zero, tier-one, hygiene * **Example (domain)**: corp-local The dates include the start date, which Jira sets when it creates the issue, and the due date, which the integration calculates from the configured priority mapping. **Example**: * Start date: Jun 20, 2026 * Due date: Jun 23, 2026 The priority reflects the zone-to-priority mapping in the integration's configuration settings. **Example**: Highest ## Synchronization behavior The integration keeps Jira aligned with BloodHound Enterprise automatically: * It runs synchronization every five minutes * It creates issues for new findings and updates existing issues when finding details change * It avoids duplicates by tracking synchronization metadata on the Jira issues * It runs a cleanup scheduler hourly and closes orphaned issues when **Auto-closure** is enabled and the cleanup interval is met No action is required for automatic synchronization. The integration will automatically create and update Jira issues based on the configured settings. If you need to manually trigger a synchronization, you can do so from the integration's [configuration settings](/integrations/atlassian/jira/configure#configure-synchronization-settings) in Jira. ## Review synchronized findings Reviewing the synchronized findings in Jira allows you to triage and manage security issues identified by BloodHound Enterprise. Use the synchronized issues the same way you manage other operational work in Jira: * Filter by labels, priority, or due date to focus on the findings you want to triage first. * Assign findings to the team that owns remediation * Update workflow state as triage and remediation progress * Add comments or work notes that document investigation and closure decisions * Use the Graph View link to move from Jira back to the BloodHound Enterprise finding when you need more context # BloodHound JSON Formats Source: https://bloodhound.specterops.io/integrations/bloodhound-api/json-formats Applies to BloodHound Enterprise and CE BloodHound requires collected data to be in in a specific JSON format, which is documented in this article. The collectors for BloodHound Enterprise (BHE) and BloodHound Community Edition (BHCE) will format the collected data correctly. Enterprise collectors run continuously and automatically upload the data to the API endpoint `/ingest`, whereas Community Edition collectors drop the JSON data to disk which then has to be uploaded manually to BloodHound via Administration → File Ingest, or to the API endpoint `/file-upload`. Users of BloodHound Enterprise can also manually upload JSON data collected from Community Edition collectors. Users of both BHE and BHCE may also create their own JSON files and upload them to BloodHound. ## Basic JSON Format The JSON format contains two main objects\*\*:\*\* -**data** -An array of collected objects. One JSON file contains only one type of object, for example computers. * **meta** * An object containing meta-information about the collection and the `data` array. * **methods** is a bitmask of the collection method used. This is used for the BloodHound ingestor to know what data to expect. Possible values can be found in models.go as `CollectionMethods`. * **type** describes the type of objects in the data array. One JSON file can only contain one object type. Possible values can be found in models.go as `DataType`. * **count** is an integer representing the number of objects in the `data` object array. * **version** is an integer representing the version of the JSON format. ``` { "data": [ { [...] } ], "meta": { "methods": 127999, "type": "users", "count": 1, "version": 5 } } ``` ## Detailed JSON Format JSON data of each node type will vary greatly. Examples of detailed JSON formats for some node types can be found in the latest version directory [in the BHCE's repository.](https://github.com/SpecterOps/BloodHound/tree/main/cmd/api/src/test/fixtures/fixtures) # Work With the BloodHound API Source: https://bloodhound.specterops.io/integrations/bloodhound-api/working-with-api Enterprise and Community Edition badge The BloodHound product family are API-first products, meaning everything functions on the underlying API layer. All data displayed in the portal, all commands given to SharpHound or AzureHound Enterprise collectors, and all data uploaded pass through the BloodHound APIs. Customers may utilize these APIs to extend the use of the BloodHound product to function with other tools in their environment. This article will show how to access the API and include some example use cases. ## API Documentation Our API reference is available [here](../../reference/). Additionally, API documentation is hosted utilizing Swagger behind authentication within your tenant environment. After logging in, you may access it by clicking the cog in the top right corner of your tenant and clicking **API Explorer.** API Explorer menu in the BloodHound UI ## Authentication The BloodHound API accepts two forms of authentication, each with its own limitations for security. * A JWT is generated through the login process using your email address, password, and 2FA token (or SAML-based authentication flow). These JWT tokens are active for 8 hours and are primarily for end-user access to the web-based application. * An API key/ID pair is generated within the Administration interface. These do not expire and are primarily for long-term API integrations. There are two methods for creating API key/ID pairs, each serving a different purpose: * Non-personal API key/ID pairs for integrations like [Splunk](/integrations/splunk/siem/install) * [Personal API key/ID pairs](#create-a-personal-api-key-and-id-pair) for day-to-day use like [BloodHound Operator](https://www.youtube.com/watch?v=9Og-6_qyw_A) ### Create a non-personal API key/ID pair Administrators can create non-personal BloodHound users solely meant for API integrations. 1. Log in as a user with the Administrator role. 2. [Create a new BloodHound user](../../manage-bloodhound/auth/users-and-roles). * Give the user a long and unique password. 3. ***Optional recommendation:*** Log in as the newly created user and enable MFA. 4. ***Optional recommendation:*** Securely dispose of the password and MFA configuration as they are not needed for authenticating with a key/ID pair and they can be reset by an Administrator if needed. 5. As the Administrator, go back to the **Manage Users** page. 6. On the API user, click the hamburger menu > **Generate / Revoke API Tokens**. Generate API Tokens 7. Click **Create Token**. 8. Give the token a descriptive name and click **Save**. 9. Save the presented API key/ID pair and click **Close**. * **The API key will never be shown again. If you lose it, you must revoke the previous key and regenerate a new one.** 10. You may now use this key ID pair for [calling the API](#call-the-api) ### Create a personal API Key and ID pair Create a personal API Key/ID pair from the **My Profile** section. 1.

In the top-right corner click **My Profile**.

Create Token 2. Click **API Key Management**. API Key Management 3. Click **Create Token**. Create Token 4. Give the token a descriptive name and click **Save**. Create Token 5. Save the presented API key/ID pair and click **Close**. * The API key will never be shown again. If you lose it, you must revoke the previous key and regenerate a new one. API Key Pair Use this key/ID pair for calling the API. ## Call the API Once you have your token, you can call the BloodHound API. ### Use a JWT/bearer token For quick tests or one-time calls, the JWT used by your browser may be the simplest route. The API accepts calls using the following header structure in the HTTP request: ```json theme={null} 'Authorization': Bearer $JWT_TOKEN ``` If you open the **Network** tab in your browser, you'll see calls against the API made using this structure. ### Use your API Key/ID pair For long-running API integrations, BloodHound's API uses hash-based message authentication code (HMAC) authentication using the API key as the secret key to verify the authenticity and integrity of the request. Calls against the API must include the following in the signed hash: * API key * HTTP method and URI * Current time * Body content (if applicable to the request) Calls against the API would need to include the following headers in the HTTP request: ```json theme={null} 'Authorization': bhesignature $TOKEN_ID 'RequestDate': $RFC3339_DATETIME 'Signature': $BASE64ENCODED_HMAC_SIGNATURE ``` By validating the hash signature against the request, the API can validate that the calls were made by the original requestor, within a reasonable timeframe, against the proper API endpoint, including the original content body, and that no replay or content modification has occurred. For a complete implementation example, see [`apiclient.py`](https://github.com/SpecterOps/bloodhound-docs/blob/main/docs/assets/apiclient.py) in the `specterops/bloodhound-docs` GitHub repository. The following code snippet from the `apiclient.py` script illustrates the authentication process: ```python theme={null} def _request(self, method: str, uri: str, body: Optional[bytes] = None) -> requests.Response: # Digester is initialized with HMAC-SHA-256 using the token key as the HMAC digest key. digester = hmac.new(self._credentials.token_key.encode(), None, hashlib.sha256) # OperationKey is the first HMAC digest link in the signature chain. This prevents replay attacks that seek to # modify the request method or URI. It is composed of concatenating the request method and the request URI with # no delimiter and computing the HMAC digest using the token key as the digest secret. # # Example: GET /api/v2/test/resource HTTP/1.1 # Signature Component: GET/api/v2/test/resource digester.update(f'{method}{uri}'.encode()) # Update the digester for further chaining digester = hmac.new(digester.digest(), None, hashlib.sha256) # DateKey is the next HMAC digest link in the signature chain. This encodes the RFC3339 formatted datetime # value as part of the signature to the hour to prevent replay attacks that are older than max two hours. This # value is added to the signature chain by cutting off all values from the RFC3339 formatted datetime from the # hours value forward: # # Example: 2020-12-01T23:59:60Z # Signature Component: 2020-12-01T23 datetime_formatted = datetime.datetime.now().astimezone().isoformat('T') digester.update(datetime_formatted[:13].encode()) # Update the digester for further chaining digester = hmac.new(digester.digest(), None, hashlib.sha256) # Body signing is the last HMAC digest link in the signature chain. This encodes the request body as part of # the signature to prevent replay attacks that seek to modify the payload of a signed request. In the case # where there is no body content the HMAC digest is computed anyway, simply with no values written to the # digester. if body is not None: digester.update(body) # Perform the request with the signed and expected headers return requests.request( method=method, url=self._format_url(uri), headers={ 'User-Agent': 'bhe-python-sdk 0001', 'Authorization': f'bhesignature {self._credentials.token_id}', 'RequestDate': datetime_formatted, 'Signature': base64.b64encode(digester.digest()), 'Content-Type': 'application/json', }, data=body, ) ``` Examples for other languages: * [PowerShell](https://github.com/SpecterOps/bloodhound-docs/blob/main/docs/assets/Get-BloodHoundAPISignature.ps1) * [C#](https://github.com/SpecterOps/bloodhound-docs/blob/main/docs/assets/BloodHoundAPIClient.cs) # Integrate BloodHound Enterprise with Cortex XSOAR Source: https://bloodhound.specterops.io/integrations/cortex-xsoar/configure Learn how to integrate BloodHound Enterprise with Cortex XSOAR by Palo Alto Networks. Applies to BloodHound Enterprise only The BloodHound Enterprise integration for [Cortex XSOAR](https://www.paloaltonetworks.com/resources/datasheets/cortex-xsoar-for-mssps#:~:text=Cortex%20XSOAR%C2%AE%EF%B8%8F%20is%20a,of%20services%20for%20their%20clients.) lets you ingest and manage BloodHound Enterprise attack path findings in Cortex XSOAR as incidents. Use this integration to: * Automatically convert BloodHound Enterprise attack path findings into Cortex XSOAR incidents * Attach remediation guidance and posture context to incidents * Run playbooks and custom commands to analyze, triage, and remediate findings Key capabilities include: * Automated incident creation with titles, descriptions, remediation guidance, impact/exposure metrics, severity, and domain/environment context * Playbook linking per incident to run custom analysis commands * Custom commands: * Object ID lookup by name * Asset information by object ID * Path analysis between two nodes in the BloodHound graph ## Prerequisites Before installing and configuring the Cortex XSOAR integration, ensure that you have the following: * Cortex XSOAR instance with an admin account * BloodHound Enterprise tenant * BloodHound Enterprise API key/ID pair We recommend a [non-personal API key/ID pair](/integrations/bloodhound-api/working-with-api#create-a-non-personal-api-key%2Fid-pair). ## Configure Cortex XSOAR Set up the SpecterOps BloodHound Enterprise integration instance in Cortex XSOAR. 1. Log in to your Cortex XSOAR instance. 2. Go to **Settings & Info** > **Settings** > **Integrations** > **Instances**. Cortex XSOAR Integrations & Instances page with SpecterOps integration visible. 1. Search for the SpecterOps integration. 2. Click **Add Instance** for the SpecterOpsBHE integration. 3. Configure settings. | Field | Description | Required? | | -------------------------------- | ------------------------------------------------------------------- | :-------: | | **Name** | Instance display name (default can be modified) | Yes | | **BloodHound Enterprise Domain** | Your tenant domain, e.g., `https://example.bloodhoundenterprise.io` | Yes | | **Token ID** | API token ID from BloodHound Enterprise | Yes | | **Token Key** | API token key from BloodHound Enterprise | Yes | | **Proxy URL** | Proxy URL to reach BloodHound Enterprise | No | | **Proxy URL Username** | Username for proxy authentication | No | | **Proxy URL Password** | Password for proxy authentication | No | | **Finding Environment** | Scope findings to one environment | No | | **Finding Category** | Scope findings to one category | No | By default, **Finding Environment** and **Finding Category** are set to **All**. Cortex XSOAR instance configuration showing fetch settings. 1. Check the **Fetches incidents** option (required). 2. Set **Incident Type** to "SpecterOpsBHE Attack Path" (optional). 3. Set the **Incidents Fetch Interval** to your preferred schedule (required). The default fetch interval is 10 minutes. Cortex XSOAR instance configuration detail view. 1. Click **Test** to verify connectivity and credentials. 2. Close the modal, then **Save** the instance. "Success" indicates working parameters and connectivity. "Error" indicates invalid parameters or connection failure. Cortex XSOAR instance save confirmation. * To add additional BloodHound Enterprise domains, create more instances with **Add Instance**. * To stop fetching, uncheck **Enable** to disable the instance. List of multiple SpecterOpsBHE instances in Cortex XSOAR. # Cortex XSOAR integration design reference Source: https://bloodhound.specterops.io/integrations/cortex-xsoar/reference Technical reference and design details for the BloodHound Enterprise Cortex XSOAR integration. Applies to BloodHound Enterprise only This document provides technical design details and API reference information for the BloodHound Enterprise integration with Cortex XSOAR. For configuration instructions, see [Configure the integration](/integrations/cortex-xsoar/configure). ## Integration type The SpecterOps BloodHound Enterprise integration is a **vulnerability management** integration that enables automated retrieval of attack path findings from BloodHound Enterprise into Cortex XSOAR. This streamlines incident creation and investigation for Active Directory and Microsoft Azure environments. ## Use cases * Automatically fetch new attack paths from BloodHound Enterprise * Create incidents in XSOAR for each detected attack path * Filter incidents by domain and finding type * Track attack paths with granular timestamp-based deduplication * Retrieve detailed information about Active Directory and Azure assets * Enrich incident data with asset details including object IDs, names, and properties * Support for both directory types (User, Computer, Group, Container, Domain, GPO, etc.) and Azure types (AZApp, AZGroup, AZUser, AZRole, AZTenant, AZServicePrincipal, etc.) * Check if attack paths exist between two principals in the environment * Search for objects by name to retrieve their object IDs * Analyze relationships between principals and assets * Support for multiple Active Directory domains * Filter by specific domains or finding types * Track attack paths per domain and finding type combination ## Authentication The integration uses HMAC-based signature authentication with the following process: 1. Generate HMAC signature using SHA-256 with the token key 2. Include token ID, request date, and signature in request headers 3. Format: `Authorization: bhesignature {token_id}` 4. Include `RequestDate` header in ISO format 5. Include `Signature` header as base64-encoded HMAC digest ## Configuration parameters | Parameter | Display Name | Type | Required | Description | | ----------------------- | ---------------------------------- | -------- | -------- | ------------------------------------------------------------------------------------------- | | `url` | Server URL | String | Yes | The BloodHound Enterprise server URL (e.g., `bhe.example.com` or `https://bhe.example.com`) | | `token_id` | Token ID | String | Yes | The API token ID for authentication | | `token_key` | Token Key | Password | Yes | The API token key for HMAC signature authentication | | `finding_domain` | Selected Environments | String | No | Comma-separated list of domain names to monitor, or "all" for all domains. Default: "all" | | `finding_category` | Selected Finding Types | String | No | Comma-separated list of finding types to monitor, or "all" for all types. Default: "all" | | `incidentFetchInterval` | Incident Fetch Interval (minutes) | Number | No | The interval in minutes between incident fetches. Default: 10 minutes | | `isFetch` | Fetch incidents | Boolean | No | Enable automatic incident fetching. Default: false | | `proxy_url` | Custom Proxy URL | String | No | Custom proxy server URL (optional) | | `proxy_username` | Proxy Username | String | No | Username for proxy authentication (if required) | | `proxy_password` | Proxy Password | Password | No | Password for proxy authentication (if required) | | `insecure` | Trust any certificate (not secure) | Boolean | No | Skip SSL certificate verification. Default: false | | `proxy` | Use system proxy settings | Boolean | No | Use system proxy settings. Default: false | ## Commands and outputs ### test-module Tests the connection to the BloodHound Enterprise API. **Arguments**: None **Context outputs**: None **Human-readable output**: * Success: "ok" * Failure: Error message indicating the specific failure reason (Unauthorized, Bad Request, Forbidden, Server Error, DNS resolution error, etc.) ### bhe-get-object-id Retrieves object IDs for one or more objects by their names. **Arguments**: | Argument | Type | Description | Required | | -------------- | ------ | -------------------------------------------------- | -------- | | `object_names` | String | Comma-separated list of object names to search for | Yes | **Context outputs**: | Path | Type | Description | | ------------------------------ | ------ | ------------------------------------------------- | | `SpecterOpsBHE.Object.Name` | String | The object name that was searched | | `SpecterOpsBHE.Object.Status` | String | Status of the search ("success" or "error") | | `SpecterOpsBHE.Object.Message` | String | Status message | | `SpecterOpsBHE.Object.Data` | Array | Array of matching objects with id, name, and type | **Human-readable output**: Object ID Search Results | Object Name | Status | Object ID | Type | | ------------------ | ------- | ------------ | -------- | | `user@example.com` | success | S-1-5-21-... | User | | `COMPUTER01` | success | S-1-5-21-... | Computer | ### bhe-fetch-asset-info Retrieves detailed information about one or more assets by their object IDs. **Arguments**: | Argument | Type | Description | Required | | ------------ | ------ | ----------------------------------------------------------- | -------- | | `object_ids` | String | Comma-separated list of object IDs to fetch information for | Yes | **Context outputs**: | Path | Type | Description | | ------------------------------ | ------ | ------------------------------------------------------------------ | | `SpecterOpsBHE.Asset.ObjectId` | String | The object ID | | `SpecterOpsBHE.Asset.Name` | String | Asset name | | `SpecterOpsBHE.Asset.Type` | String | Asset type (User, Computer, AZUser, AZApp, etc.) | | `SpecterOpsBHE.Asset.Status` | String | Status of the fetch operation | | `SpecterOpsBHE.Asset.Data` | Object | Complete asset data including properties and related entity counts | **Human-readable output**: Asset Information | Object ID | Name | Type | Status | | ------------ | ------------------ | ---- | ------- | | S-1-5-21-... | `user@example.com` | User | success | For Azure objects, the response includes additional related entity counts such as group membership counts, role assignments, inbound/outbound control counts, and abusable app role assignments (for service principals). ### bhe-does-path-exist Checks if an attack path exists between two principals in the BloodHound Enterprise graph. **Arguments**: | Argument | Type | Description | Required | | --------------- | ------ | --------------------------------- | -------- | | `FromPrincipal` | String | Object ID of the source principal | Yes | | `ToPrincipal` | String | Object ID of the target principal | Yes | **Context outputs**: | Path | Type | Description | | ---------------------------------- | ------- | -------------------------------------------- | | `SpecterOpsBHE.Path.Exists` | Boolean | Whether a path exists between the principals | | `SpecterOpsBHE.Path.FromPrincipal` | String | Source principal object ID | | `SpecterOpsBHE.Path.ToPrincipal` | String | Target principal object ID | | `SpecterOpsBHE.Path.Status` | String | Status of the path check operation | **Human-readable output**: Path Existence Check | From Principal | To Principal | Path Exists | Status | | -------------- | ------------ | ----------- | ------- | | S-1-5-21-... | S-1-5-21-... | true | success | ### fetch-incidents Fetches attack path findings from BloodHound Enterprise and creates incidents in XSOAR (automatically executed when `isFetch` is enabled). **Arguments**: None **Context outputs**: | Path | Type | Description | | -------------------------------------------------- | ------- | -------------------------------------------------------------- | | `SpecterOpsBHE.Incident.Name` | String | Incident name (format: `{INSTANCE} - {DOMAIN} - {PATH_TITLE}`) | | `SpecterOpsBHE.Incident.Type` | String | Incident type: "SpecterOpsBHE Attack Path" | | `SpecterOpsBHE.Incident.Severity` | Number | Severity level (1=Low, 2=Medium, 3=High, 4=Critical) | | `SpecterOpsBHE.Incident.AttackId` | String | Unique attack path ID | | `SpecterOpsBHE.Incident.Domain` | String | Domain name where the attack path was detected | | `SpecterOpsBHE.Incident.PathTitle` | String | Human-readable title of the attack path | | `SpecterOpsBHE.Incident.FindingType` | String | Finding type identifier | | `SpecterOpsBHE.Incident.ImpactPercentage` | Number | Impact percentage (0-100) | | `SpecterOpsBHE.Incident.ImpactCount` | Number | Number of impacted principals | | `SpecterOpsBHE.Incident.ExposurePercentage` | Number | Exposure percentage (0-100) | | `SpecterOpsBHE.Incident.ExposureCount` | Number | Number of exposed principals | | `SpecterOpsBHE.Incident.ImpactedPrincipal` | String | Object ID of the impacted principal | | `SpecterOpsBHE.Incident.ImpactedPrincipalName` | String | Name of the impacted principal | | `SpecterOpsBHE.Incident.ImpactedPrincipalKind` | String | Type of the impacted principal | | `SpecterOpsBHE.Incident.ImpactedPrincipalObjectId` | String | Object ID of the impacted principal | | `SpecterOpsBHE.Incident.NonTierZeroPrincipal` | String | Object ID of the non-tier-zero principal (if applicable) | | `SpecterOpsBHE.Incident.NonTierZeroPrincipalName` | String | Name of the non-tier-zero principal (if applicable) | | `SpecterOpsBHE.Incident.ObjectIds` | String | Comma-separated list of object IDs involved | | `SpecterOpsBHE.Incident.ObjectNames` | String | Comma-separated list of object names involved | | `SpecterOpsBHE.Incident.ShortDescription` | String | Short description of the attack path | | `SpecterOpsBHE.Incident.ShortRemediation` | String | Short remediation guidance | | `SpecterOpsBHE.Incident.LongRemediation` | String | Detailed remediation guidance | | `SpecterOpsBHE.Incident.CreatedAt` | String | Timestamp when the attack path was created | | `SpecterOpsBHE.Incident.UpdatedAt` | String | Timestamp when the attack path was last updated | | `SpecterOpsBHE.Incident.Accepted` | Boolean | Whether the attack path has been accepted | | `SpecterOpsBHE.Incident.AcceptedUntil` | String | Date until which the attack path is accepted (if applicable) | **Fetch logic**: 1. **Lock Mechanism**: Uses integration context to prevent concurrent fetch operations 2. **Domain Filtering**: Fetches available domains and filters by `finding_domain` parameter 3. **Finding Type Collection**: Collects available finding types for each domain 4. **Finding Type Filtering**: Filters finding types by `finding_category` parameter 5. **Path Metadata Fetching**: Retrieves titles, descriptions, and remediation guidance for each finding type 6. **Incremental Fetching**: Uses timestamp-based filtering to only fetch new attack paths since last run 7. **Granular Tracking**: Tracks timestamps per `{domain_name}:{finding_type}` combination for precise deduplication 8. **Pagination**: Handles pagination for large result sets (up to 1000 results per page) 9. **Incident Creation**: Creates one incident per attack path with all relevant metadata ## API endpoints The integration uses the following BloodHound Enterprise API v2 endpoints: | Endpoint | Path | Description | | ------------------ | --------------------------------- | ----------------------------- | | `available_domain` | `/api/v2/available-domains` | Get list of available domains | | `search` | `/api/v2/search?q={query}` | Search for objects by name | | `dictionary_types` | `/api/v2/{obj_type}s/{object_id}` | Get directory object details | ## Supported object types ### Active Directory types * User * Computer * Group * Container * Domain * GPO (Group Policy Object) * Aiaca * Rootca * Enterpriseca * Ntauthstore * Certtemplate * OU (Organizational Unit) ### Azure types * AZApp (Azure Application) * AZGroup (Azure Group) * AZUser (Azure User) * AZRole (Azure Role) * AZTenant (Azure Tenant) * AZServicePrincipal (Azure Service Principal) * AZAutomationAccount (Azure Automation Account) ## Error handling The integration implements comprehensive error handling with specific exception types: | Exception Type | HTTP Status | Description | | --------------------------------- | ----------- | -------------------------- | | `BloodHoundBadRequestException` | 400 | Invalid request parameters | | `BloodHoundUnauthorizedException` | 401 | Authentication failure | | `BloodHoundForbiddenException` | 403 | Insufficient permissions | | `BloodHoundNotFoundException` | 404 | Resource not found | | `BloodHoundRateLimitException` | 429 | Rate limit exceeded | | `BloodHoundServerErrorException` | 500+ | Server errors | ### Retry logic * Automatic retry for rate limit (429) and server errors (500, 502, 503, 504) * Maximum of 3 retry attempts * Immediate retry without exponential backoff ### Memory limitation handling * Gracefully handles memory limitation errors for large queries (especially for AZTenant) * Returns appropriate error messages when memory limits are encountered * Sets related entity counts to 0 when memory limitations occur # Use Cortex XSOAR with BloodHound Enterprise Source: https://bloodhound.specterops.io/integrations/cortex-xsoar/use Learn how to use Cortex XSOAR with BloodHound Enterprise to monitor and manage attack path findings. Applies to BloodHound Enterprise only After you configure the integration, Cortex XSOAR begins fetching BloodHound Enterprise attack path findings as incidents. Use the sections below to monitor ingestion, view incidents, and inspect details. See [install and configure](/integrations/cortex-xsoar/configure) for setup steps and fetch interval settings. ## Monitor ingestion and logs You can view instance logs to confirm incidents are being fetched. Cortex XSOAR instance logs showing incident fetch activity for SpecterOpsBHE integration. ## View incidents Open the **Incidents** view to see all fetched attack path incidents. Click any incident to open its details. Cortex XSOAR Incidents list showing SpecterOpsBHE attack path incidents. ## Incident details The incident details page includes key information about the attack path and related context: * Incident name and ID * Case details * Quick View side panel with labels containing attack path data Incident details view showing case details and Quick View labels for attack path data. ## Work Plan and playbook Click the **Work Plan** tab to view the playbook. The SpecterOpsBHE playbook runs custom commands to retrieve object-related information and analyze attack paths. You can click each task or script to view its results. Work Plan view showing the playbook tasks and scripts executed for the incident. ## DBot panel Use the DBot panel to review execution context and results. Locate the **root** section and expand it to see underlying data and command outputs related to the incident. DBot panel expanded to show root context and command outputs for the incident. # Integrate BloodHound Enterprise in Google SecOps Source: https://bloodhound.specterops.io/integrations/google-secops/configure Learn how to install and configure the BloodHound Enterprise integration and connector in Google SecOps. Applies to BloodHound Enterprise only The BloodHound Enterprise integration for Google SecOps lets you ingest Attack Path findings into Google SecOps so analysts can investigate and respond without leaving the platform. This guide shows you how to install the integration from the Google Marketplace, configure the integration instance, and enable the connector that creates cases, alerts, and events. Use this integration to: * Create Google SecOps cases from BloodHound Enterprise Attack Path findings * Investigate findings with BloodHound Enterprise asset lookup and path validation actions * Group related alerts by source domain to keep investigations organized ## Prerequisites Before you begin, ensure that you have the following: * A Google SecOps tenant * A BloodHound Enterprise tenant * A BloodHound Enterprise [non-personal API key/ID pair](/integrations/bloodhound-api/working-with-api#create-a-non-personal-api-key%2Fid-pair) with the **Auditor** role ## Install and configure the integration Install the integration instance in Google SecOps and connect it to your BloodHound Enterprise tenant. 1. Log in to your Google SecOps tenant with an account that has permission to install integrations. 2. Go to **Content Hub** > **Response Integrations**. 3. Search for `BloodHound Enterprise - Google SecOps`. 4. Click **Install**. 5. Click **Configure**. Configure the required fields for the integration instance: | Field | Description | | -------------------------------- | ----------------------------------------------------- | | **BloodHound Enterprise Server** | The URL of your BloodHound Enterprise tenant | | **Token ID** | The API token ID used to authenticate requests | | **Token Key** | The API token key used to sign and authorize requests | Save the configuration after you enter the required values. BloodHound Enterprise - Google SecOps integration configuration form. 1. Click **Test** to validate the server URL and API credentials. 2. Confirm that the test succeeds before you continue. Successful integration test result in Google SecOps. A successful test confirms that Google SecOps can connect to the BloodHound Enterprise API with the supplied credentials. If the test fails, review the error message and confirm that the server URL, token ID, and token key are correct. You can also refer to the [troubleshooting guide](/integrations/google-secops/troubleshoot) for more help diagnosing common issues. ## Configure the connector The connector retrieves Attack Path findings from BloodHound Enterprise and creates the corresponding cases, alerts, and events in Google SecOps. You can manually trigger the connector to run a one-time ingestion of BloodHound Enterprise findings, or you can enable it to run on a schedule. 1. Go to **Settings** > **SOAR Settings** > **Ingestion** > **Connector**. 2. Click **Create New Connector**. 3. Select the BloodHound Enterprise connector that you want to configure. 1. Open the **Parameter** tab. 2. Enter the required values for the connector. | Field | Description | | ------------------------------------ | ------------------------------------------------------------------------- | | **BloodHound Enterprise Server** | The URL of your BloodHound Enterprise tenant | | **Token ID** | The API token ID used for authentication | | **Token Key** | The API token key used to sign requests | | **Selected BloodHound Environments** | The BloodHound Enterprise environments that the connector should query | | **Selected Finding Types** | The Attack Path finding categories that the connector should ingest | | **Run Every** | The interval at which the connector polls BloodHound Enterprise | | **Product Field Name** | The source field name that Google SecOps should use for the product value | | **Event Field Name** | The source field name that Google SecOps should use for the event value | BloodHound connector Parameter tab in Google SecOps. The connector test validates connectivity, connector logic, and the required parameter values without requiring you to enable the connector first. 1. Open the **Testing** tab. 2. Click **Test Connector** to run a one-time execution. 3. Review the generated alerts and the debug logs. 4. Click **Log to System** if you want to create cases from the generated test alerts. Connector Testing tab showing the Test Connector action. 1. Enable the toggle for **Attack Paths Alert**. Connector page after the connector is enabled. 2. Open the **Logs** tab. 3. Enable the **Log Connection** toggle if logging is not already enabled. Connector logs showing generated alerts. 4. Confirm in the logs that alerts are created successfully. Connector Logs tab showing the Log Connection toggle. 5. Open the **Cases** page to review the resulting cases, alerts, and events. ## Map and model alerts Alerts are not mapped and modeled by default. Configure field mappings before you move the integration into regular analyst workflows. Open the Google SecOps settings menu and select **Mapping and Modeling**. For this example, use the **Default** family to classify alerts under a predefined set of rules. 1. Choose the **Default** family. 2. Open the **Visualization** tab. Google SecOps Mapping and Modeling page with the Default family selected. Map the incoming alert fields to the corresponding event fields. 1. Ensure that **StartTime** and **EndTime** are configured correctly. These fields are crucial for defining the time frame of the events. 2. Save the mapping configuration. 3. Test the mapping with sample alerts before you use it in production. Visualization tab showing alert field mappings in Google SecOps. ## Configure alert grouping Configure alert grouping so Google SecOps groups related BloodHound Enterprise alerts into one case per source domain. Grouping alerts from the same domain into a single case allows for: * Easier investigation and triage * Clear, organized case structures * Domain-specific incident visibility * Scalable response workflows 1. Go to **SOAR Settings** > **Advanced** > **Alert Grouping**. 2. Click **Add Rule**. 3. Configure the rule with the following values: | Setting | Value | | ------------------- | --------------------------------------- | | **Category** | `Data Source` | | **Value** | `BloodHound Enterprise - Google SecOps` | | **Group By** | `Entities` | | **Grouping Entity** | `SourceDomain` | Save the rule after you enter the values. Alert grouping rule configuration for the BloodHound Enterprise - Google SecOps integration. With this rule in place, Google SecOps groups all alerts for the same source domain into a single case, up to the platform's case and event limits. ## Next steps After the connector is running, use [cases and alerts](/integrations/google-secops/use) in Google SecOps to investigate BloodHound Enterprise findings. For help resolving issues, see [troubleshoot common issues](/integrations/google-secops/troubleshoot). # Google SecOps integration design reference Source: https://bloodhound.specterops.io/integrations/google-secops/reference Technical reference for the BloodHound Enterprise integration for Google SecOps, including architecture, authentication, connector flow, and API usage. Applies to BloodHound Enterprise only This page explains how the BloodHound Enterprise integration for Google SecOps works. It covers the integration architecture, authentication model, connector behavior, analyst actions, and BloodHound API dependencies. For setup instructions, see [Install and configure the integration](/integrations/google-secops/configure). ## Integration type The BloodHound Enterprise integration for Google SecOps is a **vulnerability management and investigation** integration. It ingests Attack Path findings into Google SecOps and exposes on-demand actions that help analysts validate paths and enrich cases with BloodHound Enterprise data. ## Use cases * Ingest BloodHound Enterprise Attack Path findings into Google SecOps on a schedule * Group related alerts by source domain to keep investigations organized * Enrich cases with BloodHound Enterprise asset details * Validate whether a shortest path exists between two assets * Use Google SecOps playbooks and response workflows with BloodHound Enterprise findings ## Core design The integration has four main parts: | Component | Purpose | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | **Integration instance** | Stores the BloodHound Enterprise server URL, token ID, and token key used for API authentication and connection tests. | | **Connector** | Polls BloodHound Enterprise for Attack Path findings, normalizes the response, and sends alerts and events to Google SecOps. | | **Google SecOps actions** | Lets analysts run **Ping**, **Get Object Id**, **Does Path Exist**, and **Fetch Assets** during an investigation. | | **Alert grouping rule** | Groups related alerts into a single case per source domain when you configure the recommended grouping rule in Google SecOps. | The following diagram shows how installation, configuration, ingestion, and analyst investigation fit together across Google SecOps and BloodHound Enterprise. Architecture diagram showing the Google SecOps integration components and workflow The workflow for installing and using the Google SecOps integration follows these stages: 1. **Google SecOps Marketplace**: The integration is available from the Google SecOps marketplace inside the tenant. 2. **App installation**: A Google SecOps administrator installs the integration from the marketplace. 3. **Input configuration**: Google SecOps provides the configuration UI where you enter the values required for the integration. 4. **Connector**: The connector pulls findings from BloodHound Enterprise on a schedule, then parses and normalizes alerts and events for Google SecOps. 5. **Actions**: Analysts can use actions in playbooks to enrich investigations, automate response steps, and streamline incident handling. ## Investigation model The connector produces Google SecOps alerts and events from BloodHound Enterprise findings. With the recommended alert grouping rule in place, Google SecOps groups those alerts into one case per source domain. | Object | Purpose | | --------- | ----------------------------------------------------------------------------------------------------------------------- | | **Case** | Represents the investigation container for a source domain, such as `GHOST.CORP`. | | **Alert** | Represents a distinct BloodHound Enterprise finding or path title within the case. | | **Event** | Represents an individual Attack Path occurrence and its supporting details, such as object IDs and path traversal data. | ## Authentication The integration uses BloodHound API signed requests. Google SecOps stores the BloodHound Enterprise credentials in the integration and connector configuration UI. Each request includes: 1. `Authorization: bhesignature {token_id}` 2. `RequestDate: {rfc3339_timestamp}` 3. `Signature: {base64_hmac_signature}` The signature uses HMAC-SHA-256 and chains the request method, request URI, request timestamp, and request body. For more detail, see [Use the BloodHound API](/reference/overview) and [Work with the BloodHound API](/integrations/bloodhound-api/working-with-api). ## Configuration inputs ### Integration instance Configure the integration instance with the core BloodHound Enterprise connection details: | Field | Description | | -------------------------------- | ---------------------------------------------- | | **BloodHound Enterprise Server** | The URL of your BloodHound Enterprise tenant | | **Token ID** | The API token ID used to authenticate requests | | **Token Key** | The API token key used to sign requests | ### Connector Configure the connector with the ingestion scope and Google SecOps mapping values: | Field | Description | | ------------------------------------ | ------------------------------------------------------------------- | | **BloodHound Enterprise Server** | The URL of your BloodHound Enterprise tenant | | **Token ID** | The API token ID used for authentication | | **Token Key** | The API token key used to sign requests | | **Selected BloodHound Environments** | The BloodHound Enterprise environments that the connector queries | | **Selected Finding Types** | The Attack Path finding categories that the connector ingests | | **Run Every** | The interval at which the connector polls BloodHound Enterprise | | **Product Field Name** | The source field name that Google SecOps uses for the product value | | **Event Field Name** | The source field name that Google SecOps uses for the event value | ## Connector flow The connector follows this workflow during ingestion: 1. Read the configured BloodHound Enterprise server, credentials, environments, and finding types. 2. Query available environments from BloodHound Enterprise and filter them to the configured set. 3. Query the available Attack Path finding types for each selected environment. 4. Download the finding title, description, and remediation content for each selected finding type. 5. Retrieve Attack Path detail records for each environment and finding type combination. 6. Create Google SecOps alerts and events from the returned findings. 7. Rely on Google SecOps alert grouping to roll related alerts into one case per source domain. 8. Track previously processed finding timestamps so the connector does not recreate findings it has already ingested. ## Analyst actions Google SecOps exposes the following BloodHound Enterprise actions to analysts: | Action | Purpose | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | **Ping** | Verifies that Google SecOps can reach the BloodHound Enterprise server with the configured credentials. | | **Get Object Id** | Searches for a node by name and returns matching object IDs. | | **Does Path Exist** | Checks whether a shortest path exists between two specified nodes. | | **Fetch Assets** | Retrieves detailed asset information for an object ID, including properties, relationship-based data, admin counts, and group memberships. | ## API endpoints The integration depends on the following BloodHound Enterprise API endpoints: | Endpoint | Purpose | | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | | [`GET /api/v2/available-domains`](/reference/search/get-available-domains) | Lists the environments available to the configured API token | | [`GET /api/v2/domains/{domain_id}/available-types`](/reference/attack-paths/list-available-attack-paths) | Lists the Attack Path finding types for a selected environment | | [`GET /api/v2/domains/{domain_id}/details`](/reference/attack-paths/list-domain-attack-paths-details) | Returns detailed Attack Path records for a selected environment and finding type | | [`GET /api/v2/domains/{domain_id}/sparkline`](/reference/attack-paths/list-attack-path-sparkline-values) | Returns time-windowed Attack Path values used for finding retrieval workflows | | `GET /api/v2/assets/findings/{finding_type}/title` | Returns the human-readable title for a finding type | | `GET /api/v2/assets/findings/{finding_type}/short_description` | Returns the short description for a finding type | | `GET /api/v2/assets/findings/{finding_type}/short_remediation` | Returns the short remediation text for a finding type | | `GET /api/v2/assets/findings/{finding_type}/long_remediation` | Returns the long remediation text for a finding type | | [`GET /api/v2/search`](/reference/search/search-for-objects) | Searches for graph objects by name or object ID | | [`GET /api/v2/graphs/shortest-path`](/reference/graph/get-the-shortest-path-graph) | Returns the shortest path graph between two objects | | [`GET /api/v2/azure/{entity_type}`](/reference/azure-entities/get-azure-entity) | Returns Azure entity details and related entity counts for an object ID | ## Error handling The functional specification calls for graceful handling and clear logging for common API failures. | Status | Meaning | Expected behavior | | ------ | ----------------- | ----------------------------------------------------------------------- | | `400` | Bad Request | Log the validation error and stop the current request | | `401` | Unauthorized | Log the authentication failure and prompt for corrected credentials | | `403` | Forbidden | Log the authorization failure and stop the current request | | `404` | Not Found | Log the missing endpoint or resource and skip retry | | `429` | Too Many Requests | Log the rate-limit condition and defer processing until a later run | | `5xx` | Server Error | Log the server-side failure and treat it as a connector or action error | Use the Google SecOps **Test**, **Test Connector**, and connector **Logs** views to validate configuration changes and troubleshoot failed runs. For common operator-facing issues, see [Troubleshoot the Google SecOps integration](/integrations/google-secops/troubleshoot). ## Platform dependencies | Dependency | Role | | ----------------------------- | ---------------------------------------------------------------------------------------------- | | **BloodHound Enterprise API** | Source of Attack Path findings, finding metadata, and analyst action results | | **Google SecOps** | Hosts the integration instance, connector runtime, cases, alerts, events, and playbook actions | # Troubleshoot the Google SecOps integration Source: https://bloodhound.specterops.io/integrations/google-secops/troubleshoot Learn how to diagnose and resolve common Google SecOps integration issues with BloodHound Enterprise. Applies to BloodHound Enterprise only Use this page to troubleshoot common issues with the BloodHound Enterprise integration for Google SecOps. Start by testing the integration instance and the connector configuration, then review the connector logs for more detail. ## Integration or connector test fails If the integration or connector test does not succeed: 1. Run **Test** on the integration instance to validate the BloodHound Enterprise server URL, token ID, and token key. 2. Run **Test Connector** from the connector's **Testing** tab to verify the connector logic and required parameter values. 3. Review the generated alerts and the debug logs. 4. Confirm that the connector creates alerts successfully before you enable it for ongoing ingestion. ## API authentication (401 Unauthorized) Possible causes include the following: * The BloodHound Enterprise API token or token key is expired or invalid. * The configured token ID, token key, or server URL is incorrect. * The API token does not have permission to access the required Attack Path endpoints. To resolve the issue: 1. Verify that the configured **Token ID**, **Token Key**, and **BloodHound Enterprise Server** values are correct. 2. Confirm that the API token is still active in BloodHound Enterprise. 3. Generate a new API token if the current one is expired or invalid, then update the integration and connector settings. 4. Confirm that the API token has permission to access the Attack Path endpoints used by the integration. 5. Restart the connector and validate the connection again from the **Testing** tab. # Use Google SecOps with BloodHound Enterprise Source: https://bloodhound.specterops.io/integrations/google-secops/use Learn how to investigate BloodHound Enterprise findings in Google SecOps by using cases, alerts, events, playbooks, and actions. Applies to BloodHound Enterprise only After you complete the [installation and configuration](/integrations/google-secops/configure), Google SecOps begins receiving BloodHound Enterprise Attack Path data through the connector. This page explains how that data is organized and how analysts can work with it during an investigation. ## Understand the investigation structure The integration organizes BloodHound Enterprise findings into Google SecOps cases, alerts, and events. | Object | Purpose | | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Case** | Groups related BloodHound Enterprise findings for investigation. With alert grouping configured, Google SecOps groups related alerts into one case per source domain. | | **Alert** | Represents a unique BloodHound Enterprise finding or path title within a case. | | **Event** | Captures an individual Attack Path occurrence and its supporting details, such as the involved nodes and object IDs. | ## Review cases, alerts, and events Use the following workflow to inspect the findings created by the connector. With alert grouping configured, a case is created for each unique domain. The case contains alerts for each distinct BloodHound Enterprise finding or path title, and the events under those alerts capture the details of each Attack Path occurrence. 1. Open your Google SecOps dashboard. 2. Select **Cases** from the navigation menu. 3. Review the list of cases created by the BloodHound Enterprise connector. Each alert corresponds to a distinct BloodHound Enterprise finding or path title. 1. Open a case for the domain that you want to investigate. 2. Review the alerts in that case. Google SecOps case showing alerts generated from BloodHound Enterprise findings. Event details include the step-by-step path traversal and identifiers such as `object_id`. 1. Open an alert in the case. 2. Review the events listed under that alert. Google SecOps alert showing the events generated for a BloodHound Enterprise finding. 3. Double-click an event to open the full Attack Path details. Google SecOps event details view showing Attack Path traversal data. ## Work with playbooks The **BloodHound Attack Path Alerts Playbook** can run against generated cases. You can also create your own playbook if you want to extend the workflow in Google SecOps. Google SecOps playbook tab for a generated BloodHound Enterprise case. 1. Go to **Response** > **Playbooks**. 2. Click the add (**+**) icon. Google SecOps Playbooks page showing the add icon. 3. Select **Playbook** as the type and click **Create**. Google SecOps dialog for selecting Playbook as the item type. 4. Build the custom playbook by adding components from **Actions**, **Triggers**, **Blocks**, and **Flows**. Google SecOps playbook editor showing available actions, triggers, blocks, and flows. After Google SecOps creates the cases, one playbook runs for each case. The following example shows the consolidated playbook results for one case. Playbook results for a generated BloodHound Enterprise case in Google SecOps. ## Run BloodHound Enterprise actions The integration includes on-demand actions that help analysts enrich investigations with data from BloodHound Enterprise. | Action | Description | | ----------------------- | ----------------------------------------------------------------------------------------------------- | | **Ping** | Verifies connectivity to the BloodHound Enterprise server. | | **Get Object Id** | Retrieves the object ID for a named node, such as a user, group, or computer. | | **Does Path Exist** | Checks whether a shortest path exists between two specified nodes in the BloodHound Enterprise graph. | | **Fetch Assets** | Retrieves detailed information about an asset based on its object ID. | | **Path Does Not Exist** | Logs that no shortest path exists between the specified nodes. | # API and Integrations Source: https://bloodhound.specterops.io/integrations/overview Leverage BloodHound's REST API and third-party integrations to extend functionality and maximize your security infrastructure investments. ## BloodHound API BloodHound Enterprise includes a REST API that allows you to programmatically interact with your BloodHound data and automate various tasks. ## BloodHound Integrations SpecterOps is built on community. Our strategic integrations enable BloodHound Enterprise customers to extend identity Attack Path Management to proactively secure and manage their Active Directory, Entra ID, and hybrid environments and respond faster to threats. The sections below describe officially supported integrations, third-party integrations, and community-developed integrations. ### Supported integrations The following integrations are officially supported by SpecterOps. The BloodHound Enterprise integration for Cortex XSOAR lets you ingest and manage BloodHound Enterprise Attack Path findings in Cortex XSOAR as incidents. | | | | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Supported actions** |
  • Automatically convert BloodHound Enterprise Attack Path findings into Cortex XSOAR incidents.
  • Attach remediation guidance and posture context to incidents.
  • Run playbooks and custom commands to analyze, triage, and remediate findings.
| | **Common use cases** |
  • Automated incident creation with titles, descriptions, remediation guidance, impact/exposure metrics, severity, and domain/environment context.
  • Playbook linking per incident to run custom analysis commands.
| | **Custom commands** |
  • Object ID lookup by name.
  • Asset information by object ID.
  • Path analysis between two nodes in the BloodHound graph.
| | **Integration instructions** | Configure the Cortex XSOAR integration |
The BloodHound Enterprise Google Security Operations (SecOps) is an integration that automatically synchronizes Bloodhound Enterprise (BHE) Attack Path findings to SecOps cases for remediation tracking. This integration enables security teams to manage and track the remediation of Active Directory and Azure Attack Paths directly within their existing SecOps workflows. | | | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Supported actions** |
  • Automatic creation of cases from BHE findings.
  • Playbooks created to manage BHE-generated cases.
  • Customizable SecOps collections schedules.
  • Group related alerts under a single case.
  • Ability to investigate relevant asset information from BHE.
  • Verify Attack Paths exist between any two assets.
| | **Common use cases** |
  • Enrich security cases with identity Attack Path context.
  • Prioritize alerts involving high-value identity assets.
  • Investigate whether an alerted asset is on a critical Attack Path.
  • Verify Attack Paths between two assets during incident response.
  • Consolidate related identity risk findings into a single investigation.
  • Improve coordination between SOC and Identity teams.
| | **Integration instructions** | Configure the Google SecOps integration |
The BloodHound Enterprise Jira integration is a Jira Cloud app built on Atlassian Forge that automatically synchronizes BloodHound Enterprise Attack Path findings to Jira issues for remediation tracking. This integration enables security teams to manage and track the remediation of Active Directory and Azure Attack Paths directly within Jira Software and Jira Service Management workflows. | | | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Supported actions** |
  • Automatically synchronize BloodHound Enterprise findings to Jira issues.
  • Map BloodHound Enterprise zones to Jira priorities.
  • Set due dates based on finding priority.
  • Filter by BloodHound Enterprise domains and zones for synchronization.
  • Prevent duplicate tickets with finding-aware deduplication.
  • Add direct Graph View links to BloodHound Enterprise finding details.
  • Close Jira tickets automatically after findings are remediated.
  • Support Jira Service Management request types and incidents.
| | **Common use cases** |
  • Manage identity Attack Paths through existing Jira workflows.
  • Create remediation tickets for identity Attack Paths.
  • Track remediation against defined SLAs.
  • Route findings to the appropriate teams.
  • Provide investigation context within every ticket.
  • Close tickets automatically when findings are resolved.
  • Measure progress in reducing Attack Paths.
| | **Integration instructions** | Configure the Jira integration |
The BHE Splunk SIEM App enables customers to ingest Path, Posture, and Impacted Principals data into Splunk. The app also includes pre-built dashboards and alerts for Exposure, Path Details, and Impacted Principals. | | | | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Supported actions** |
  • Ability to ingest Attack Path Finding Details.
  • Pull information related to an asset from the API.
  • Use BloodHound Enterprise data to examine a path between two objects.
| | **Common use cases** |
  • Use the dashboards to track and report on Active Directory and Azure Attack Paths in your environment and exposure over time.
  • Create alerts to detect when new Attack Paths emerge, or your exposure increases.
  • Enrich your SIEM with BloodHound Enterprise's Attack Path details.
| | **Integration instructions** | Integrate BloodHound Enterprise with Splunk |
The BloodHound Enterprise Splunk SOAR integration includes the ability to pull findings into a SplunkSOAR environment, as well as to enrich alerts from other platforms via data from BloodHound Enterprise. | | | | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Supported actions** | Pull findings from BloodHound Enterprise Attack Paths. | | **Common use cases** |
  • Enrich existing alerts with BloodHound Enterprise Attack Path findings and object descriptions.
  • Receive alerts for increases to Attack Paths, tier zero assets, and domain exposure.
  • Enable defenders to see all Attack Path findings from BloodHound as Splunk SOAR events.
  • Leverage BloodHound Enterprise findings to remediate and remove Attack Paths.
| | **Integration instructions** | Integrate BloodHound Enterprise with Splunk SOAR | | **FedRAMP** | Yes |
The BloodHound Enterprise ServiceNow integration provides the ability to generate tickets to track and monitor vulnerabilities within environments, as identified by BloodHound Enterprise. | | | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Supported actions |
  • Integration with ServiceNow's Security Incident Response (SIR) module.
  • Ability to generate tickets to track and monitor vulnerabilities within their environments, as identified by BloodHound Enterprise.
| | Common use cases |
  • Create ticketing workflows for Attack Path resolution.
  • Monitor identity vulnerabilities over time.
  • Allow integration of BloodHound Enterprise findings and remediation tasks into existing ServiceNow SIR workflows.
| | Integration instructions | ServiceNow integration instructions | | FedRAMP | Yes | | Supplemental information | YouTube video |
The Vulnerability Response (VR) integration for BloodHound Enterprise enables organizations to seamlessly connect their BloodHound Enterprise tenant with ServiceNow's Vulnerability Response capabilities, providing automated vulnerable item creation and management based on Attack Path findings. | | | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Supported actions |
  • Automatically import BloodHound Enterprise Attack Path findings.
  • Integrate with ServiceNow's Vulnerability Response (VR) framework.
  • Use a guided setup wizard for streamlined configuration.
  • Support multiple environments with configurable filtering.
  • Synchronize data with the BloodHound API in real time.
  • Visualize findings in ServiceNow's Vulnerability Manager Workspace.
  • Run scheduled and on-demand data imports.
| | Common use cases |
  • Reduce attack surface by identifying critical Active Directory vulnerabilities.
  • Prioritize remediation based on exploitability.
  • Centralize security management within ServiceNow.
  • Automate vulnerability tracking and reporting.
  • Use Attack Path analysis to support remediation decisions.
| | Integration instructions | ServiceNow VR integration instructions |
### Third-party integrations The following integrations are developed by third-party vendors and are not officially supported by SpecterOps. The Axonius integration enables Axonius users to fetch and catalog users and devices from BloodHound Enterprise, providing visibility into identity relationships and potential Attack Paths. | | | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Supported actions** | Fetch BloodHound Enterprise Attack Path Details:
  • All Tier Zero Assets
  • All Computer Admin Users
  • All Users with RDP Access
  • Assets by Attack Path
  • Only Enabled Users
| | **Common use cases** |
  • Identify which identities hold administrative or privileged access rights within the environment.
  • Discover users who hold administrative or privileged access rights within the environment, and any associated devices where that user has admin rights.
  • Identify devices and assets that are within an Attack Path.
| | **Integration instructions** | Configure the Axonius adapter for BloodHound |
Add two-factor authentication and flexible security policies to BloodHound Enterprise SAML 2.0 logins with Duo Single Sign-On. Our cloud-hosted SSO identity provider offers inline user enrollment, self-service device management, and support for a variety of authentication methods — such as passkeys and security keys, Duo Push, or Verified Duo Push — in the Universal Prompt. | | | | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Supported actions** |
  • Duo SSO prompts users for two-factor authentication and performs endpoint assessment and verification before permitting access to BloodHound Enterprise.
  • Define policies that enforce unique controls for accessing BloodHound Enterprise.
| | **Common use cases** | Provides an additional layer of security for users accessing the BloodHound Enterprise platform. | | **Integration instructions** | Configure single sign-on | | **FedRAMP** | Yes |
Integrating with SpecterOps BloodHound Enterprise helps you reduce the risk of attacks by enabling you to easily identify, prioritize, and eliminate the most vital avenues that attackers can exploit. | | | | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Supported actions** |
  • Quest OnDemand Audit ingests BloodHound Enterprise's defined Tier Zero assets.
  • Quest OnDemand Audit ingests BloodHound Enterprise's Attack Path edge data.
| | **Common use cases** |
  • Identify all critical Tier Zero assets via BloodHound Enterprise and automatically monitor them for suspicious activity through integration with OnDemand Audit.
  • Leverage OnDemand Audit's detailed user activity history to inspect BloodHound Enterprise's Attack Path edges before removing access to a path, ensuring there are no unexpected consequences to remediation.
  • Create alert-enabled searches for historical changes to Tier Zero objects to ensure real-time monitoring of critical assets.
| | **Integration instructions** | | | **FedRAMP** | No | | **Supplemental information** |
  • Built-in BloodHound Tier Zero asset searches.
  • Monitoring audit health status.
|
### Community integrations The following integrations are developed by the BloodHound community and are not officially supported by SpecterOps. by @RantaSec by @falconforceteam by @Eli4m Please share your integrations with us in the [BloodHound Gang community Slack](/resources/community-support/getting-help). # Integrate BloodHound Enterprise with ServiceNow Security Incident Response Source: https://bloodhound.specterops.io/integrations/service-now/security-incident-response/configure Learn how to install and configure the integration to automate the creation of security incidents based on attack path findings. Applies to BloodHound Enterprise only The [Security Incident Response (SIR)](https://store.servicenow.com/store/app/5400757f1b45a610a85b16db234bcb85) integration for BloodHound Enterprise supports the following use cases: * Create SIR ticketing workflows for BloodHound Enterprise attack path findings * Integrate BloodHound Enterprise attack path findings into existing ticketing workflows * Monitor identity vulnerabilities over time ## Prerequisites Before you begin the installation and configuration process, ensure the following prerequisites are met: * Admin access to a ServiceNow instance with the [Security Incident Response (SIR) module](https://www.servicenow.com/docs/r/security-management/security-incident-response/install-and-configure-sir.html) installed and configured * Access to the ServiceNow Store to install the BloodHound Enterprise app * Admin access to a BloodHound Enterprise tenant * A BloodHound Enterprise [non-personal API key/ID pair](/integrations/bloodhound-api/working-with-api#create-a-non-personal-api-key%2Fid-pair) with the **Auditor** role ## Install the application Installing the BloodHound Enterprise app on ServiceNow involves the following steps: 1. Log in to your ServiceNow instance as an admin. 2. Click **System Applications** > **All Available Applications** > **All**. 1. In the search bar, enter *SpecterOps BloodHound* to find the app. 2. Select the app from the search results. 1. Click **Install** to install the app on your ServiceNow instance. 2. Follow the prompts to complete the installation. ## Create an application user The integration requires creating a user and assigning the role. The integration runs on behalf of the user account that you create in this step. It should be a dedicated service account associated with the non-personal API key/ID pair you created in BloodHound Enterprise. 1. Click **All** > **User Administration** > **Users**. 2. Click **New**. 3. Enter required user details. 4. Click **Submit**. The user must have the role to perform necessary actions, such as creating and updating ServiceNow tickets. 1. In the **Roles** related list, click **Edit**. 2. In the **Collection** list, select the role and click **Add**. 3. Click **Save**. ## Configure the application The integration provides a guided setup experience to connect to BloodHound Enterprise, filter attack path types, configure field mapping, and set the import schedule. Follow the steps below to complete the configuration. ### Change application scope Before starting the configuration, change the application scope to to ensure that you have access to all necessary components and configurations. 1. Click the (globe) icon in the top-right corner and select **Application Scope**. 2. In the search filter, enter and select it. ### Connect to BloodHound Enterprise The first step in the guided setup is to connect to your BloodHound Enterprise tenant by providing the tenant URL and API credentials. 1. In the top-left corner of ServiceNow, click **All**. 2. In the search box, enter and select . 3. Click **Get Started** in the *Connect to SpecterOps BloodHound* section to start the configuration process. 4. Click **Configure**. 5. Click **New** to add credentials. 6. Enter your BloodHound Enterprise tenant URL, token key, and token ID and click **Submit**. The token key and ID refer to the non-personal API key/ID pair you created in BloodHound Enterprise. The tenant URL is the URL you use to access your BloodHound Enterprise tenant. 7. Click the (close) icon. 8. Click **Mark as Complete** to proceed to the next configuration step. ### Filter attack path types Next, configure filters to specify which attack path findings should create ServiceNow tickets. You can filter by environment and attack type to control the scope of findings that generate incidents. 1. Click **Get Started** in the *Filter Attack Path Types* section. 2. Click **Configure** to select environments. 3. Click **New**. 4. Click the (lock) icon to select a single environment. Alternatively, click the **Select All Environments** checkbox to indiscriminately select *all* environments. 5. After clicking the (lock) icon, click the (search) icon to display a list of available environments. 6. Click an environment to select it. You must repeat steps 4-6 for each environment that you want to include. 7. After selecting all required environments, click **Submit**. 8. Click the (close) icon. 9. Click **Mark as Complete**. 10. Scroll down the page to the *Filter Configuration* section and click **Configure**. 11. Click an environment to update the default configuration. 12. Edit the fields as required. 13. Click the **Select All Attack Types** checkbox to update finding types. 14. Click **Update** to save the configuration. 15. Click the (close) icon. 16. Click **Mark as Complete**. ### Configure field mapping Field mapping allows you to specify how BloodHound Enterprise attack path finding fields map to ServiceNow SIR ticket fields. You can use the default mapping or customize it as needed. 1. Click **Get Started** in the *SpecterOps to ServiceNow Field Mapping* section. A view of the ServiceNow user interface showing the process of getting started with field mapping. 2. Click **Configure** to review the mapping. Update it if necessary, or use the default mapping. A view of the ServiceNow user interface showing the default field mapping. The following table describes the default field mapping: | **SpecterOps BloodHound Fields** | **ServiceNow SIR Fields** | | -------------------------------- | ------------------------- | | id | correlation id | | composite risk | risk score | | description + remediation | description | | domain name + title + id | short description | | from principal | contact type | | server url | external url | 3. Click the (close) icon. 4. Click **Mark as Complete**. ### Configure import schedule The final step in the guided setup is to configure the import schedule to specify how often the integration should fetch attack path findings from BloodHound Enterprise and create ServiceNow tickets. 1. Click **Get Started** in the *Configure Import Schedule* section. 2. Click **Configure** to schedule an import. 3. Click the **Run** dropdown menu and select one of the available options. 4. Enter frequency details and click **Update**. You can also click **Execute Now** to run the import immediately. 5. Click the (close) icon. 6. Click **Mark as Complete**. The configuration is now complete. The integration will start fetching attack path findings from BloodHound Enterprise based on the configured schedule and create ServiceNow tickets accordingly. ## Next steps [View and manage](/integrations/service-now/security-incident-response/use) SIR tickets created from BloodHound Enterprise attack path findings in ServiceNow. # Use Security Incident Response Integration with BloodHound Enterprise Source: https://bloodhound.specterops.io/integrations/service-now/security-incident-response/use Learn how to use the ServiceNow Security Incident Response integration to manage security incidents based on BloodHound Enterprise attack path findings. Applies to BloodHound Enterprise only After [installation and configuration](/integrations/service-now/security-incident-response/configure) are complete, the integration begins fetching attack path findings from the BloodHound Enterprise API. The integration creates a Security Incident Response (SIR) ticket for each attack path finding. To view and manage security incidents created by the integration: 1. Log in to your ServiceNow instance. 2. Click **All** and enter `sn_si_incident.list` in the search bar to navigate to the list of security incidents. 1. Click a number to view attack path findings and remediation documentation in the incident details. A view of the ServiceNow user interface showing a list of security incidents created by the integration, with one incident selected to view details. 2. Update incident fields as required. A view of the ServiceNow user interface showing the process of updating a security incident. For example, you can post comments in the **Work Notes** field. A view of the ServiceNow user interface showing the Work Notes field of a security incident. # Integrate BloodHound Enterprise with ServiceNow Vulnerability Response Source: https://bloodhound.specterops.io/integrations/service-now/vulnerability-response/configure Learn how to install and configure the integration to automate vulnerability management based on attack path findings. Applies to BloodHound Enterprise only The Vulnerability Response (VR) integration for BloodHound Enterprise enables organizations to seamlessly connect their BloodHound Enterprise tenant with ServiceNow's Vulnerability Response capabilities, providing automated vulnerable item creation and management based on attack path findings. ## Prerequisites Before you begin the installation and configuration process, ensure the following prerequisites are met: | Type | Requirements | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **System** |
  • Admin access to a ServiceNow instance (Paris release or later recommended) with the [Vulnerability Response (VR) module](https://www.servicenow.com/docs/r/xanadu/security-management/sem-install-and-configure.html) installed and configured
  • Admin access to a BloodHound Enterprise tenant
  • A BloodHound Enterprise [non-personal API key/ID pair](/integrations/bloodhound-api/working-with-api#create-a-non-personal-api-key%2Fid-pair) with the **Auditor** role
| | **Network** |
  • Network connectivity between the ServiceNow instance and BloodHound Enterprise API endpoints
  • Outbound HTTPS (port 443) access from ServiceNow to your BloodHound tenant
  • Proper firewall configurations to allow API communications
| | **Knowledge** |
  • Basic understanding of ServiceNow administration
  • Familiarity with the BloodHound Enterprise platform
  • Knowledge of API integrations and security best practices
| ## Install the ServiceNow app Installing the BloodHound Enterprise app on ServiceNow involves the following steps: 1. Log in to your ServiceNow instance as an admin. 2. Click **System Applications** > **All Available Applications** > **All**. 1. In the search bar, enter *SpecterOps BloodHound* to find the app. 2. Select the app from the search results. 1. Click **Install** to install the app on your ServiceNow instance. 2. Follow the prompts to complete the installation. ## Create an application user The integration requires creating a user and assigning the role. The integration runs on behalf of the user account that you create in this step. It should be a dedicated service account associated with the non-personal API key/ID pair you created in BloodHound Enterprise. 1. Click **All** > **User Administration** > **Users**. 2. Click **New**. 3. Enter required user details. 4. Click **Submit**. The user must have the role to perform necessary actions, such as creating and updating ServiceNow tickets. 1. In the **Roles** related list, click **Edit**. 2. In the **Collection** list, select the role and click **Add**. 3. Click **Save**. ## Configure the application The integration provides a guided setup experience to connect your ServiceNow instance to BloodHound Enterprise, filter attack path types, and schedule imports. Follow the steps below to complete the configuration. ### Change application scope Before starting the configuration, change the application scope to to ensure that you have access to all necessary components and configurations. 1. Click the (globe) icon in the top-right corner and select **Application Scope**. 2. In the search filter, enter and select it. ### Connect to BloodHound Enterprise The first step in the guided setup is to connect to your BloodHound Enterprise tenant by providing the tenant URL and API credentials. 1. In the top-left corner of ServiceNow, click **All**. 2. In the search box, enter and select . 3. Click **Get Started** in the *Connect to SpecterOps BloodHound* section to start the configuration process. 4. Click **Configure**. 5. Click **New** to add credentials. 6. Enter your BloodHound Enterprise tenant URL, token key, and token ID and click **Submit**. The token key and ID refer to the non-personal API key/ID pair you created in BloodHound Enterprise. The tenant URL is the URL you use to access your BloodHound Enterprise tenant. 7. Click the (close) icon. 8. Click **Mark as Complete** to proceed to the next configuration step. ### Filter attack path types Next, configure filters to specify which attack path findings should create ServiceNow tickets. You can filter by environment and attack type to control the scope of findings that generate incidents. 1. Click **Get Started** in the *Filter Attack Path Types* section. 2. Click **Configure** to select environments. 3. Click **New**. 4. Click the (lock) icon to select a single environment. Alternatively, click the **Select All Environments** checkbox to indiscriminately select *all* environments. 5. After clicking the (lock) icon, click the (search) icon to display a list of available environments. 6. Click an environment to select it. You must repeat steps 4-6 for each environment that you want to include. 7. After selecting all required environments, click **Submit**. 8. Click the (close) icon. 9. Click **Mark as Complete**. 10. Scroll down the page to the *Filter Configuration* section and click **Configure**. 11. Click an environment to update the default configuration. 12. Edit the fields as required. 13. Click the **Select All Attack Types** checkbox to update finding types. 14. Click **Update** to save the configuration. 15. Click the (close) icon. 16. Click **Mark as Complete**. ### Configure import schedule The final step in the guided setup is to configure the import schedule to specify how often the integration should fetch attack path findings from BloodHound Enterprise and create ServiceNow tickets. 1. Click **Get Started** in the *Configure Import Schedule* section. 2. Click **Configure** to schedule an import. 3. Click the **Run** dropdown menu and select one of the available options. 4. Enter frequency details and click **Update**. You can also click **Execute Now** to run the import immediately. 5. Click the (close) icon. 6. Click **Mark as Complete**. The configuration is now complete. The integration will start fetching attack path findings from BloodHound Enterprise based on the configured schedule and create ServiceNow tickets accordingly. ## Next steps Learn [how to use](/integrations/service-now/vulnerability-response/use) the integration to view attack path data from BloodHound Enterprise in ServiceNow's Vulnerability Manager Workspace. # Troubleshoot Common Issues Source: https://bloodhound.specterops.io/integrations/service-now/vulnerability-response/troubleshoot Learn how to troubleshoot common installation, configuration, and performance issues with the Vulnerability Response Integration for ServiceNow. Applies to BloodHound Enterprise only ## Installation issues Installation issues can arise due to various factors such as compatibility problems, insufficient permissions, or system resource limitations. Use the following troubleshooting steps to resolve common installation issues. * Verify ServiceNow instance compatibility * Check system logs for specific error messages * Ensure adequate system resources are available * Contact ServiceNow support if issues persist * Verify installation completed successfully * Check application scope settings * Refresh browser cache and retry * Re-install application if necessary ## Configuration issues Configuration issues can lead to failed API connections, data synchronization problems, or performance degradation. Use the following troubleshooting steps to resolve common configuration issues. ### API connection problems * Verify API credentials (Token ID and Key) * Check network connectivity and firewall rules * Validate API endpoint URLs * Test connectivity using external tools * Regenerate API tokens in BloodHound portal * Verify token permissions and access rights * Check token expiration dates * Update credentials in ServiceNow configuration ### Data synchronization issues * Verify scheduled import jobs are running * Check data filter configurations * Validate environment selections * Review system logs for import errors * Check API rate limiting settings * Verify data mapping configurations * Review network stability during sync operations * Adjust import schedules if necessary ## Performance issues Performance issues can arise due to factors such as large data volumes, inefficient queries, or resource constraints. Use the following troubleshooting steps to optimize performance. ### Slow data loading * Optimize database queries * Implement data caching strategies * Review system resource utilization * Consider data archiving for old records * Increase timeout settings * Implement data pagination * Schedule imports during low-traffic periods * Optimize data filtering to reduce payload size # Use the Vulnerability Response Integration with BloodHound Enterprise Source: https://bloodhound.specterops.io/integrations/service-now/vulnerability-response/use Learn how to navigate the Vulnerability Manager Workspace to see attack path data from BloodHound Enterprise. Applies to BloodHound Enterprise only The SpecterOps BloodHound Vulnerability Response Integration for ServiceNow provides a powerful solution for automating vulnerability management and remediation workflows based on attack path analysis. After [installation and configuration](/integrations/service-now/vulnerability-response/configure) is complete, you can start using the integration to view attack path data in ServiceNow's Vulnerability Manager Workspace. This section provides an overview of how to access and navigate the workspace to see the collected data from BloodHound Enterprise. See the ServiceNow [documentation](https://www.servicenow.com/docs/r/xanadu/security-management/vulnerability-response/vuln-landing-page.html) for more information about using the Vulnerability Manager Workspace. To access the Vulnerability Manager Workspace and view BloodHound data: 1. Log into your ServiceNow instance. 2. Click **All** and enter *Vulnerability Manager Workspace* in the search bar and select it. A view of the ServiceNow interface showing the All menu and search bar with Vulnerability Manager Workspace entered 1. Under the **View by** dropdown, select **All**. A view of the ServiceNow interface showing the View by dropdown with All selected 2. Scroll down and select **BloodHound Enterprise**. A view of the ServiceNow interface showing the BloodHound Enterprise option selected The following visualization shows the collected data in a graphical format. A view of the ServiceNow interface showing the graphical visualization of BloodHound data ## Next steps After successful implementation and initial use of the integration, consider the following next steps to maximize value: 1. **User training**: Provide comprehensive training to security teams and ServiceNow users 2. **Process integration**: Integrate the solution with existing security processes and workflows 3. **Performance optimization**: Monitor and optimize performance based on usage patterns 4. **Regular updates**: Stay current with application updates and new features 5. **Expansion**: Consider expanding integration to additional environments and use cases ## Maintenance and support Ongoing maintenance and support are critical for ensuring the long-term success of the integration. Consider the following best practices: * **Regular health checks**: Schedule periodic reviews of integration health and performance * **Update management**: Plan for regular application and system updates * **Documentation updates**: Maintain current documentation as configurations change * **User feedback**: Collect and act on user feedback for continuous improvement # Integrate BloodHound Enterprise with Splunk SIEM Source: https://bloodhound.specterops.io/integrations/splunk/siem/install Learn how to install and configure the BloodHound Enterprise Splunk app to ingest BloodHound Enterprise data into Splunk. Applies to BloodHound Enterprise only The [BloodHound Enterprise Splunk app](https://splunkbase.splunk.com/app/7818) ingests your BloodHound Enterprise data into Splunk. * Use the dashboards to track the Active Directory and Azure attack paths of your environment * Create alerts to detect when new attack paths emerge or exposure increases * Enrich your security information and event management (SIEM) data with information about the attack paths to and from principals in your environment ## Prerequisites Before you begin the installation and configuration process, ensure the following prerequisites are met: * Splunk instance (version 9.0.1 or later) and an admin account * BloodHound Enterprise tenant * BloodHound Enterprise [non-personal API key/ID pair](/integrations/bloodhound-api/working-with-api#create-a-non-personal-api-key%2Fid-pair) with the **Auditor** role ## Install the app Installing the BloodHound Enterprise Splunk app involves the following steps: 1. Log in to Splunk Enterprise as an admin. 2. Click **Apps** > **Manage apps**. Use one of the following methods to install the BloodHound Enterprise Splunk app: Install directly from Splunkbase: 1. Click **Browse More Apps**. 2. Search for *BloodHound Enterprise*. 3. Click **Install** 4. Enter your Splunkbase credentials to authorize the download when prompted. Install from a downloaded package: 1. Download the BloodHound Enterprise Splunk app package from [Splunkbase](https://splunkbase.splunk.com/app/7818). 2. Click **Install App from File**. 3. Select the downloaded package and click **Upload**. After installing the app, restart your Splunk instance to apply the changes. See [Splunk's documentation](https://help.splunk.com/en/splunk-enterprise/administer/admin-manual/9.0/welcome-to-splunk-enterprise-administration/start-splunk-enterprise-and-perform-initial-tasks/start-and-stop-splunk-enterprise) for more information. ## Configure the app (required) This section describes the minimum required configuration steps to get the BloodHound Enterprise Splunk app up and running. It involves the following steps: 1. Configure a Splunk index 2. Configure Splunk app API credentials 3. Configure Splunk data inputs Optional configurations are available in the [Configure the Splunk app (optional)](#configure-the-app-optional) section. Create a dedicated index for the BloodHound Enterprise Splunk app data: 1. Click **Settings** > **Indexes** > **New Index**. 2. In the **Index Name** field, enter `bhe-splunk-app`. 3. In the **Data Integrity Check** field, select **Enabled**. 4. In the **App** field, select **BloodHound Enterprise**. 5. Click **Save**. A view of the Splunk 'New Index' configuration page showing the fields filled out for creating the 'bhe-splunk-app' index. Configure the BloodHound Enterprise Splunk app with your BloodHound Enterprise API credentials. We recommend a [non-personal API key/ID pair](/integrations/bloodhound-api/working-with-api#create-a-non-personal-api-key%2Fid-pair) with the **Auditor** role for the Splunk integration. 1. Click **Apps** > **Manage Apps**. 2. Filter for the BloodHound Enterprise Splunk app and click on it. 3. Click the **Administration** drop-down menu and select **Configuration**. 4. Click **Add** to open the **Add Account** screen. 5. Complete the configuration fields: | Field | Description | | ----------------- | ------------------------------------------------------------------------------------ | | **Account Name** | Unique name to identify the BloodHound Enterprise account in Splunk | | **Tenant Domain** | Your BloodHound Enterprise tenant (e.g., `https://mydomain.bloodhoundenterprise.io`) | | **Token ID** | Token ID associated with the BloodHound Enterprise account | | **Token key** | Token key associated with the BloodHound Enterprise account | 6. Click **Save** to apply the configuration. A view of the Splunk 'Add Account' page showing the fields configuring the app's API credentials. Data inputs define what data the BloodHound Enterprise Splunk app collects from the BloodHound Enterprise API. You can create multiple inputs of the same type, each with different configurations (e.g., different BloodHound Enterprise accounts, indices, and collection intervals). 1. Click **Apps** > **Manage Apps**. 2. Filter for the BloodHound Enterprise Splunk app and click on it. 3. Click the **Administration** drop-down menu and select **Inputs**. 4. Click **Create New Input**. 5. Select an input type from the drop-down menu. The BloodHound Enterprise Splunk app supports the following input types:
Input type Description
Attack Paths Retrieves a list of attack paths from the BloodHound Enterprise API and a list of various findings across a given time range.
Audit Logs Retrieves a list of audit logs from the BloodHound Enterprise API.
  • Requires the API user to have either the Administrator or Auditor role in BHE.
  • You can set the "Historical Polling Days" field to retrieve logs from the past N days, starting from the current date.
  • After the app fetches all logs for the specified period, the input continues polling only the latest audit logs.
  • If you need logs from a different time range, you can create a new input and fetch them separately.
Tier Zero Assets Ingests data for all asset members that belong to the Tier Zero privilege zone.
Posture Statistics Retrieves a history of statistics stored in the database using the BloodHound Enterprise API.
6. Complete the configuration fields for the selected input type. The following table describes fields that are common across all input types: | Field | Description | | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | **Name** | Unique name identifying the input | | **Interval** | Interval (in seconds) at which the input runs | | **Index** | Index where the BloodHound Enterprise data is stored (`bhe-splunk-app`). You must clear the `default` value and search for the correct index. | | **Bloodhound Account** | BloodHound Enterprise account name (configured in Splunk) that will be used to fetch the data | 7. Click **Add** to create the input. A view of the Splunk 'Create New Input' configuration page showing the fields filled out for creating a new input. Repeat the above steps to create additional inputs as needed. Data will now begin flowing into the environment. You can monitor this progress through Splunk itself with the following query: ```spl theme={null} index=_internal source="*splunkd.log" "BHE " ```
## Configure the app (optional) This section describes optional configuration options for the BloodHound Enterprise Splunk app, including: 1. Configure a Splunk search macro 2. Configure a Splunk proxy 3. Configure Splunk logging The BloodHound Enterprise Splunk app includes a [search macro](https://help.splunk.com/en/splunk-enterprise/manage-knowledge-objects/knowledge-management-manual/9.4/search-macros/use-search-macros-in-searches) (`bhe_index`) that points to the default index where Splunk stores BloodHound Enterprise data (`bhe-splunk-app`). A view of the Splunk 'Search Macros' page showing the default 'bhe_index' macro. To view or modify the search macro: 1. Click **Settings** > **Advanced search**. 2. Click **Search macros**. 3. Filter for `bhe_index`. If you used a different index name, edit the macro **Definition** field to match that name. ```spl theme={null} index=my_custom_bhe_index ``` If you maintain separate indexes per input type, modify the macro definition accordingly. For example: ```spl theme={null} index=attack_path_index OR index=audit_log_index OR index=posture_stats_index ``` Splunk allows you to configure a proxy to route traffic through an intermediary server. This might be useful for network security and compliance requirements. 1. Click **Apps** > **Manage Apps**. 2. Filter for the BloodHound Enterprise Splunk app and click on it. 3. Click the **Administration** drop-down menu and select **Configuration**. 4. Click the **Proxy Settings** tab. 5. Complete the configuration fields: | Field | Description | | -------------- | --------------------------------------------------------------- | | **Enable** | Checkbox to enable or disable the proxy configuration | | **Proxy Type** | Drop-down to choose the type of proxy (http, socks4, socks5) | | **Host** | Enter the proxy hostname or IP address | | **Port** | Specify the port number (e.g., 8080) | | **Username** | If authentication is required, enter the username | | **Password** | If authentication is required, enter the corresponding password | 6. Click **Save** to apply the proxy settings. 7. Restart Splunk. See [Splunk's documentation](https://help.splunk.com/en/splunk-enterprise/administer/admin-manual/9.0/welcome-to-splunk-enterprise-administration/start-splunk-enterprise-and-perform-initial-tasks/start-and-stop-splunk-enterprise) for more information. You can configure logging settings for the BloodHound Enterprise Splunk app to help with troubleshooting and monitoring. 1. Click **Apps** > **Manage Apps**. 2. Filter for the BloodHound Enterprise Splunk app and click on it. 3. Click the **Administration** drop-down menu and select **Configuration**. 4. Click the **Logging** tab. 5. Select one of the following options from the **Log level** drop-down menu: | Log level | Description | | ----------- | ------------------------------------- | | **DEBUG** | Most verbose; use for troubleshooting | | **INFO** | Standard logs (default) | | **WARNING** | Warnings only | | **ERROR** | Errors only | 6. Click **Save** to apply the changes. The BloodHound Enterprise Splunk app writes logs to: ```text theme={null} $SPLUNK_HOME/var/log/splunk/ta_bloodhound_enterprise_.log ``` You can search logs in Splunk using: ```spl theme={null} index="_internal" sourcetype="ta_bloodhound_enterprise:log" ``` Use the `tail` command to monitor logs in real-time: ```bash theme={null} tail -f $SPLUNK_HOME/var/log/splunk/ta_bloodhound_enterprise_.log ``` ## Monitor and troubleshoot The **BHE Integration Health** dashboard is designed to help you monitor and troubleshoot errors related to the BloodHound Enterprise Splunk app. This dashboard provides real-time insights into the system failures, allowing you to quickly identify and resolve issues. It retrieves and displays error logs with the following Splunk query: ```spl theme={null} index=bhe-splunk-app sourcetype=BHE:error ``` This dashboard does not provide filters. Here are some recommendations for using the **BHE Integration Health** dashboard to troubleshoot issues: * Identify the function causing the error in the Detailed Error Logs table * Look for recurring errors in Error Summary and Top Error Functions * Apply the relevant steps above based on the error type * If issues persist, inspect Splunk internal logs See [Troubleshooting](/integrations/splunk/siem/troubleshoot) for common issues and resolutions. To access the **BHE Integration Health** Dashboard: 1. Log in to Splunk Enterprise as an admin. 2. Click **Apps** > **Manage apps**. 3. Filter for the BloodHound Enterprise Splunk app and click on it. 4. Click the **Administration** drop-down menu and select **BHE Integration Health Dashboard**. ### Error Trend Over Time This panel shows which functions are generating the most errors in the BloodHound Enterprise Splunk app. A view of the Error Trend Over Time panel in the BloodHound Enterprise Splunk app ### Errors by Function This panel shows a chart of errors (by function) generating errors in the BloodHound Enterprise Splunk app. A view of the Errors by Function panel in the BloodHound Enterprise Splunk app ### Top 10 Frequent Errors This panel shows a chart of the top ten most frequent error messages occurring in the BloodHound Enterprise Splunk app. A view of the Top 10 Frequent Errors panel in the BloodHound Enterprise Splunk app ### Raw Error Logs This panel provides a detailed table of raw error logs generated by the BloodHound Enterprise, including timestamps, function names, and error messages. A view of the Raw Error Logs panel in the BloodHound Enterprise Splunk app # Troubleshoot the BloodHound Enterprise Splunk app Source: https://bloodhound.specterops.io/integrations/splunk/siem/troubleshoot Learn how to troubleshoot common issues with the BloodHound Enterprise Splunk app using the BHE Integration Health dashboard. Applies to BloodHound Enterprise only This page covers common errors you may encounter when using the BloodHound Enterprise Splunk app, their possible causes, and steps to resolve them. The app includes a built-in **BHE Integration Health** dashboard that you can use to monitor the status of the integration and troubleshoot common issues. See the [BHE Integration Health](/integrations/splunk/siem/install#monitor-and-troubleshoot) section in the installation guide for details about accessing and using the dashboard. ## List index out of range * Causes: * API response does not contain the expected structure * Code accesses an empty list without validation * Steps: * Identify the function in the error log table * Review recent API changes that could affect response shape * Add safe checks before indexing lists (e.g., verify length > 0) ## API authentication (401 Unauthorized) * Causes: * Expired or invalid API token * Incorrect credentials in the BHE App configuration * Steps: * Verify the API token value and expiry * Confirm correct credentials in the BHE App settings * If using OAuth, refresh the access token and restart the app ## Connectivity issues (Timeout/500) * Causes: * Network problems between Splunk and the BHE API * API service downtime or high latency * Steps: * Test connectivity with `ping` or `curl` from the Splunk host * Verify the BHE API endpoint is reachable from Splunk * Check provider status or maintenance notices ## Proxy errors Example error: ``` HTTPSConnectionPool(host='demo.bloodhoundenterprise.io', port=443): Max retries exceeded with url: /api/v2/posture-stats (Caused by ProxyError('Cannot connect to proxy.', NewConnectionError(': Failed to establish a new connection: [Errno 113] No route to host'))) ``` * Causes: * Incorrect proxy settings in Splunk * Proxy server unreachable or misconfigured * Steps: * Verify Splunk proxy settings match your network configuration * Check the proxy server status and reachability from Splunk * Test connectivity with `curl -x https://demo.bloodhoundenterprise.io` # Use the BloodHound Enterprise Splunk app Source: https://bloodhound.specterops.io/integrations/splunk/siem/use Learn how to use the BloodHound Enterprise Splunk app to visualize and analyze BloodHound Enterprise data within Splunk. Applies to BloodHound Enterprise only The BloodHound Enterprise Splunk app provides several dashboards that allow you to gain insights into your environments. These dashboards visualize data ingested from various BloodHound Enterprise data inputs, including posture statistics, attack paths, audit logs, and tier zero assets. You configure the [data inputs](/integrations/splunk/siem/install#configure-the-app-required) for these dashboards during the installation and configuration of the BloodHound Enterprise Splunk app. Each dashboard offers filtering options to help you analyze data based on different parameters such as BloodHound Enterprise tenant, domain, and time range. Dashboards also provide standard Splunk controls for managing dashboards and visualizations. See the Splunk [documentation](https://docs.splunk.com/Documentation/Splunk/latest/Viz/Dashboards) for more information. ## Dashboard Summary Use this overview to find the right dashboard quickly. Detailed panels and filters are documented in each subsection below. | Dashboard | Purpose | Data Input | | ---------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------ | | Posture History | Monitor posture trends (exposure, findings, attack paths, Tier Zero assets) over time for selected tenants and environments | Posture Statistics | | Attack Paths | Analyze attack paths across domains, including principals involved, exposure levels, severity, and associated findings | Attack Paths | | Audit Logs | Filter and analyze administrative and system audit events collected by BloodHound Enterprise | Audit Logs | | Tier Zero Assets | Inventory Tier Zero assets across domains and analyze their distribution and details | Tier Zero Assets | ## Posture History The **Posture History** dashboard helps you monitor [posture](/analyze-data/findings/posture) trends over time for your BloodHound Enterprise tenants and environments. It provides insights about trends in exposure levels, findings, attack paths, and Tier Zero assets. All panels in this dashboard use data from the **Posture Statistics** data input and share the following filters: * BloodHound Tenant * Environment * Time Range The following sections describe each panel on this dashboard. ### Exposure This panel shows the trend (by percentage) of exposure over time for the selected BloodHound tenant(s) and environment(s) within a specified time range. A view of the Posture History Exposure panel in the BloodHound Enterprise Splunk app ### Findings This panel shows the trend (by count) of posture findings over time for the selected BloodHound tenant(s) and environment(s) within a specified time range. A view of the Posture History Findings panel in the BloodHound Enterprise Splunk app ### Attack Path This panel shows the trend (by count) of critical attack paths over time for the selected BloodHound tenant(s) and environment(s) within a specified time range. A view of the Posture History Attack Path panel in the BloodHound Enterprise Splunk app ### Assets This panel shows the trend (by count) of Tier Zero assets over time for the selected BloodHound tenant(s) and environment(s) within a specified time range. A view of the Posture History Assets panel in the BloodHound Enterprise Splunk app ## Attack Paths The **Attack Paths** dashboards allows you to analyze attack paths identified by BloodHound Enterprise across your configured domains. It provides detailed information about principals that can compromise the Tier Zero Privilege Zone, their exposure levels, severity, and associated findings. ### Overview This dashboard summarizes attack path findings (by count, severity, and frequency) across selected BloodHound tenant(s) and environment(s) within a specified time range. All panels in this dashboard use data from the **Attack Paths** data input and share the following filters: * BloodHound Tenant * Environment * Severity * Time Range #### Total Domain Wise Attack Paths per Domain This panel shows the total count of attack paths (by domain) identified in the selected BloodHound tenant(s) and environment(s) within a specified time range. A view of the Attack Paths Total Domain Wise Attack Paths per Domain panel in the BloodHound Enterprise Splunk app #### Severity Breakdown This panel shows the distribution of findings (by severity) for the selected BloodHound tenant(s) and environment(s) within a specified time range. A view of the Attack Paths Severity Breakdown panel in the BloodHound Enterprise Splunk app #### Top 5 Non-Tier Zero Principals Involved This panel shows the top five non-tier Zero principals most frequently involved in attack path findings for the selected BloodHound tenant(s) and environment(s) within a specified time range. A view of the Attack Paths Top 5 Non-Tier Zero Principals Involved panel in the BloodHound Enterprise Splunk app #### Top 5 Most Common Findings This panel shows the top five most common finding types (by frequency) for the selected BloodHound tenant(s) and environment(s) within a specified time range. A view of the Attack Paths Top 5 Most Common Findings panel in the BloodHound Enterprise Splunk app #### Top 5 Most Common Findings Per Environment This panel shows the top five most common finding types (by frequency) per environment for the selected BloodHound tenant(s) and environment(s) within a specified time range. A view of the Attack Paths Top 5 Most Common Findings Per Environment panel in the BloodHound Enterprise Splunk app #### Top 10 Attack Paths by Exposure This panel shows the top ten attack paths (by exposure percentage) for the selected BloodHound tenant(s) and environment(s) within a specified time range. A view of the Attack Paths Top 10 Attack Paths by Exposure panel in the BloodHound Enterprise Splunk app Details also include links to BloodHound Enterprise remediation documentation. ### Details This dashboard provides more granular details about specific attack paths identified by BloodHound Enterprise. It allows you to investigate principals involved in attack paths, their exposure levels, and associated findings. All panels in this dashboard use data from the **Attack Paths** data input and share the following filters: * BloodHound Tenant * Environment * Attack Paths * Severity * Time Range #### Principals This panel shows all principals based on the selected filters. It provides the following detailed information about each principal: | | | | ----------------------- | -------------------- | | Non-Tier Zero Principal | Impact Count | | Tier Zero Principal | SAM Account Name | | Display Name | Sensitive | | Finding Name | Last Logon | | Distinguished Name | Last Logon Timestamp | | Severity Level | Created Timestamp | | Impact Percentage | First Seen | | Last Updated | | A view of the Attack Paths Details Principals panel in the BloodHound Enterprise Splunk app #### Maximum Exposure Percentage This panel shows the highest exposure (by percentage) for the specified filters. A view of the Attack Paths Details Maximum Exposure Percentage panel in the BloodHound Enterprise Splunk app #### Total Number of Findings This panel shows the total number of findings (by count) for the specified filters. A view of the Attack Paths Details Total Number of Findings panel in the BloodHound Enterprise Splunk app ### Finding Trends This dashboard provides trend analysis of attack path findings over time. It helps you understand how the exposure and frequency of findings change over time for selected BloodHound tenant(s) and environment(s) within a specified time range. All panels in this dashboard use data from the **Attack Paths** data input and share the following filters: * BloodHound Tenant * Environment * Category * Time Period #### Attack Path Trends This panel shows the trend (by category) of attack paths over time for the selected BloodHound tenant(s) and environment(s) within a specified time range. Categories include: * Tier Zero * Kerberos * AD Certificate Services * Relay attacks * Least privilege * Entra ID * Hybrid * Microsoft Graph * Azure Resource Manager A view of the Attack Paths Finding Trends panel in the BloodHound Enterprise Splunk app ## Audit Logs This dashboard allows you to filter and analyze administrative and system audit events collected by BloodHound Enterprise. All panels in this dashboard use data from the **Audit Logs** data input and share the following filters: * BloodHound Tenant * Event Type * Actor Name * Time Range The audit log table provides the following information about each event: | | | | ----------------- | ------------------- | | ID | Created At | | Actor ID | Actor Name | | Actor Email | Action (event type) | | Fields | Request ID | | Source IP address | Commit ID | | Status | | A view of the Audit Logs dashboard in the BloodHound Enterprise Splunk app Clicking on any row in the Audit Logs table will open a detailed view of the selected audit event, providing additional context and information. ## Tier Zero Assets This dashboard provides an inventory of Tier Zero assets identified by BloodHound Enterprise across your configured domains. It helps you analyze the distribution and details of Tier Zero assets. All panels in this dashboard use data from the **Tier Zero Assets** data input and share the following filters: * BloodHound Tenant * Environment * Type ### Tier Zero Assets List This panel provides a detailed listing of Tier Zero assets across your configured domains. It includes the following information about each asset: * Name * Environment * Type * Object ID A view of the Tier Zero Assets List panel in the BloodHound Enterprise Splunk app ### Tier Zero Assets Distribution By Environment This panel shows how Tier Zero assets are distributed across the selected BloodHound tenant(s), environment(s), and asset type(s). A view of the Tier Zero Assets Distribution By Environment panel in the BloodHound Enterprise Splunk app ## Search See the Splunk [documentation](https://help.splunk.com/en/splunk-enterprise/search/search-manual/10.0/search-overview/get-started-with-search) for details about using Splunk Search to create custom queries and visualizations based on BloodHound Enterprise data. ## Administration See [install and configure](/integrations/splunk/siem/install) the BloodHound Enterprise Splunk app for details about configuring data inputs and other administrative tasks. # Integrate BloodHound Enterprise with Splunk SOAR Source: https://bloodhound.specterops.io/integrations/splunk/soar/configure Learn how to install and configure the BloodHound Enterprise Splunk SOAR app to ingest attack path findings into Splunk SOAR. Applies to BloodHound Enterprise only Splunk SOAR (formerly Phantom) helps security teams orchestrate tools and automate response workflows. This guide focuses on installing and configuring the BloodHound Enterprise app in Splunk SOAR. For platform concepts, terminology, and product capabilities, see the [Splunk SOAR documentation](https://help.splunk.com/en/splunk-soar). The [BloodHound Enterprise for Splunk SOAR app](https://splunkbase.splunk.com/app/7772) allows you to view attack path findings from BloodHound Enterprise within the Splunk SOAR platform. This integration enables security teams to monitor and respond to potential attack paths in real-time using Splunk SOAR's automation capabilities. Integrating BloodHound with Splunk SOAR provides the following advantages: * **Get real-time visibility into attack path findings**: View BloodHound Enterprise findings in Splunk SOAR as they are detected. * **Automate response playbooks from BloodHound detections**: Trigger investigation and containment workflows automatically when BloodHound Enterprise identifies a risk. * **Reduce manual triage and improve consistency**: Standardize repeatable response actions across your existing security tooling. * **Accelerate mitigation of privilege escalation risks**: Use automated tasks to respond to high-impact identity threats faster. ## Prerequisites Before you begin the installation and configuration process, ensure the following prerequisites are met: * Admin access to a Splunk SOAR instance * Access to a BloodHound Enterprise tenant * BloodHound Enterprise [non-personal API key/ID pair](/integrations/bloodhound-api/working-with-api#create-a-non-personal-api-key%2Fid-pair) with the **Auditor** role ## Install the app Installing the BloodHound Enterprise for Splunk SOAR app involves the following steps: 1. Log in to your Splunk SOAR instance as an admin. 2. Click on the **Home** dropdown in the top-left corner and select **Apps**. Splunk SOAR home dropdown with Apps option highlighted 1. Enter *BloodHound* in the app search box. Splunk SOAR app search box 2. Click **Install**. Splunk SOAR install app confirmation After installing the app, you can see it in the **Unconfigured Apps** section. Splunk SOAR unconfigured apps section ## Configure the app After installing the BloodHound Enterprise for Splunk SOAR app, you need to configure it to connect to your BloodHound Enterprise tenant and start ingesting attack path findings. The configuration process involves the following steps: On the **Unconfigured Apps** page, click **Configure New Asset** for the BloodHound Enterprise app. Splunk SOAR unconfigured apps section with Configure New Asset 1. Enter the **Asset name** and the **Asset description**. Splunk SOAR BloodHound Enterprise app details page with Configure button highlighted 2. Click **Save**. 1. Click **Asset Settings** to set up the connection to BloodHound Enterprise. 2. Enter the following details: | Field | Description | | -------------------------------- | -------------------------------------------------------------------------- | | **BloodHound Enterprise Domain** | The URL you use to access your BloodHound Enterprise tenant | | **Token Key** | The token key from your BloodHound Enterprise non-personal API key/ID pair | | **Token ID** | The token ID from your BloodHound Enterprise non-personal API key/ID pair | Splunk SOAR BloodHound Enterprise app asset settings page for API credentials configuration with BloodHound Enterprise Domain, Token Key, and Token ID fields 3. Click **Save**. 1. Click **Ingest Settings** to set up how the app ingests data from BloodHound Enterprise. 2. Configure the following settings: | Field | Description | | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | **Label to apply to objects from this source** | Select **events** to label ingested data as events in Splunk SOAR | | **Select a polling interval or schedule to configure polling on this asset** | Choose how often the app should poll BloodHound Enterprise for new findings. For testing, you can select **Off** and use manual polling. | Splunk SOAR ingest settings page with Label to apply to objects from this source and Select a polling interval or schedule to configure polling on this asset fields highlighted 3. Click **Save**. Go back to **Asset Settings** and click **Test Connectivity** to verify the configuration. Splunk SOAR test connectivity page If the configuration is correct, Splunk SOAR confirms that the app is connected successfully, as shown in the following image. Splunk SOAR successful connectivity confirmation If you set the polling interval to **Off** for testing, you can manually poll for events to start ingesting data from BloodHound Enterprise. 1. Click **Ingest Settings**. 2. Enter the following values: | Field | Description | | ---------------------- | ------------------------------------------------------------------------------------ | | **Maximum containers** | The maximum number of containers (event groupings) to ingest per polling cycle. | | **Maximum artifacts** | The maximum number of artifacts (individual data items) to ingest per polling cycle. | See the [Splunk SOAR documentation](https://help.splunk.com/en/splunk-soar) for more information about these settings. 3. Click **Poll Now**. Splunk SOAR poll now option After polling completes, confirm that containers and artifacts were added successfully, as shown below. Splunk SOAR successful data ingestion confirmation 4. Click **Close**. ## Next steps The configuration is now complete. You can [view attack path findings](/integrations/splunk/soar/use) from BloodHound Enterprise in Splunk SOAR and use them to trigger automated response playbooks. # Use the Splunk SOAR integration for BloodHound Enterprise Source: https://bloodhound.specterops.io/integrations/splunk/soar/use Learn how to use the BloodHound Enterprise Splunk SOAR app to view attack path findings in Splunk SOAR. Applies to BloodHound Enterprise only After installation and configuration are complete, you can view attack path findings from BloodHound Enterprise in Splunk SOAR. This allows you to monitor potential attack paths in real-time and trigger automated response playbooks based on BloodHound Enterprise detections. To view attack path findings from BloodHound Enterprise in Splunk SOAR: 1. Log in to your Splunk SOAR instance. 2. Click the **Events** tab to view all polled events. Splunk SOAR events view showing BloodHound Enterprise findings See the [Splunk SOAR documentation](https://help.splunk.com/en/splunk-soar/soar-cloud/use-soar-cloud/introduction/about-splunk-soar-cloud) for more information about working with events. # Configure ETAC Source: https://bloodhound.specterops.io/manage-bloodhound/auth/environment-targeted-access-control Configure Environment Targeted Access Control to limit user access by environment. Applies to BloodHound Enterprise only Environment Targeted Access Control (ETAC) helps you apply least-privilege access in BloodHound Enterprise. Use ETAC to limit which environments **User** and **Read-only** user roles can access. This is a SpecterOps-managed feature. If it is not enabled in your environment, contact your account team for assistance. ## How ETAC works ETAC adds environment scoping to the baseline permissions granted by the assigned role. This can be useful for large, complex environments where users only need access to a subset of environments to perform their work. * ETAC is a premium add-on and may not be available in every tenant. * ETAC applies to **User** and **Read-only** roles only. ETAC settings appear when you create or edit a user with one of those roles. * If you do not select any environments, the user has no environment access by default. * If you select specific environments, the user can access data for those environments only. Roles still define the baseline [permissions](/manage-bloodhound/auth/users-and-roles#user-role-definitions) that control which actions a user can perform. ETAC further limits which environments those actions apply to. ## What users experience After you save ETAC settings, scoped users see only the data and navigation options allowed by both their role and ETAC configuration. * On the **Attack Paths** and **Posture** pages, users can see data from authorized environments only. Filters do not include unauthorized environments. * On the **Explore** page, users can access data from assigned environments only. If a search returns results from unauthorized environments, the graph still represents the full result set, but nodes and edges from unauthorized environments are hidden, and a message indicates that role-based access filtering is applied. A view of the graph on the Explore page with ETAC filtering applied, showing hidden objects from unauthorized environments * On the **Zone Builder** page, a *Permission Denied!* message can appear for ETAC users even when they have authorized environment access, depending on their role permissions and ETAC scope. When access is allowed, users can view objects from their authorized environments only; available actions still depend on role permissions. * Access to all other pages is unaffected, but the baseline [permissions](/manage-bloodhound/auth/users-and-roles#user-role-definitions) of the assigned role still apply. ## Configure ETAC for a user Use the create or edit user workflow to configure ETAC for an eligible role and assign environments. In the left menu, click **Administration** > **Manage Users**. To create a new user, click **Create User**. To edit an existing user, click the hamburger menu next to the user record in the list and select **Update User**. In the **Role** field, select **User** or **Read-only**. When you select an eligible role, the ETAC options display in a new section beside the **Add/Edit User** form. A view of the ETAC controls in the create/edit user workflow By default, no environments are selected, which means a user in this state has no environment access and cannot use the following pages until you select environments and save the user record: * **Attack Paths**: The user sees no data and cannot use filters. * **Explore**: The user can open the page, but sees a *Role-based access filtering applied* message and cannot see data. * **Posture**: The user can open the page, but cannot see data or use filters. * **Zone Builder**: The user can open the page, but may receive a *Permission Denied!* message. Users can view objects from their authorized environments only; available actions still depend on role permissions. Choose one or more environments from the list to grant user access to the data in those environments only. Use the search box to filter the list when you need to find a specific environment quickly. Click **Save** to create or update the user record. The user can access only the environments and pages allowed by the saved configuration. # Enable/Disable Multi-Factor Authentication Source: https://bloodhound.specterops.io/manage-bloodhound/auth/mfa Applies to BloodHound Enterprise and CE ## Purpose This article describes how to enable/disable Multi-Factor Authentication (MFA) for a BloodHound user configured for built-in authentication. ## Process 1. Log into your BloodHound tenant. 2. In the top right, click settings  **My Profile** 3. Toggle the Multi-Factor Authentication switch 4. Continue in one of the two headings below: * Enabling MFA * Disabling MFA ### **Enabling MFA** 1. In the pop-up, confirm your user's password and click on **Next** 2. Scan the QR code with your multi-factor authentication application, enter the 6-digit one-time password, and click **Next** 3. Multi-factor authentication is now enabled, click **Close** ### **Disabling MFA** 1. In the pop-up, confirm your user's password and click on **Disable Multi-Factor Authentication** 2. Multi-factor authentication is now disabled ## Outcome If enabling MFA, next time you log in, you'll need to use both your password and authentication code. If you lose your authentication code device, you'll need to contact an Administrator of your BloodHound tenant who can reset your MFA configuration. # OIDC in BloodHound Source: https://bloodhound.specterops.io/manage-bloodhound/auth/oidc BloodHound supports OIDC for Single Sign On to authenticate users to your tenant environment. Applies to BloodHound Enterprise and CE You can configure multiple SSO providers within your tenant if necessary. This page provides the overall steps to configure an OIDC provider within BloodHound. Entra ID is not currently supported with BloodHound's OIDC implementation. Use the [SAML configuration](/manage-bloodhound/auth/saml-entra-id) instead. ## Order of Operations You must configure OIDC in BloodHound in the following order: 1. Determine the Identity Provider (IDP) name you will use for the OIDC configuration. The same value must be configured in both the IDP and BloodHound. The BloodHound callback URL will include this value. 2. Configure the IDP for BloodHound. See the [Configure Okta](/manage-bloodhound/auth/oidc-okta) guide for configuring Okta as your OIDC provider. 3. Create the OIDC configuration in BloodHound. 4. Create new users or modify existing users using the UI or via the newly created OIDC provider. users must have an email address that is **unique** across all authentication methods (built-in, OIDC, SAML). Account creation fails if a duplicate email is detected. A view of the error message shown when attempting to create a user with a duplicate email address. ## User Role Mapping First name, last name, and email are populated when the correct key/value pairs are provided in the assertion payload. If omitted, fields default to the user's email. A role is applied when the role attribute/claim key is present and its value is a properly formatted BloodHound role. Role values use the prefix `bh-` and are written in kebab-case. See [Administer Users and Roles](/manage-bloodhound/auth/users-and-roles) for capabilities and scopes. | **Role** | **Key Value** | | ------------- | ---------------- | | Administrator | bh-administrator | | Power User | bh-power-user | | Auditor | bh-auditor | | User | bh-user | | Read Only | bh-read-only | | Upload Only | bh-upload-only | Only one role can be passed per user. If multiple roles are provided, BloodHound ignores them and applies the provider's default role. ## BloodHound Icons If your IDP supports custom icons for configured applications, please feel free to use the logos below: * [Enterprise Dark-colored icon](https://raw.githubusercontent.com/SpecterOps/BloodHound-docs/main/docs/logo/BHE_PurpleField.png) * [Enterprise Light-colored icon](https://raw.githubusercontent.com/SpecterOps/BloodHound-docs/main/docs/logo/BHE_WhiteField.png) * [BHCE Dark-colored icon](https://raw.githubusercontent.com/SpecterOps/BloodHound-docs/main/docs/logo/BHCE_RedField.png) * [BHCE Light-colored icon](https://raw.githubusercontent.com/SpecterOps/BloodHound-docs/main/docs/logo/BHCE_WhiteField.png) ## Configure BloodHound Ensure you have configured an IDP for BloodHound as described in **Order of Operations** before proceeding. You must be logged in as an Administrator to perform this action. In the left menu, click **Administration** > **Authentication** > **SSO Configuration**. Click **Create Provider** > ** Provider**. A view of the SSO Configuration page with the Create Provider button highlighted. Enter the provider details. | Field | Auth Type | Description | | ----------------- | ------------- | ---------------------------------------------------------------------------------- | | **Provider Name** | OIDC and SAML | Name of the SAML or OIDC application in your identity provider; must match exactly | | **Client ID** | OIDC only | Client identifier issued by the identity provider | | **Issuer** | OIDC only | Issuer URL from the identity provider | | **Metadata File** | SAML only | SAML metadata XML from the identity provider | | **Default Role** | OIDC and SAML | Role applied when the provider does not supply one | Click **Submit**. ### Create new users on login Enabling this option will have BloodHound create a new user on the first login with SSO (Just-In-Time). The user will be granted the role passed in the role claim if included, else the default role will be assigned. User names (first and last) are only written when the user is first created and will not be updated on subsequent logins. If users are initially created with incorrect names (e.g., email address in the first name field), you must either manually update each user in BloodHound or delete and recreate the user accounts. ### Allow IDP to modify roles Enabling this option allows the SSO provider to modify user roles. This is accomplished by updating the role claim associated with the user account. The role will be updated on the next login. Only one role can be passed per user. If multiple roles are provided, BloodHound ignores them and applies the provider's default role. To have this take effect immediately, disable then re-enable the user to invalidate current sessions and force a fresh login. See [User Role Mapping](#user-role-mapping) for role claim reference. BloodHound will provide the URLs related to this new provider integration. Please take a moment to verify that the ** URL** matches the **Single sign on URL** specified in the application integration page during setup of the integration. ## Configure Users By default, all users utilize a username and password via the built-in authentication service. When [creating or modifying a user](/manage-bloodhound/auth/users-and-roles), you can change this setting. When creating a new user, ensure the user does not share an email address with any other users (across all authentication methods). You must be logged in as an Administrator to perform this action. In the left menu, click **Administration** > Users > **Manage Users**. Locate the user you want to configure with authentication, click the hamburger menu button on the right side of the row, then **Update User**. A view of the Manage Users page with the hamburger menu button highlighted for a user. In the **Update User** dialog, select the **Single Sign-On** authentication method, then select the appropriate SSO provider. A view of the Update User dialog with the Single Sign-On authentication method selected. * When Provisioning is enabled without the Modify Role option, a user's role may be updated manually after creation. * If both Provisioning and Modify Role are enabled, role updates must come through the SSO provider (manual updates in BloodHound are disabled). A view of the Update User dialog with the Single Sign-On authentication method selected and provisioning enabled. Click **Save**. # OIDC: Okta Configuration Source: https://bloodhound.specterops.io/manage-bloodhound/auth/oidc-okta This document provides instructions for creating an application within Okta for compatibility with BloodHound Enterprise. Applies to BloodHound Enterprise and CE ## Create an Okta application To create an Okta application for BloodHound, complete the following steps: Follow the [Okta documentation](https://help.okta.com/oie/en-us/content/topics/apps/apps_app_integration_wizard_oidc.htm) to create a new application. Set your application type to **Native**. When configuring the Okta application, use the following settings: | Field | Value | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Login redirect** | `https://{domainname}/api/v2/sso/{chosenProviderName}/callback`

**Example**: `https://test.bloodhoundenterprise.io/api/v2/sso/bhestandard/callback` | | **Logout redirect** | `https://{domainname}/`

**Example**: `https://test.bloodhoundenterprise.io/` | Note the following values: * **Client ID** * **Issuer URL** You'll use the **Client ID** from the Okta *Client Credentials* and the **Issuer URL** from the Okta *Authorization Server* when you [configure BloodHound](/manage-bloodhound/auth/oidc#configure-bloodhound).
If you want to map additional user attributes (first name, last name, role) from Okta to BloodHound, you must create [custom claims](https://help.okta.com/oie/en-us/content/topics/apps/federated-claims-overview.htm) in Okta. Go to **Security** > **API** > **Authorization Servers** > **Claims** and create the following claims: | Field | Setting | | ------------------------- | ---------------------- | | **Name** | first\_name | | **Include in token type** | ID Token → Always | | **Value type** | Expression | | **Value** | `user.firstName` | | **Include in** | Any scope (or Profile) | | Field | Setting | | ------------------------- | ---------------------- | | **Name** | last\_name | | **Include in token type** | ID Token → Always | | **Value type** | Expression | | **Value** | `user.lastName` | | **Include in** | Any scope (or Profile) | | Field | Setting | | ------------------------- | ---------------------- | | **Name** | roles | | **Include in token type** | ID Token → Always | | **Value type** | Groups | | **Filter** | Starts with --> `bh-` | | **Include in** | Any scope (or Profile) |
# Authentication and Authorization Source: https://bloodhound.specterops.io/manage-bloodhound/auth/overview Create and administer users of BloodHound using built-in authentication or SAML. # SAML in BloodHound Source: https://bloodhound.specterops.io/manage-bloodhound/auth/saml BloodHound supports SAML 2.0 for Single Sign On to authenticate users to your tenant environment. Applies to BloodHound Enterprise and CE You may configure multiple SAML providers within your tenant if necessary. This page provides the overall steps to configure a SAML provider within BloodHound. We have provided walkthroughs for creating the configuration within the provider for your convenience. ## Order of Operations You must configure SAML in BloodHound in the following order: 1. Determine the Identity Provider (IDP) you will utilize for the SAML configuration. The same value must be configured in both the IDP and BloodHound. The BloodHound Assertion Consumer Service (ACS) URL will include this value. 2. Configure the IDP for BloodHound. You can follow one of the guides below based on your IDP: * [SAML: Active Directory Federation Services (ADFS) Configuration](/manage-bloodhound/auth/saml-adfs) * [SAML: Auth0 Configuration](/manage-bloodhound/auth/saml-auth0) * [SAML: Entra ID Configuration](/manage-bloodhound/auth/saml-entra-id) * [SAML: Google IDP Configuration](/manage-bloodhound/auth/saml-google) * [SAML: Okta Configuration](/manage-bloodhound/auth/saml-okta) 3. Create the SAML Configuration in BloodHound. 4. [Create new users or modify existing users](/manage-bloodhound/auth/users-and-roles) using the UI or via the newly created SAML provider. users must have an email address that is **unique** across all authentication methods (built-in, OIDC, SAML). Account creation fails if a duplicate email is detected. A view of the error message shown when attempting to create a user with a duplicate email address. ## SAML Attribute Quick Reference | **Data Type** | **Value** | | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **IDP Name Format** | urn:oasis:names:tc:SAML:2.0:attrname-format:uri | | **Required SAML Attributes** | Either of the following will map to the user's email address in BloodHound:

`http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`

*urn:oid:0.9.2342.19200300.100.1.3* | | **Optional SAML Attributes** | The following will map to the user's first name in BloodHound:

`http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name`

| | - | The following will map to the user's last name in BloodHound:

`http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname`

| | - | The following will map to the user role in BloodHound:

`http://schemas.microsoft.com/ws/2008/06/identity/claims/role`

| ## User Role Mapping First name, last name, and email are populated when the correct key/value pairs are provided in the assertion payload. If omitted, fields default to the user's email. A role is applied when the role attribute/claim key is present and its value is a properly formatted BloodHound role. Role values use the prefix `bh-` and are written in kebab-case. See [Administer Users and Roles](/manage-bloodhound/auth/users-and-roles) for capabilities and scopes. | **Role** | **Key Value** | | ------------- | ---------------- | | Administrator | bh-administrator | | Power User | bh-power-user | | Auditor | bh-auditor | | User | bh-user | | Read Only | bh-read-only | | Upload Only | bh-upload-only | Only one role can be passed per user. If multiple roles are provided, BloodHound ignores them and applies the provider's default role. ## BloodHound Icons If your IDP supports custom icons for configured applications, please feel free to use the logos below: * [Enterprise Dark-colored icon](https://raw.githubusercontent.com/SpecterOps/BloodHound-docs/main/docs/logo/BHE_PurpleField.png) * [Enterprise Light-colored icon](https://raw.githubusercontent.com/SpecterOps/BloodHound-docs/main/docs/logo/BHE_WhiteField.png) * [BHCE Dark-colored icon](https://raw.githubusercontent.com/SpecterOps/BloodHound-docs/main/docs/logo/BHCE_RedField.png) * [BHCE Light-colored icon](https://raw.githubusercontent.com/SpecterOps/BloodHound-docs/main/docs/logo/BHCE_WhiteField.png) ## Configure BloodHound Ensure you have configured an IDP for BloodHound as described in **Order of Operations** before proceeding. You must be logged in as an Administrator to perform this action. In the left menu, click **Administration** > **Authentication** > **SSO Configuration**. Click **Create Provider** > ** Provider**. A view of the SSO Configuration page with the Create Provider button highlighted. Enter the provider details. | Field | Auth Type | Description | | ----------------- | ------------- | ---------------------------------------------------------------------------------- | | **Provider Name** | OIDC and SAML | Name of the SAML or OIDC application in your identity provider; must match exactly | | **Client ID** | OIDC only | Client identifier issued by the identity provider | | **Issuer** | OIDC only | Issuer URL from the identity provider | | **Metadata File** | SAML only | SAML metadata XML from the identity provider | | **Default Role** | OIDC and SAML | Role applied when the provider does not supply one | Click **Submit**. ### Create new users on login Enabling this option will have BloodHound create a new user on the first login with SSO (Just-In-Time). The user will be granted the role passed in the role claim if included, else the default role will be assigned. User names (first and last) are only written when the user is first created and will not be updated on subsequent logins. If users are initially created with incorrect names (e.g., email address in the first name field), you must either manually update each user in BloodHound or delete and recreate the user accounts. ### Allow IDP to modify roles Enabling this option allows the SSO provider to modify user roles. This is accomplished by updating the role claim associated with the user account. The role will be updated on the next login. Only one role can be passed per user. If multiple roles are provided, BloodHound ignores them and applies the provider's default role. To have this take effect immediately, disable then re-enable the user to invalidate current sessions and force a fresh login. See [User Role Mapping](#user-role-mapping) for role claim reference. BloodHound will provide the URLs related to this new provider integration. Please take a moment to verify that the ** URL** matches the **Single sign on URL** specified in the application integration page during setup of the integration. ## Configure Users By default, all users utilize a username and password via the built-in authentication service. When [creating or modifying a user](/manage-bloodhound/auth/users-and-roles), you can change this setting. When creating a new user, ensure the user does not share an email address with any other users (across all authentication methods). You must be logged in as an Administrator to perform this action. In the left menu, click **Administration** > Users > **Manage Users**. Locate the user you want to configure with authentication, click the hamburger menu button on the right side of the row, then **Update User**. A view of the Manage Users page with the hamburger menu button highlighted for a user. In the **Update User** dialog, select the **Single Sign-On** authentication method, then select the appropriate SSO provider. A view of the Update User dialog with the Single Sign-On authentication method selected. * When Provisioning is enabled without the Modify Role option, a user's role may be updated manually after creation. * If both Provisioning and Modify Role are enabled, role updates must come through the SSO provider (manual updates in BloodHound are disabled). A view of the Update User dialog with the Single Sign-On authentication method selected and provisioning enabled. Click **Save**. # SAML: ADFS Configuration Source: https://bloodhound.specterops.io/manage-bloodhound/auth/saml-adfs This document provides instructions for creating an application within ADFS for compatibility with BloodHound Enterprise. Applies to BloodHound Enterprise and CE ## Create an Application 1. In the AD FS management console, right-click on Relaying Party Trust and click “Add Relaying Party Trust”. 2. Choose “Claims aware” and click “Start”. 3. Insert the metadata URL based on your chosen name and click “Next.” 4. Enter the preferred display name and click “Next.” 5. Choose the desired Access Control Policy. (Note that access and permissions are configured within BloodHound Enterprise). 6. Review the information presented and click “Next”. 7. Leave the “Configure claims issuance policy for this application” box checked and click “Close”. ## Complete SAML Integration Configuration 1. On the “Edit Claim Issuance Policy” dialog box, click “Add Rule…”. 2. Choose “Send LDAP Attributes as Claims” and click “Next. 3. Fill out the following and click “Finish”. LDAP Attribute: E-Mail-Addresses Outgoing Claim Type : E-Mail Address 4. Click “Add Rule” to add another claim rule. 5. Choose “Transform and Incoming Claim” and click “Next”. 6. Fill out the following and click “Finish”. Incoming claim type: E-Mail Address Outgoing claim type: Name ID Outgoing name ID format: Email Choose “Pass through all claim values” 7. Click “Apply”. 8. Download the metadata file provided by your ADFS environment. By default, this is hosted at: [https://YOURDOMAIN/federationmetadata/2007-06/federationmetadata.xml](https://YOURDOMAIN/federationmetadata/2007-06/federationmetadata.xml) 9. Follow the instructions at [SAML in BloodHound Enterprise](/manage-bloodhound/auth/saml) to create the SAML provider in BloodHound Enterprise. # SAML: Auth0 Configuration Source: https://bloodhound.specterops.io/manage-bloodhound/auth/saml-auth0 This document provides instructions for creating an application within Auth0 for compatibility with BloodHound Enterprise. Applies to BloodHound Enterprise and CE ## Create an Auth0 Application 1. Create an authentication application for BloodHound Enterprise in Auth0. 2. Assign a recognizable name to the application and select the integration option for "Regular Web Application." 3. After the creation of the application in Auth0 you should see the application details page. 4. Click on the "Addons" tab. 5. Enable the "SAML2 Webapp" toggle and download the Identify Provider Metadata file. 6. Follow the instructions at [SAML in BloodHound Enterprise](/manage-bloodhound/auth/saml) to create the SAML provider in BloodHound Enterprise. # SAML: Entra ID Configuration Source: https://bloodhound.specterops.io/manage-bloodhound/auth/saml-entra-id This document provides instructions for creating an application within Entra ID for compatibility with BloodHound Enterprise. Applies to BloodHound Enterprise and CE ## Create an Enterprise Application 1. Login to Azure at [https://portal.azure.com](https://portal.azure.com) 2. Navigate to the **Enterprise Applications** section of Entra ID. 3. Click **New Application**. 4. Click **Create your own application**. 5. Provide a name for your application and click **Create**. ## Configure Single Sign-On Settings 1. Your browser should redirect you to your newly created application. Click on **Single sign-on**. 2. Click on **SAML**. 3. Click **Edit** under the Basic SAML Configuration section. 4. Configure SAML. The following screenshot shows the tenant codename is "demo" and the provider name is "entra". 5. Azure will inform you the settings have saved successfully. 6. Click the **X** to close the dialog. 7. Scroll down to the **SAML Certificates** section and download the **Metadata XML**. 8. Use the **Users and Groups** section to configure groups and users which you would like to grant access to BloodHound Enterprise. 9. Use the downloaded metadata.xml file and follow the instructions at [SAML in BloodHound Enterprise](/manage-bloodhound/auth/saml) to Create the SAML Configuration in BloodHound. ## Troubleshooting Verify your attributes and claims use a proper schema in the claim name, and that you have a properly mapped claim for "user.mail" as in the example below. An indicator that this is necessary is when an authentication attempt returns the response: "*assertion does not meet requirements for user lookup*". # SAML: Google IDP Configuration Source: https://bloodhound.specterops.io/manage-bloodhound/auth/saml-google This document provides instructions for creating an application within Google for compatibility with BloodHound Enterprise. Applies to BloodHound Enterprise and CE ## Create a Google Application 1. On the Admin Console for Google Workspaces, use the left navigation bar and go to Apps -> Web and Mobile Apps 2. Select “Add App” -> Add Custom SAML app 3. Give the app an appropriate name, such as BloodHound Enterprise. Optionally, add an icon and description. 4. On the next screen, download the metadata file and continue. 5. Enter the ACS URL and Entity ID as provided in the BloodHound Enterprise console: 6. On the next screen, it is required to send the email attribute to BloodHound. BloodHound will accept either of the following values as the “App Attributes”: * [http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress](http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress) * urn:oid:0.9.2342.19200300.100.1.3 7. Follow the instructions at [SAML in BloodHound Enterprise](/manage-bloodhound/auth/saml) to create the SAML provider in BloodHound Enterprise. # SAML: Okta Configuration Source: https://bloodhound.specterops.io/manage-bloodhound/auth/saml-okta This document provides instructions for creating an application within Okta for compatibility with BloodHound Enterprise. Applies to BloodHound Enterprise and CE ## Create an Okta Application 1. Navigate to the organization applications page and create a new SAML application integration. 2. Give the application a name and an icon if desired. 3. Once finished, click next to begin setting the SAML configuration for this application. ## Okta SAML Settings The following SAML settings are required for Okta to integrate with BloodHound Enterprise: | **SAML Setting** | **Value** | | ------------------------ | ------------ | | **Name ID format** | EmailAddress | | **Application username** | Email | ## Okta Attribute Statements The following attribute settings are required for Okta to integrate with BloodHound Enterprise: | | | | | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ---------- | | **Name** | **Name Format** | **Value** | | [http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress](http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress) | URI Reference | user.email | Complete SAML Integration Configuration 1. Once all the information is entered, your screen should look similar to the example below. Once confirmed, click next to continue. 2. Complete creation of the SAML integration with the following options below: 3. Once completed you should now see the application home page. You may then click on **View Setup Instructions** to view the integration setup details. 4. Copy the metadata provided by Okta and save it into a metadata.xml file. **ATTENTION FIREFOX USERS:** FireFox may prepend an additional heading to the metadata.xml file, resulting in an error creating the SAML integration within BloodHound Enterprise. If your extracted metadata.xml looks like the following, delete line 1 try again. See [https://support.mozilla.org/en-US/questions/1387904](https://support.mozilla.org/en-US/questions/1387904) for more details. 5. Follow the instructions at [SAML in BloodHound Enterprise](/manage-bloodhound/auth/saml) to create the SAML provider in BloodHound Enterprise. # Administer Users and Roles Source: https://bloodhound.specterops.io/manage-bloodhound/auth/users-and-roles Applies to BloodHound Enterprise and CE ## Purpose This article provides a summary of assignable roles that are available when creating new users in BloodHound. ## Creating users Users are created through **Settings ** **Administration ** **Manage Users**, and clicking the button **Create User**. The following properties must be set on each user: | **Property** | **Description** | | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Email Address | Text field for the user's email address. | | Principal Name | Text field for the username used for logging into BloodHound. Can be the same as email address. | | First Name | Text field for the user's first name. | | Last Name | Text field for the user's first name. | | Authentication Method | Drop-down selection for one of the available authentication methods to be used for the user.

\* Username / Password - Built-in authentication via username and password, supports TOTP-based multifactor authentication.
\* SAML - SAML 2.0-based Single-Sign-On as described in [SAML in BloodHound Enterprise](/manage-bloodhound/auth/saml).
| | Initial Password | Text field for the user's initial password. | | Force Password Reset? | Selecting this check box forces the user to reset their password on the next logon. Must comply with password requirements:

\* At least 12 characters long
\* Contain at least 1 lowercase character, 1 uppercase character, 1 number and 1 special character (!@#\$%^&\*) | | Role | Drop-down selection for one the available roles. | ## User Role Definitions BloodHound offers multiple roles for access control. Each user must be assigned one role. In BloodHound Enterprise, [Environment Targeted Access Control (ETAC)](/manage-bloodhound/auth/environment-targeted-access-control) can further limit which environments **User** and **Read-only** roles can access. ETAC does not change the baseline permissions in the role matrix below. Instead, it limits which environments those permissions apply to. For OpenGraph extensions, BloodHound separates read and write permissions. Users without permission to upload or delete extension schemas can still view extension content that their role allows, but the **Upload** and **Delete** extension buttons remain disabled. Scroll right to view the full table of permissions for each role. | | **Administrator** | **Power User** | **Auditor** | **User** | **Read-only** | **Upload-only** | | ------------------------------------------------------------------------------------------- | :--------------------------: | :--------------------------: | :--------------------------: | :--------------------------: | :--------------------------: | :--------------------------: | | **Tenant Administration** | | | | | | | | Add, Remove, Modify users | | - | - | - | - | - | | View users | | - | | - | - | - | | Add, Remove all API keys | | - | - | - | - | - | | View all API keys | | - | | - | - | - | | Add, Remove, View owned API keys | | | | | | - | | Add, Remove SAML provider configurations | | - | - | - | - | - | | View SAML provider configurations | | - | | - | - | - | | Clear the BloodHound database | | - | - | - | - | - | | View audit log | | - | | - | - | - | | Configure ETAC settings \[BHE] | | - | - | - | - | - | | Upload and delete OpenGraph extension schemas | | - | - | - | - | - | | View OpenGraph extensions, findings, and edges | | | | | | - | | **Attack Path Analysis** | | | | | | | | View any available tenant data, including active Attack Paths \[BHE], and explore the Graph | | | | | | - | | Create, Edit, Delete, Share owned Saved Cypher Queries | | | - | | - | - | | Accept Attack Path Impacted Principals \[BHE] | | | - | - | - | - | | Modify Tier Zero / High-Value Members | | | - | - | - | - | | Add, Edit, Remove Privilege Zones, Labels, and Selectors | | | - | - | - | - | | Approve and Revoke certification of Privilege Zone members | | | - | - | - | - | | View, Search, and Filter Privilege Zones Certification Queue | | | | - | - | - | | View, Search, and Filter Privilege Zones History Log | | | | | | - | | **Collector Clients and File Ingest** | | | | | | | | Download collector installation packages | | | | | | | | View collector client details \[BHE] | | | | | - | - | | View and Filter Finished Jobs Log and job details panel | | | | - | - | - | | Run collector client on-demand scan \[BHE] | | | - | - | - | - | | Add, modify, and remove a collector client \[BHE] | | | - | - | - | - | | Regenerate collector client credentials \[BHE] | | | - | - | - | - | | File Ingest | | | | - | - | | # BloodHound Configuration Supplement Source: https://bloodhound.specterops.io/manage-bloodhound/bh-config This page provides example configuration details for BloodHound and BloodHound Enterprise Applies to BloodHound Enterprise and CE # Configuration Elements Configuration elements with a `.` in their name are part of a nested JSON configuration block: `tls.cert_file` translates to the following JSON structure: ```json theme={null} { "tls": { "cert_file": "/path/to/cert" } } ``` ## version Version of the configuration file. This is useful for detecting when new, breaking configuration changes occur. ## bind\_addr Bind address for the API. Example: `:` ## slow\_query\_threshold Threshold in milliseconds for caching queries. ## max\_graphdb\_cache\_size Number of cache items for graph queries. ## max\_api\_cache\_size Number of cache items for API utilities. ## metrics\_port Bind address for the tool and metric API. **This port is sensitive and access must be guarded!** Example: `:` ## root\_url External facing HTTP URL that represents the root path for the application. Example: `http://localhost/` ## work\_dir Local directory for storing work files and temporary ingest artifacts. Example: `/opt/bhe/work` ## log\_level Default log level for the application. This parameter may be set to one of the following values: * `"DEBUG"` * `"INFO"` * `"ERROR"` ## log\_path If not empty, enables the application to record logs to the provided file along with std out. ## tls TLS configuration to start the BloodHound API using HTTPS. For a step-by-step example on BloodHound Community Edition, see [Enable Transport Layer Security (TLS)](/get-started/custom-installation#enable-transport-layer-security-tls) in the custom installation guide. ### tls.cert\_file Path to the TLS certificate file. ### tls.key\_file Path to the TLS certificate signing key. ## graph\_driver Determines the driver to use when accessing the graph database. This parameter may be set to one of the following values: * `"neo4j"` * `"pg"` ## database ### database.connection Primary database connection URL. This URL must be specified in one of the following formats: * `"postgresql://user:password@host:port/db_name"` ## neo4j ### neo4j.connection Graph database connection URL when Neo4j is enabled. This URL must be specified in one of the following formats: * `"neo4j://user:password@host:port/db_name"` ## crypto Cryptographic configuration settings. ### crypto.jwt JWT configuration settings. ### crypto.jwt.signing\_key Base64 encoded byte array for signing key for user session JWTs. This value **must be 32 bytes** in length when decoded as the JWT signing method used by the API is `HMAC-SHA2-256`. ### crypto.argon2 Argon2 cryptographic settings for password enabled authentication. ### crypto.argon2.memory\_kibibytes Amount of memory the Argon2 password hash function should utilize. See the [recommended parameters](https://www.password-hashing.net/argon2-specs.pdf) section of the Argon2 specification for further information about this value. ### crypto.argon2.num\_iterations Number of iterations the Argon2 password hash function should execute. See the [recommended parameters](https://www.password-hashing.net/argon2-specs.pdf) section of the Argon2 specification for further information about this value. ### crypto.argon2.num\_threads Number of threads the Argon2 password hash function should utilize during digest. See the [recommended parameters](https://www.password-hashing.net/argon2-specs.pdf) section of the Argon2 specification for further information about this value. ## saml ### saml.sp\_cert Certificate that the API instance should use when presenting as a SAML Service Provider. ### saml.sp\_key Private RSA key that the API instance should use when presenting as a SAML Service Provider. ### saml.sp\_ca\_chain Certificate chain that contains the signing authority for the certificate and private key the API instance should use when presenting as a SAML Service Provider. ## default\_admin Default admin user configuration details. This configuration drives the creation of the first user that may log in and finish setting up the BloodHound instance. ### default\_admin.principal\_name Principal name of the default admin user. `Deprecated` ### default\_admin.password Initial password for the default admin user. ### default\_admin.email\_address Email address for the default admin user. ### default\_admin.first\_name First name for the default admin user. ### default\_admin.last\_name Last name for the default admin user. ### default\_admin.expire\_now Expires the default admin user's initial password, requiring a password reset on first logon. This parameter may be set to one of the following values: * `true` * `false` ## collectors\_bucket\_url Collector bucket URL for collectors sourced upstream. `For BloodHound Enterprise internal use only.` ## collectors\_base\_path Collector base path for collectors sourced upstream. `For BloodHound Enterprise internal use only.` ## datapipe\_interval Interval in seconds that the service will wait before checking for new data. ## enable\_startup\_wait\_period Enables a startup wait period that defers ingest and analysis until a given amount of time. This parameter may be set to one of the following values: * `true` * `false` ## enable\_api\_logging Enables API HTTP request logging. This parameter may be set to one of the following values: * `true` * `false` ## enable\_cypher\_mutations Enables graph database mutations via the cypher search endpoint. This parameter may be set to one of the following values: * `true` * `false` ## disable\_analysis Disables graph data analysis. This parameter may be set to one of the following values: * `true` * `false` ## disable\_cypher\_complexity\_limit Disables cypher complexity limiting. This parameter may be set to one of the following values: * `true` * `false` ## disable\_ingest Disables graph data ingest. This parameter may be set to one of the following values: * `true` * `false` ## disable\_migrations Disables database migrations. This parameter may be set to one of the following values: * `true` * `false` ## graph\_query\_memory\_limit Graph query memory limit in gigabytes. ## fedramp\_eula\_text Text to display for the alternative FedRAMP EULA acceptance page. ## enable\_text\_logger Enables text output instead of JSON output for the API log. This parameter may be set to one of the following values: * `true` * `false` ## recreate\_default\_admin Allow recreating the default admin account to help with lockouts/loading database dumps. This parameter may be set to one of the following values: * `true` * `false` ## force\_download\_embedded\_collectors Forces Bloodhound users to download the collectors that are embedded in the container. BHCE only allows for downloading embedded collectors. This parameter may be set to the following values: * `true` * `false` ## enable\_user\_analytics Enables SpecterOps to gather analytics on user activity to help enhance the product. This parameter may be set to the following values: * `true` * `false` # OS Environment Config Format An operator may set any option via an environment variable by prefixing it with `bhe_` and replacing dots (`.`) with underscores (`_`): ```bash theme={null} export bhe_bind_addr="192.168.100.100" export bhe_root_url="https://example.com" export bhe_database_connection="postgres://bhe:weneedbetterpasswords@localhost:5432/bhe" export bhe_neo4j_connection="neo4j://bhe:weneedbetterpasswords@localhost:7687/bhe" bhapi ``` # Example JSON Configuration An operator may use the below example to author a JSON configuration: ```json theme={null} { "version": 1, "bind_addr": "0.0.0.0:8080", "root_url": "http://0.0.0.0:8080/", "work_dir": "/opt/bhe_work", "log_level": "INFO", "log_path": "", "tls": { "cert_file": "", "key_file": "" }, "graph_driver": "pg", "database": { "connection": "postgresql://bhe:bhe4eva@localhost/bhe" }, "default_admin": { "principal_name": "admin", "password": "admin", "email_address": "admin@example.com", "first_name": "Initial", "last_name": "Admin", "expire_now": true }, "crypto": { "jwt": { "signing_key": "" }, "argon2": { "memory_kibibytes": 1048576, "num_iterations": 4, "num_threads": 4 } }, "saml": { "sp_cert": "CERT CONTENT", "sp_key": "-----BEGIN PRIVATE KEY-----\nKEY CONTENT\n-----END PRIVATE KEY-----" }, "graph_query_memory_limit": 2, "force_download_embedded_collectors": false, "enable_user_analytics": false } ``` # BloodHound Shortcuts Source: https://bloodhound.specterops.io/manage-bloodhound/bh-shortcuts List of the keyboard shortcuts available in BloodHound ## Global shortcuts | Command | Action | | ------------------- | ------------------------------ | | Alt/Option + \[1-8] | Navigate sidebar pages | | Alt/Option + H | Launch keyboard shortcuts list | | Alt/Option + D | Navigate to Documentation | | Alt/Option + U | Launch File Upload dialog | | Alt/Option + M | Toggle Dark Mode | ## Explore page | Command | Action | | ---------------------- | ------------------------ | | Alt/Option + / | Jump to Node Search | | Alt/Option + P | Jump to Pathfinding | | Alt/Option + C | Jump to Query Editor | | Alt/Option + S | Save Current Query | | Alt/Option + R | Run Current Cypher Query | | Alt/Option + Shift + / | Search Current Nodes | | Alt/Option + T | Toggle Table View | | Alt/Option + I | Toggle Node Info Panel | | Alt/Option + G | Reset Graph View | ## Attack Paths page Applies to BloodHound Enterprise only | Command | Action | | -------------- | ---------------------------- | | Alt/Option + R | Reset to Default View | | Alt/Option + K | Jump to Next Finding | | Alt/Option + J | Jump to Previous Finding | | Alt/Option + E | Jump to Environment Selector | ## Posture page Applies to BloodHound Enterprise only | Command | Action | | -------------- | ---------------------------- | | Alt/Option + E | Jump to Environment Selector | | Alt/Option + Z | Jump to Zone Selector | | Alt/Option + / | Filter Table Data | # BloodHound Enterprise NIST CSF v1.1 Compliance Resource Source: https://bloodhound.specterops.io/manage-bloodhound/compliance-framework/nist-csf-v1-1 The Following information is meant to provide a more detailed and in-depth view of compliance items that BloodHound Enterprise can provide coverage for. ## Identify (ID) Asset Management (ID.AM) The devices and systems that enable the organization to achieve business purposes are identified and managed consistent with their relative importance to the organizational objectives the organization’s risk strategy. ### ID.AM-1 #### Requirement Physical Devices and systems within the organization are inventoried. #### Solution BloodHound Enterprise collects information on all physical systems operating within a Windows Active Directory environment/Azure Environment. BloodHound Enterprise monitors the addition/removal of physical assets connecting to the organizations environment. ###### **References** CIS CSC 1 COBIT 5 BAI09.01, BAI09.02 ISA 62443-2-1:2009 4.2.3.4 ISA 62443-3-3:2013 SR 7.8 ISO/IEC 27001:2013 A.8.1.1, A.8.1.2 NIST SP 800-53 Rev. 4 CM-8, PM-5 ### ID.AM-2 #### Requirement Inventory of Software, Services, and Systems managed by the organization are maintained. #### Solution BloodHound Enterprise collects information on all Systems in a domain that are connected to the organizations Active Directory/Azure Environment. BloodHound Enterprise monitors the environment for the addition/removal of systems from the organizations environment. ###### **References** CIS CSC 2 COBIT 5 BAI09.01, BAI09.02, BAI09.05 ISA 62443-2-1:2009 4.2.3.4 ISA 62443-3-3:2013 SR 7.8 ISO/IEC 27001:2013 A.8.1.1, A.8.1.2, A.12.5.1 NIST SP 800-53 Rev. 4 CM-8, PM-5 ### ID.AM-5 #### Requirement Resources are prioritized based on their classification, criticality, and business value. #### Solution BloodHound Enterprise allows organizations to assign assets to Tier Zero (T0) based on the organizations classification, criticality, and business value. Prioritized resources are audited and accounted for during BloodHound Enterprise collection scans. ###### **References** CIS CSC 13, 14 COBIT 5 APO03.03, APO03.04, APO12.01, BAI04.02, BAI09.02 ISA 62443-2-1:2009 4.2.3.6 ISO/IEC 27001:2013 A.8.2.1 NIST SP 800-53 Rev. 4 CP-2, RA-2, SA-14, SC-6 ## Identity(ID)Risk Assessment(ID.RA) The organization understands the cybersecurity risk to organizational operations (including mission, functions, image, or reputation), organizational assets, and individuals. ### ID.RA-1 #### Requirement Asset vulnerabilities are identified and documented. #### Solution BloodHound Enterprise analyzes the Active Directory/Azure environment for identity attack paths that potentially impact an organizations security posture. All Identity vulnerabilities are identified during BloodHound collection activities and presented in the reporting dashboard with additional information to support documenting threats. ###### **References** CIS CSC 4 COBIT 5 APO12.01, APO12.02, APO12.03, APO12.04, DSS05.01, DSS05.02 ISA 62443-2-1:2009 4.2.3, 4.2.3.7, 4.2.3.9, 4.2.3.12 ISO/IEC 27001:2013 A.12.6.1, A.18.2.3 NIST SP 800-53 Rev. 4 CA-2, CA-7, CA-8, RA-3, RA-5, SA-5, SA-11, SI-2, SI-4, SI-5 ### ID.RA-3 #### Requirement Threats, both internal and external, are identified and documented. #### Solution BloodHound Enterprise analyzes the Active Directory/Azure environment for identity attack paths that potentially impact an organizations security posture. All Identity threat vectors are identified during BloodHound collection activities and presented in the reporting dashboard with additional information to support documenting threats. ###### **References** CIS CSC 4 COBIT 5 APO12.01, APO12.02, APO12.03, APO12.04 ISA 62443-2-1:2009 4.2.3, 4.2.3.9, 4.2.3.12 ISO/IEC 27001:2013 Clause 6.1.2 NIST SP 800-53 Rev. 4 RA-3, SI-5, PM-12, PM-16 ### ID.RA-5 #### Requirement Threats, vulnerabilities, likelihoods, and impacts are used to determine risk #### Solution BloodHound Enterprise analyzes the organizational environment for identity attack path vectors and assigns a quantifiable risk metric and category to each detected identity attack path. The assigned risk metric is calculated by determining what percentage of the environment could be impacted by a specific identity vulnerability which will be quantified as percentage and assigned a criticality rating. ###### **References** CIS CSC 4 COBIT 5 APO12.02 ISO/IEC 27001:2013 A.12.6.1 NIST SP 800-53 Rev. 4 RA-2, RA-3, PM-16 ## Protect(PR) Identity Management, Authentication, and Access Control (PR.AC) Access to physical and logical assets and associated facilities is limited to authorized users, processes, and devices, and is managed consistent with the assessed risk of unauthorized access to authorized activities and transactions. ### PR.AC-4 #### Requirement Employ the principal of least privilege, allowing only authorized access for users (or processing on the behalf of users) that necessary to accomplish assigned organizational tasks. #### Solution BloodHound Enterprise audits and reports the health of organizational privilege access models and identifies potential vulnerable attack paths and misconfigurations within the privilege access architecture scheme. ###### **References** CIS CSC, 16 COBIT 5 DSS05.04, DSS05.05, DSS05.07, DSS06.03 ISA 62443-2-1:2009 4.3.3.2.2, 4.3.3.5.2, 4.3.3.7.2, 4.3.3.7.4 ISA 62443-3-3:2013 SR 1.1, SR 1.2, SR 1.4, SR 1.5, SR 1.9, SR 2.1 ISO/IEC 27001:2013, A.7.1.1, A.9.2.1 NIST SP 800-53 Rev. 4 AC-1, AC-2, AC-3, AC-16, AC-19, AC-24, IA-1, IA-2, IA-4, IA-5, IA-8, PE-2, PS-3 ## Protect(PR) Information Protection, Processes, and Procedures(PR.IP) Security policies (that address purpose, scope, roles, responsibilities, management commitment, and coordination among organizational entities), processes, and procedures are maintained and used to manage protection of information systems and assets. ### PR.IP-1 #### Requirement A baseline configuration of information technology/industrial control systems is created and maintained incorporating security principles. #### Solution BloodHound Enterprise collects information on all physical systems operating within a Windows Active Directory environment/Azure Environment. BloodHound Enterprise establishes an initial baseline for the environment during setup and maintains that baseline with periodic scheduled and on-demand environment scans. ###### **References** CIS CSC 3, 9, 11 COBIT 5 BAI10.01, BAI10.02, BAI10.03, BAI10.05 ISA 62443-2-1:2009 4.3.4.3.2, 4.3.4.3.3 ISA 62443-3-3:2013 SR 7.6 ISO/IEC 27001:2013 A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4 NIST SP 800-53 Rev. 4 CM-2, CM-3, CM-4, CM- 5, CM-6, CM-7, CM-9, SA-10 ## Detect(DE) Anomalies and Events (DE.AE) Anomalous activity is detected and the potential impact of events is understood. ### DE.AE-1 #### Requirement A baseline of network operations and expected data flows for users and systems is established and managed. #### Solution BloodHound Enterprise collects information on all physical systems and Active Directory/Azure users operating within a Windows Active Directory environment/Azure Environment. BloodHound Enterprises configurable scan options allows organizations to establish and monitor their organizational baseline of systems, users, and groups. ###### **References** CIS CSC 1, 4, 6, 12, 13, 15, 16 COBIT 5 DSS03.01 ISA 62443-2-1:2009 4.4.3.3 ISO/IEC 27001:2013 A.12.1.1, A.12.1.2, A.13.1.1, A.13.1.2 NIST SP 800-53 Rev. 4 AC-4, CA-3, CM-2, SI-4 ### DE.AE-2 #### Requirement Detected events are analyzed to understand attack targets and methods. #### Solution BloodHound Enterprise reports identity attack paths and assigns a risk exposure severity rating to each vector based on the percentage of the organizations environment that is exposed to risk. Additional information pertaining to specific events is included in the BloodHound Enterprise GUI. ###### **References** CIS CSC 3, 6, 13, 15 COBIT 5 DSS05.07 ISA 62443-2-1:2009 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8 ISA 62443-3-3:2013 SR 2.8, SR 2.9, SR 2.10, SR 2.11, SR 2.12, SR 3.9, SR 6.1, SR 6.2 ISO/IEC 27001:2013 A.12.4.1, A.16.1.1, A.16.1.4 NIST SP 800-53 Rev. 4 AU-6, CA-7, IR-4, SI-4 ### DE.AE-3 #### Requirement Event data is collected and correlated from multiple sources and sensors. #### Solution BloodHound Enterprise's Identity Attack Path solution provides unique graph based representations of the logical relationships that may be vulnerable to identity attacks. The information provided by BloodHound Enterprise can be used in combination with other defense appliance output and correlated to assist in satisfying this requirement. ###### **References** CIS CSC 1, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16 COBIT 5 BAI08.02 ISA 62443-3-3:2013 SR 6.1 ISO/IEC 27001:2013 A.12.4.1, A.16.1.7 NIST SP 800-53 Rev. 4 AU-6, CA-7, IR-4, IR-5, IR-8, SI-4 ### DE.AE-4 #### Requirement Impact of events is determined. #### Solution BloodHound Enterprise will assign a severity rating category and exposure percentage for all identified attack paths within an organizations Active Directory/Azure environment. ###### **References** CIS CSC 4, 6 COBIT 5 APO12.06, DSS03.01 ISO/IEC 27001:2013 A.16.1.4 NIST SP 800-53 Rev. 4 CP-2, IR-4, RA-3, SI-4 ### DE.AE-5 #### Requirement Incident alert thresholds are established. #### Solution BloodHound Enterprise allows Tier Zero to be defined per the needs of the organization in order to correctly define the individual organizations Tier Zero asset group. The Analysis feature will enumerate all detectable identity attack paths and assign and risk exposure rating to each vulnerable identity/asset/object allowing for the establishment of organizational incident alert thresholds. ###### **References** CIS CSC 6, 19 COBIT 5 APO12.06, DSS03.01 ISA 62443-2-1:2009 4.2.3.10 ISO/IEC 27001:2013 A.16.1.4 NIST SP 800-53 Rev. 4 IR-4, IR-5, IR-8 ## Detect(DE) Security Continuous Monitoring(DE.CM) The information system and assets are monitored to identify cybersecurity events and verify the effectiveness of protective measures. ### DE.CM-1 #### Requirement The network is monitored to detect potential cybersecurity events #### Solution BloodHound Enterprise scheduled and on-demand collection scans will gather information in accordance with organizational security policy and report all identity attack path vulnerabilities and misconfigurations found during scan and data analysis actions. ###### **References** CIS CSC 1, 7, 8, 12, 13, 15, 16 COBIT 5 DSS01.03, DSS03.05, DSS05.07 ISA 62443-3-3:2013 SR 6.2 NIST SP 800-53 Rev. 4 AC-2, AU-12, CA-7, CM-3, SC-5, SC-7, SI-4 ### DE.CM-8 #### Requirement Vulnerability Scans are performed. #### Solution BloodHound Enterprise scheduled and on-demand collection scans will gather information in accordance with organizational security policy and report all identity attack path vulnerabilities found during scan and data analysis actions. ###### **References** CIS CSC 4, 20 COBIT 5 BAI03.10, DSS05.01 ISA 62443-2-1:2009 4.2.3.1, 4.2.3.7 ISO/IEC 27001:2013 A.12.6.1 NIST SP 800-53 Rev. 4 RA-5 ## Respond(RS)Analysis(RS.AN) Analysis is conducted to ensure effective response and support recovery activities. ### RS.AN-1 #### Requirement Notifications from detection systems are investigated. #### Solution BloodHound Enterprise includes configurable notifications highlight anomalous identity behavior and group relationships within the organizational environment. Notifications in conjunction with information dashboard supports the rapid identification and mitigation of identity attack paths that can contribute to satisfying this control. ###### **References** CIS CSC 4, 6, 8, 19 COBIT 5 DSS02.04, DSS02.07 ISA 62443-2-1:2009 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8 ISA 62443-3-3:2013 SR 6.1 ISO/IEC 27001:2013 A.12.4.1, A.12.4.3, A.16.1.5 NIST SP 800-53 Rev. 4 AU-6, CA-7, IR-4, IR-5, PE-6, SI-4 ### RS.AN-2 #### Requirement The impact of the incident is understood. #### Solution BloodHound Enterprise detects and assigns risk categories based on the percentage of the organizations environment that is exposed to an identity attack path. Risk categories reflect the percentage of assets within the organizational environment that are vulnerable, aiding analysis in determining the impact of an incident. ###### **References** COBIT 5 DSS02.02 ISA 62443-2-1:2009 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8 ISO/IEC 27001:2013 A.16.1.4, A.16.1.6 NIST SP 800-53 Rev. 4 CP-2, IR-4 ## Respond(RS)Mitigations(RS.MI) Activities are performed to prevent expansion of an event, mitigate its effects, and resolve the incident. ### RS.MI-2 #### Requirement Incidents are mitigated. #### Solution BloodHound Enterprise provides remediation guidance related to scan findings to mitigate the impact of organizational identity attack path exposure. ###### **References** CIS CSC 4, 19 COBIT 5 APO12.06 ISA 62443-2-1:2009 4.3.4.5.6, 4.3.4.5.10 ISO/IEC 27001:2013 A.12.2.1, A.16.1.5 NIST SP 800-53 Rev. 4 IR-4 # BloodHound Enterprise NIST CSF v2 Compliance Resource Source: https://bloodhound.specterops.io/manage-bloodhound/compliance-framework/nist-csf-v2 The Following information is meant to provide a more detailed and in-depth view of compliance items that BloodHound Enterprise can provide coverage for. ## Identify(ID)Asset Management(ID.AM) Assets (e.g., data, hardware, software, systems, facilities, services, people) that enable the organization to achieve business purposes are identified and managed consistent with their relative importance to organizational objectives and the organization’s risk strategy ### ID.AM-01 ##### Requirement Inventories of hardware managed by the organization are maintained. ##### Solution BloodHound Enterprise collects information on all physical systems operating within a Windows Active Directory environment/Azure Environment. BloodHound Enterprise monitors the addition/removal of physical assets connecting to the organizations environment. ##### **References/Previous Versions** NIST Cybersecurity Framework v1.1: ID.AM-1: Physical devices and systems within the organization are inventoried ### ID.AM-02[](#ID.AM-02) ##### Requirement Inventories of software, services, and systems managed by the organization are maintained ##### Solution BloodHound Enterprise collects information on all Systems in a domain that are connected to the organizations Active Directory/Azure Environment. BloodHound Enterprise monitors the environment for the addition/removal of systems from the organizations environment. ##### **References** NIST Cybersecurity Framework v1.1: ID.AM-2: Software platforms and applications within the organization are inventoried ### ID.AM-05 ##### Requirement Assets are prioritized based on classification, criticality, resources, and impact on the mission ##### Solution BloodHound Enterprise allows organizations to assign assets to Tier Zero (T0) based on the organizations classification, criticality, and business value. Prioritized resources are audited and accounted for during BloodHound Enterprise collection scans. ##### **References** NIST Cybersecurity Framework v1.1: ID.AM-5: Resources (e.g., hardware, devices, data, time, personnel, and software) are prioritized based on their classification, criticality, and business value ## Identity(ID)Risk Assessment(ID.RA) The organization understands the cybersecurity risk to organizational operations (including mission, functions, image, or reputation), organizational assets, and individuals. ### ID.RA-01 ##### Requirement Vulnerabilities in assets are identified, validated, and recorded ##### Solution BloodHound Enterprise analyzes the Active Directory/Azure environment for identity attack paths that potentially impact an organizations security posture. All Identity vulnerabilities are identified during BloodHound collection activities and presented in the reporting dashboard with additional information to support documenting threats. ##### **References** NIST Cybersecurity Framework v1.1: ID.RA-1: Asset vulnerabilities are identified and documented ### ID.RA-03 ##### Requirement Internal and external threats to the organization are identified and recorded. ##### Solution BloodHound Enterprise analyzes the Active Directory/Azure environment for identity attack paths that potentially impact an organizations security posture. All Identity threat vectors are identified during BloodHound collection activities and presented in the reporting dashboard with additional information to support documenting threats. ##### **References** NIST Cybersecurity Framework v1.1: ID.RA-3: Threats, both internal and external, are identified and documented ### ID.RA-05 ##### Requirement Threats, vulnerabilities, likelihoods, and impacts are used to understand inherent risk and inform risk response prioritization. ##### Solution BloodHound Enterprise analyzes the organizational environment for identity attack path vectors and assigns a quantifiable risk metric and category to each detected identity attack path. The assigned risk metric is calculated by determining what percentage of the environment could be impacted by a specific identity vulnerability which will be quantified as percentage and assigned a criticality rating. ##### **References** NIST Cybersecurity Framework v1.1: ID.RA-5: Threats, vulnerabilities, likelihoods, and impacts are used to determine risk ## Protect(PR) Identity Management, Authentication, and Access Control (PR.AA) Access to physical and logical assets and associated facilities is limited to authorized users, processes, and devices, and is managed consistent with the assessed risk of unauthorized access to authorized activities and transactions. ### PR.AA-5 #### Requirement Access permissions, entitlements, and authorizations are defined in a policy, managed, enforced, and reviewed, and incorporate the principles of least privilege and separation of duties #### Solution BloodHound Enterprise audits and reports the health of organizational privilege access models and identifies potential vulnerable attack paths and misconfigurations within the privilege access architecture scheme. ##### **References** NIST Cybersecurity Framework v1.1: PR.AC-1: Identities and credentials are issued, managed, verified, revoked, and audited for authorized devices, users and processes NIST Cybersecurity Framework v1.1: PR.AC-3: Remote access is managed NIST Cybersecurity Framework v1.1: PR.AC-4: Access permissions and authorizations are managed, incorporating the principles of least privilege and separation of duties. CSC v8 : 3.3, 6.8 ## Protect(PR) Platform Security(PR.PS) The hardware, software (e.g., firmware, operating systems, applications), and services of physical and virtual platforms are managed consistent with the organization’s risk strategy to protect their confidentiality, integrity, and availability ### PR.PS-01 #### Requirement Configuration management practices are established and applied #### Solution BloodHound Enterprise’s data collection activities gathers and audits asset configurations for identity attack path analysis. The collected data can be used to validate configuration architecture throughout the Active Directory/Azure environment. BloodHound Enterprise automatically highlights misconfigurations in your environment and assigns them a quantifiable risk metric and criticality rating based on the level of exposure detected. ##### **References** NIST Cybersecurity Framework v1.1: PR.IP-1: A baseline configuration of information technology/industrial control systems is created and maintained incorporating security principles (e.g. concept of least functionality) NIST Cybersecurity Framework v1.1: PR.IP-3: Configuration change control processes are in place NIST Cybersecurity Framework v1.1: PR.PT-2: Removable media is protected and its use restricted according to policy NIST Cybersecurity Framework v1.1: PR.PT-3: The principle of least functionality is incorporated by configuring systems to provide only essential capabilities CSC v8: 4.1, 4.2 ## Detect(PR) Adverse Event Analysis(DE.AE) Anomalies, indicators of compromise, and other potentially adverse events are analyzed to characterize the events and detect cybersecurity incidents ### DE.AE-02 #### Requirement Potentially adverse events are analyzed to better understand associated activities #### Solution BloodHound Enterprise reports identity attack paths and assigns a risk exposure severity rating to each vector based on the percentage of the organizations environment that is exposed to risk. Additional information pertaining to specific events is included in the BloodHound Enterprise GUI. ##### **References** NIST Cybersecurity Framework v1.1: DE.AE-2: Detected events are analyzed to understand attack targets and methods CSC v8: 8.11 ### DE.AE-04 #### Requirement The estimated impact and scope of adverse events are understood. #### Solution BloodHound Enterprise reports identity attack paths and assigns a risk exposure severity rating to each vector based on the percentage of the organizations environment that is exposed to risk. Additional information pertaining to specific events is included in the BloodHound Enterprise GUI. ##### **References** NIST Cybersecurity Framework v1.1: DE.AE-4: Impact of events is determined ### DE.AE-08 #### Requirement Incidents are declared when adverse events meet the defined incident criteria. #### Solution BloodHound Enterprise allows Tier Zero to be defined per the needs of the organization in order to correctly define the individual organizations Tier Zero asset group. During analysis, BloodHound Enterprise will enumerate all detectable identity attack paths and assign and risk exposure rating to each vulnerable identity/asset/object allowing for the establishment of organizational incident alert thresholds and aid in the definition of incident alert criteria. ##### **References** NIST Cybersecurity Framework v1.1: DE.AE-5: Incident alert thresholds are established. ## Detect(DE) Adverse Event Analysis(DE.CM) Assets are monitored to find anomalies, indicators of compromise, and other potentially adverse events. ### DE.CM-01 #### Requirement Networks and network services are monitored to find potentially adverse events. #### Solution BloodHound Enterprise collects information on all physical systems and Active Directory/Azure users operating within a Windows Active Directory environment/Azure Environment. BloodHound Enterprises configurable scan options allows organizations to establish and monitor their organizational baseline of systems, users, and groups and monitor that baseline via the reporting dashboard to identify adverse and unsafe events. ##### **References** NIST Cybersecurity Framework v2.0: DE.CM-01: Networks and network services are monitored to find potentially adverse events ### DE.CM-09 #### Requirement Computing hardware and software, runtime environments, and their data are monitored to find potentially adverse events #### Solution BloodHound Enterprise collects information on all Active Directory/Azure systems operating within a Windows Active Directory environment/Azure Environment. BloodHound Enterprise monitors the various assets for trust violations and other identity based events. ##### **References** Subcategory is new to this version of the framework and incorporates the following items from the previous version: PR.DS-6: Integrity checking mechanisms are used to verify software, firmware, and information integrity PR.DS-8: Integrity checking mechanisms are used to verify hardware integrity DE.CM-4: Malicious code is detected DE.CM-5: Unauthorized mobile code is detected DE.CM-7: Monitoring for unauthorized personnel, connections, devices, and software is performed. ## Risk(RS) Incident Analysis(RS.AN) Assets are monitored to find anomalies, indicators of compromise, and other potentially adverse events. ### RS.AN-03 #### Requirement Analysis is performed to establish what has taken place during an incident and the root cause of the incident. #### Solution BloodHound Enterprise collects information on all physical systems and Active Directory/Azure users operating within a Windows Active Directory environment/Azure Environment. BloodHound Enterprises configurable scan options and reporting features provide insights for determining the impact of an incident and understanding the root cause. ##### **References** NIST Cybersecurity Framework v1.1: RS.AN-3: Forensics are performed ### RS.AN-08 #### Requirement An incident’s magnitude is estimated and validated #### Solution BloodHound Enterprise will audit all identities and objects within your AD environment/Azure environment and provide risk metrics quantifying exposure to identity vulnerabilities as part of your incident validation and estimation activities. ##### **References** NIST Special Publication 800-53 Revision 5: IR-4, IR-8, RA-3. RA-7 ## Risk(RS) Incident Mitigation(RS.MI) Assets are monitored to find anomalies, indicators of compromise, and other potentially adverse events and remediation guidance is provided to mitigate incidents when risk is detected. ### RS.MI-02 #### Requirement Mitigation is performed to restore what has taken place during an incident and address root cause of the incident. #### Solution BloodHound Enterprise collects information on all physical systems and Active Directory/Azure users operating within a Windows Active Directory environment/Azure Environment. BloodHound Enterprises configurable scan options and reporting features provide insights for determining the impact of an incident and understanding the root cause. BloodHound Enterprise provides actionable remediation guidance which enables analysts and responders to proactively prevent and mitigate incidents as they are discovered. ##### **References** NIST Cybersecurity Framework v1.1: RS.AN-3: Forensics are performed # BloodHound Enterprise NIST SP 800-171 Compliance Resource Source: https://bloodhound.specterops.io/manage-bloodhound/compliance-framework/nist-sp-800-171 The Following information is meant to provide a more detailed and in-depth view of compliance items that BloodHound Enterprise can assist in providing coverage for. ## 3.1 - ACCESS CONTROL ### 3.1.1 #### Basic Requirement Limit system access to authorized users, processes acting on behalf of authorized users, and devices (including other systems). **3.1.1 Discussion** Access control policies (e.g., identity- or role-based policies, control matrices, and cryptography) control access between active entities or subjects (i.e., users or processes acting on behalf of users) and passive entities or objects (e.g., devices, files, records, and domains) in systems. Access enforcement mechanisms can be employed at the application and service level to provide increased information security. Other systems include systems internal and external to the organization. This requirement focuses on account management for systems and applications. The definition of and enforcement of access authorizations, other than those determined by account type (e.g., privileged verses non-privileged) are addressed in requirement 3.1.2. #### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access levels and validate access permissions. ### 3.1.2 #### Basic Requirement Limit system access to the types of transactions and functions that authorized users are permitted to execute. **3.1.2 Discussion** Organizations may choose to define access privileges or other attributes by account, by type of account, or a combination of both. System account types include individual, shared, group, system, anonymous, guest, emergency, developer, manufacturer, vendor, and temporary. Other attributes required for authorizing access include restrictions on time-of-day, day-of-week, and point-of- origin. In defining other account attributes, organizations consider system-related requirements (e.g., system upgrades scheduled maintenance,) and mission or business requirements, (e.g., time zone differences, customer requirements, remote access to support travel requirements). #### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access levels and validate access permissions. ### 3.1.5 #### Basic Requirement Employ the principle of least privilege, including for specific security functions and privileged accounts. **3.1.5 Discussion** Organizations employ the principle of least privilege for specific duties and authorized accesses for users and processes. The principle of least privilege is applied with the goal of authorized privileges no higher than necessary to accomplish required organizational missions or business functions. Organizations consider the creation of additional processes, roles, and system accounts as necessary, to achieve least privilege. Organizations also apply least privilege to the development, implementation, and operation of organizational systems. Security functions include establishing system accounts, setting events to be logged, setting intrusion detection parameters, and configuring access authorizations (i.e., permissions, privileges). Privileged accounts, including super user accounts, are typically described as system administrator for various types of commercial off-the-shelf operating systems. Restricting privileged accounts to specific personnel or roles prevents day-to-day users from having access to privileged information or functions. Organizations may differentiate in the application of this requirement between allowed privileges for local accounts and for domain accounts provided organizations retain the ability to control system configurations for key security parameters and as otherwise necessary to sufficiently mitigate risk. #### Solution BloodHound Enterprise audits and reports the health of organizational privilege access models and identifies potential vulnerable attack paths and misconfigurations within the privilege access architecture scheme. ### 3.1.6 #### Basic Requirement Use non-privileged accounts or roles when accessing non-security functions. **3.1.6 Discussion** This requirement limits exposure when operating from within privileged accounts or roles. The inclusion of roles addresses situations where organizations implement access control policies such as role-based access control and where a change of role provides the same degree of assurance in the change of access authorizations for the user and all processes acting on behalf of the user as would be provided by a change between a privileged and non-privileged account. #### Solution BloodHound Enterprise audits and reports the health of organizational privilege access models and identifies potential vulnerable attack paths and misconfigurations within the privilege access architecture scheme. ### 3.1.7 #### Basic Requirement Prevent non-privileged users from executing privileged functions and capture the execution of such functions in audit logs. **3.1.7 Discussion** Privileged functions include establishing system accounts, performing system integrity checks, conducting patching operations, or administering cryptographic key management activities. Non- privileged users are individuals that do not possess appropriate authorizations. Circumventing intrusion detection and prevention mechanisms or malicious code protection mechanisms are examples of privileged functions that require protection from non-privileged users. Note that this requirement represents a condition to be achieved by the definition of authorized privileges in 3.1.2. #### Solution BloodHound Enterprise audits and reports the health of organizational privilege access models and identifies potential vulnerable attack paths and misconfigurations within the privilege access architecture scheme when used in conjunction with external logging solutions to satisfy this requirement. ## 3.3 - AUDIT AND ACCOUNTABILITY ### 3.3.1 #### Basic Requirement Create and retain system audit logs and records to the extent needed to enable the monitoring, analysis, investigation, and reporting of unlawful or unauthorized system activity. **3.3.1 Discussion** An event is any observable occurrence in a system, which includes unlawful or unauthorized system activity. Organizations identify event types for which a logging functionality is needed as those events which are significant and relevant to the security of systems and the environments in which those systems operate to meet specific and ongoing auditing needs. Event types can include password changes, failed logons or failed accesses related to systems, administrative privilege usage, or third-party credential usage. In determining event types that require logging, organizations consider the monitoring and auditing appropriate for each of the CUI security requirements. Monitoring and auditing requirements can be balanced with other system needs. For example, organizations may determine that systems must have the capability to log every file access both successful and unsuccessful, but not activate that capability except for specific circumstances due to the potential burden on system performance. Audit records can be generated at various levels of abstraction, including at the packet level as information traverses the network. Selecting the appropriate level of abstraction is a critical aspect of an audit logging capability and can facilitate the identification of root causes to problems. Organizations consider in the definition of event types, the logging necessary to cover related events such as the steps in distributed, transaction-based processes (e.g., processes that are distributed across multiple organizations) and actions that occur in service-oriented or cloud- based architectures. Audit record content that may be necessary to satisfy this requirement includes time stamps, source and destination addresses, user or process identifiers, event descriptions, success or fail indications, filenames involved, and access control or flow control rules invoked. Event outcomes can include indicators of event success or failure and event-specific results (e.g., the security state of the system after the event occurred). Detailed information that organizations may consider in audit records includes full text recording of privileged commands or the individual identities of group account users. Organizations consider limiting the additional audit log information to only that information explicitly needed for specific audit requirements. This facilitates the use of audit trails and audit logs by not including information that could potentially be misleading or could make it more difficult to locate information of interest. Audit logs are reviewed and analyzed as often as needed to provide important information to organizations to facilitate risk-based decision making. \[SP 800-92] provides guidance on security log management. #### Solution BloodHound Enterprise audits and reports the health of organizational privilege access models and identifies potential vulnerable attack paths and misconfigurations within the privilege access architecture scheme when used in conjunction with external logging solutions to satisfy this requirement. ### 3.3.2 #### Basic Requirement Ensure that the actions of individual system users can be uniquely traced to those users, so they can be held accountable for their actions. **3.3.2 Discussion** This requirement ensures that the contents of the audit record include the information needed to link the audit event to the actions of an individual to the extent feasible. Organizations consider logging for traceability including results from monitoring of account usage, remote access, wireless connectivity, mobile device connection, communications at system boundaries, configuration settings, physical access, nonlocal maintenance, use of maintenance tools, temperature and humidity, equipment delivery and removal, system component inventory, use of mobile code, and use of Voice over Internet Protocol (VoIP). #### Solution BloodHound Enterprise collects and analyses information on all objects with in the Active Directory/Azure environment. Collected data can be used to examine the scope of access that individual network users are granted. When used with native system logs, BloodHound Enterprise can aid in activity attribution and auditing. ### 3.3.5 #### Basic Requirement Correlate audit record review, analysis, and reporting processes for investigation and response to indications of unlawful, unauthorized, suspicious, or unusual activity **3.3.5 Discussion** Correlating audit record review, analysis, and reporting processes helps to ensure that they do not operate independently, but rather collectively. Regarding the assessment of a given organizational system, the requirement is agnostic as to whether this correlation is applied at the system level or at the organization level across all systems. #### Solution BloodHound Enterprise detects and assigns risk categories based on the percentage of the organizations environment that is exposed to an identity attack path. Risk categories reflect the percentage of assets within the organizational environment that are vulnerable, aiding analysis in determining the impact of an incident. Impact scores, scope of access, and native system logs can be used as compensating controls to satisfy this requirement. ## 3.4 - CONFIGURATION MANAGEMENT ### 3.4.1 #### Basic Requirement Establish and maintain baseline configurations and inventories of organizational systems (including hardware, software, firmware, and documentation) throughout the respective system development life cycles **3.4.1 Discussion** Baseline configurations are documented, formally reviewed, and agreed-upon specifications for systems or configuration items within those systems. Baseline configurations serve as a basis for future builds, releases, and changes to systems. Baseline configurations include information about system components (e.g., standard software packages installed on workstations, notebook computers, servers, network components, or mobile devices; current version numbers and update and patch information on operating systems and applications; and configuration settings and parameters), network topology, and the logical placement of those components within the system architecture. Baseline configurations of systems also reflect the current enterprise architecture. Maintaining effective baseline configurations requires creating new baselines as organizational systems change over time. Baseline configuration maintenance includes reviewing and updating the baseline configuration when changes are made based on security risks and deviations from the established baseline configuration Organizations can implement centralized system component inventories that include components from multiple organizational systems. In such situations, organizations ensure that the resulting inventories include system-specific information required for proper component accountability (e.g., system association, system owner). Information deemed necessary for effective accountability of system components includes hardware inventory specifications, software license information, software version numbers, component owners, and for networked components or devices, machine names and network addresses. Inventory specifications include manufacturer, device type, model, serial number, and physical location. \[SP 800-128] provides guidance on security-focused configuration management. #### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound Enterprise’s initial collection and scheduled collections can be used to establish and monitor your organizations identity baseline. ### 3.4.5 #### Basic Requirement Define, document, approve, and enforce physical and logical access restrictions associated with changes to organizational systems **3.4.5 Discussion** Any changes to the hardware, software, or firmware components of systems can potentially have significant effects on the overall security of the systems. Therefore, organizations permit only qualified and authorized individuals to access systems for purposes of initiating changes, including upgrades and modifications. Access restrictions for change also include software libraries. Access restrictions include physical and logical access control requirements, workflow automation, media libraries, abstract layers (e.g., changes implemented into external interfaces rather than directly into systems), and change windows (e.g., changes occur only during certain specified times). In addition to security concerns, commonly-accepted due diligence for configuration management includes access restrictions as an essential part in ensuring the ability to effectively manage the configuration. \[SP 800-128] provides guidance on configuration change control. #### Solution BloodHound Enterprise collects information on all systems, users, and objects in a domain that are connected to the organizations Active Directory/Azure Environment. BloodHound Enterprise monitors the environment for the addition/removal of systems from the organizations environment. ### 3.4.6 #### Basic Requirement Employ the principle of least functionality by configuring organizational systems to provide only essential capabilities **3.4.6 Discussion** Systems can provide a wide variety of functions and services. Some of the functions and services routinely provided by default, may not be necessary to support essential organizational missions, functions, or operations. It is sometimes convenient to provide multiple services from single system components. However, doing so increases risk over limiting the services provided by any one component. Where feasible, organizations limit component functionality to a single function per component. Organizations review functions and services provided by systems or components of systems, to determine which functions and services are candidates for elimination. Organizations disable unused or unnecessary physical and logical ports and protocols to prevent unauthorized connection of devices, transfer of information, and tunneling. Organizations can utilize network scanning tools, intrusion detection and prevention systems, and end-point protections such as firewalls and host-based intrusion detection systems to identify and prevent the use of prohibited functions, ports, protocols, and services #### Solution BloodHound Enterprise audits and reports the health of organizational privilege access models and identifies potential vulnerable attack paths and misconfigurations within the privilege access architecture scheme. ## 3.6 - INCIDENT RESPONSE ### 3.6.1 #### Basic Requirement Establish an operational incident-handling capability for organizational systems that includes preparation, detection, analysis, containment, recovery, and user response activities **3.6.1 Discussion** Organizations recognize that incident handling capability is dependent on the capabilities of organizational systems and the mission/business processes being supported by those systems. Organizations consider incident handling as part of the definition, design, and development of mission/business processes and systems. Incident-related information can be obtained from a variety of sources including audit monitoring, network monitoring, physical access monitoring, user and administrator reports, and reported supply chain events. Effective incident handling capability includes coordination among many organizational entities including mission/business owners, system owners, authorizing officials, human resources offices, physical and personnel security offices, legal departments, operations personnel, procurement offices, and the risk executive. As part of user response activities, incident response training is provided by organizations and is linked directly to the assigned roles and responsibilities of organizational personnel to ensure that the appropriate content and level of detail is included in such training. For example, regular users may only need to know who to call or how to recognize an incident on the system; system administrators may require additional training on how to handle or remediate incidents; and incident responders may receive more specific training on forensics, reporting, system recovery, and restoration. Incident response training includes user training in the identification/reporting of suspicious activities from external and internal sources. User response activities also includes incident response assistance which may consist of help desk support, assistance groups, and access to forensics services or consumer redress services, when required. \[SP 800-61] provides guidance on incident handling. \[SP 800-86] and \[SP 800-101] provide guidance on integrating forensic techniques into incident response. \[SP 800-161] provides guidance on supply chain risk managemen #### Solution BloodHound Enterprise provides visibility into the logical relationships and access scope for an organizational Active Directory/Azure environments. Proactively monitoring the organizational environment in conjunction with native security tools contributes to satisfying this requirement. ### 3.6.2 #### Basic Requirement Track, document, and report incidents to designated officials and/or authorities both internal and external to the organization. **3.6.2 Discussion** Tracking and documenting system security incidents includes maintaining records about each incident, the status of the incident, and other pertinent information necessary for forensics, evaluating incident details, trends, and handling. Incident information can be obtained from a variety of sources including incident reports, incident response teams, audit monitoring, network monitoring, physical access monitoring, and user/administrator reports. Reporting incidents addresses specific incident reporting requirements within an organization and the formal incident reporting requirements for the organization. Suspected security incidents may also be reported and include the receipt of suspicious email communications that can potentially contain malicious code. The types of security incidents reported, the content and timeliness of the reports, and the designated reporting authorities reflect applicable laws, Executive Orders, directives, regulations, and policies. \[SP 800-61] provides guidance on incident handling. #### Solution BloodHound Enterprise provides visibility into the logical relationships and access scope for an organizational Active Directory/Azure environments. Proactively monitoring the organizational environment in conjunction with native security tools contributes to satisfying this requirement. ## 3.11 - RISK ASSESSMENT ### 3.11.1 #### Basic Requirement Periodically assess the risk to organizational operations (including mission, functions, image, or reputation), organizational assets, and individuals, resulting from the operation of organizational systems and the associated processing, storage, or transmission of CUI. **3.11.1 Discussion** Clearly defined system boundaries are a prerequisite for effective risk assessments. Such risk assessments consider threats, vulnerabilities, likelihood, and impact to organizational operations, organizational assets, and individuals based on the operation and use of organizational systems. Risk assessments also consider risk from external parties (e.g., service providers, contractors operating systems on behalf of the organization, individuals accessing organizational systems, outsourcing entities). Risk assessments, either formal or informal, can be conducted at the organization level, the mission or business process level, or the system level, and at any phase in the system development life cycle. \[SP 800-30] provides guidance on conducting risk assessments. #### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound Enterprise’s scheduled collection feature is designed to continuously monitor your environment for Tier Zero risk exposure. ### 3.11.2 #### Basic Requirement Scan for vulnerabilities in organizational systems and applications periodically and when new vulnerabilities affecting those systems and applications are identified. **3.11.2 Discussion** Organizations determine the required vulnerability scanning for all system components, ensuring that potential sources of vulnerabilities such as networked printers, scanners, and copiers are not overlooked. The vulnerabilities to be scanned are readily updated as new vulnerabilities are discovered, announced, and scanning methods developed. This process ensures that potential vulnerabilities in the system are identified and addressed as quickly as possible. Vulnerability analyses for custom software applications may require additional approaches such as static analysis, dynamic analysis, binary analysis, or a hybrid of the three approaches. Organizations can employ these analysis approaches in source code reviews and in a variety of tools (e.g., static analysis tools, web-based application scanners, binary analyzers) and in source code reviews. Vulnerability scanning includes: scanning for patch levels; scanning for functions, ports, protocols, and services that should not be accessible to users or devices; and scanning for improperly configured or incorrectly operating information flow control mechanisms. To facilitate interoperability, organizations consider using products that are Security Content Automated Protocol (SCAP)-validated, scanning tools that express vulnerabilities in the Common Vulnerabilities and Exposures (CVE) naming convention, and that employ the Open Vulnerability Assessment Language (OVAL) to determine the presence of system vulnerabilities. Sources for vulnerability information include the Common Weakness Enumeration (CWE) listing and the National Vulnerability Database (NVD). Security assessments, such as red team exercises, provide additional sources of potential vulnerabilities for which to scan. Organizations also consider using scanning tools that express vulnerability impact by the Common Vulnerability Scoring System (CVSS). In certain situations, the nature of the vulnerability scanning may be more intrusive or the system component that is the subject of the scanning may contain highly sensitive information. Privileged access authorization to selected system components facilitates thorough vulnerability scanning and protects the sensitive nature of such scanning. \[SP 800-40] provides guidance on vulnerability management. #### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound Enterprise’s scheduled collection feature is designed to continuously monitor your environment for Tier Zero risk exposure. ### 3.11.3 #### Requirement Remediate vulnerabilities in accordance with risk assessments. **3.11.3 Discussion** Vulnerabilities discovered, for example, via the scanning conducted in response to 3.11.2, are remediated with consideration of the related assessment of risk. The consideration of risk influences the prioritization of remediation efforts and the level of effort to be expended in the remediation for specific vulnerabilities. #### Solution BloodHound Enterprise provides remediation guidance related to scan findings to mitigate the impact of organizational identity attack path exposure. ## 3.12 - SECURITY ASSESSMENT ### 3.12.1 #### Basic Requirement Periodically assess the security controls in organizational systems to determine if the controls are effective in their application. **3.12.1 Discussion** Organizations assess security controls in organizational systems and the environments in which those systems operate as part of the system development life cycle. Security controls are the safeguards or countermeasures organizations implement to satisfy security requirements. By assessing the implemented security controls, organizations determine if the security safeguards or countermeasures are in place and operating as intended. Security control assessments ensure that information security is built into organizational systems; identify weaknesses and deficiencies early in the development process; provide essential information needed to make risk-based decisions; and ensure compliance to vulnerability mitigation procedures. Assessments are conducted on the implemented security controls as documented in system security plans. Security assessment reports document assessment results in sufficient detail as deemed necessary by organizations, to determine the accuracy and completeness of the reports and whether the security controls are implemented correctly, operating as intended, and producing the desired outcome with respect to meeting security requirements. Security assessment results are provided to the individuals or roles appropriate for the types of assessments being conducted. Organizations ensure that security assessment results are current, relevant to the determination of security control effectiveness, and obtained with the appropriate level of assessor independence. Organizations can choose to use other types of assessment activities such as vulnerability scanning and system monitoring to maintain the security posture of systems during the system life cycle. \[SP 800-53] provides guidance on security and privacy controls for systems and organizations. \[SP 800-53A] provides guidance on developing security assessment plans and conducting assessments. #### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound Enterprise’s scheduled collection feature is designed to continuously monitor your environment for Tier Zero risk exposure. BloodHound Enterprise’s reporting feature can be used to asses the effectiveness identity and access control systems. ### 3.12.2 #### Basic Requirement Develop and implement plans of action designed to correct deficiencies and reduce or eliminate vulnerabilities in organizational systems. **3.12.2 Discussion** The plan of action is a key document in the information security program. Organizations develop plans of action that describe how any unimplemented security requirements will be met and how any planned mitigations will be implemented. Organizations can document the system security plan and plan of action as separate or combined documents and in any chosen format. Federal agencies may consider the submitted system security plans and plans of action as critical inputs to an overall risk management decision to process, store, or transmit CUI on a system hosted by a nonfederal organization and whether it is advisable to pursue an agreement or contract with the nonfederal organization. \[NIST CUI] provides supplemental material for Special Publication 800-171 including templates for plans of action. #### Solution BloodHound Enterprise provides remediation guidance related to scan findings to mitigate the impact of organizational identity attack path exposure. Findings are given a criticality rating based on identity exposure contributing to the prioritization of remedial actions when executing a response plan. ### 3.12.3 #### Basic Requirement Monitor security controls on an ongoing basis to ensure the continued effectiveness of the controls. **3.12.3 Discussion** Continuous monitoring programs facilitate ongoing awareness of threats, vulnerabilities, and information security to support organizational risk management decisions. The terms continuous and ongoing imply that organizations assess and analyze security controls and information security-related risks at a frequency sufficient to support risk-based decisions. The results of continuous monitoring programs generate appropriate risk response actions by organizations. Providing access to security information on a continuing basis through reports or dashboards gives organizational officials the capability to make effective and timely risk management decisions. Automation supports more frequent updates to hardware, software, firmware inventories, and other system information. Effectiveness is further enhanced when continuous monitoring outputs are formatted to provide information that is specific, measurable, actionable, relevant, and timely. Monitoring requirements, including the need for specific monitoring, may also be referenced in other requirements. \[SP 800-137] provides guidance on continuous monitoring. #### Solution BloodHound Enterprise scheduled and on-demand collection scans will gather information in accordance with organizational security policy and report all identity attack path vulnerabilities and misconfigurations found during scan and data analysis actions ## 3.14 - SYSTEM AND INFORMATION INTEGRITY ### 3.14.1 #### Basic Requirement Identify, report, and correct system flaws in a timely manner. **3.14.1 Discussion** Organizations identify systems that are affected by announced software and firmware flaws including potential vulnerabilities resulting from those flaws and report this information to designated personnel with information security responsibilities. Security-relevant updates include patches, service packs, hot fixes, and anti-virus signatures. Organizations address flaws discovered during security assessments, continuous monitoring, incident response activities, and system error handling. Organizations can take advantage of available resources such as the Common Weakness Enumeration (CWE) database or Common Vulnerabilities and Exposures (CVE) database in remediating flaws discovered in organizational systems. Organization-defined time periods for updating security-relevant software and firmware may vary based on a variety of factors including the criticality of the update (i.e., severity of the vulnerability related to the discovered flaw). Some types of flaw remediation may require more testing than other types of remediation. \[SP 800-40] provides guidance on patch management technologies. #### Solution BloodHound Enterprise scheduled and on-demand collection scans will gather information in accordance with organizational security policy and report all identity attack path vulnerabilities and misconfigurations found during scan and data analysis actions. ### 3.14.2 #### Basic Requirement Provide protection from malicious code at designated locations within organizational systems. **3.14.2 Discussion** Designated locations include system entry and exit points which may include firewalls, remote- access servers, workstations, electronic mail servers, web servers, proxy servers, notebook computers, and mobile devices. Malicious code includes viruses, worms, Trojan horses, and spyware. Malicious code can be encoded in various formats (e.g., UUENCODE, Unicode), contained within compressed or hidden files, or hidden in files using techniques such as steganography. Malicious code can be inserted into systems in a variety of ways including web accesses, electronic mail, electronic mail attachments, and portable storage devices. Malicious code insertions occur through the exploitation of system vulnerabilities. Malicious code protection mechanisms include anti-virus signature definitions and reputation- based technologies. A variety of technologies and methods exist to limit or eliminate the effects of malicious code. Pervasive configuration management and comprehensive software integrity controls may be effective in preventing execution of unauthorized code. In addition to commercial off-the-shelf software, malicious code may also be present in custom-built software. This could include logic bombs, back doors, and other types of cyber-attacks that could affect organizational missions/business functions. Traditional malicious code protection mechanisms cannot always detect such code. In these situations, organizations rely instead on other safeguards including secure coding practices, configuration management and control, trusted procurement processes, and monitoring practices to help ensure that software does not perform functions other than the functions intended. \[SP 800-83] provides guidance on malware incident prevention. #### Solution BloodHound Enterprise scheduled and on-demand collection scans will gather information in accordance with organizational security policy and report all identity attack path vulnerabilities and misconfigurations found during scan and data analysis actions. BloodHound Enterprise can be configured to monitor organizational specific assets and provide proactive risk assessment with associated assets as part of scheduled collection scans to satisfy this requirement. # BloodHound Enterprise NIST SP 800-53 Rev.8 Compliance Resource Source: https://bloodhound.specterops.io/manage-bloodhound/compliance-framework/nist-sp-800-53 The Following information is meant to provide a more detailed and in-depth view of compliance items that BloodHound Enterprise can provide coverage for. ## AC-2 - Account Management ### Summary Accounts are assigned, managed, and maintained in accordance with organizational policy ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit and verify access levels for users and groups throughout the enterprise. Related Controls: IA-1, PM-9, PM-24, PS-8, SI-12. **References** OMB A-130, SP 800-12, SP 800-30, SP 800-39, SP 800-100, IR 7874 ## AC-3 - Access Enforcement ### Summary Enforce approved authorizations for logical access to information and system resources in accordance with applicable access control policies. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access levels and validate access enforcement controls. #### **References** OMB A-130, SP 800-12, SP 800-30, SP 800-39, SP 800-100, IR 7874 ## AC-4 - Information Flow Enforcement ### Summary Enforce approved authorizations for controlling the flow of information within the system and between connected systems based on organization-defined information flow control policies. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. The logical relationships between AD/Azure objects and Tier Zero assets aid in validating information flow enforcement architecture. #### **References** SP 800-12, SP 800-30, SP 800-39 ## CA-2 - Security Assessments ### Summary Security Assessments, mandates regular evaluations of security controls within a system to verify their effectiveness and correct implementation. These assessments, which should occur periodically and after significant system changes, involve examining documentation, interviewing personnel, and technical testing. The findings must be documented and reviewed by organizational officials to guide corrective actions. Additionally, independent assessments by external parties are recommended to ensure an unbiased perspective on the security posture. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. Dashboard and reporting features in BloodHound Enterprise provide continuous evaluation of relationships in your environment and provide actionable data in support of organizational security assessment activities and policies. #### **References** SP 800-12, SP 800-30, SP 800-39 ## CA-3 - System Interconnections ### Summary System Interconnections, requires the management, approval, and monitoring of connections between different systems. This control emphasizes establishing and documenting agreements for interfacing systems, assessing security risks associated with these interconnections, and ensuring compliance with relevant security requirements. Organizations must maintain an inventory of all interconnections and regularly review and update the security controls associated with them to mitigate any potential security risks ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound Enterprises dashboard and reports both illustrate the logical system interconnections within the environment and evaluates and reports any discovered risk. #### **References** SP 800-12, SP 800-30, SP 800-39 ## CA-7 - Continuous Monitoring ### Summary Continuous Monitoring, mandates the establishment of a continuous monitoring strategy to maintain the security of systems and environments. This strategy should include defining the frequency and scope of monitoring to ensure ongoing awareness of security controls' effectiveness. Organizations are required to deploy automated tools to support real-time analysis and reporting of security alerts. The results of continuous monitoring must be reviewed and used to respond to risks in a timely manner. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound Enterprise’s scheduled collection feature is designed to continuously monitor your environment for Tier Zero risk exposure. #### **References** SP 800-12, SP 800-30, SP 800-39 ## CA-8-Penetration Testing ### Summary Penetration Testing, involves conducting simulated attacks on systems to identify vulnerabilities and assess the effectiveness of existing security controls. This control requires organizations to plan and execute regular penetration testing based on documented procedures that define the scope, testing methods, and evaluation criteria. The results should be analyzed to determine system weaknesses and develop strategies for mitigation. Regular updates and improvements to the security posture are essential following these tests. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. BloodHound Enterprise provides actionable intelligence on the risks present in your environment which can both aid penetration test assessment functions and activities. #### **References** SP 800-12, SP 800-30, SP 800-39 ## CM-2-Baseline Configuration ### Summary Baseline Configuration, mandates the development, documentation, and maintenance of a baseline configuration for organizational systems. This baseline serves as a standard for proper system configuration and includes information on system components, security controls, and user-accessible functions. Organizations are required to review and update the baseline regularly, ensuring that any deviations are authorized, documented, and justified. This control is crucial for maintaining the integrity and security of system configurations over time. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound Enterprise’s initial collection and scheduled collections can be used to establish and monitor your organizations identity baseline. #### **References** SP 800-12, SP 800-30, SP 800-39 ## CM-8-Information System Component Inventory ### Summary Information System Component Inventory, requires organizations to maintain an accurate, up-to-date inventory of all system components that are within the authorization boundary of the information systems. This inventory should include details like the component's identification, version, and configuration. The control emphasizes the need to verify the presence of authorized components and detect unauthorized components to ensure the integrity of the system. Regular reviews and updates of the inventory are mandatory to reflect changes due to system modifications or upgrades. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. #### **References** SP 800-12, SP 800-30, SP 800-39 ## CP-2-Contingency Plan ### Summary Contingency Plan, requires organizations to develop, document, and implement plans to recover and restore organizational IT system functionalities in the event of a disruption, compromise, or failure. The contingency plans must be coordinated with organizational emergency plans, regularly reviewed and updated, and communicated to relevant personnel. Organizations must also test the plans to ensure they are effective and feasible, using tests that reflect realistic conditions to identify potential weaknesses in the plans. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and inform the development of organizational contingency plans. #### **References** SP 800-12, SP 800-30, SP 800-39 ## IA-1-Identification and Authentication ### Summary Identification and Authentication Policy and Procedures, mandates that organizations develop, document, and maintain an identification and authentication policy that includes procedures to manage and control user identification and authentication mechanisms. This policy should align with the organization's security requirements and include details on how identity and authentication systems are implemented and managed. The control also emphasizes the need for ongoing updates and dissemination of the policy to ensure it remains effective and relevant to current security challenges. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. #### **References** SP 800-12, SP 800-30, SP 800-39 ## IA-2-Identification and Authentication (Organizational Users) ### Summary Identification and Authentication (Organizational Users), requires that the identity of organizational users is verified before granting access to organizational information systems. This control involves establishing and managing unique user IDs, employing robust authentication processes (like passwords, tokens, or biometric data), and ensuring that authentication mechanisms meet the required security strength levels. Additionally, the control mandates periodic updates and reviews of the authentication mechanisms to adapt to emerging threats and ensure the security of user access. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. #### **References** SP 800-12, SP 800-30, SP 800-39 ## IA-4-Identifier Management ### Summary Identifier Management, requires the management of user identifiers by ensuring they are uniquely assigned to individual users. This control requires organizations to establish a system for managing identifiers that includes issuing, maintaining, and revoking identifiers as needed. It also emphasizes the need to protect identifier information to prevent misuse or unauthorized access. Regular audits are required to ensure that identifiers are not shared and are disabled or removed when no longer associated with an active user account. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment and highlight instances of misconfigured identities. #### **References** SP 800-12, SP 800-30, SP 800-39 ## IA-8-Identification and Authentication ### Summary Identification and Authentication (Non-Organizational Users), focuses on ensuring that non-organizational users (such as contractors, customers, or partners) are uniquely identified and authenticated before accessing organizational information systems. This control requires organizations to implement measures that are consistent with the risk associated with such external users. It involves establishing terms and conditions for non-organizational user access, using robust authentication mechanisms, and monitoring and controlling these authentication processes to mitigate potential security risks effectively. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. #### **References** SP 800-12, SP 800-30, SP 800-39 ## IR-5-Incident Monitoring ### Summary Incident Monitoring, requires organizations to establish and maintain the capability to detect, analyze, and respond to information security incidents in real time. This control involves the continuous monitoring of information system activity to identify occurrences that may indicate a security incident. Organizations must also implement effective communication channels that allow for timely dissemination of incident information. The control emphasizes the need for maintaining historical incident data to support after-action reviews and to improve incident response effectiveness and prevention strategies. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. #### **References** SP 800-12, SP 800-30, SP 800-39 ## PM-5-Information System Inventory ### Summary Information System Inventory, requires organizations to develop, document, maintain, and review an inventory of information systems that includes all components within the authorization boundary. This inventory should capture the interfaces between systems (both internal and external), the data classification associated with the systems, and the organizational responsible entities. The control emphasizes the importance of keeping the inventory current to support effective risk management and security decision-making processes. Regular updates and validations of the inventory ensure accuracy and completeness in reflecting system changes. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. #### **References** SP 800-12, SP 800-30, SP 800-39 ## RA-2-Security Categorization ### Summary Security Categorization, requires organizations to categorize information and information systems according to the risk of harm that could result from unauthorized access, use, disclosure, disruption, modification, or destruction. This categorization should be based on the potential impact to organizational operations, assets, individuals, other organizations, and national security. The categorization must guide the selection of security controls appropriate to protecting the information system at the required security level. The process should be consistent with applicable laws, executive orders, directives, policies, standards, and guidelines. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. #### **References** SP 800-12, SP 800-30, SP 800-39 ## RA-3-Risk Assessment ### Summary Risk Assessment, mandates organizations to conduct comprehensive assessments of risks to their operations, assets, individuals, and other organizations resulting from the operation of information systems. This includes identifying potential threats and vulnerabilities, evaluating the likelihood and impact of different scenarios, and determining the potential adverse effects. Organizations are required to periodically perform these risk assessments to account for changes in the operational environment or in response to new threats. The results should be used to update security measures and inform risk response decisions. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. #### **References** SP 800-12, SP 800-30, SP 800-39 ## RA-5-Vulnerability Scanning ### Summary RA-5, Vulnerability Scanning, requires organizations to periodically scan information systems and hosted applications to identify security vulnerabilities. The control stipulates that the scans should be conducted using updated tools and techniques, tailored to the system's security requirements and the organization's risk environment. Findings from these scans must be analyzed, documented, and reviewed by designated officials to prioritize remediation actions based on risk. The control also mandates that organizations establish processes to remediate vulnerabilities within an acceptable timeframe to maintain the security and integrity of the system. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. #### **References** SP 800-12, SP 800-30, SP 800-39 ## SA-5-Information System Documentation ### Summary Information System Documentation, mandates that organizations maintain documentation for information systems and their environments of operation. This documentation should accurately reflect the current configuration and architecture of the system, including details of all components, interfaces, and security controls. The purpose is to ensure that all aspects of the system are fully documented to support effective management, maintenance, and upgrades. Organizations must ensure that this documentation is available to authorized personnel and protected from unauthorized access or modification. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. #### **References** SP 800-12, SP 800-30, SP 800-39 ## SA-11-Security Testing and Evaluation ### Summary Developer Security Testing and Evaluation, requires organizations to require developers to conduct security testing and evaluation of the information system and its components. This includes unit testing, integration testing, system testing, and regression testing to identify flaws and vulnerabilities in the system. The control specifies that these tests should be comprehensive, covering security functionality, boundary testing, and penetration testing. Results from these tests must be documented and used to make necessary corrections before deploying the system. Organizations are also encouraged to employ independent evaluators to verify the results and effectiveness of the developer's tests. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. #### **References** SP 800-12, SP 800-30, SP 800-39 ## SI-2-Flaw Remediation ## Summary Flaw Remediation, requires organizations to identify, report, and correct information system flaws in a timely manner. This control involves regularly scanning for vulnerabilities using updated tools, and promptly addressing detected flaws to mitigate potential security risks. The control also emphasizes the importance of prioritizing the remediation of flaws based on the severity of the potential impact on the organization. Furthermore, organizations are mandated to install security-relevant software and firmware updates and patches to ensure systems remain resilient against known threats. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. #### **References** SP 800-12, SP 800-30, SP 800-39 ## SI-4-Information Systems Monitoring ### Summary Information System Monitoring, mandates continuous monitoring activities to detect unauthorized access, use, disclosure, disruption, modification, or destruction of information and information systems. This control requires organizations to deploy monitoring tools capable of generating alerts in response to detected anomalies. The scope of monitoring should include network traffic, user activities, and system configurations. Organizations are also expected to regularly review and update their monitoring strategies to adapt to new threats and incorporate advancements in technology. Additionally, the results of monitoring activities should be protected, analyzed, and used to inform risk management decisions. ### Solution BloodHound Enterprise identifies and catalogues all Active Directory/Azure accounts during its collection process. The collected accounts are analyzed and displayed in graph format to illustrate the various relationships and permission profiles in order to easily audit/verify access and authorization levels within the enterprise. BloodHound enterprise will assign a risk metric, represented as exposure to tier 0 assets, and report the overall level of exposure present in an environment. #### **References** SP 800-12, SP 800-30, SP 800-39 # BloodHound Enterprise Compliance Framework Source: https://bloodhound.specterops.io/manage-bloodhound/compliance-framework/overview BloodHound Enterprise helps organizations meet their compliance requirements by providing visibility into identity attack paths and enabling organizations to remediate them. BloodHound Enterprise can help satisfy controls across multiple compliance frameworks, including: * [NIST CSF v1.1](/manage-bloodhound/compliance-framework/nist-csf-v1-1) * [NIST CSF v2](/manage-bloodhound/compliance-framework/nist-csf-v2) * [NIST SP 800-171](/manage-bloodhound/compliance-framework/nist-sp-800-171) * [NIST SP 800-53 Rev. 8](/manage-bloodhound/compliance-framework/nist-sp-800-53) ## How BloodHound Enterprise Helps with Compliance BloodHound Enterprise helps organizations meet their compliance requirements in several ways: 1. **Asset Management**: BloodHound Enterprise provides a comprehensive inventory of Active Directory and Azure assets through automated scans of the environment. 2. **Risk Assessment**: BloodHound Enterprise's attack path analysis and risk scoring help organizations understand and quantify their cybersecurity risk. 3. **Configuration Management**: BloodHound Enterprise helps establish access and identity baselines and detects deviations from those baselines. 4. **Monitoring**: BloodHound Enterprise provides routine and on-demand scans to continuously monitor for identity attack paths. 5. **Incident Response**: BloodHound Enterprise's attack path analysis helps organizations understand and respond to identity-based threats. For more information about how BloodHound Enterprise maps to specific compliance controls, see the [Compliance Resources](/manage-bloodhound/compliance-framework/resources) page. # BloodHound Enterprise Compliance Framework Resource Source: https://bloodhound.specterops.io/manage-bloodhound/compliance-framework/resources BloodHound Enterprise aids numerous organizations in meeting their compliance requirements through our continuous monitoring of identity Attack Path exposure within their environments. We're eager to support you and your auditors in gaining a deeper understanding of the inner workings of BloodHound Enterprise and how we can help you meet your compliance goals. Below, you'll find tables outlining various standard controls, detailing how BloodHound Enterprise supports these controls, and mapping them to relevant sections within the specific compliance frameworks. Within each table, the specific controls can be expanded to learn how BloodHound Enterprise satisfies each particular control. ## Asset Management | | | | | | | | | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | | **Control Category/Activity** | **How Does BloodHound Enterprise Satisfy This Control?** | **[NIST CSF v1.1](/manage-bloodhound/compliance-framework/nist-csf-v1-1)** | **[NIST CSF v2](/manage-bloodhound/compliance-framework/nist-csf-v2)** | **[NIST 800-171](/manage-bloodhound/compliance-framework/nist-sp-800-171)** | **[NIST 800-53 rev 8](/manage-bloodhound/compliance-framework/nist-sp-800-53)** | | | Asset Management

The organization retains control over a system of devices, which undergoes reconciliation at intervals defined by the organization. | BloodHound Enterprise provides a comprehensive inventory of Active Directory and Azure assets through automated scans of the environment. | [ID.AM-1](/manage-bloodhound/compliance-framework/nist-csf-v1-1#ID.AM-1)

[ID.AM-2](/manage-bloodhound/compliance-framework/nist-csf-v1-1#ID.AM-2)

[ID.AM-5](/manage-bloodhound/compliance-framework/nist-csf-v1-1#ID.AM-5)

[PR.IP-1](/manage-bloodhound/compliance-framework/nist-csf-v1-1#PR.IP-1) | [ID.AM-01](/manage-bloodhound/compliance-framework/nist-csf-v2#ID.AM-01)

[ID.AM-02](/manage-bloodhound/compliance-framework/nist-csf-v2#ID.AM-02)

[ID.AM-05](/manage-bloodhound/compliance-framework/nist-csf-v2#ID.AM-05) | [3.1.1](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.1.1)

[3.4.1](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.4.1) | [CM-8](/manage-bloodhound/compliance-framework/nist-sp-800-53#CM-8-Information-System-Component-Inventory)

[CP-2](/manage-bloodhound/compliance-framework/nist-sp-800-53#CP-2-Contingency-Plan)

[PM-5](/manage-bloodhound/compliance-framework/nist-sp-800-53#PM-5-Information-System-Inventory)

[RA-2](/manage-bloodhound/compliance-framework/nist-sp-800-53#RA-2-Security-Categorization) | | ## Risk Assessment | | | | | | | | | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | | **Control Category/Activity** | **How Does BloodHound Enterprise Satisfy This Control?** | **[NIST CSF v1.1](/manage-bloodhound/compliance-framework/nist-csf-v1-1)** | **[NIST CSF v2](/manage-bloodhound/compliance-framework/nist-csf-v2)** | **[NIST 800-171](/manage-bloodhound/compliance-framework/nist-sp-800-171)** | **[NIST 800-53 rev 8](/manage-bloodhound/compliance-framework/nist-sp-800-53)** | | | Risk Assessment

The organization employs mechanisms to understand the cybersecurity risk to operations, assets, and individuals. | BloodHound Enterprise's attack path analysis and risk scoring help to satisfy this control. | [ID.RA-1](/manage-bloodhound/compliance-framework/nist-csf-v1-1#ID.RA-1)

[ID.RA-3](/manage-bloodhound/compliance-framework/nist-csf-v1-1#ID.RA-3)

[ID.RA-5](/manage-bloodhound/compliance-framework/nist-csf-v1-1#ID.RA-5) | [ID.RA-01](/manage-bloodhound/compliance-framework/nist-csf-v2#ID.RA-01)

[ID.RA-03](/manage-bloodhound/compliance-framework/nist-csf-v2#ID.RA-03)

[ID.RA-05](/manage-bloodhound/compliance-framework/nist-csf-v2#ID.RA-05) | [3.11.1](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.11.1)

[3.11.2](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.11.2)

[3.11.3](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.11.3)

[3.12.1](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.12.1)

[3.12.2](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.12.1)

[3.12.3](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.12.3)

[3.14.1](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.14.1)

[3.14.2](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.14.2) | [CA-2](/manage-bloodhound/compliance-framework/nist-sp-800-53#CA-2---Security-Assessments)

[CA-7](/manage-bloodhound/compliance-framework/nist-sp-800-53#CA-7---Continuous-Monitoring)

[CA-8](/manage-bloodhound/compliance-framework/nist-sp-800-53#CA-8-Penetration-Testing)

[RA-3](/manage-bloodhound/compliance-framework/nist-sp-800-53#RA-3-Risk-Assessment)

[RA-5](/manage-bloodhound/compliance-framework/nist-sp-800-53#RA-5-Vulnerability-Scanning)

[SA-5](/manage-bloodhound/compliance-framework/nist-sp-800-53#SA-5-Information-System-Documentation)

[SA-11](/manage-bloodhound/compliance-framework/nist-sp-800-53#SA-11-Security-Testing-and-Evaluation)

[SI-2](/manage-bloodhound/compliance-framework/nist-sp-800-53#SI-2-Flaw-Remediation)

[SI-4](/manage-bloodhound/compliance-framework/nist-sp-800-53#SI-4-Information-Systems-Monitoring) | | ## Configuration Management | | | | | | | | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | - | | **Control Category/Activity** | **How Does BloodHound Enterprise Satisfy This Control?** | **[NIST CSF v1.1](/manage-bloodhound/compliance-framework/nist-csf-v1-1)** | **[NIST CSF v2](/manage-bloodhound/compliance-framework/nist-csf-v2)** | **[NIST 800-171](/manage-bloodhound/compliance-framework/nist-sp-800-171)** | **[NIST 800-53 rev 8](/manage-bloodhound/compliance-framework/nist-sp-800-53)** | | | Configuration Management

The organization employs proactive mechanisms to detect deviations from baseline configurations within production environments. | Analysis of Active Directory/Azure Identities audits user and object permissions for deviations from established access and identity baselines | [PR.AC-4](/manage-bloodhound/compliance-framework/nist-csf-v1-1#PR.AC-4)

[PR.IP-1](/manage-bloodhound/compliance-framework/nist-csf-v1-1#PR.IP-1)

[DE.AE-1](/manage-bloodhound/compliance-framework/nist-csf-v1-1#DE.AE-1) | [PR.PS-01](/manage-bloodhound/compliance-framework/nist-csf-v2#PR.PS-01) | [3.1.1](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.12.3)

[3.1.2](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.1.2)

[3.1.5](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.1.2)

[3.1.6](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.1.6)

[3.1.7](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.1.7)

[3.4.5](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.4.5)

[3.4.6](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.4.6) | [AC-2](/manage-bloodhound/compliance-framework/nist-sp-800-53#AC-2----Account-Management)

[AC-3](/manage-bloodhound/compliance-framework/nist-sp-800-53#AC-3---Access-Enforcement)

[IA-1](/manage-bloodhound/compliance-framework/nist-sp-800-53#IA-1-Identification-and-Authentication)

[IA-2](/manage-bloodhound/compliance-framework/nist-sp-800-53#IA-2-Identification-and-Authentication-\(Organizational-Users\))

[IA-4](/manage-bloodhound/compliance-framework/nist-sp-800-53#IA-4-Identifier-Management)

[IA-8](/manage-bloodhound/compliance-framework/nist-sp-800-53#IA-8-Identification-and-Authentication) | | ## Detection | | | | | | | | | - | - | - | - | - | - | - | | | | | | | | | | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | | **Control Category/Activity** | **How Does BloodHound Enterprise Satisfy This Control?** | **[NIST CSF v1.1](/manage-bloodhound/compliance-framework/nist-csf-v1-1)** | **[NIST CSF v2](/manage-bloodhound/compliance-framework/nist-csf-v2)** | **[NIST 800-171](/manage-bloodhound/compliance-framework/nist-sp-800-171)** | **[NIST 800-53 rev 8](/manage-bloodhound/compliance-framework/nist-sp-800-53)** | | | **Control Category/Activity** | **How Does BloodHound Enterprise Satisfy This Control?** | **[NIST CSF v1.1](/manage-bloodhound/compliance-framework/nist-csf-v1-1)** | **[NIST CSF v2](/manage-bloodhound/compliance-framework/nist-csf-v2)** | **[NIST 800-171](/manage-bloodhound/compliance-framework/nist-sp-800-171)** | **[NIST 800-53 rev 8](/manage-bloodhound/compliance-framework/nist-sp-800-53)** | | | Detection

The organization employs mechanisms within the environment that continuously monitor for anomalies and events. | Identity Attack Path vectors are assigned a severity rating in BloodHound Enterprise when detected during routine and on-demand scans | [DE.AE-2](/manage-bloodhound/compliance-framework/nist-csf-v1-1#DE.AE-2)

[DE.AE-4](/manage-bloodhound/compliance-framework/nist-csf-v1-1#DE.AE-4)

[DE.AE-5](/manage-bloodhound/compliance-framework/nist-csf-v1-1#DE.AE-5)

[DE.CM-1](/manage-bloodhound/compliance-framework/nist-csf-v1-1#DE.CM-1)

[DE.CM-8](/manage-bloodhound/compliance-framework/nist-csf-v1-1#DE.CM-8) | [DE.AE-02](/manage-bloodhound/compliance-framework/nist-csf-v2#DE.AE-02)

[DE.AE-04](/manage-bloodhound/compliance-framework/nist-csf-v2#DE.AE-04)

[DE.AE-08](/manage-bloodhound/compliance-framework/nist-csf-v2#DE.AE-08) | [3.3.1](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.4.6)

[3.3.2](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.4.6)

[3.3.5](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.3.5) | [CA-3](/manage-bloodhound/compliance-framework/nist-sp-800-53#IA-8-Identification-and-Authentication)

[CM-2](/manage-bloodhound/compliance-framework/nist-sp-800-53#IA-8-Identification-and-Authentication) | | ## Respond | | | | | | | | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | - | | **Control Category/Activity** | **How Does BloodHound Enterprise Satisfy This Control?** | **[NIST CSF v1.1](/manage-bloodhound/compliance-framework/nist-csf-v1-1)** | **[NIST CSF v2](/manage-bloodhound/compliance-framework/nist-csf-v2)** | **[NIST 800-171](/manage-bloodhound/compliance-framework/nist-sp-800-171)** | **[NIST 800-53 rev 8](/manage-bloodhound/compliance-framework/nist-sp-800-53)** | | | Respond

Activities are performed to ensure effective response, support recovery activities, and mitigating steps are taken to prevent the expansion of an incident. | BloodHound Enterprise detects and reports identified attack paths with a quantifiable risk metric and inventory of all impacted systems. Relevant remediation and mitigation documentation provided during analysis may help to satisfy this control. | [RS.AN-1](/manage-bloodhound/compliance-framework/nist-csf-v1-1#RS.AN-1)

[RS.AN-2](/manage-bloodhound/compliance-framework/nist-csf-v1-1#RS.AN-2)

[RS.MI-2](/manage-bloodhound/compliance-framework/nist-csf-v1-1#RS.MI-2) | [RS.MI-02](/manage-bloodhound/compliance-framework/nist-csf-v2#RS.AN-03) | [3.3.1](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.3.1)

[3.3.2](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.3.1)

[3.3.5](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.3.5)

[3.6.1](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.3.5)

[3.6.2](/manage-bloodhound/compliance-framework/nist-sp-800-171#3.3.5) | [CA-7](/manage-bloodhound/compliance-framework/nist-sp-800-53#CA-7---Continuous-Monitoring)

[IR-5](/manage-bloodhound/compliance-framework/nist-sp-800-53#IR-5-Incident-Monitoring) | | # Administration Source: https://bloodhound.specterops.io/manage-bloodhound/overview Administer a BloodHound instance and its related components: users, roles, authentication, collector status, and general security. ## [BloodHound Configuration](/manage-bloodhound/bh-config) ## [BloodHound Enterprise Compliance Framework](/manage-bloodhound/compliance-framework/overview) ## [Authentication and Authorization](/manage-bloodhound/auth/overview) ## [Secure BloodHound and Collectors](/manage-bloodhound/securing-bloodhound-and-collectors/overview) # SharpHound Enterprise Service Hardening Source: https://bloodhound.specterops.io/manage-bloodhound/securing-bloodhound-and-collectors/sharphound-hardening The BloodHound team recommends the hardening actions described on this page to protect the SharpHound service account. The hardening recommendations are focused on the remediation of the attack techniques targeting service accounts. Applies to BloodHound Enterprise only Many of the attacks involve privileged collection, in which the SharpHound service account gathers data from domain-joined Windows computers. During privileged collection, an attacker with administrative access to a computer in the domain could attempt to compromise the SharpHound service account, as the account will log in on the computer during the collection. This page will describe the attacks we want to prevent and the remediations that can be applied. All attacks can be remediated in more than one method. The first section will highlight the remediations that we recommend without going into detail about the different attack techniques and alternative remediations. ## Recommended hardening ### gMSA We recommend using a [Group Managed Service Account](https://learn.microsoft.com/en-us/windows-server/security/group-managed-service-accounts/group-managed-service-accounts-overview) (gMSA) for the SharpHound service account rather than a regular AD user. Follow the article: [Create a gMSA for SharpHound](/install-data-collector/install-sharphound/create-gmsa). A gMSA is a type of service account where the password is managed by Active Directory, eliminating the need for manual password management. This will ensure the service account password is: * A 240-byte randomly generated complex password * Not known to any individuals nor stored in any notes, password vault, etc. * Rotated regularly Using a gMSA will reduce the risk of having the SharpHound service account compromised by password spray (guessing) and password hash cracking and prevent the attacker from obtaining the password from elsewhere. Furthermore, using a gMSA makes it possible to have the SharpHound service account in the Protected Users group, which is covered in the following recommendation. ### Protected Users group We recommend adding the SharpHound service account as a member of the [Protected Users](https://learn.microsoft.com/en-us/windows-server/security/credentials-protection-and-management/protected-users-security-group) group. Protected Users is an AD security group designed to reduce credential exposure. Members of this group automatically have non-configurable protections applied to their accounts. Protected Users members cannot be delegated with Kerberos and cannot authenticate using NTLM, effectively remediating Kerberos delegation attacks, NTLM relay attacks, and NTLM cracking. However, Microsoft advises against adding service accounts to the group as authentication for the service account may fail. Our testing has shown that adding the SharpHound service account to Protected Users works if the SharpHound service account is a gMSA, but the SharpHound service crashes after four hours if it is a regular AD user. Adding the SharpHound service account to Protected Users also prevents SharpHound from authenticating with NTLM during collection. This means SharpHound cannot collect the NTLM data from domain controllers and ADCS servers that BloodHound uses to create the following edges: * [CoerceAndRelayNTLMToLDAP](/resources/edges/coerce-and-relay-ntlm-to-ldap) * [CoerceAndRelayNTLMToLDAPS](/resources/edges/coerce-and-relay-ntlm-to-ldaps) * [CoerceAndRelayNTLMToADCS](/resources/edges/coerce-and-relay-ntlm-to-adcs) ### Tiering SharpHound We recommend tiering the SharpHound service account to follow the principle of "[elevated user accounts should not be used to log on to lower Tier assets](https://techcommunity.microsoft.com/t5/core-infrastructure-and-security/protecting-domain-administrative-credentials/ba-p/259210)". Follow the article: [Tiered SharpHound Strategy](/install-data-collector/install-sharphound/tiered-collector-strategy). This recommendation is especially for organizations seeking to implement the [Active Directory Tier Model](https://learn.microsoft.com/en-us/microsoft-identity-manager/pam/tier-model-for-partitioning-administrative-privileges) or [Enterprise Access Model](https://learn.microsoft.com/en-us/security/privileged-access-workstations/privileged-access-access-model) ## Attacks and remediations This section explores the types of attacks that SharpHound Enterprise may be at risk of, all of which are mitigated with the recommended hardening. ### Attack 1: Finding/guessing service account password When a service account is created as a regular AD user, the person creating the account must set a password for the service account and must store this password somewhere. Attackers will attempt to obtain the password by guessing/spraying with common passwords and passwords of other accounts in the environment. They will also look for service account passwords in file shares, key vaults, etc. #### Preferred remediation: gMSA When the service account is created as a gMSA, the password will be managed completely by AD. That means the password of the account cannot be a weak/guessable password, and it will not be stored anywhere the attacker can gain read access. #### Alternative remediation: Strong password stored securely When the password is strong, it decreases the risk of an attacker guessing the password. The SharpHound service account password can be copy-pasted when installing SharpHound Enterprise. Setting the password to a random string of 100 characters will therefore not cause inconvenience. The password should be stored securely in a password vault where only the right personnel have access. ### Attack 2: Kerberos delegation attacks When collecting data, the SharpHound service account will only perform a network logon (type 3) on remote computers. This logon type will not save the service account credentials in the remote computer's memory. However, Kerberos delegation breaks this rule if the remote computer is configured with Kerberos unconstrained delegation. In that case, the remote computer will receive a copy of the service account's Kerberos session ticket (TGT), which an attacker can extract from memory and utilize to authenticate as the service account. If the computer is configured with unconstrained delegation, the service account does not need to log on to the computer – the attacker can obtain service tickets as any user for services it is allowed to delegate to. #### Preferred remediation: Protected Users group (gMSA only) Members of the Protected Users group cannot be delegated, as described by Microsoft [here](https://learn.microsoft.com/en-us/windows-server/security/credentials-protection-and-management/protected-users-security-group#domain-controller-protections-for-protected-users). This means the SharpHound service account will not be vulnerable to the Kerberos delegation attacks. This remediation will break the SharpHound service if a regular AD user is used instead of a gMSA. #### Alternative remediation: Mark the account as sensitive It is possible to prevent an AD principal from using Kerberos delegation services by enabling the account option "Account is sensitive and cannot be delegated": On a Group Manages Service Account (gMSA), this account option is not visible in the GUI, but you can set the account option through PowerShell: The value should be *True* for the account to be protected. ### Attack 3: Authentication relaying The data collection performed by the SharpHound service account happens over the SMB protocol and is authenticated on the remote computer using Kerberos by default. However, an attacker with administrative rights on the remote computer can downgrade the authentication to NTLM. This allows the attacker to perform an NTLM relay attack, where the ongoing NTLM authentication is relayed to a target computer, giving the attacker a session on the target as the SharpHound service account. It is possible to relay Kerberos authentication under specific circumstances, but we have not found it to be possible with the SharpHound service account. #### Preferred remediation: Protected Users group (gMSA only) Members of the Protected Users group cannot authenticate with NTLM, as described by Microsoft [here](https://learn.microsoft.com/en-us/windows-server/security/credentials-protection-and-management/protected-users-security-group#domain-controller-protections-for-protected-users). This means the SharpHound service account will not be vulnerable to NTLM attacks. This remediation will break the SharpHound service if a regular AD is used instead of a gMSA. #### Alternative remediation: Block outgoing NTLM Outgoing NTLM can be denied entirely from the SharpHound server by configuring the security option [Network security: Restrict NTLM: Outgoing NTLM traffic to remote servers](https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/network-security-restrict-ntlm-outgoing-ntlm-traffic-to-remote-servers) to *Deny All:* This prevents the attacker from downgrading the Kerberos authentication to NTLM and remediates NTLM attacks. ### Attack 4: NTLM cracking As mentioned in the previous attack (Authentication relaying), an attacker with administrative rights on the remote computer can downgrade the Kerberos authentication of the SharpHound service account to NTLM. If NTLMv2 is not enforced, the attacker can even downgrade to NTLMv1. When the attacker has downgraded the authentication to NTLM, it becomes possible for the attacker to capture the Net-NTLMv1/v2 hash in the authentication. This hash can then be cracked offline to obtain the password of the SharpHound service account. While NTLMv1 is significantly faster to crack as it is based on DES encryption, both NTLMv1 and NTLMv2 can be cracked. #### Remediation *See the remediation of the previous attack (Authentication relaying).* ### Attack 5: Reusable credentials in LSASS When an account logs in on a Windows computer, the account's credentials will be cached in LSASS memory in a reusable format, such that Windows can reauthenticate the user without the user having to type in their password again and again. This is known as Single Sign-On (SSO). An attacker with administrative access to the computer can read the cached credentials out of LSASS and thereby compromise the account. #### Remediation - No remediation is required The SharpHound service account performs only a network logon (type 3) when it collects data, and this logon type does not leave credentials in LSASS memory, as explained by Microsoft [here](https://learn.microsoft.com/en-us/windows-server/identity/securing-privileged-access/reference-tools-logon-types) (unless Kerberos delegation is enabled, which we have covered separately). ### Attack 6: Cached Domain Credentials Cracking Normally, when a domain user logs in on a domain-joined Windows computer, the authentication will involve a Domain Controller telling the computer if the account's credentials are valid. But if the computer cannot reach a Domain Controller (due to network issues etc.), this authentication process does not work. Windows will, by default, cache the account's credentials in the security registry hive in a non-reusable format (MS-Cache v2 hash), such that the computer can verify the credential even if it cannot reach any Domain Controller. An attacker with administrative access to the computer can read the cached credentials out of the registry hive, crack the password hash, and thereby compromise the account. #### Remediation - No remediation is required The SharpHound service account performs only a Network logon (type 3) when it collects data. This logon type will not generate the cached domain credentials. ### Attack 7: Kerberoasting Any AD user can request and receive a Kerberos service ticket of any user (service account) with a Service Principal Name (SPN) attribute set. This service ticket is encrypted with a Kerberos key derived from the service account's password. An attacker can obtain the password of the service account by cracking the service ticket (guessing the password that decrypts the ticket). This attack is known as Kerberoasting. #### Remediation - No remediation required The SharpHound service account will not have an SPN set. # Architecture Source: https://bloodhound.specterops.io/on-premises/architecture Understand the architecture, components, and data flow of on-premises deployments of BloodHound Enterprise. Applies to BloodHound Enterprise only On-premises deployments of BloodHound Enterprise give you full control over your deployment infrastructure while maintaining the same powerful identity security capabilities as the SaaS version. ## Deployment architecture On-premises deployments of BloodHound Enterprise consist of two primary parts: * **BloodHound Enterprise host** - Runs the BloodHound application, database, and supporting infrastructure * **Collector hosts** - Run lightweight collector services (SharpHound, AzureHound, or OpenHound) to gather data from your identity infrastructure ### Core components All on-premises deployments include the following core application components: | Component | Purpose | | ----------------------------- | --------------------------------------------------------------- | | **BloodHound Enterprise API** | Application server, UI, graph analysis, and collector ingestion | | **PostgreSQL 18.x** | Database server for application data and graph storage | ### Deployment-specific components Embedded cluster deployments include the following infrastructure and management components: | Component | Purpose | | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | **k0s Kubernetes distribution** | Bundled Kubernetes distribution that runs BloodHound Enterprise on your Linux host | | **Embedded ingress controller** | Exposes the BloodHound Enterprise application endpoint and terminates HTTPS for the configured FQDN by default | | **Installation Wizard** | Host-local web UI that completes configuration and runs preflight checks | | **SpecterOps - BloodHound Enterprise Portal** | Hosted portal that provides installer access, generates deployment-specific installation commands, and tracks online installations and updates | ### Data collectors Collectors run separately from the BloodHound Enterprise host and gather configuration data from your identity infrastructure: | Collector | Target Environment | Data Collected | | ------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | **SharpHound Enterprise** | Active Directory | AD objects, relationships, ACLs, sessions | | **AzureHound Enterprise** | Azure / Entra ID | Azure AD objects, role assignments, resource relationships | | **OpenHound** | Other identity providers, platforms, and custom sources | Varies by source; data collected and converted into BloodHound Enterprise-compatible graphs | ## Data flow Data flows through the system in the following sequence: 1. **Collection** - Collectors gather configuration data from Active Directory, Entra ID, or other identity sources 2. **Transmission** - Data is transmitted over encrypted HTTPS/TLS to the BloodHound Enterprise API 3. **Processing** - The BloodHound Enterprise API processes and stores data in PostgreSQL 4. **Analysis** - Graph analysis identifies privilege relationships and Attack Paths 5. **Visualization** - Results are displayed in the BloodHound Enterprise UI Collectors have zero local storage of collected data. All data is transmitted directly to the BloodHound Enterprise host and stored in PostgreSQL. # Install and Configure Source: https://bloodhound.specterops.io/on-premises/install Use this guide to install and configure an on-premises instance of BloodHound Enterprise with the embedded cluster deployment option. Applies to BloodHound Enterprise only This guide provides step-by-step instructions for installing and configuring a BloodHound Enterprise instance with the embedded cluster installer. The embedded cluster installer bundles a Kubernetes distribution (k0s) with the BloodHound Enterprise application, so you can deploy everything on a single Linux host without needing to set up Kubernetes separately. ## Pre-install checklist The following checklist summarizes the install-blocking gates and pre-deployment work that must be completed before running the installer. For detailed host sizing, collector specifications, supported filesystems, and kernel versions, see the full [system requirements](/on-premises/system-requirements). * Provision a dedicated x86-64 Linux host (any distribution with systemd) with root or sudo access, sized per the [BloodHound Enterprise host](/on-premises/system-requirements#bloodhound-enterprise-host) specification * Back the host with SSD storage (P99 write latency ≤10ms) — the installer blocks on spinning disk * Plan the HTTPS endpoint for the BloodHound Enterprise FQDN. By default, the embedded cluster ingress serves the application on port `443`; add an external [reverse proxy](/on-premises/system-requirements#reverse-proxy) only if your environment requires one * Create a [DNS A record](/on-premises/system-requirements#dns) for the BloodHound Enterprise FQDN that resolves for both users and collectors * Open inbound TCP `443` (users and collectors) and `30080` (Installation Wizard) * For online installs, allow outbound HTTPS to `replicated.app`, `proxy.replicated.com`, and `registry.replicated.com`; for air-gapped installs, download the installer bundle ahead of time ## Web-based installer The web-based installer provides an interactive setup experience through a web interface. It guides you through configuration steps such as domain setup, certificate management, and database connectivity. ### Configure install options The web-based installer is available through the **SpecterOps - BloodHound Enterprise Portal**. You'll work with two interfaces during installation: * The **Installer** tab in the portal generates the download and install commands you run on your Linux host. The **Update** tab in the portal tracks active and inactive online installations. * The **Installation Wizard** is a companion UI served from your Linux host on port `30080`. After the initial install completes, you use the Installation Wizard to finish configuration (domain, certificates, database) and run preflight checks. If you don't have access to the portal, contact your team administrator or your BloodHound Enterprise account team to request an invitation. 1. Log in to the [SpecterOps - BloodHound Enterprise Portal](https://enterprise.replicated.com/bloodhound-enterprise). 2. Click the **Install** tab at the top of the page to open the Installation Guide. The installation options displayed on this page depend on the licensing associated with your account. The first step prompts you to choose your installation options, such as network availability and whether to show instructions for relocating images to a private registry. 1. Enter a name for your BloodHound Enterprise instance. 2. Choose one of the following **Network Availability** options: | Option | Use this when | | ----------------------------------------- | ----------------------------------------------------------- | | **Outbound requests allowed** | The Kubernetes cluster can make outbound internet requests. | | **Outbound requests require HTTPS Proxy** | Outbound traffic must go through a proxy server. | If you choose the proxy option, a **Configure proxy URL** field appears on the next page of the installer where you can enter your proxy URL. The installer also adds `--proxy $YOUR_PROXY_URL` to the download *and* install commands on the next page to ensure the installer can access the internet through your proxy. 3. Click the toggle for **Show instructions to relocate images to your private registry** if your environment requires the cluster to pull container images from a private registry you control. This is typically required for air-gapped or restricted networks, or to meet internal supply-chain and compliance requirements. If you choose this option, the next page provides a field for entering your private registry URL and instructions for running the necessary commands on your Linux host to: * Pull the necessary container images * Tag them with your private registry URL * Push them to your registry The installer also adds `--registry $YOUR_REGISTRY_URL` to the install command on the next page to ensure the cluster pulls images from your registry during installation. A screenshot showing the initial installation options in the BloodHound Enterprise web-based installer 4. Click **Continue**. ### Install on the Linux host The installation instructions page generates the exact download and install commands for your release, including a signed URL, a short-lived bearer token, and your specific version. The following screenshot shows an example of the install instructions page in the portal, which provides the commands you run on your Linux host to download and install BloodHound Enterprise. A screenshot showing an example of the installation instructions in the BloodHound Enterprise web-based installer On the installation instructions page, complete the following fields before copying the commands: 1. In the **Select Version** field, choose the version of BloodHound Enterprise you want to install. 2. If you selected the proxy option in the previous installation options, enter your proxy URL in the **Configure proxy URL** field. The installer uses this URL to access the internet for downloading container images and other dependencies. The URL must be accessible from the Linux host where you run the installer. 3. If you selected the private registry option in the previous installation options, enter your registry URL in the provided field. The installer uses this URL to pull container images during installation. The URL must be accessible from the Linux host where you run the installer and from the Kubernetes cluster. SSH into the Linux host where you want to install BloodHound Enterprise. From the installation instructions page, copy and run the command to download the archive of installation assets on your Linux host. From the installation instructions page, copy and run the command to extract the archive of installation assets on your Linux host. **Example:** ```bash theme={null} tar -xvzf bloodhound-enterprise.tar.gz ``` The installer archive contains the embedded cluster application and all necessary dependencies, including the k0s Kubernetes distribution and your license file. The installer will set up everything for you on a single Linux host. Copy all files from the `assets/` directory to the home directory on your Linux host. **Example:** ```bash theme={null} cp assets/* ~/ ``` From the installation instructions page, copy and run the install command on your Linux host, providing the path to your license file. After extraction, your `license.yaml` file is present alongside all installation assets. **Example:** ```bash theme={null} sudo ./bloodhound-enterprise install --license license.yaml ``` When prompted on the Linux host, enter an admin password. This is the **Installation Wizard password**. You use it to log in to the Installation Wizard later. When the installer prompts you to generate a self-signed certificate for the Installation Wizard, accept the prompt. The installer uses this certificate to serve the Installation Wizard over HTTPS on port `30080`. The install fails if you decline. The install command doesn't accept a flag for providing your own certificate at this stage. You'll be able to upload a certificate for your BloodHound Enterprise instance later in the Installation Wizard's **Certificates** step. After the installation completes, the installer prints the Installation Wizard URL: ```http theme={null} https://:30080 ``` As mentioned in the pre-installation checklist, the Installation Wizard listens on port `30080`. If you haven't already done so, expose this port on the Linux host. Don't click **Finish** on the installation instructions page yet; you'll do that after completing the Installation Wizard in the next section. ### Configure the environment The Installation Wizard guides you through the final configuration steps to complete the installation. Follow the prompts to enter the necessary information and complete each step. The wizard guides you through the final configuration steps and preflight checks. Open the Installation Wizard URL in a browser and enter the admin password that you set when you ran the install command on your Linux host. Browsers display a certificate warning at `https://:30080` because the Installation Wizard uses the self-signed certificate generated during install; this is expected. If your browser blocks the self-signed certificate, export the certificate presented on port `30080`, add it to the trusted root/intermediate certificate store as appropriate, then reload the page. Use a lowercase hostname with no underscores. Kubernetes ingress rules require lowercase hostnames. * For **production** deployments, this should match the [DNS A record](/on-premises/system-requirements#dns) you created for the BloodHound Enterprise FQDN in the pre-install checklist. * For **testing** purposes, this can be a hostname that resolves to the Linux host's IP address in your local `/etc/hosts` file. For example: ```txt theme={null} 192.168.1.50 bloodhound-enterprise.test ``` A screenshot showing the domain configuration step of the BloodHound Enterprise web-based installer Port `443` must be open on the Linux host and reachable from the browser you use to access the BloodHound Enterprise application. Configure how users reach the BloodHound Enterprise application. **Recommended default:** Select **Ingress** for most deployments. **Why:** The embedded cluster includes its own ingress controller for the BloodHound Enterprise FQDN. **Ingress** uses that built-in path and lets the installer handle HTTPS termination on port `443` with the certificate you configure in the next step. Choose **ClusterIP**, **NodePort**, or **LoadBalancer** only if your environment already requires one of those Kubernetes service exposure patterns or you are integrating with your own frontend networking layer. A screenshot showing the application access configuration step of the BloodHound Enterprise web-based installer Choose whether to have the installer generate a TLS certificate for HTTPS access to your BloodHound Enterprise instance or upload your own. This certificate is used by the embedded cluster ingress that serves the BloodHound Enterprise application on port `443`. Upload your own certificate if your organization requires a certificate issued by your internal or public certificate authority. A screenshot showing the certificate configuration step of the BloodHound Enterprise web-based installer Choose between the embedded PostgreSQL database or provide connection details for an external PostgreSQL database. If you choose to use an external PostgreSQL database, ensure that the database is running PostgreSQL 18 and is reachable on port `5432` from the Linux host where BloodHound Enterprise is installed. A screenshot showing the database configuration step of the BloodHound Enterprise web-based installer Configure the network settings the embedded cluster uses to communicate internally and reach external services. Accept the defaults unless your environment requires specific overrides. A screenshot showing the setup step of the BloodHound Enterprise web-based installer Review and resolve any blocking preflight checks before continuing the installation. Preflight checks verify that your cluster meets the requirements for a BloodHound Enterprise installation or upgrade before deployment begins. A screenshot showing the preflight checks step of the BloodHound Enterprise web-based installer When you see the Installation Complete message, click **Finish** to exit the Installation Wizard. A screenshot showing the installation success step of the BloodHound Enterprise web-based installer After exiting the Installation Wizard, return to the **Install** tab in the portal (installation instructions) and click **Finish**. You'll be redirected to the **Update** tab, where you can view your active installation. A screenshot showing the Update tab with active installations in the BloodHound Enterprise Portal Back on your Linux host, press `Ctrl+C` to exit the installation process and stop the web interface for the wizard. BloodHound Enterprise continues running and is accessible at the FQDN you configured in the **Domain Configuration** step. ### Access BloodHound Enterprise The last step of the installation process is to access the BloodHound Enterprise application in a browser and log in with the default admin credentials. In a browser, navigate to the BloodHound Enterprise FQDN you configured in the Installation Wizard. * The default username is `admin`. Enter the username in the **Email Address** field. * The default password is written to the BloodHound application logs on the Linux host. To retrieve the initial password, load the `kubectl` shell environment and inspect the deployment logs: 1. Load the `kubectl` shell environment: ```bash theme={null} sudo ./bloodhound-enterprise shell ``` 2. Print the initial admin password from the `bloodhound` deployment's logs: ```bash theme={null} kubectl logs -n bloodhound-enterprise deployment/bloodhound | grep "Initial Password Set To:" ``` Targeting the deployment avoids needing to look up the current pod name, which changes between installations and restarts. With BloodHound Enterprise installed and accessible, complete the following next steps in the application: * Change the default admin password (**Administration** > **Manage Users**) * [Create users](/manage-bloodhound/auth/users-and-roles) * [Configure collectors](/install-data-collector/overview) ## Troubleshooting When troubleshooting, you can inspect cluster state and view logs using the `kubectl` command-line tool. SSH into the Linux host where BloodHound Enterprise is installed. Run the following command to load the `kubectl` shell environment: ```bash theme={null} sudo ./bloodhound-enterprise shell ``` The appropriate `kubeconfig` is exported, and the location of useful binaries like `kubectl` and Replicated's preflight and support-bundle plugins is added to `PATH`. Use the available binaries as needed. See the following tabs for common cluster inspection commands: List all pods across every namespace: ```bash theme={null} kubectl get pods -A ``` List all services across every namespace: ```bash theme={null} kubectl get services -A ``` View logs for a specific pod: ```bash theme={null} kubectl logs -n [namespace] [pod-name] ``` BloodHound application pods run in the `bloodhound-enterprise` namespace. When finished, type `exit` or press `Ctrl + D` to exit the shell. # On-premises BloodHound Enterprise Source: https://bloodhound.specterops.io/on-premises/overview Learn about self-hosted deployment for BloodHound Enterprise, giving you full control over your infrastructure and data. Applies to BloodHound Enterprise only An on-premises deployment of BloodHound Enterprise is a self-hosted option that runs on infrastructure that you own. It gives you complete control over your deployment while delivering the same powerful capabilities as the SpecterOps-hosted version. You maintain full control over: * **Data residency** - All collected data stays within your environment * **Infrastructure** - Deploy on your own servers or virtual machines * **Updates** - Control when and how updates are applied * **Network isolation** - Run in air-gapped or restricted network environments ## SaaS vs on-premises On-premises deployments provide the same core BloodHound Enterprise functionality, but differ in infrastructure management and control. **Choose on-premises if you:** * Require data to remain within your infrastructure * Need full control over the deployment environment * Have existing infrastructure and operational expertise * Prefer to manage updates and maintenance on your own schedule **Choose SaaS if you:** * Want SpecterOps to manage infrastructure and updates * Don't have dedicated infrastructure or Kubernetes expertise * Want automatic updates and new features as they're released ## Deployment On-premises deployments of BloodHound Enterprise use an **embedded cluster** deployment option. An embedded cluster packages BloodHound Enterprise and a Kubernetes cluster together for deployment on a single Linux host. This option is based on the open-source Kubernetes distribution [k0s](https://docs.k0sproject.io/stable/), includes a built-in installation UI, exposes the application through a built-in ingress path, and runs preflight checks. It does not require existing Kubernetes infrastructure or operational expertise. An embedded cluster deployment has two primary parts: * **BloodHound Enterprise host** * Runs the BloodHound Enterprise application on Linux * Includes a bundled Kubernetes cluster (k0s) * Can use an external PostgreSQL database * **Collector hosts and services** * Run one or more collectors that upload configuration data to BloodHound Enterprise * SharpHound Enterprise runs as a Windows service for on-premises Active Directory and AD CS collection * AzureHound Enterprise runs as a containerized service for Entra ID, Azure Resource Manager, and Microsoft Graph collection * OpenHound for BloodHound Enterprise runs as a containerized service for supported platform collection, such as GitHub, Jamf, and Okta **Key data and security characteristics** * Collectors gather *configuration data* to map identity relationships * Data is transmitted over HTTPS with TLS * Collectors *do not* store collected data locally * You control upload authorization with a [collection schedule](/collect-data/enterprise-collection/collection-schedule) in BloodHound Enterprise ## Installation The installation process involves the following steps: | Step | What happens | Typical time | | -------------------------------- | -------------------------------------------------------------------------------------- | ------------- | | 1. Confirm prerequisites | Validate Linux host, PostgreSQL 18 (if using an external database), ports, and access. | 0.5-2 hours | | 2. Install BloodHound Enterprise | Use the web-based installer for a guided setup. | 30-60 min | | 3. Configure connectivity | Configure hostname, ingress, SSL/TLS, and database connections. | 30-60 min | | 4. Install and deploy collectors | Prepare collector systems and deploy the collectors you need. | 5-15 min each | | 5. Run first collection | Start with the simplest collection level to minimize friction. | Varies | | 6. Review results | Validate identity Attack Paths and plan next actions. | Varies | ## Next steps * Review the [architecture](/on-premises/architecture) and [system requirements](/on-premises/system-requirements) with infrastructure and security owners in your organization. * Coordinate with your organizational stakeholders to schedule the installation window. * Proceed to the full [installation guide](/on-premises/install) for step-by-step commands and troubleshooting. # System Requirements Source: https://bloodhound.specterops.io/on-premises/system-requirements Review hardware, software, and network requirements for the embedded cluster deployment option of an on-premises instance of BloodHound Enterprise. Applies to BloodHound Enterprise only This page defines the infrastructure, network, collector, and security prerequisites for an embedded cluster deployment of an on-premises instance of BloodHound Enterprise. ## BloodHound Enterprise host Provision one Linux VM to the required specification below. BloodHound Enterprise uses an all-in-one deployment model for embedded cluster installations. The application and Kubernetes run on the same host, along with the bundled PostgreSQL database if you choose that option during installation (see [Database](#database)). Undersized compute can still install successfully but will degrade graph analysis under load. The disk latency check is a hard gate, so spinning disk blocks the installer. | Requirement | Specification | Preflight | | ------------ | ------------------------------------ | -------------------------------------- | | OS | Any systemd-based Linux distribution | Blocks install | | Architecture | x86-64 only (no ARM) | Blocks install | | Kernel | 4.3+ | Blocks install | | cgroups | v1 or v2 | Blocks install | | Filesystem | XFS with ftype=1; ext4 is fine | Blocks install | | SELinux | Supported (embedded cluster 2.8.0+) | - | | Access | Root or sudo | Blocks install | | CPU | 48 cores | No check, but required for performance | | RAM | 160 GB | No check, but required for performance | | Storage | 680 GB SSD (640 app/db + 40 cluster) | No check, but required for performance | | Disk latency | P99 write \<=10ms (use SSDs) | Blocks install | Not supported: STIG/CIS-hardened images, single-stack IPv6. ## Database BloodHound Enterprise requires PostgreSQL 18. During installation, you can either use the bundled PostgreSQL instance that runs on the BloodHound Enterprise host, or provide connection details for an external PostgreSQL database that you manage. | Requirement | Specification | | ------------------ | ------------- | | PostgreSQL version | 18 | | Port | 5432 (TCP) | ## Reverse proxy Embedded cluster deployments include a built-in ingress controller that exposes BloodHound Enterprise over HTTPS on port `443` by default. In the Installation Wizard, you configure the application FQDN, select **Ingress**, and either generate a TLS certificate or upload your own certificate and key. An external reverse proxy or load balancer is optional. Use one only if your environment requires capabilities such as a corporate WAF, centralized certificate management, or hostname multiplexing. Do not expose BloodHound Enterprise over unencrypted HTTP. Users and collectors should connect to the application over HTTPS. | Requirement | Specification | | ---------------------- | ------------------------------------------------------------------------------ | | Default access pattern | Embedded cluster ingress on port `443` | | TLS certificate | Generated in the Installation Wizard or customer-provided | | External reverse proxy | Optional for WAF, centralized certificate management, or hostname multiplexing | ## DNS Create one `A` record for the BloodHound Enterprise FQDN. By default, it should resolve to the Linux host that runs the embedded cluster. If you use an external reverse proxy or load balancer, it should resolve to that frontend instead. The record must be resolvable from both user workstations and collector hosts. If collectors operate in separate network segments or separate DNS zones, the same record must resolve there as well or uploads will fail. Without a DNS record, users must connect by IP address, and SSL certificates will not validate. ## Network/firewall BloodHound Enterprise requires two inbound ports and (for online installations) outbound HTTPS access to the packaging service. No inbound internet access is required and BloodHound Enterprise does not need to be reachable from the internet. Open the following ports. For air-gapped environments, skip the outbound rules entirely. ### Inbound The inbound ports to the BloodHound Enterprise server are: | Port | Protocol | Purpose | | ----- | -------- | ---------------------------------------------------------------------------------------------------- | | 443 | TCP | User and collector HTTPS access (via embedded cluster ingress or an optional external reverse proxy) | | 30080 | TCP | Installation Wizard access (restrict to admins) | ### Outbound The outbound destinations from the BloodHound Enterprise server (online install only) are: | Destination | Port | Purpose | | --------------------------------------------------------------------------- | ---- | ------------------------------------------------------ | | `replicated.app`
`proxy.replicated.com`
`registry.replicated.com` | 443 | Installer, updates, image registry, license validation | **Recommendation** Use the online installation model whenever possible. If the BloodHound Enterprise server can reach the listed destinations on port 443, installation and future updates remain automated. In air-gapped environments, skip the outbound rules but expect every update to be a manual download, transfer, and apply cycle. ## Collectors Collectors run separately from the BloodHound Enterprise host and upload data to it over HTTPS. Provision collector hosts according to the requirements in the relevant collector documentation. | Collector | Use case | Requirements | | ----------------------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | SharpHound Enterprise | Active Directory data collection | [SharpHound Enterprise system requirements](/install-data-collector/install-sharphound/system-requirements) | | AzureHound Enterprise | Entra ID and Azure data collection | [AzureHound Enterprise system requirements](/install-data-collector/install-azurehound/system-requirements) | | OpenHound for BloodHound Enterprise | Platform data collection, such as GitHub, Jamf, and Okta | [OpenHound for BloodHound Enterprise system requirements](/openhound/enterprise) | # Upgrade an external PostgreSQL database Source: https://bloodhound.specterops.io/on-premises/upgrade-postgres Upgrade an external PostgreSQL database from version 16 to 18 for on-premises deployments of BloodHound Enterprise. Applies to BloodHound Enterprise only This guide applies to on-premises deployments of BloodHound Enterprise using an **external** PostgreSQL database instead of the PostgreSQL database bundled with the embedded cluster. PostgreSQL 18 enables new capabilities, delivers meaningful performance improvements, and is required for BloodHound Enterprise. If your external database is still running PostgreSQL 16, follow the steps on this page to upgrade and migrate your data. The PostgreSQL 18 Docker image uses a different volume mount path than version 16. If your external database runs in Docker, you cannot simply update the image tag — the volume mount point has changed. | PostgreSQL version | Volume mount path | | ------------------ | -------------------------- | | 16 (and earlier) | `/var/lib/postgresql/data` | | 18 (and later) | `/var/lib/postgresql` | Starting a PostgreSQL 18 container against an existing PostgreSQL 16 volume will fail. ## Before you begin Back up your database before starting the upgrade. Verify that your backup is complete and restorable before proceeding. Confirm the following before you begin: * You have access to a PostgreSQL 18 instance (or can upgrade your existing host to PostgreSQL 18) * You have sufficient disk space for a full database dump * Your database administrator is available to adapt commands to your specific environment * You know your PostgreSQL connection details (host, port, username, database name) The exact commands in this guide vary depending on how your external database is deployed (Docker, bare-metal, VM, or managed service). Work with your database administrator to adapt the procedure to your environment. For a concrete Docker Compose reference, see the [Community Edition upgrade guide](/get-started/upgrade-postgres). ## Upgrade process Stop the BloodHound Enterprise application to prevent new writes to the database during the upgrade. Use `pg_dump` to export your existing database to a compressed dump file. Replace the placeholder values with your actual connection details: ```bash theme={null} pg_dump -h [host] -p 5432 -U [username] -d [database] -Fc -Z 9 -f pg16_backup.dump ``` Verify that the dump file was created and has a non-zero size before continuing. If your PostgreSQL instance uses a data volume or directory, create a backup of it before making changes. The exact method depends on your deployment. Upgrade your PostgreSQL instance to version 18 using the method appropriate for your environment. PostgreSQL major version upgrades are not backward-compatible. After upgrading to PostgreSQL 18, you cannot start the service against a PostgreSQL 16 data directory without first restoring from your dump file. After PostgreSQL 18 is running, restore the database from the dump file: ```bash theme={null} pg_restore -h [host] -p 5432 -U [username] -d [database] --clean --if-exists pg16_backup.dump ``` Wait for the restore to complete before proceeding. Start the BloodHound Enterprise application and confirm it connects to the upgraded database successfully. ## Verify the upgrade After the application restarts, confirm that the database is healthy and your data is intact. Connect to your PostgreSQL instance and run the following query to confirm PostgreSQL 18 is running: ```sql theme={null} SELECT version(); ``` The output should include `PostgreSQL 18`. Open your browser and navigate to your BloodHound Enterprise instance. Log in and verify that your data, collectors, and configuration are intact. Review the BloodHound Enterprise application logs for any database connection errors or migration issues immediately after restart. ## Clean up After you have verified that the upgrade was successful and your data is intact, remove the temporary dump file and any volume backups you created during the migration. Remove any snapshot or volume backups using the tools appropriate for your environment. # OpenGraph API Source: https://bloodhound.specterops.io/opengraph/developer/api Information on how to use the OpenGraph API Applies to BloodHound Enterprise and CE # API Endpoints | | | | | -------- | ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------- | | `POST` | [/api/v2/custom-nodes](/reference/custom-node-management/create-custom-nodes) | Create a new custom node type with configuration. | | `GET` | [/api/v2/custom-nodes](/reference/custom-node-management/get-custom-nodes) | Get all custom node types and their configurations. | | `GET` | [/api/v2/custom-nodes/\{kind\_name}](/reference/custom-node-management/get-custom-node) | Get configuration for a specific node type. | | `PUT` | [/api/v2/custom-nodes/\{kind\_name}](/reference/custom-node-management/update-custom-node) | Update the configuration of an existing node type. | | `DELETE` | [/api/v2/custom-nodes/\{kind\_name}](/reference/custom-node-management/delete-custom-node) | Delete a custom node type. | | `POST` | [/api/v2/clear-database](/reference/database/delete-your-bloodhound-data) | Delete selected BloodHound data, including targeted graph data such as HasSession edges. | | `GET` | [/api/v2/extensions](/reference/opengraph-experimental/list-opengraph-extensions-information) | Get a list of all OpenGraph extensions. | | `PUT` | [/api/v2/extensions](/reference/opengraph-experimental/upserts-the-opengraph-extension) | Upserts the OpenGraph extension. | | `DELETE` | [/api/v2/extensions/\{extension\_id}](/reference/opengraph-experimental/delete-opengraph-extension) | Delete an OpenGraph extension. | | `GET` | [/api/v2/extensions-edges](/reference/opengraph-experimental/list-edge-kinds) | Get a list of all edge kinds across OpenGraph schemas. | | `GET` | [/api/v2/nodes/\{node\_id}](/reference/opengraph-experimental/get-node-by-graph-node-id) | Get details of a specific node by its graph-assigned integer ID. | | `GET` | [/api/v2/relationships/\{relationship\_id}](/reference/opengraph-experimental/get-relationship-by-graph-relationship-id) | Get details of a specific relationship by its graph-assigned integer ID. | # OpenGraph Best Practices Source: https://bloodhound.specterops.io/opengraph/developer/best-practices Dos and don'ts for OpenGraph Applies to BloodHound Enterprise and CE # Introduction This page collects best practices for creating [Graph Extensions](/opengraph/library) and tooling for OpenGraph. # Creating a new OpenGraph extension ## Elements of a Complete Submission This section lists the elements that are mandatory and nice-to-have in an OpenGraph extension submission. ### Mandatory 1. A Collector/Hound * A script that collects all information needed to populate the graph * The collector should create JSON that can be uploaded to BloodHound 2. Documentation on * Minimum system requirements to run the tool * OS * Software * Resources * How to install the collector * How to use the collector * Minimum permissions needed to collect the information * As a privileged user * As an unprivileged user * Command line options/switches * Examples of running the tool from the command line ### Nice to Have 1. Nodes and Edges Documentation (online) * Hosted wiki (e.g., GitHub) or * Markdown file in the repository * List of relevant information to document * General * Abuse Info * Remediation Info * OPSEC * References * Other fields as applicable 2. Optional API upload * Ability to upload the JSON output to a BloodHound instance via the [API](/integrations/bloodhound-api/working-with-api) without user interaction * Requires an [API key](/integrations/bloodhound-api/working-with-api#authentication) 3. Cypher Queries "Starter Pack" * Cypher Queries to help new users explore the new elements introduced to the Graph * Should be in the Custom Query JSON format for easy ingestion 4. Privilege Zone Rules * Queries for creating [Cypher-based](/analyze-data/privilege-zones/rules#cypher) Privilege Zone rules to help users classify high-value nodes in the graph 5. Icon Definition Pack * Including a script to upload them. See [example](/opengraph/custom-icons#example-with-python) * You can use a [Bearer Token](/integrations/bloodhound-api/working-with-api#use-a-jwt-bearer-token) instead of an API key as this script will typically run only once. * Do not hardcode credentials; use place holder for users to modify. Only necessary for generic graph data. Structured graphs include icon definitions in the [extension definition schema](/opengraph/developer/graph-definition). 6. [Arrows.app](https://arrows.app) diagram illustrating nodes and attack paths between them # Custom Icons Source: https://bloodhound.specterops.io/opengraph/developer/custom-icons Define custom icons and colors for OpenGraph node kinds. Applies to BloodHound Enterprise and CE To help visually differentiate your OpenGraph nodes in BloodHound, you can define custom icons and colors. The method for doing so depends on whether your extension produces a generic graph or a structured graph: * **Generic graphs**: Use the [`POST /api/v2/custom-nodes`](/reference/custom-node-management/create-custom-nodes) API endpoint to define custom icons and colors. * **Structured graphs**: Define icons and colors directly in the [`node_kinds`](/opengraph/developer/graph-definition#node_kinds) array of your extension definition schema. ## Icon options BloodHound supports the full free, solid icon set from Font Awesome. View available icons on the [official Font Awesome docs](https://fontawesome.com/search?o=r\&ic=free\&s=solid). When specifying an icon: * Use the Font Awesome icon name without any prefix, for example `house` or `smile` (not `fa-house` or `fas-house`). * Optionally specify a color per node kind. Acceptable values are `#RGB` or `#RRGGBB`. * If an icon name cannot be resolved, BloodHound renders a `(?)` icon. Use the `PUT /api/v2/custom-nodes` endpoint to correct invalid mappings. ## Examples The following examples show how to define custom icons for OpenGraph node kinds using an extension definition schema, the API Explorer, and a Python script. ### Extension definition schema For structured graphs, define icons and colors directly in the [`node_kinds`](/opengraph/developer/graph-definition#node_kinds) array of your extension definition schema. ```json theme={null} { "node_kinds": [ { "name": "example_Person", "display_name": "Person", "description": "A person in the graph", "icon": "user", "color": "#2299FF" }, { "name": "example_Device", "display_name": "Device", "description": "A device in the graph", "icon": "desktop", "color": "#00AA55" } ] } ``` ### Direct API call The following example request payload defines custom icons for three node kinds (`person`, `device`, and `alert`). Use this payload with the [`POST /api/v2/custom-nodes`](/reference/custom-node-management/create-custom-nodes) endpoint to create custom node icon mappings. ```json POST /api/v2/custom-nodes theme={null} { "custom_types": { "person": { "icon": { "type": "font-awesome", "name": "user", "color": "#2299FF" } }, "device": { "icon": { "type": "font-awesome", "name": "desktop" } }, "alert": { "icon": { "type": "font-awesome", "name": "house" } } } } ``` ### BloodHound API Explorer You can set custom icons directly from the BloodHound **API Explorer** without writing any code. Navigate to the API Explorer from the BloodHound menu. BloodHound menu showing the API Explorer option Under **Custom Node Management**, expand the `POST /api/v2/custom-nodes` endpoint and click **Try it out**. API Explorer showing the POST custom-nodes endpoint Enter your custom icon configuration in the **Request body** field and click **Execute**. API Explorer with a custom icon payload ready to execute A `201` response confirms your custom icons were created successfully. API Explorer showing a successful 201 response with the custom icon configuration ### Python script Use the following Python script to define custom icons via the API. You will need to generate a [Bearer Token](/reference/overview#jwt-bearer-token). Update the `url` variable and replace the placeholder token before running the script. ```python theme={null} import requests import json import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) url = "http://127.0.0.1:8080/api/v2/custom-nodes" headers = { "Authorization": "Bearer eyscduDoG7TmxU", "Content-Type": "application/json" } def define_icon(icon_type, icon_name, icon_color): payload = { "custom_types": { icon_type: { "icon": { "type": "font-awesome", "name": icon_name, "color": icon_color } } } } response = requests.post( url, headers=headers, json=payload, verify=False # Disables SSL verification ) print(f"Sent icon for: {icon_type}") print("Status Code:", response.status_code) print("Response Body:", response.text) print("---") # Call function for each icon type you want to send define_icon("Node1", "burst", "#03CEFC") define_icon("Node2", "home", "#D67500") ``` Call `define_icon` as many times as needed for each of your node types. # OpenGraph Edges Source: https://bloodhound.specterops.io/opengraph/developer/edges Define relationships between nodes in your OpenGraph data payloads. Applies to BloodHound Enterprise and CE OpenGraph edges define relationships between nodes. Use this page to validate edge kinds, endpoint matching behavior, and post-processing outcomes before ingest. Use this page to validate structure before ingestion. For a full data payload example, see [Graph Data](/opengraph/developer/graph-data). At minimum, each edge must include a `start` endpoint, an `end` endpoint, and a `kind` that describes the relationship type. ```json highlight={6,10,14} theme={null} { "graph": { "nodes": [], "edges": [ { "start": { "match_by": "id", "value": "node-12345" }, "end": { "match_by": "id", "value": "node-67890" }, "kind": "RelationshipType" } ] } } ``` An object that defines how to match the starting node of the edge. See [Endpoint Matching](#endpoint-matching). An object that defines how to match the ending node of the edge. See [Endpoint Matching](#endpoint-matching). A string that describes the relationship type. * A descriptive name that identifies the edge kind and does not overlap with [built-in edge](/resources/edges/overview) kinds. Consider using a prefix related to your data source or environment separated from the name by an underscore. For example, `Okta_ResetPassword`. For [structured graphs](/opengraph/overview#structured-graphs), a prefix that matches the extension's [`namespace`](/opengraph/developer/graph-definition#namespacing) is required. The `tag_` prefix is reserved in any letter case, including `tag_`, `Tag_`, and `TAG_`. Do not use this prefix for custom kinds. If any kind uses it, BloodHound rejects the entire upload. * Must match the regex pattern `^[A-Za-z0-9_]+$`, which means edge kinds can only contain uppercase letters, lowercase letters, numbers, and underscores. Spaces, dashes, backticks, and other special characters are not allowed in edge kinds. PascalCase is recommended for readability and consistency. Neo4j Cypher allows many special characters in symbolic names when the name is enclosed in backticks. BloodHound OpenGraph ingest is more restrictive: edge `kind` values must match `^[A-Za-z0-9_]+$`, so upload validation rejects backtick-escaped names, spaces, dashes, and other special characters. A key-value map of custom edge properties. Values must be strings, numbers, booleans, or arrays of primitives. Nested objects and arrays of objects are not allowed. Unless otherwise noted, examples below show edge objects only. ## Endpoint Matching Edges in OpenGraph data define relationships between nodes using a `start` endpoint object and an `end` endpoint object. You can control how BloodHound resolves each endpoint using one of three matching strategies in the `match_by` field. * `id` for direct node identifier matching * `property` for node property matching * `name` for legacy node name matching (deprecated) This flexibility allows you to link nodes based on their unique database identifiers or by dynamically finding them based on specific property values. Use identifier matching when possible. Property matching is more flexible, but it is slower and should be used only when you cannot match by node ID. ### Match by Identifier This is the default and most common method. It resolves an endpoint by unique node [`id`](/opengraph/developer/nodes#param-id). ```json Linking a specific user to a server using their unique IDs theme={null} { "start": { "match_by": "id", "value": "user-12345" }, "end": { "match_by": "id", "value": "server-98765" } } ``` Starting endpoint definition for the edge. Strategy for matching the starting node. If omitted, defaults to `id` for unique identifier matching. String containing the specific ID of the starting node. Ending endpoint definition for the edge. Strategy for matching the ending node. If omitted, defaults to `id` for unique identifier matching. String containing the specific ID of the ending node. Optional kind filter used in `start` to constrain the lookup to a specific node kind. For example, setting `kind: "User"` ensures that even if a name exists across multiple entity types, only the one classified as a `User` is selected. Optional kind filter used in `end` to constrain the lookup to a specific node kind. For example, setting `kind: "Server"` limits endpoint resolution to nodes classified as `Server`. Not used in this mode. If provided alongside `start.match_by: "id"`, validation fails. Not used in this mode. If provided alongside `end.match_by: "id"`, validation fails. ### Match by Property Use this strategy when you do not know the unique ID of the target node but can identify it using one or more known [`properties`](/opengraph/developer/nodes#param-properties) (for example, username, email address, hostname, or custom property). This method allows for dynamic resolution based on data available at the time of ingestion. To use this strategy, set the `match_by` property to `property`. ```json Linking a user to a server by matching the user's username property and the server's hostname property theme={null} { "start": { "match_by": "property", "property_matchers": [ { "key": "username", "operator": "equals", "value": "alice.smith" }, { "key": "active", "operator": "equals", "value": true } ], "kind": "User" }, "end": { "match_by": "property", "property_matchers": [ { "key": "hostname", "operator": "equals", "value": "db-prod-01" } ] } } ``` Starting endpoint definition for the edge. Strategy for matching the starting node. Set to `property` to match against one or more of the starting node's property values. Array of matchers used to find the starting node. BloodHound attempts to find a node that satisfies all matchers. At least one matcher is required, but you can provide multiple matchers in the array. The system will attempt to find a node that satisfies all conditions simultaneously. Optional kind filter used to narrow node resolution for the starting node. Ending endpoint definition for the edge. Strategy for matching the ending node. Set to `property` to match against one or more of the ending node's property values. Array of matchers used to find the ending node. At least one matcher is required, but you can provide multiple matchers in the array. The system will attempt to find a node that satisfies all conditions simultaneously. Not used in this mode. Providing `start.value` when `start.match_by` is `property` causes validation errors. Not used in this mode. Providing `end.value` when `end.match_by` is `property` causes validation errors. Name of the node property to check. Matching operator. `equals` is currently the only supported value. Expected value for the property matcher. ### Match by Name (deprecated) Use this legacy strategy to resolve an endpoint by a `name` string. This strategy predates [Match by Property](/opengraph/developer/edges#match-by-property) and uses the `value` field directly, similar to [Match by Identifier](/opengraph/developer/edges#match-by-identifier). It is retained for backward compatibility. The `name` matching strategy is deprecated and will be removed in a future release. Migrate to [Match by Property](/opengraph/developer/edges#match-by-property) with an equality matcher on the `name` property. Mixing `match_by: "name"` with `property_matchers` fails schema validation. To use this strategy, set the `match_by` property to `name` and provide the name string in `value`. Combine with an optional `kind` filter to disambiguate nodes that share names across kinds. ```json Linking a user to a server by their names theme={null} { "start": { "match_by": "name", "value": "alice", "kind": "User" }, "end": { "match_by": "name", "value": "file-server-1", "kind": "Server" } } ``` Internally, BloodHound rewrites a `name` match to a property match against the `name` property. The equivalent payload using [Match by Property](/opengraph/developer/edges#match-by-property): ```json theme={null} { "start": { "match_by": "property", "property_matchers": [ { "key": "name", "operator": "equals", "value": "alice" } ], "kind": "User" }, "end": { "match_by": "property", "property_matchers": [ { "key": "name", "operator": "equals", "value": "file-server-1" } ], "kind": "Server" } } ``` Starting endpoint definition for the edge. Strategy for matching the starting node. Set to `name` to match against a name string. Name string used to look up the starting node. Optional kind filter used to constrain the lookup to a specific node kind. Recommended when names overlap across kinds. Ending endpoint definition for the edge. Strategy for matching the ending node. Set to `name` to match against a name string. Name string used to look up the ending node. Optional kind filter used to constrain the lookup to a specific node kind. Recommended when names overlap across kinds. Not used in this mode. Providing `property_matchers` when `start.match_by` is `name` causes validation errors. Not used in this mode. Providing `property_matchers` when `end.match_by` is `name` causes validation errors. ## Post-processing Post-processing in BloodHound runs during the analysis phase. During this phase, BloodHound generates specific edges to enrich the graph and reflect the evaluated graph state. After ingest completes, BloodHound builds a complete graph, deletes existing post-processed edges, and regenerates them. As a result, post-processed edge kinds that you add directly in OpenGraph payloads do not persist. BloodHound creates the following edges during post-processing: * [`ADCSESC1`](/resources/edges/adcs-esc1) * [`ADCSESC3`](/resources/edges/adcs-esc3) * [`ADCSESC4`](/resources/edges/adcs-esc4) * [`ADCSESC6a`](/resources/edges/adcs-esc6a) * [`ADCSESC6b`](/resources/edges/adcs-esc6b) * [`ADCSESC9a`](/resources/edges/adcs-esc9a) * [`ADCSESC9b`](/resources/edges/adcs-esc9b) * [`ADCSESC10a`](/resources/edges/adcs-esc10a) * [`ADCSESC10b`](/resources/edges/adcs-esc10b) * [`ADCSESC13`](/resources/edges/adcs-esc13) * [`AddMember`](/resources/edges/add-member) * [`AdminTo`](/resources/edges/admin-to) * [`AZAddOwner`](/resources/edges/az-add-owner) * [`AZMGAddMember`](/resources/edges/az-mg-add-member) * [`AZMGAddOwner`](/resources/edges/az-mg-add-owner) * [`AZMGAddSecret`](/resources/edges/az-mg-add-secret) * [`AZMGGrantAppRoles`](/resources/edges/az-mg-grant-app-roles) * [`AZMGGrantRole`](/resources/edges/az-mg-grant-role) * [`AZRoleApprover`](/resources/edges/az-role-approver) * [`CanPSRemote`](/resources/edges/can-ps-remote) * [`CanRDP`](/resources/edges/can-rdp) * [`CoerceAndRelayNTLMToADCS`](/resources/edges/coerce-and-relay-ntlm-to-adcs) * [`CoerceAndRelayNTLMToLDAP`](/resources/edges/coerce-and-relay-ntlm-to-ldap) * [`CoerceAndRelayNTLMToLDAPS`](/resources/edges/coerce-and-relay-ntlm-to-ldaps) * [`CoerceAndRelayNTLMToSMB`](/resources/edges/coerce-and-relay-ntlm-to-smb) * [`DCSync`](/resources/edges/dc-sync) * [`EnrollOnBehalfOf`](/resources/edges/enroll-on-behalf-of) * [`EnterpriseCAFor`](/resources/edges/enterprise-ca-for) * [`ExecuteDCOM`](/resources/edges/execute-dcom) * [`ExtendedByPolicy`](/resources/edges/extended-by-policy) * [`GoldenCert`](/resources/edges/golden-cert) * [`HasTrustKeys`](/resources/edges/has-trust-keys) * [`IssuedSignedBy`](/resources/edges/issued-signed-by) * [`Owns`](/resources/edges/owns) * [`OwnsLimitedRights`](/resources/edges/owns-limited-rights) * [`ProtectAdminGroups`](/resources/edges/protect-admin-groups) * [`SyncLAPSPassword`](/resources/edges/sync-laps-password) * [`SyncedToADUser`](/resources/edges/synced-to-ad-user) * [`SyncedToEntraUser`](/resources/edges/synced-to-entra-user) * [`TrustedForNTAuth`](/resources/edges/trusted-for-nt-auth) * [`WriteOwner`](/resources/edges/write-owner) * [`WriteOwnerLimitedRights`](/resources/edges/write-owner-limited-rights) To create one of BloodHound's built-in post-processed edges using OpenGraph, include the supporting edges that cause BloodHound to generate that relationship during post-processing. For example, if you include an `AdminTo` edge directly in your OpenGraph payload, BloodHound removes it during post-processing and the edge does not persist in the final graph as expected. Instead of adding `AdminTo` edges directly, include the supporting edges that cause the post-processor to generate the `AdminTo` edge. The common pattern that triggers the creation of the `AdminTo` edge is: ```mermaid theme={null} graph LR Entity -->|MemberOfLocalGroup| LocalGroup -->|LocalToComputer| Computer ``` See the following example OpenGraph payload that produces the effect: ```json theme={null} { "graph": { "nodes": [ { "id": "TESTNODE", "kinds": ["User"] } ], "edges": [ { "start": { "match_by": "id", "value": "TESTNODE" }, "end": { "match_by": "id", "value": "S-1-5-21-2697957641-2271029196-387917394-2171-544" }, "kind": "MemberOfLocalGroup" } ] } } ``` ## Schema Use the schema below as the source of truth for validation requirements. You can also download the same schema as a file: opengraph-edge.json. ```json theme={null} { "title": "Generic Ingest Edge", "description": "Defines an edge between two nodes in a generic graph ingestion system. Each edge specifies a start and end node using one of three matching strategies: by unique identifier (match_by: id), by name (match_by: name, deprecated), or by one or more property matchers (match_by: property). A kind is required to indicate the relationship type. Optional properties may include custom attributes. You may optionally constrain the start or end node to a specific kind using the kind field inside each reference.", "type": "object", "$defs": { "property_map": { "type": ["object", "null"], "description": "A key-value map of edge attributes. Values must not be objects. If a value is an array, it must contain only primitive types (e.g., strings, numbers, booleans) and must be homogeneous (all items must be of the same type).", "additionalProperties": { "anyOf": [ { "type": "string" }, { "type": "number" }, { "type": "boolean" }, { "type": "array", "anyOf": [ { "items": { "type": "string" } }, { "items": { "type": "number" } }, { "items": { "type": "boolean" } } ] } ] } }, "endpoint": { "type": "object", "properties": { "match_by": { "type": "string", "enum": ["id", "name", "property"], "default": "id", "description": "Whether to match the start node by its unique object ID or by a series of property matches. Note that the name value here is deprecated and will be removed in future versions. Users are advised to use the multi-property match strategy moving forward." }, "property_matchers": { "type": "array", "minItems": 1, "items": { "type": "object", "properties": { "key": { "type": "string" }, "operator": { "type": "string", "enum": ["equals"] }, "value": { "type": ["string", "number", "boolean"] } }, "required": ["key", "operator", "value"] } }, "value": { "type": "string", "description": "The value used for matching — either an object ID or a name, depending on match_by." }, "kind": { "type": "string", "description": "Optional kind filter; the referenced node must have this kind." } }, "if": { "allOf": [ { "properties": { "match_by": { "type": "string", "const": "property" } } }, { "not": { "properties": { "match_by": { "type": "null" } } } } ] }, "then": { "required": ["property_matchers"], "not": { "required": ["value"] } }, "else": { "required": ["value"], "not": { "required": ["property_matchers"] } } } }, "properties": { "start": { "$ref": "#/$defs/endpoint" }, "end": { "$ref": "#/$defs/endpoint" }, "kind": { "type": "string", "description": "Edge kind name must contain only alphanumeric characters and underscores.", "pattern": "^[A-Za-z0-9_]+$" }, "properties": { "$ref": "#/$defs/property_map" } }, "required": ["start", "end", "kind"], "examples": [ { "start": { "match_by": "id", "value": "user-1234" }, "end": { "match_by": "id", "value": "server-5678" }, "kind": "has_session", "properties": { "timestamp": "2025-04-16T12:00:00Z", "duration_minutes": 45 } }, { "start": { "match_by": "property", "property_matchers": [ { "key": "prop_1", "operator": "equals", "value": "value" } ] }, "end": { "match_by": "id", "value": "server-5678" }, "kind": "has_session", "properties": { "timestamp": "2025-04-16T12:00:00Z", "duration_minutes": 45 } }, { "start": { "match_by": "name", "value": "alice", "kind": "User" }, "end": { "match_by": "name", "value": "file-server-1", "kind": "Server" }, "kind": "accessed_resource", "properties": { "via": "SMB", "sensitive": true } }, { "start": { "value": "admin-1" }, "end": { "value": "domain-controller-9" }, "kind": "admin_to", "properties": { "reason": "elevated_permissions", "confirmed": false } }, { "start": { "match_by": "name", "value": "Printer-007" }, "end": { "match_by": "id", "value": "network-42" }, "kind": "connected_to", "properties": null } ] } ``` ## Troubleshooting * **Upload fails on edge kind pattern:** Ensure `kind` matches `^[A-Za-z0-9_]+$`. * **Endpoint validation fails:** Use either `value` for `id`/`name` matching, or `property_matchers` for `property` matching, not both. * **Expected edge disappears after ingest:** Check whether it is a post-processed edge kind. * **Upload rejected due to reserved kind:** Check whether the edge kind begins with `tag` in any casing (for example, `tag_`, `Tag_`, `TAG_`). BloodHound rejects the entire upload if a reserved kind prefix is found. # Graph Data Overview Source: https://bloodhound.specterops.io/opengraph/developer/graph-data Learn about the JSON structure and schema requirements for OpenGraph data payloads. Applies to BloodHound Enterprise and CE An OpenGraph data payload is a JSON document generated by an OpenGraph collector and uploaded to BloodHound. It represents entities as nodes and relationships as edges so you can search, explore, and analyze your environment, including Attack Paths. This page explains the structure and schema requirements for OpenGraph data payloads. It also provides a minimal viable payload example that you can use as a starting point for your own OpenGraph data. ## Before you begin Full OpenGraph support requires a PostgreSQL graph database and one of the following editions: * BloodHound Enterprise (uses PostgreSQL by default) * BloodHound Community v8.0.0+ (requires changing to a [PostgreSQL database](/get-started/custom-installation#postgresql)) While many OpenGraph features may work on a Neo4j database, there are functional and performance limitations (see the [OpenGraph FAQ](/opengraph/faq#why-is-it-taking-so-long-to-ingest-opengraph-data)). For full support, migrate to a PostgreSQL database. ## File requirements OpenGraph data payloads are accepted as `.json` files or `.zip` archives. A single upload operation can include a mix of valid `.json` and `.zip` files from different collectors (for example, SharpHound and OpenGraph). Additional constraints include: * `.zip` archives can contain multiple `.json` payload files. * Nested `.zip` archives are not supported. * If you upload a `.zip` archive, every file in the archive must be a valid data payload. ## Data payload structure All OpenGraph extensions must use the same data payload format. At minimum, a data payload must include the following top-level structure: ```json theme={null} { "graph": { "nodes": [], "edges": [] } } ``` The `nodes` and `edges` arrays must conform to minimum JSON schemas. BloodHound validates that the JSON is well-formed and that nodes and edges meet these schema requirements, but it does not enforce additional structure or constraints beyond them. For field-level details and schema requirements, see the following pages: | Page | Description | | ----------------------------------------- | -------------------------------------------------------------------------------------------------------- | | [Metadata](/opengraph/developer/metadata) | Defines payload-wide ingest behavior to facilitate organizing and managing graph data by source. | | [Nodes](/opengraph/developer/nodes) | Represents entities in your graph, such as users, devices, repositories, applications, and environments. | | [Edges](/opengraph/developer/edges) | Represents relationships between nodes in your graph indicating some form of interaction or access. | ## Graph structure To enforce additional structure beyond the minimum JSON schemas and enable advanced features in BloodHound, you can use an extension definition schema to define node and edge types, environments, and relationships. When you upload a data payload that conforms to an installed extension definition schema, BloodHound produces a **structured graph** with enhanced features compared to a generic graph. | Graph | Use this when | | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | Generic graph | You only need to ingest and [search](/analyze-data/explore/search#search) nodes and edges. | | Structured graph | You need schema-driven [analysis](/analyze-data/findings/analysis) and [pathfinding](/analyze-data/explore/search#pathfinding). | ## Data source OpenGraph extensions can collect data from many different systems. By default, BloodHound identifies this as *sourceless data*. If you're using multiple extensions, you may have multiple data sources feeding into your BloodHound graph database without any way to differentiate them. To facilitate organizing and managing OpenGraph data in BloodHound, you can register a `source_kind` and apply it to the relevant nodes in your data payload. Once registered, BloodHound reuses it for subsequent uploads with the same node kinds, even if the data payload omits it. For example, if you wanted to delete all data for a source, you could navigate to the **Administration** > **Database Management** page and select the name of the defined source. You can still remove OpenGraph data without registering and applying a `source_kind` by checking the **Sourceless data** option. However, that could have unintended consequences if you have multiple OpenGraph data sources without a registered `source_kind`. You can **register** a `source_kind` in two ways: * Specify it in at least one payload's [`metadata`](/opengraph/developer/metadata) object. After that initial registration, later payloads can reuse it without re-registering it. * Define it as an [`environments.source_kind`](/opengraph/developer/graph-definition#param-source-kind) value in an extension definition schema. You can **apply** a `source_kind` in two ways: * Specify it in the payload's [`metadata`](/opengraph/developer/metadata) object, which applies it to all defined nodes. * Explicitly include it in a node's [`kinds`](/opengraph/developer/nodes#param-kinds) array, which applies it only to that node. In structured graphs, nodes *must* include a registered `source_kind` to produce findings and metrics. Specific [node properties](/opengraph/developer/nodes#findings-and-metrics) are also required to produce findings for structured graphs in BloodHound Enterprise. The following points describe how `source_kind` influences metrics calculations: * **Exposure metrics:** Traverse upstream toward the origin and stop when a node in the same zone is encountered (for Tier Zero paths, this means stopping when a Tier Zero node is reached). The origin node itself is not included. * **Impact metrics:** Traverse downstream from the target node and include nodes in the same zone until the downstream set is fully enumerated. ## Minimal viable data payload The following is a minimal example payload for a generic graph. You can use it as a starting point for your own OpenGraph data payload. Copy and paste the following example into a new `.json` file or download this example file. When working with JSON files, use a plain text editor and UTF-8 encoding. Some text editors may introduce unexpected, non-standard characters that can cause parsing errors. It's always a good idea to validate your JSON with a [linter](https://jsonlint.com/) before uploading it to BloodHound. ```json theme={null} { "graph": { "nodes": [ { "id": "123", "kinds": ["Person"], "properties": { "displayname": "bob", "name": "BOB" } }, { "id": "234", "kinds": ["Person"], "properties": { "displayname": "alice", "name": "ALICE" } } ], "edges": [ { "kind": "Knows", "start": { "value": "123", "match_by": "id" }, "end": { "value": "234", "match_by": "id" } } ] } } ``` To test the ingestion in your BloodHound instance, navigate to **Explore** → **Cypher**. Enter the following query and hit `Run`: ```cypher theme={null} match p=()-[:Knows]-() return p ``` You should get something that looks like this: BOB->Knows->Alice ## Troubleshooting * **Upload fails before schema checks:** Confirm the file is valid JSON and encoded in UTF-8. * **Upload fails with payload structure errors:** Confirm the payload has top-level `graph` with both `nodes` and `edges` arrays. * **ZIP upload fails unexpectedly:** Ensure the archive does not contain nested ZIP files. # Graph Definition Source: https://bloodhound.specterops.io/opengraph/developer/graph-definition Define structured graph behavior for OpenGraph extensions with an extension definition schema. Applies to BloodHound Enterprise and Community Edition An extension definition schema tells BloodHound how to interpret, interact with, and represent extension-specific [data payloads](/opengraph/developer/graph-data). Use it when you want your extension to produce a structured graph and enable advanced analysis features in BloodHound. If you only need generic graph support, upload a valid OpenGraph data payload and skip this page. An extension definition schema is a JSON file that OpenGraph extension developers provide for BloodHound users to install on the [OpenGraph Management](/opengraph/extensions/manage) page before they upload data payloads that conform to the schema. In BloodHound Enterprise v9.3.0 and later, supported platform extensions (GitHub, Jamf, and Okta) are pre-installed. Other SpecterOps-supported schemas, such as SCIM, must still be uploaded manually. This page describes the components of an extension definition schema. For a full example of how these components work together, see the [example schema](/opengraph/developer/graph-definition#example-schema) section at the end of the page. ```json theme={null} { "schema": { ... }, "node_kinds": [ ... ], "relationship_kinds": [ ... ], "environments": [ ... ], "relationship_findings": [ ... ] } ``` Extension metadata identifying the extension, including version and namespace. Defines custom node types, visual representations, and Entity Panel content for an extension. Defines custom edge types, traversability behavior, and Entity Panel content for an extension. Defines the environments for a platform and identifies which node kinds an extension treats as principals within each environment. BloodHound Enterprise uses these environment definitions to group analysis, findings, and metrics. Defines the findings and remediation guidance BloodHound Enterprise uses when relationships of the specified kind represent potential Attack Paths in a target environment (based on Privilege Zone rules). ## Namespacing BloodHound uses namespaces to organize graph data. Each extension has a namespace key, which is used as a prefix in relevant `name` fields to indicate that the data belongs to that namespace. Namespacing allows multiple extensions to define similar graph data without conflicts, because the namespace prefix makes each name unique. With the exception of `schema.name`, all `name` fields must follow these namespacing rules to avoid collisions with built-in graph elements and other extensions. This does not apply to `display_name` fields, which are used only for human-readable labels. * Must be unique within the extension * Must be prefixed with the extension's namespace separated by an underscore (for example, `namespace_Name`) * Must include more than just the namespace prefix Although the `schema.name` field does not use a namespace prefix, it must still be unique for each extension. BloodHound treats extensions with matching `schema.name` fields as the same extension and overwrites existing extension definition schemas when uploaded. * If names are not unique or properly namespaced, schema uploads fail validation. * The `tag` namespace prefix is reserved in any letter case, including `tag`, `Tag`, and `TAG`. Do not use this prefix in an extension definition schema. If you do, BloodHound rejects the upload. ## Custom Entity Panel content Use the `info` object to define custom Entity Panel sections for node kinds and relationship kinds. When a user selects a node or relationship in Explore, BloodHound renders an Entity Panel with the specified accordion sections as defined by the `info` entries. Each `info` object is a map of section identifiers to rendered sections. Use stable identifiers for the keys so future schema updates can modify the same section without changing its identity. ```json theme={null} "info": { "overview": { "title": "Overview", "position": 1, "markdown": { "content": "This content appears in the Entity Panel." } } } ``` | Field | Requirement | Description | | ------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------- | | `info` key | Required | Stable section identifier. Must match `^[a-z0-9_-]{1,128}$`, which allows lowercase letters, numbers, hyphens, and underscores. | | `title` | Required | Section title displayed in the Entity Panel accordion header. | | `position` | Required | Integer that controls the order of extension-defined sections. Lower values render first. Use `1` or greater. | | `markdown.content` | Required | Markdown content rendered in the section body. | Every Entity Panel starts with **Object Information** at position `0`. This section lists all properties for the selected node or relationship. BloodHound renders additional `info` entries for the selected node's primary kind or the selected relationship's kind after **Object Information**. BloodHound orders those extension-defined sections by `position`, then `title`. If no `info` entries are defined for the selected kind, BloodHound still renders **Object Information**, but does not render additional extension-defined Entity Panel sections. ## Findings and metrics Applies to BloodHound Enterprise only This is a SpecterOps-managed feature. If it is not enabled in your environment, contact your account team for assistance. To produce findings and metrics, your data payload and extension definition schema must adhere to specific requirements that enable BloodHound Enterprise to identify and analyze the relevant nodes, edges, and environments in the graph. | Field | Where | Requirement | | ---------------------------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [`environment_kind`](/opengraph/developer/graph-definition#param-environment-kind) | Extension definition schema | Must match the environment node kind defined in the [`node_kinds.name`](/opengraph/developer/graph-definition#param-name-1) field.

If a relationship-based finding exists for the environment, [`relationship_findings.environment_kind`](/opengraph/developer/graph-definition#param-environment-kind-1) must also match. | | [`source_kind`](/opengraph/developer/graph-data#data-source) | Data payload | Must be present on nodes for them to be included in findings and metrics. | | [`environmentid`](/opengraph/developer/nodes#param-properties-environmentid) | Data payload | For nodes that belong to an environment, must be set to the `graph.nodes.id` of that environment node.

That environment's node kind must match the [`node_kinds.name`](/opengraph/developer/graph-definition#param-name-1) with the corresponding [`environments.environment_kind`](/opengraph/developer/graph-definition#param-environment-kind) definition. | | [`collected`](/opengraph/developer/nodes#param-properties-collected) | Data payload | Must be set to `true` on nodes with an environment kind to indicate that data has actually been collected for that environment.

For example, your payload might include multiple environment nodes, but only nodes for environments with successfully collected data should have `collected: true`. | Users must also create Privilege Zone [rules](/analyze-data/privilege-zones/rules) *after* installing an extension and *prior* to uploading a data payload to see findings in BloodHound Enterprise. ## `schema` Defines metadata about the extension itself. ```json theme={null} { "name": "SOOkta", "display_name": "Okta Extension (by SpecterOps)", "version": "v2.8.1", "namespace": "Okta" } ``` Unique name that identifies an extension in BloodHound. Human-readable label shown for this extension in BloodHound. Extension schema version, prefixed with `v` followed by semantic version format, for example `v1.0.0`. Namespace key used as a prefix for all `name` fields in the `node_kinds`, `relationship_kinds`, and `relationship_findings` arrays to indicate that the data belongs to that namespace. ## `node_kinds` Defines all node types in your extension. Each node kind represents an entity type (for example: user, device, or environment). ```json theme={null} [ { "name": "Okta_User", "display_name": "Okta User", "description": "An Okta user account", "is_display_kind": true, "icon": "user", "color": "#d33115", "info": { "overview": { "title": "Overview", "position": 1, "markdown": { "content": "Okta user accounts represent identities that can authenticate to Okta." } } } } ] ``` Unique node kind identifier. Must follow [namespacing](/opengraph/developer/graph-definition#namespacing) rules. For each node in a data payload to be included in a [structured graph](/opengraph/overview#structured-graphs), at least one value in its `kinds` array must match a `node_kinds.name` defined in the extension definition schema. The node schema for an environment must be defined here to be used for findings and metrics in BloodHound Enterprise. Human-readable label shown for this node kind in BloodHound. Optional description that explains what the node kind represents. Determines whether to use the `icon` and `color` definitions for nodes of this kind in the graph. Optional Font Awesome [icon name](https://fontawesome.com/search?s=solid) (without the "fa-" prefix) to show for nodes of this kind in the graph. Optional Hex color code (in `#RGB` or `#RRGGBB` format, the `#` is required) to apply to nodes of this kind in the graph. Optional [custom Entity Panel content](/opengraph/developer/graph-definition#custom-entity-panel-content) for nodes of this kind. ## `relationship_kinds` Defines what kind of connections exist. Each relationship kind represents a specific type of connection that can exist between nodes. ```json theme={null} [ { "name": "Okta_ResetPassword", "description": "Ability to reset passwords or temporary credentials for scoped Okta users", "is_traversable": true, "info": { "abuse": { "title": "Abuse", "position": 1, "markdown": { "content": "An attacker can use this relationship to reset a user's password and access the account." } } } } ] ``` Unique relationship kind identifier. Must follow [namespacing](/opengraph/developer/graph-definition#namespacing) rules. For each edge in a data payload to be included in a [structured graph](/opengraph/overview#structured-graphs), `kind` must match a `relationship_kinds.name` defined in the extension definition schema. Optional description of what the relationship means. Controls whether edges of this relationship kind are used for pathfinding and Attack Path detection. When `is_traversable` is set to `true` on a relationship kind, all edges of that kind inherit the same traversability behavior. Only traversable edges are included in pathfinding and considered for findings and metrics. Optional [custom Entity Panel content](/opengraph/developer/graph-definition#custom-entity-panel-content) for relationships of this kind. ## `environments` Applies to BloodHound Enterprise only Environments are platform-specific groupings of nodes that BloodHound Enterprise uses to scope findings and metrics. Each environment definition specifies which node kinds represent principals within each environment. ```json theme={null} [ { "environment_kind": "Okta_Organization", "source_kind": "Okta", "principal_kinds": [ "Okta_User", "Okta_Application", "Okta_ApiServiceIntegration" ] } ] ``` Represents which node type within the extension is considered an environment for organizational and analytics purposes. Must match a node kind defined in the `node_kinds` array. For findings and metrics in BloodHound Enterprise, the [`graph.nodes.properties.environmentid`](/opengraph/developer/nodes#param-properties-environmentid) field for applicable nodes in the data payload must match this field. Source kind that associates this environment definition with a specific platform for environment organization and selection. See [Data source](/opengraph/developer/graph-data#data-source) for details. Node kinds defined by this extension/schema that BloodHound should treat as principals. Must match a `node_kinds.name` defined in this schema. BloodHound Enterprise incorporates these node kinds in findings and metrics. ## `relationship_findings` Applies to BloodHound Enterprise only Defines findings based on relationships. Each finding represents a single instance of an Attack Path. BloodHound Enterprise identifies instances of a traversable edge targeting a node of higher privilege (violating a Privilege Zone boundary), analyzes the risk, and provides remediation guidance. **Best practice** If a finding `name` would match its associated `relationship_kind`, include `_Finding` as a suffix in the name to distinguish them. For example: * Relationship kind name: `Okta_ResetPassword` * Finding name: `Okta_ResetPassword_Finding` ```json theme={null} [ { "name": "Okta_ResetPassword_Finding", "display_name": "Password Reset Permission Across Privilege Zones", "environment_kind": "Okta_Organization", "relationship_kind": "Okta_ResetPassword", "remediation": { "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "long_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.", "short_remediation": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "long_remediation": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur." } } ] ``` Unique finding identifier. Must follow [namespacing](/opengraph/developer/graph-definition#namespacing) rules and be unique across all schema definition names, including node kinds, relationship kinds, and findings. Human-readable label shown for this finding in BloodHound. Environment kind where this finding should be evaluated. Must match an `environments.environment_kind` and `node_kinds.name` defined in any installed extension definition schema. For intra-extension findings, this typically matches the environment kind defined by the same extension schema. For cross-platform findings (for example, hybrid relationships), the impacted environment kind may be different. Relationship kind that contributes to this finding when matching edges are present and the edge crosses a Privilege Zone boundary. Must match a `relationship_kinds.name` defined in any installed extension definition schema. Remediation guidance to resolve the finding, including both short and long forms. A concise summary of the recommended remediation action. A detailed explanation of the finding and its cause, which can include Markdown formatting for better readability. A concise summary of the steps to remediate the finding. A detailed, step-by-step guide to remediate the finding, which can include Markdown formatting for better readability. ## Example schema The following example schema is based on Okta to illustrate how the different components of the schema work together. ```json theme={null} { "schema": { "name": "SOOkta", "display_name": "Okta Extension (by SpecterOps)", "version": "v1.0.0", "namespace": "Okta" }, "node_kinds": [ { "name": "Okta_User", "display_name": "Okta User", "description": "An Okta user account", "is_display_kind": true, "icon": "user", "color": "#d33115", "info": { "overview": { "title": "Overview", "position": 1, "markdown": { "content": "Okta user accounts represent identities that can authenticate to Okta." } } } } ], "relationship_kinds": [ { "name": "Okta_ResetPassword", "description": "Ability to reset passwords or temporary credentials for scoped Okta users", "is_traversable": true, "info": { "abuse": { "title": "Abuse", "position": 1, "markdown": { "content": "An attacker can use this relationship to reset a user's password and access the account." } } } } ], "environments": [ { "environment_kind": "Okta_Organization", "source_kind": "Okta", "principal_kinds": [ "Okta_User", "Okta_Application", "Okta_ApiServiceIntegration" ] } ], "relationship_findings": [ { "name": "Okta_ResetPassword_Finding", "display_name": "Password Reset Permission Across Privilege Zones", "environment_kind": "Okta_Organization", "relationship_kind": "Okta_ResetPassword", "remediation": { "short_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "long_description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.", "short_remediation": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "long_remediation": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur." } } ] } ``` # OpenGraph Graph Theory Source: https://bloodhound.specterops.io/opengraph/developer/graph-theory Attack Graph Model Design Requirements and Examples Applies to BloodHound Enterprise and CE # Introduction For several years, one of the biggest pain-points with contributing to BloodHound has been in getting nodes and edges ingested and correctly displayed in the GUI. BloodHound OpenGraph changes that. Now it is easy for anyone to add nodes and edges into BloodHound through the easy-to-use `/file-upload/` endpoint. However, while the process of adding nodes and edges to the product is greatly simplified, the product will not function as expected without a well-designed attack graph model. This document seeks to educate users on attack graph model design theory, best-practices, and requirements. An attack graph is a tool - a powerful force multiplier when wielded correctly, a frustrating and confusing hazard when not. This document aims to equip you with the knowledge and skills necessary to effectively wield this tool. # Basic Attack Graph Vocabulary and Design Theory Graphs are [well-understood](https://en.wikipedia.org/wiki/Graph_%28discrete_mathematics%29), well-studied mathematical constructs. You can find thousands of guides, tools, and academic papers that make use of graphs. This document will not replace a proper education or time spent working with graphs. But in this section we will touch on the most fundamental aspects of a graph you must understand in order to effectively get BloodHound to work with your nodes and edges. Every graph is constructed from two fundamental components: vertices (nodes) and edges (relationships): Node1 -- Edge1 --> Node2 The above graph has two nodes and one edge. The edge is **directed**. The source node of the edge is “Node 1”. The destination node of the edge is “Node 2”. **Every** edge in a BloodHound attack graph is **directed**, and is **one-way**. There are no bi-directional (“two-way”) edges in a BloodHound graph. In a BloodHound attack graph, the direction of the **edge** must match the direction of **access** or **attack**. Let's look at an example with Active Directory group memberships. In the BloodHound attack graph, we model Active Directory security group memberships like this: User -- MemberOf --> Group Think about the direction of the edge. Now think for a moment and try to figure out why we don't model AD security group memberships like this instead: Group -- HasMember --> User This seems perfectly reasonable at first glance, does it not? But remember that we are constructing an **attack graph** in order to discover **attack paths**. Edge directionality must serve attack path discovery. The direction of the edge going from the group to the user does not expose any attack path. Just because a user is a member of a group does not mean the group has any “control” of the user. But when the direction of the edge is from the user to the group, that DOES serve attack path discovery. Why? Because in Windows and Active Directory, members of security groups gain the privileges held by those groups. Let's extend the model a bit to make this easier to see: User -- MemberOf -> Group -- GenericAll --> Domain The user is a member of a group, and the group has full control of the domain. When the user authenticates to Active Directory, their Kerberos ticket will include the SID of the group. When the user uses that ticket to perform some action against the domain object, the security reference monitor will inspect the ticket, see the group SID, and grant the user all the permissions against the domain that the group has. **In reality the process is much more involved than this, but work with me here, people.** The above diagram shows a **path** connecting two **non-adjacent** nodes. **Adjacent** nodes are those that are connected together by an edge. In the above diagram, the adjacent nodes are: 1. “User” and “Group” via the “MemberOf” edge 2. “Group” and “Domain” via the “GenericAll” edge The “User” and “Domain” nodes are non-adjacent, yet there is a **path** connecting the “User” node to the “Domain” node. When designing your attack graph model, you **must** be aware of the **patterns** that will emerge from your design. There are many examples out there of people who want to make a contribution to the BloodHound graph who do not seem to be aware of this. Instead of proposing nodes/edges that create multi-node patterns, they propose nodes/edges that result **only** in one-to-one patterns: Badly connected nodes In the above graph there are two patterns: 1. From the red (top left) to the pink (top right) node 2. From the blue (bottom left) to the green (bottom right) node What's wrong with this design? Think of the graph as a map of **one-way streets**. In the above graph we have two one-way streets. But this map kinda sucks, doesn't it? You can only start in two places and you can only go to two places. You can't go from the red (top left) node to the blue (bottom left) node because there is no **path** connecting those nodes. This is a much better map: Well connected nodes Now is there a **path** from the red (top left) node to the blue (bottom left) node? Yes! It goes **through** the green (bottom right) node! The difference in the two graphs is the level of **connectedness**, or how well-linked the nodes are to one another. Let's belabor the point a little more to make it even more clear. The top model would be analogous to having a node represent both a **person** and the **address** where they live, with the edge representing the fact that they live at that address: Badly connected nodes While the bottom graph would be analogous to having the nodes represent the **addresses** and the edges represent **streets**: Well connected nodes It should be obvious that for the sake of **pathfinding**, the **second** model is the **only** model that will work. **This is actually how Google Maps works under the hood — it is a graph where locations are nodes and streets are edges.** If your graph model does not create paths connecting non-adjacent nodes, you should use a relational database instead. A graph database is the wrong tool for data that only produces one-to-one patterns. This article is adapted from [Andy Robbins](https://www.linkedin.com/in/robbinsandy/)' blog post, “[Attack Graph Model Design Requirements and Examples](https://specterops.io/blog/2025/08/01/attack-graph-model-design-requirements-and-examples/),” which goes beyond what's described here. # OpenGraph Metadata Source: https://bloodhound.specterops.io/opengraph/developer/metadata Learn how metadata influences ingestion and metrics behavior. Applies to BloodHound Enterprise and CE The `metadata` object defines payload-wide ingest behavior. One of its primary uses is registering a `source_kind`, which facilitates organizing and managing graph data by [source](/opengraph/developer/graph-data#data-source). ```json highlight={3} theme={null} { "metadata": { "source_kind": "GitHub" }, "graph": { "nodes": [], "edges": [] } } ``` The `metadata` object is optional for both generic graphs and structured graphs. Optional top-level metadata object that configures payload-wide ingest behavior. The `metadata` object accepts only `source_kind`. Any other fields cause validation errors. Source system label used to register a source kind and apply it to all nodes in a data payload. During ingestion, BloodHound appends this value to each node's `kinds` array. You can register a `source_kind` through the `metadata` object or through the extension definition schema. Once registered, BloodHound reuses it for subsequent uploads with the same node kinds, even if the payload omits `metadata.source_kind`. ## Schema Use the following JSON schema for validation requirements. ```json theme={null} { "title": "Metadata", "description": "Optional metadata about the ingest payload", "type": "object", "properties": { "source_kind": { "type": ["string","null"] } }, "additionalProperties": false } ``` ## Troubleshooting * **`source_kind` does not appear in expected nodes:** Verify that `metadata` is at the top level of the data payload and not nested under `graph`. * **Unexpected kind labels after ingest:** Confirm that your `source_kind` value is intentional because `metadata.source_kind` appends it to every node in the data payload. # OpenGraph Nodes Source: https://bloodhound.specterops.io/opengraph/developer/nodes Define graph objects in your OpenGraph data payloads. Applies to BloodHound Enterprise and CE Nodes represent the entities in your graph, such as users, devices, repositories, applications, and (for findings and metrics) environments. Each node requires a stable identifier and a `kinds` array, and can also include optional properties. Use this page to validate structure before ingestion. For a full data payload example, see [Graph Data](/opengraph/developer/graph-data). At minimum, each node must include a unique identifier and a `kinds` array. ```json highlight={5-6} theme={null} { "graph": { "nodes": [ { "id": "123", "kinds": ["KindName"], "properties": { "key": "value" } } ], "edges": [] } } ``` Unique identifier for the node in your data payload. Use a stable, globally unique value from the source system when possible. Every node must have a globally unique `id` to distinguish it from every other node in BloodHound's graph database. Use the identifier that the source system itself uses to differentiate objects, such as a GUID when available. | Identifier quality | Examples | | -------------------------------- | ---------------------------------------------------------------- | | **Good**: globally unique | GUIDs, SIDs, certificate thumbprints | | **Avoid**: not guaranteed unique | Usernames, email addresses, hostnames, auto-incremented integers | Do not use colons (`:`) in node `id` values. BloodHound does not fully support colons in OpenGraph object IDs. An array of strings that classify the node. You can include up to three kinds per node. The first value is the primary kind, which controls the node styling in the graph. In Cypher queries, node kinds function as labels for pattern matching. Use the colon (`:`) syntax to match nodes by kind: `MATCH (n:Okta_User)` or `MATCH (n:Okta_User|Okta_Group)`. All values in the `kinds` array can be used as labels, enabling flexible querying by any assigned kind. * Node `kinds` must not overlap with [built-in node](/resources/nodes/overview) kinds. Consider using a prefix related to your data source or environment separated by an underscore. For example, `Okta_User`. For [structured graphs](/opengraph/overview#structured-graphs), a prefix that matches the extension's [`namespace`](/opengraph/developer/graph-definition#namespacing) is required. The `tag_` prefix is reserved in any letter case, including `tag_`, `Tag_`, and `TAG_`. Do not use this prefix for custom kinds. If any kind uses it, BloodHound rejects the entire upload. * For a node to participate in a structured graph, at least one value in its `kinds` array should match a [`node_kinds.name`](/opengraph/developer/graph-definition#node_kinds) defined in an installed extension definition schema. Optional key-value map of custom node attributes. Values must be primitives or homogeneous arrays of primitives. BloodHound displays node properties in the **Entity Panel** when you click a node in the graph. See [Property rules](/opengraph/developer/nodes#property-rules) and [Reserved properties](/opengraph/developer/nodes#reserved-properties) below for details on allowed property formats and reserved property names. Associates a node with a specific environment. To enable findings in structured graphs, define an environment node in `graph.nodes`. To be included in analysis, metrics, and findings generation, a node must have `properties.environmentid` set to an environment node's `id`. Environment nodes themselves must also have `properties.environmentid` set, but the value does not have to match the node's own `id`. For example, a child environment node can have `properties.environmentid` set to a parent node environment `id`. Indicates whether an environment node kind was observed directly during data collection. In structured graphs, only environment nodes with `properties.collected` set to `true` and a registered `source_kind` are used to produce findings. You can register and apply `source_kind` through [`metadata`](/opengraph/developer/metadata) or apply it directly in node `kinds` after it has been registered. This property is not necessary for nodes of other kinds. ## Property rules Properties must adhere to the following constraints: * Values must be: * string * number * boolean * array of primitives * Nested objects are not allowed * Arrays of objects are not allowed * Arrays must be homogeneous (e.g., all strings or all numbers) * Property names should be lowercase. BloodHound currently accepts property names with uppercase characters, but will enforce lowercase property names in a future release. * Property names are case-sensitive. For example, `exampleid` and `ExampleID` are treated as different properties on the same node. This can cause issues in languages that treat JSON keys as case-insensitive (for example, PowerShell and some .NET-based tools). * Special characters in property names or values may cause ingest to fail or lead to unexpected behavior. Avoid spaces and special characters. * The `lastseen` property is injected automatically by BloodHound during ingestion and overwrites any value provided in the payload. * The `reconcile` property is stripped during ingestion. * The following property values are uppercased during ingest if they are present as strings: * `name` * `operatingsystem` * `distinguishedname` * `environmentid` ## Reserved properties Some node properties are reserved and have special behavior during ingest. These properties must not be included in the `properties` object of a node definition, as they may cause ingest to fail or lead to unexpected behavior. ### `objectid` The property name `objectid` is reserved and **must not** be included in the `properties` object of a node definition. The top-level `id` field serves as the node identifier in your payload. During ingest, BloodHound maps this `id` value internally to `objectid`. If you also define `objectid` in `properties`, ingest fails due to a conflicting definition. Do not include `objectid` inside `properties`. Use only the root-level `id` field. ### `ref` The property name `ref` is reserved and **must not** be included in the `properties` object of a node definition. ## Findings and metrics Applies to BloodHound Enterprise only This is a SpecterOps-managed feature. If it is not enabled in your environment, contact your account team for assistance. To produce findings and metrics, your data payload and extension definition schema must adhere to specific requirements that enable BloodHound Enterprise to identify and analyze the relevant nodes, edges, and environments in the graph. | Field | Where | Requirement | | ---------------------------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [`environment_kind`](/opengraph/developer/graph-definition#param-environment-kind) | Extension definition schema | Must match the environment node kind defined in the [`node_kinds.name`](/opengraph/developer/graph-definition#param-name-1) field.

If a relationship-based finding exists for the environment, [`relationship_findings.environment_kind`](/opengraph/developer/graph-definition#param-environment-kind-1) must also match. | | [`source_kind`](/opengraph/developer/graph-data#data-source) | Data payload | Must be present on nodes for them to be included in findings and metrics. | | [`environmentid`](/opengraph/developer/nodes#param-properties-environmentid) | Data payload | For nodes that belong to an environment, must be set to the `graph.nodes.id` of that environment node.

That environment's node kind must match the [`node_kinds.name`](/opengraph/developer/graph-definition#param-name-1) with the corresponding [`environments.environment_kind`](/opengraph/developer/graph-definition#param-environment-kind) definition. | | [`collected`](/opengraph/developer/nodes#param-properties-collected) | Data payload | Must be set to `true` on nodes with an environment kind to indicate that data has actually been collected for that environment.

For example, your payload might include multiple environment nodes, but only nodes for environments with successfully collected data should have `collected: true`. | Users must also create Privilege Zone [rules](/analyze-data/privilege-zones/rules) *after* installing an extension and *prior* to uploading a data payload to see findings in BloodHound Enterprise. ## Schema Use the following JSON schema for validation requirements. You can also download the same schema as a file: opengraph-node.json. ```json theme={null} { "title": "Generic Ingest Node", "description": "A node used in a generic graph ingestion system. Each node must have a unique identifier (`id`) and at least one kind describing its role or type. Nodes may also include a `properties` object containing custom attributes.", "type": "object", "$defs": { "property_map": { "type": ["object", "null"], "description": "A key-value map of entity attributes. Values must not be objects. If a value is an array, it must contain only primitive types (e.g., strings, numbers, booleans) and must be homogeneous (all items must be of the same type).", "additionalProperties": { "anyOf": [ { "type": "string" }, { "type": "number" }, { "type": "boolean" }, { "type": "array", "anyOf": [ { "items": { "type": "string" } }, { "items": { "type": "number" } }, { "items": { "type": "boolean" } } ] } ] }, "not": { "required": ["objectid"] } } }, "properties": { "id": { "type": "string" }, "properties": { "$ref": "#/$defs/property_map" }, "kinds": { "type": ["array"], "items": { "type": "string" }, "minItems": 0, "maxItems": 3, "description": "An array of kind labels for the node. The first element is treated as the node's primary kind and is used to determine which icon to display in the graph UI. This primary kind is only used for visual representation and has no semantic significance for data processing." } }, "required": ["id", "kinds"], "examples": [ { "id": "user-1234", "kinds": ["Person"] }, { "id": "device-5678", "properties": { "manufacturer": "Brandon Corp", "model": "4000x", "is_active": true, "rating": 43.5, "environmentid": "my-environment-001", "collected": true }, "kinds": ["Device", "Asset"] }, { "id": "location-001", "properties": { "environmentid": "my-environment-001", "collected": false }, "kinds": ["Location"] } ] } ``` ## Troubleshooting * **Upload fails with schema validation errors:** Verify that every property value is a primitive or a homogeneous array of primitives. * **Unexpected duplicate properties in results:** Check for case differences such as `exampleid` and `ExampleID`. * **Upload fails on reserved property conflict:** Remove `objectid` from `properties` and keep the identifier only in `id`. * **Upload rejected due to reserved kind:** Check whether any node kinds begin with the reserved `tag_` prefix in any letter case, including `tag_`, `Tag_`, and `TAG_`. If any node kind uses this prefix, BloodHound rejects the entire upload. * **Search or Pathfinding behaves unexpectedly for a known node:** Confirm the node `id` does not contain a colon (`:`). BloodHound does not fully support colons in object IDs. # OpenGraph Community Incentive Program Source: https://bloodhound.specterops.io/opengraph/developer/ocip Description of the OpenGraph Community Incentive Program (OCIP) and how to participate ### Now That You Have OpenGraph, Show Us What You Can Do with It BloodHound has always been more than a tool; it’s been a platform shaped by the community that uses it to break things better. From the earliest privilege escalation chains to cutting-edge cross-domain attack paths, some of the best BloodHound content hasn’t come from us. It’s come from you. Now that OpenGraph is live in BloodHound 8.0, we’re throwing down a challenge: * Use it. * Abuse it. * Break new ground with it. We’re inviting the community to submit writeups, talk proposals, and real-world research that shows what’s possible with OpenGraph. Whether you’re owning data lakes or MacBooks, getting to a production database via GitHub, or just building something weird that works, we want to see it! Top submissions will get: * All authors of **blog posts** on research involving OpenGraph will receive a BloodHound Challenge coin and swag in the mail. * For **research talks** on OpenGraph accepted by a security conference with more than 200 attendees, we’ll have a special bonus. You can choose between any SpecterOps training class or a free trip to attend [SO-CON 2026](https://specterops.io/so-con/) (ticket, flight, and hotel included). Selected authors or researchers will be invited to participate in a SpecterOps webcast​ Maximum of 10 people per categories are eligible to receive this incentive award, at SpecterOps' discretion Submit your research using this [form](https://www.surveymonkey.com/r/bhe-opengraph-research). Let’s see what kind of trouble you can get into! ​ # Computed Edges Source: https://bloodhound.specterops.io/opengraph/extensions/github/computed-edges How OpenHound GitHub and GitHound compute effective branch access and secret scanning alert access edges Applies to BloodHound Enterprise and CE This document describes the computed edge logic used by both the [OpenHound GitHub collector](/openhound/collectors/github/overview) and [GitHound](https://github.com/SpecterOps/GitHound), and the edges that logic produces. For the empirical testing that validates the underlying security model, see [Mitigating Controls](/opengraph/extensions/github/mitigating-controls). ## Computed Branch Access Edges ### Overview The GitHub collectors compute effective branch push access as a post-collection step after branches and branch protection rules have been collected and before workflow analysis. In GitHound, this logic is implemented by `Compute-GitHoundBranchAccess`. **Why it exists:** The raw permission edges in the graph (`GH_WriteRepoContents`, `GH_PushProtectedBranch`, `GH_BypassBranchProtection`) are each necessary but not sufficient for push access. A user with `GH_WriteRepoContents` may be blocked by branch protection rules, while a user with `GH_PushProtectedBranch` only bypasses push restrictions (not PR reviews). Determining whether someone can actually push requires cross-referencing role permissions, branch protection rule settings, per-rule allowances, and `enforce_admins` state. This function performs that analysis and emits computed edges that represent verified push capability. **Key characteristics:** * Pure in-memory computation — no API calls * Operates over the full accumulated node and edge collections from prior steps * Produces only edges (no new nodes) ### Edge Kinds Produced | Edge Kind | Source | Target | Traversable | Description | | ---------------------- | ---------------------- | --------------- | ----------- | ----------------------------------------------------------------------------------------- | | `GH_CanCreateBranch` | `GH_RepoRole` | `GH_Repository` | Yes | Role can create new branches | | `GH_CanCreateBranch` | `GH_User` or `GH_Team` | `GH_Repository` | Yes | Per-rule allowance delta — actor can create branches when role alone doesn't grant access | | `GH_CanWriteBranch` | `GH_RepoRole` | `GH_Branch` | Yes | Role can push to this specific branch | | `GH_CanWriteBranch` | `GH_User` or `GH_Team` | `GH_Branch` | Yes | Per-rule allowance delta — actor can push when role alone doesn't grant access | | `GH_CanEditProtection` | `GH_RepoRole` | `GH_Branch` | Yes | Role can modify/remove the BPR(s) governing this branch | Most edges emit from `GH_RepoRole`. Per-actor edges from `GH_User`/`GH_Team` are only emitted when per-rule allowances (`pushAllowances`, `bypassPullRequestAllowances`) grant access beyond what the role provides. ### Reason Values Each computed edge includes a `reason` property explaining why access was granted: | Reason | Meaning | | -------------------------- | ---------------------------------------------------------------------- | | `no_protection` | No branch protection rule applies to this branch | | `admin` | Admin access bypasses the gate | | `push_protected_branch` | Role has `push_protected_branch` permission (bypasses push gate) | | `bypass_branch_protection` | Role has `bypass_branch_protection` permission (bypasses merge gate) | | `push_allowance` | Actor is in `pushAllowances` for the matching BPR | | `bypass_pr_allowance` | Actor is in `bypassPullRequestAllowances` (bypasses PR reviews only) | | `edit_repo_protections` | Role can modify/remove this BPR (used on `GH_CanEditProtection` edges) | ### Composition Queries Each computed edge includes a `query_composition` property containing a Cypher query that reveals the underlying graph elements that caused the edge to be created. | Edge Type | Source | What the query shows | | ---------------------- | ---------------------- | ----------------------------------------------------------------------- | | `GH_CanWriteBranch` | RepoRole → Branch | Role's permission edges + BPR protecting the branch | | `GH_CanCreateBranch` | RepoRole → Repository | Role's permission edges + wildcard BPR (if any) | | `GH_CanEditProtection` | RepoRole → Branch | Role's edit/admin permission edge + repo's branches + protecting BPR(s) | | `GH_CanWriteBranch` | User/Team → Branch | Actor's allowance edges to the BPR + actor's role path with permissions | | `GH_CanCreateBranch` | User/Team → Repository | Actor's push allowance to the wildcard BPR + actor's role path | ### The Two-Gate Model The computation evaluates two independent gates per branch. An actor must pass **both** gates to push. #### Merge Gate Active when `required_pull_request_reviews` or `lock_branch` is true on the protecting BPR. | Bypass Mechanism | Scope | Suppressed by `enforce_admins`? | | ----------------------------- | ---------- | ---------------------------------------------------- | | `GH_AdminTo` (admin access) | Role-level | Yes | | `GH_BypassBranchProtection` | Role-level | Yes | | `bypassPullRequestAllowances` | Per-actor | Yes (PR reviews only, does not bypass `lock_branch`) | #### Push Gate Active when `push_restrictions` is true on the protecting BPR. | Bypass Mechanism | Scope | Suppressed by `enforce_admins`? | | --------------------------- | ---------- | ------------------------------- | | `GH_AdminTo` (admin access) | Role-level | **No** | | `GH_PushProtectedBranch` | Role-level | **No** | | `pushAllowances` | Per-actor | **No** | The asymmetry is critical: `enforce_admins` only suppresses merge-gate bypasses. Admin users and users with `push_protected_branch` can always bypass push restrictions regardless of `enforce_admins`. ### Relationship to Raw Permission Edges The raw permission edges remain in the graph for detailed analysis: | Raw Edge | Traversable | Why not traversable | | --------------------------- | ----------- | ---------------------------------------------------- | | `GH_WriteRepoContents` | No | Necessary but not sufficient — BPR may block push | | `GH_PushProtectedBranch` | No | Bypasses push-gate only — merge-gate may still block | | `GH_BypassBranchProtection` | No | Bypasses merge-gate only — push-gate may still block | The computed edges (`GH_CanCreateBranch`, `GH_CanWriteBranch`) are **traversable** because they represent verified push capability after evaluating all gates and bypass mechanisms. ### Algorithm The computation operates in three phases. #### Phase 1: Index Building Constructs lookup structures from the raw node and edge collections for O(1) access during evaluation. **Node and edge indexes:** | Index | Key | Value | Purpose | | ----------- | --------------------- | ----------------- | ---------------------- | | `$nodeById` | node ID | node object | Look up any node by ID | | `$outbound` | `"edgeKind\|startId"` | list of end IDs | Follow edges forward | | `$inbound` | `"edgeKind\|endId"` | list of start IDs | Follow edges backward | **Domain-specific indexes:** | Index | Key | Value | Purpose | | ---------------------- | --------- | -------------------------------- | ----------------------------------- | | `$repoBranches` | repo ID | list of branch IDs | Enumerate branches per repo | | `$branchToBPR` | branch ID | BPR ID | Find protecting rule for a branch | | `$rolePermissions` | role ID | HashSet of permission edge kinds | Direct permissions per role | | `$pushAllowanceActors` | BPR ID | HashSet of actor IDs | Actors with push allowance per rule | | `$bypassPRActors` | BPR ID | HashSet of actor IDs | Actors with PR bypass per rule | #### Phase 2: Role Permission Resolution Builds full permission sets for all roles by traversing the `GH_HasBaseRole` inheritance chain. **`Get-BaseRolePerms`** performs a forward-transitive closure: given a role, follows outbound `GH_HasBaseRole` edges to collect all inherited permissions. For example: | Role | Direct Permissions | Inherited Permissions | Full Permission Set | | ------------------------ | ----------------------------------------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------- | | `repoAdmin` | `{GH_AdminTo, GH_PushProtectedBranch, GH_BypassBranchProtection}` | (none) | `{GH_AdminTo, GH_PushProtectedBranch, GH_BypassBranchProtection}` | | `repoMaintain` | `{GH_PushProtectedBranch}` | `{GH_WriteRepoContents}` (from write via HasBaseRole) | `{GH_PushProtectedBranch, GH_WriteRepoContents}` | | `repoWrite` | `{GH_WriteRepoContents}` | (none) | `{GH_WriteRepoContents}` | | Custom role (base=write) | `{GH_BypassBranchProtection}` | `{GH_WriteRepoContents}` (from write via HasBaseRole) | `{GH_BypassBranchProtection, GH_WriteRepoContents}` | #### Phase 3a: Role-Level Edge Emission For each repository and each write-capable role, evaluates whether the role's permissions alone are sufficient to bypass branch protection. **GH\_CanEditProtection:** If the role has `GH_EditRepoProtections` or `GH_AdminTo`, emit an edge from the role to each protected branch on the repo. **GH\_CanCreateBranch:** Evaluates whether the role can create new branches by checking for a wildcard (`*`) BPR with both `push_restrictions` and `blocks_creations` enabled: * No wildcard blocking BPR → emit `role → repo` (reason: `no_protection`) * Wildcard BPR exists + role has admin → emit `role → repo` (reason: `admin`) * Wildcard BPR exists + role has `push_protected_branch` → emit `role → repo` (reason: `push_protected_branch`) * Otherwise → no edge **GH\_CanWriteBranch:** Evaluates the merge gate and push gate for each branch using only the role's permissions: 1. Look up the protecting BPR (if any) 2. Evaluate the merge gate — blocked unless bypassed by admin or `bypass_branch_protection` (both suppressed by `enforce_admins`) 3. Evaluate the push gate — blocked unless bypassed by admin or `push_protected_branch` (neither affected by `enforce_admins`) 4. Branch is accessible only if both gates pass #### Phase 3b: Per-Actor Allowance Delta Per-rule allowances (`pushAllowances`, `bypassPullRequestAllowances`) are actor-specific — they grant access to individual users or teams, not to roles. This phase computes the **delta**: branches an actor can access via allowances that their role alone doesn't cover. For each actor in any per-rule allowance on a repository: 1. **Compute covered branches:** Union of role-accessible branches across all leaf roles the actor reaches 2. **Prerequisite check:** The actor must have write access (via their role) to the repo. Allowances don't grant write access — they only modify which branches a writer can push to 3. **GH\_CanCreateBranch delta:** If a wildcard blocking BPR exists and the actor's role doesn't grant `GH_CanCreateBranch`, check if the actor is in `pushAllowances` for the wildcard BPR. If so, emit `actor → repo` (reason: `push_allowance`) 4. **GH\_CanWriteBranch delta:** For each branch not covered by the actor's role, re-evaluate both gates considering the actor's allowance memberships. If both gates pass, emit `actor → branch` ### Edge Deduplication An `$emittedEdges` hashtable keyed by `"startId|endId|kind"` prevents duplicate edges when multiple code paths could emit the same edge. ### Graph Traversal Paths **Role-level (common case):** ``` User → GH_HasRole → RepoRole → GH_CanWriteBranch → Branch User → GH_HasRole → OrgRole → GH_HasBaseRole → ... → RepoRole → GH_CanWriteBranch → Branch ``` **Per-actor allowance delta:** ``` User → GH_CanWriteBranch → Branch Team → GH_CanWriteBranch → Branch ``` *** ## Computed Secret Scanning Access Edges ### Overview The GitHub collectors compute effective secret scanning alert read access as a post-collection step after secret scanning alerts have been collected and before app installation analysis. In GitHound, this logic is implemented by `Compute-GitHoundSecretScanningAccess`. **Why it exists:** The raw `GH_ViewSecretScanningAlerts` permission edges connect roles to organizations or repositories, but do not connect roles directly to the individual alert nodes. Without computed edges, BloodHound pathfinding cannot traverse from a role to the alert (and onward via `GH_ValidToken` to the compromised user identity). This function bridges that gap by resolving which specific alerts each role can read. **Key characteristics:** * Pure in-memory computation — no API calls * Produces only edges (no new nodes) * Simpler than branch access computation — no gate evaluation needed ### Edge Kind Produced | Edge Kind | Source | Target | Traversable | Description | | ------------------------------- | ------------- | ------------------------ | ----------- | ------------------------------------------------ | | `GH_CanReadSecretScanningAlert` | `GH_OrgRole` | `GH_SecretScanningAlert` | Yes | Org role can read all alerts in the organization | | `GH_CanReadSecretScanningAlert` | `GH_RepoRole` | `GH_SecretScanningAlert` | Yes | Repo role can read alerts in the repository | ### Reason Values | Reason | Meaning | | ---------------------- | ----------------------------------------------------------------------------------- | | `org_role_permission` | Org role has `GH_ViewSecretScanningAlerts` on the organization containing the alert | | `repo_role_permission` | Repo role has `GH_ViewSecretScanningAlerts` on the repository containing the alert | ### Algorithm #### Phase 1: Index Building Constructs lookup structures from the raw node and edge collections. * `$alertNodeIds` — HashSet of all `GH_SecretScanningAlert` node IDs * `$orgAlerts` — maps each org ID to its contained alert IDs * `$repoAlerts` — maps each repo ID to its contained alert IDs * `GH_ViewSecretScanningAlerts` edges are split into `$orgViewEdges` (target is `GH_Organization`) and `$repoViewEdges` (target is `GH_Repository`) #### Phase 2: Org-Level Emission For each `GH_ViewSecretScanningAlerts` edge targeting a `GH_Organization`: 1. Get the source role ID and target org ID 2. Look up all alerts in that org 3. For each alert: emit `GH_CanReadSecretScanningAlert` from the org role to the alert (reason: `org_role_permission`) #### Phase 3: Repo-Level Emission For each `GH_ViewSecretScanningAlerts` edge targeting a `GH_Repository`: 1. Get the source role ID and target repo ID 2. Look up all alerts in that repo 3. For each alert: emit `GH_CanReadSecretScanningAlert` from the repo role to the alert (reason: `repo_role_permission`) ### Security Significance This edge completes a critical attack path: an actor who can view secret scanning alerts gains access to the raw leaked secret values. When the leaked secret is a valid GitHub Personal Access Token (detected by the `GH_ValidToken` edge), the actor can impersonate the token owner and exercise all permissions granted to that token. **Complete attack path:** ``` User → GH_HasRole → OrgRole → GH_CanReadSecretScanningAlert → SecretScanningAlert → GH_ValidToken → CompromisedUser ``` # GH_AddAssignee Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_addassignee [Repository] Repo role can assign users to issues and pull requests Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_AddAssignee edge represents a role's ability to assign users to issues and pull requests. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_AddAssignee --> repo ``` # GH_AddCollaborator Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_addcollaborator [Organization] Org role can add outside collaborators Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_AddCollaborator edge represents that a role has the ability to add outside collaborators to organization repositories. This permission is typically restricted to Owners, as it grants repository access to external users who are not members of the organization. Outside collaborators bypass organizational membership controls, making this permission significant for security because it can be used to grant access to untrusted external identities without the visibility that full membership provides. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_AddCollaborator --> node2 ``` # GH_AddLabel Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_addlabel [Repository] Repo role can add labels to issues and pull requests Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_AddLabel edge represents a role's ability to add labels to issues and pull requests. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_AddLabel --> repo ``` # GH_AddMember Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_addmember Team role can add members to the team (maintainer privilege) Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_TeamRole](/opengraph/extensions/github/nodes/gh_teamrole) * Destination: [GH\_Team](/opengraph/extensions/github/nodes/gh_team) * Traversable: ✅ ## General Information The traversable GH\_AddMember edge indicates that a team role with the Maintainer permission level can add new members to the team. This edge is traversable because the ability to add members grants indirect access -- a maintainer can add any user to the team, and that user then inherits all of the team's repository permissions, effectively expanding the attack surface. ```mermaid theme={null} graph LR user("GH_User alice") maintainerRole("GH_TeamRole security-team\\maintainer") team("GH_Team security-team") repoRole("GH_RepoRole GitHound\\admin") user -- GH_HasRole --> maintainerRole maintainerRole -- GH_AddMember --> team team -- GH_HasRole --> repoRole ``` # GH_AdminTo Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_adminto [Repository] Repo role has admin access to the repository. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_AdminTo edge represents a role's full administrative access to the repository. Admin is the highest built-in repository role and grants control over all repository settings, including dangerous operations like deleting the repository or modifying its visibility. Admin access bypasses most protections including branch protection rules, unless `enforce_admins` is explicitly enabled on the branch protection rule. This edge is a key permission in the computed branch access model and is a high-value target in attack path analysis. ```mermaid theme={null} graph LR user1("GH_User alice") adminRole("GH_RepoRole GitHound\admin") repo("GH_Repository GitHound") orgOwners("GH_OrgRole SpecterOps\Owners") allRepoAdmin("GH_RepoRole SpecterOps\all_repo_admin") user1 -- GH_HasRole --> adminRole adminRole -- GH_AdminTo --> repo orgOwners -- GH_HasBaseRole --> allRepoAdmin allRepoAdmin -- GH_AdminTo --> repo ``` # GH_BypassBranchProtection Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_bypassbranchprotection [Repository] Repo role can bypass merge-gate branch protections (PR reviews, lock branch). Suppressed by enforce_admins. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_BypassBranchProtection edge represents a role's ability to bypass branch protection rules on the repository. This permission is available to Admin roles and custom roles that have been granted this specific permission. Bypassing branch protection allows merging pull requests without satisfying required review or status check requirements, effectively circumventing the merge gate. This bypass is suppressed when `enforce_admins` is enabled on the branch protection rule, which forces even admins to comply with the protection policy. ```mermaid theme={null} graph LR user1("GH_User alice") adminRole("GH_RepoRole GitHound\admin") customRole("GH_RepoRole GitHound\release_manager") repo("GH_Repository GitHound") user1 -- GH_HasRole --> adminRole adminRole -- GH_BypassBranchProtection --> repo customRole -- GH_BypassBranchProtection --> repo ``` # GH_BypassPullRequestAllowances Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_bypasspullrequestallowances User or team can bypass pull request requirements on a branch protection rule Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) * Destination: [GH\_BranchProtectionRule](/opengraph/extensions/github/nodes/gh_branchprotectionrule) * Traversable: ❌ ## General Information The non-traversable GH\_BypassPullRequestAllowances edge represents a per-actor allowance that bypasses the pull request review requirement on a branch protection rule. This edge identifies specific users or teams that can merge code without going through the normal PR review process. This is a significant security concern because these actors can push or merge changes directly, circumventing code review controls that protect branch integrity. Note that this bypass is suppressed when `enforce_admins` is enabled on the branch protection rule, meaning even listed actors must follow the PR review requirement. ```mermaid theme={null} graph LR user1("GH_User alice") team1("GH_Team release-managers") bpr1("GH_BranchProtectionRule main") user1 -- GH_BypassPullRequestAllowances --> bpr1 team1 -- GH_BypassPullRequestAllowances --> bpr1 ``` # GH_CallsWorkflow Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_callsworkflow [Workflow] Job calls a reusable workflow — GH_WorkflowJob → GH_Workflow Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_WorkflowJob](/opengraph/extensions/github/nodes/gh_workflowjob) * Destination: [GH\_Workflow](/opengraph/extensions/github/nodes/gh_workflow) * Traversable: ❌ ## General Information The traversable GH\_CallsWorkflow edge links a workflow job to a reusable workflow it invokes via the `uses:` key at the job level. This edge captures the reusable workflow call graph, enabling analysts to trace inherited permissions and secret access through called workflows. ### Local vs. remote reusable workflows * **Local** (`./. github/workflows/_ci.yml`): the destination is matched by `name` against workflows in the same repository. * **Remote** (`org/repo/.github/workflows/file.yml@ref`): the destination is matched by the full reference string. If the called workflow has not been collected, the edge destination will not resolve. The `reusable_ref` property on the edge always contains the raw `uses:` value from the workflow file. # GH_CanAccess Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_canaccess Personal access token or app installation can access this repository or organization Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_PersonalAccessToken](/opengraph/extensions/github/nodes/gh_personalaccesstoken), [GH\_AppInstallation](/opengraph/extensions/github/nodes/gh_appinstallation) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_CanAccess edge indicates that a personal access token or app installation has been granted access to specific repositories. This edge represents the scope of access granted to a token or app rather than a direct attack path, providing visibility into which repositories are reachable through non-human credentials. It is non-traversable because token and app access does not transitively extend to other principals. ```mermaid theme={null} graph LR pat("GH_PersonalAccessToken pat-alice-readonly") install("GH_AppInstallation ci-bot#6789") repo1("GH_Repository GitHound") repo2("GH_Repository BloodHound") pat -- GH_CanAccess --> repo1 install -- GH_CanAccess --> repo1 install -- GH_CanAccess --> repo2 ``` # GH_CanAssumeIdentity Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_canassumeidentity Repository can assume this cloud identity via OIDC federation (Azure workload identity or AWS IAM role) Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) * Destination: [AZFederatedIdentityCredential](/resources/nodes/az-federated-identity-credential), `AWSRole` * Traversable: ✅ ## General Information The traversable GH\_CanAssumeIdentity edge is a hybrid edge connecting GitHub OIDC token sources to cloud identity targets configured for GitHub Actions federation. This edge represents a verified path from GitHub Actions to cloud resource access. It is traversable because an attacker who can execute workflows in the source repository, branch, or environment can obtain an OIDC token that the cloud provider will accept, granting access to the associated cloud identity and its permissions. This edge is critical for identifying cross-cloud lateral movement paths from GitHub into Azure and AWS. ```mermaid theme={null} graph LR repo("GH_Repository GitHound") branch("GH_Branch main") env("GH_Environment production") azFic("AZFederatedIdentityCredential gh-deploy-prod") awsRole("AWSRole gh-actions-deploy-prod") repo -- GH_CanAssumeIdentity --> azFic branch -- GH_CanAssumeIdentity --> awsRole env -- GH_CanAssumeIdentity --> azFic ``` # GH_CanCreateBranch Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_cancreatebranch [Repository - Computed] Role can create new branches in this repository (unprotected branches that bypass the merge gate) Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole), [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ✅ ## General Information The traversable GH\_CanCreateBranch edge is a computed edge indicating that a role or actor can create new branches in a repository. The computation evaluates whether a wildcard (`*`) BPR with push restrictions and `blocks_creations` exists. If no such BPR exists, any write-capable role can create branches. If one exists, admin or `push_protected_branch` permission is required, or the actor must be listed in pushAllowances. Per-actor edges from [GH\_User](/opengraph/extensions/github/nodes/gh_user) or [GH\_Team](/opengraph/extensions/github/nodes/gh_team) are only emitted when BPR allowances grant branch creation access beyond what the role provides. Each edge includes a `reason` property and a `query_composition` Cypher query showing the underlying graph evidence. ## Scenarios ### `no_protection` — No wildcard BPR blocking creations No wildcard (`*`) BPR with `blocks_creations` exists. Any write-capable role can create new branches. ```mermaid theme={null} graph LR role("GH_RepoRole write") -->|GH_WriteRepoContents| repo("GH_Repository") role ==>|GH_CanCreateBranch| repo ``` ### `admin` — Admin bypasses wildcard BPR A wildcard BPR with `push_restrictions` and `blocks_creations` prevents branch creation. The admin role bypasses this restriction. ```mermaid theme={null} graph LR role("GH_RepoRole admin") -->|GH_AdminTo| repo("GH_Repository") repo -->|GH_HasBranch| branch("GH_Branch main") bpr("GH_BranchProtectionRule\npattern=*\npush_restrictions\nblocks_creations") -->|GH_ProtectedBy| branch role ==>|GH_CanCreateBranch| repo ``` ### `push_protected_branch` — Push-protected role bypasses wildcard BPR A wildcard BPR blocks creations. The [GH\_PushProtectedBranch](/opengraph/extensions/github/edges/gh_pushprotectedbranch) permission bypasses the push gate regardless of `enforce_admins`. ```mermaid theme={null} graph LR role("GH_RepoRole maintain") -->|GH_WriteRepoContents| repo("GH_Repository") role -->|GH_PushProtectedBranch| repo repo -->|GH_HasBranch| branch("GH_Branch main") bpr("GH_BranchProtectionRule\npattern=*\npush_restrictions\nblocks_creations") -->|GH_ProtectedBy| branch role ==>|GH_CanCreateBranch| repo ``` ### `push_allowance` — Per-actor push restriction bypass User or Team listed in the wildcard BPR's `pushAllowances` can create branches. This is a per-actor delta edge — only emitted when the actor's role doesn't already grant [GH\_CanCreateBranch](/opengraph/extensions/github/edges/gh_cancreatebranch). ```mermaid theme={null} graph LR user("GH_User alice") -->|GH_HasRole| role("GH_RepoRole write") role -->|GH_WriteRepoContents| repo("GH_Repository") repo -->|GH_HasBranch| branch("GH_Branch main") bpr("GH_BranchProtectionRule\npattern=*\npush_restrictions\nblocks_creations") -->|GH_ProtectedBy| branch user -->|GH_RestrictionsCanPush| bpr user ==>|GH_CanCreateBranch| repo ``` # GH_CanEditProtection Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_caneditprotection [Repository - Computed] Repo role can modify or remove branch protection rules for the repository/branch (computed from GH_EditRepoProtections + GH_ProtectedBy) Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) * Traversable: ✅ ## General Information The traversable GH\_CanEditProtection edge is a computed edge indicating that a role can modify or remove branch protection rules in a repository. This edge is emitted when the role has [GH\_EditRepoProtections](/opengraph/extensions/github/edges/gh_editrepoprotections) or [GH\_AdminTo](/opengraph/extensions/github/edges/gh_adminto) permissions and the repository contains at least one protected branch. Repo-targeted edges model the repo-wide security impact for attack path traversal; branch-targeted edges are also emitted as supporting evidence for each protected branch governed by those rules. ## Scenarios ### `admin` — Admin can edit protections The admin role has [GH\_AdminTo](/opengraph/extensions/github/edges/gh_adminto) which implicitly grants the ability to modify or remove any branch protection rule. ```mermaid theme={null} graph LR role("GH_RepoRole admin") -->|GH_AdminTo| repo("GH_Repository") repo -->|GH_HasBranch| branch("GH_Branch main") bpr("GH_BranchProtectionRule") -->|GH_ProtectedBy| branch role ==>|GH_CanEditProtection| repo role ==>|GH_CanEditProtection| branch ``` ### `edit_repo_protections` — Explicit edit permission A custom or standard role with the [GH\_EditRepoProtections](/opengraph/extensions/github/edges/gh_editrepoprotections) permission can modify or remove branch protection rules. ```mermaid theme={null} graph LR role("GH_RepoRole custom") -->|GH_EditRepoProtections| repo("GH_Repository") repo -->|GH_HasBranch| branch("GH_Branch main") bpr("GH_BranchProtectionRule") -->|GH_ProtectedBy| branch role ==>|GH_CanEditProtection| repo role ==>|GH_CanEditProtection| branch ``` # GH_CanPwnRequest Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_canpwnrequest [Computed] Repo role can exploit a pwn-requestable workflow to execute arbitrary code with the target's secrets and permissions — GH_RepoRole → GH_Repository / GH_Branch Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) * Traversable: ✅ ## General Information The traversable GH\_CanPwnRequest edge indicates that a repository role can exploit a pwn-requestable workflow to execute arbitrary code with the base branch's secrets, `GITHUB_TOKEN` permissions, and OIDC identity. This is a computed edge that combines workflow analysis with repository access and fork policy evaluation. ### Pwn Request Conditions A workflow is considered pwn-requestable (`is_pwn_requestable = true`) when **all** of the following are true: 1. **`pull_request_target` trigger**: The workflow is triggered by `pull_request_target`, which runs in the context of the base branch and has access to the base branch's secrets and permissions. 2. **Attacker-controlled checkout**: A step uses `actions/checkout` with a `ref` parameter pointing to the pull request head, meaning attacker-supplied code from the fork replaces the trusted repository contents. Detected ref patterns: * `${{ github.event.pull_request.head.sha }}` * `${{ github.event.pull_request.head.ref }}` * `${{ github.head_ref }}` ### Edge Drawing Conditions An edge is drawn from a [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) to the repository and its branches when: 1. **Read access**: The role has a [GH\_ReadRepoContents](/opengraph/extensions/github/edges/gh_readrepocontents) edge to the repository. 2. **Forkability**: The repository can be forked by the role holder. 3. **Pwn-requestable workflow**: The repository has at least one workflow with `is_pwn_requestable = true`. ### Attack Impact An attacker who exploits a pwn request gains code execution in the workflow runner with access to: * **Repository secrets** scoped to the base branch * **Organization secrets** accessible by the repository * **`GITHUB_TOKEN`** with the workflow's declared permissions * **OIDC tokens** if `id-token: write` is set, enabling cloud identity assumption via [GH\_CanAssumeIdentity](/opengraph/extensions/github/edges/gh_canassumeidentity) * **Environment secrets** if the workflow job targets a deployment environment ### Caveats * **OIDC traversal requires `id-token: write`**: The attack chain from GH\_CanPwnRequest through [GH\_CanAssumeIdentity](/opengraph/extensions/github/edges/gh_canassumeidentity) to a cloud role is only valid if the pwn-requestable workflow or job explicitly declares `id-token: write`. * **`GITHUB_TOKEN` permissions**: The `permissions:` block controls what the token can do, but does not limit secret access, OIDC token requests, or arbitrary code execution. ```mermaid theme={null} graph LR role("GH_RepoRole repo-read") repo("GH_Repository private-app") branch("GH_Branch main") wf("GH_Workflow vulnerable-ci.yml") secret("GH_RepoSecret DEPLOY_KEY") cloud("AWSRole deploy-prod") role -- GH_CanPwnRequest --> repo role -- GH_CanPwnRequest --> branch repo -.- |GH_HasWorkflow| wf repo -.- |GH_Contains| secret branch -- GH_CanAssumeIdentity --> cloud ``` # GH_CanReadSecretScanningAlert Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_canreadsecretscanningalert [Computed] Role can read secret scanning alerts (computed from GH_ViewSecretScanningAlerts permission + GH_Contains) Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_SecretScanningAlert](/opengraph/extensions/github/nodes/gh_secretscanningalert) * Traversable: ✅ ## General Information The traversable GH\_CanReadSecretScanningAlert edge is a computed edge indicating that a role can read a specific secret scanning alert, including the leaked secret value. The computation cross-references [GH\_ViewSecretScanningAlerts](/opengraph/extensions/github/edges/gh_viewsecretscanningalerts) permission edges with [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) structural edges (org-level and repo-level) to determine which alerts each role can access. This edge is traversable because reading an alert reveals the leaked secret — if the secret is a valid GitHub Personal Access Token, the [GH\_ValidToken](/opengraph/extensions/github/edges/gh_validtoken) edge enables identity compromise of the token's owner. Each edge includes a `reason` property (`org_role_permission` or `repo_role_permission`) and a `query_composition` Cypher query showing the underlying graph evidence. ## Scenarios ### `org_role_permission` — Org role views alerts via organization An org role with [GH\_ViewSecretScanningAlerts](/opengraph/extensions/github/edges/gh_viewsecretscanningalerts) to the organization can read all secret scanning alerts across the entire org. The computation follows [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) edges from the organization to each alert. ```mermaid theme={null} graph LR role("GH_OrgRole security_manager") -->|GH_ViewSecretScanningAlerts| org("GH_Organization") org -->|GH_Contains| alert("GH_SecretScanningAlert #42") role ==>|GH_CanReadSecretScanningAlert| alert alert -.->|GH_ValidToken| user("GH_User jdoe") ``` ### `repo_role_permission` — Repo role views alerts via repository A repo role with [GH\_ViewSecretScanningAlerts](/opengraph/extensions/github/edges/gh_viewsecretscanningalerts) to the repository can read secret scanning alerts in that specific repo. The computation follows [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) edges from the repository to each alert. ```mermaid theme={null} graph LR role("GH_RepoRole admin") -->|GH_ViewSecretScanningAlerts| repo("GH_Repository") repo -->|GH_Contains| alert("GH_SecretScanningAlert #17") role ==>|GH_CanReadSecretScanningAlert| alert alert -.->|GH_ValidToken| user("GH_User jdoe") ``` # GH_CanWriteBranch Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_canwritebranch [Repository - Computed] Role can push to this branch after evaluating branch protection rules, push restrictions, and bypass allowances Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole), [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) * Destination: [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) * Traversable: ✅ ## General Information The traversable GH\_CanWriteBranch edge is a computed edge indicating that a role or actor can push to a specific branch. The computation evaluates both the merge gate (PR review requirements) and push gate (push restrictions) of any branch protection rule protecting the branch. Role-level edges are the common case; per-actor edges from [GH\_User](/opengraph/extensions/github/nodes/gh_user) or [GH\_Team](/opengraph/extensions/github/nodes/gh_team) are only emitted when BPR allowances grant access beyond what the role provides. Each edge includes a `reason` property (`no_protection`, `admin`, `push_protected_branch`, `bypass_branch_protection`, `push_allowance`, `bypass_pr_allowance`) and a `query_composition` Cypher query showing the underlying graph evidence. ## Scenarios ### `no_protection` — Unprotected branch Branch has no BPR. Any write-capable role can push directly. ```mermaid theme={null} graph LR role("GH_RepoRole write") -->|GH_WriteRepoContents| repo("GH_Repository") repo -->|GH_HasBranch| branch("GH_Branch develop") role ==>|GH_CanWriteBranch| branch ``` ### `admin` — Admin bypasses both gates BPR blocks both the merge gate (PR reviews) and push gate (push\_restrictions). The admin role bypasses both gates. Requires `enforce_admins=false`; when `enforce_admins=true`, admin cannot bypass the merge gate. ```mermaid theme={null} graph LR role("GH_RepoRole admin") -->|GH_AdminTo| repo("GH_Repository") repo -->|GH_HasBranch| branch("GH_Branch main") bpr("GH_BranchProtectionRule\nrequired_pull_request_reviews\npush_restrictions\nenforce_admins=false") -->|GH_ProtectedBy| branch role ==>|GH_CanWriteBranch| branch ``` ### `push_protected_branch` — Push gate bypass Push gate blocked by `push_restrictions` (no merge gate block). The [GH\_PushProtectedBranch](/opengraph/extensions/github/edges/gh_pushprotectedbranch) permission bypasses the push gate regardless of `enforce_admins`. ```mermaid theme={null} graph LR role("GH_RepoRole maintain") -->|GH_WriteRepoContents| repo("GH_Repository") role -->|GH_PushProtectedBranch| repo repo -->|GH_HasBranch| branch("GH_Branch main") bpr("GH_BranchProtectionRule\npush_restrictions") -->|GH_ProtectedBy| branch role ==>|GH_CanWriteBranch| branch ``` ### `bypass_branch_protection` — Merge gate bypass Merge gate blocked by PR reviews. The [GH\_BypassBranchProtection](/opengraph/extensions/github/edges/gh_bypassbranchprotection) permission bypasses the merge gate. Requires `enforce_admins=false`; suppressed when `enforce_admins=true`. ```mermaid theme={null} graph LR role("GH_RepoRole custom") -->|GH_WriteRepoContents| repo("GH_Repository") role -->|GH_BypassBranchProtection| repo repo -->|GH_HasBranch| branch("GH_Branch main") bpr("GH_BranchProtectionRule\nrequired_pull_request_reviews\nenforce_admins=false") -->|GH_ProtectedBy| branch role ==>|GH_CanWriteBranch| branch ``` ### `push_allowance` — Per-actor push restriction bypass User or Team listed in the BPR's `pushAllowances` bypasses the push gate. This is a per-actor delta edge — only emitted when the actor's role-level access doesn't already cover the branch. ```mermaid theme={null} graph LR user("GH_User alice") -->|GH_HasRole| role("GH_RepoRole write") role -->|GH_WriteRepoContents| repo("GH_Repository") repo -->|GH_HasBranch| branch("GH_Branch main") bpr("GH_BranchProtectionRule\npush_restrictions") -->|GH_ProtectedBy| branch user -->|GH_RestrictionsCanPush| bpr user ==>|GH_CanWriteBranch| branch ``` ### `bypass_pr_allowance` — Per-actor PR review bypass User or Team listed in the BPR's `bypassPullRequestAllowances` bypasses the merge gate (PR reviews only, not `lock_branch`). Requires `enforce_admins=false`. This is a per-actor delta edge — only emitted when the actor's role-level access doesn't already cover the branch. ```mermaid theme={null} graph LR user("GH_User alice") -->|GH_HasRole| role("GH_RepoRole write") role -->|GH_WriteRepoContents| repo("GH_Repository") repo -->|GH_HasBranch| branch("GH_Branch main") bpr("GH_BranchProtectionRule\nrequired_pull_request_reviews\nenforce_admins=false") -->|GH_ProtectedBy| branch user -->|GH_BypassPullRequestAllowances| bpr user ==>|GH_CanWriteBranch| branch ``` # GH_CloseDiscussion Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_closediscussion [Repository] Repo role can close discussions Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_CloseDiscussion edge represents a role's ability to close discussions, preventing further replies. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_CloseDiscussion --> repo ``` # GH_CloseIssue Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_closeissue [Repository] Repo role can close issues Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_CloseIssue edge represents a role's ability to close issues. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_CloseIssue --> repo ``` # GH_ClosePullRequest Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_closepullrequest [Repository] Repo role can close pull requests Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ClosePullRequest edge represents a role's ability to close pull requests. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ClosePullRequest --> repo ``` # GH_Contains Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_contains Container relationship for organizational hierarchy (org contains secrets/variables, repo contains secrets/variables, environment contains secrets/variables) Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) * Destination: [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole), [GH\_TeamRole](/opengraph/extensions/github/nodes/gh_teamrole), [GH\_OrgSecret](/opengraph/extensions/github/nodes/gh_orgsecret), [GH\_AppInstallation](/opengraph/extensions/github/nodes/gh_appinstallation), [GH\_PersonalAccessToken](/opengraph/extensions/github/nodes/gh_personalaccesstoken), [GH\_PersonalAccessTokenRequest](/opengraph/extensions/github/nodes/gh_personalaccesstokenrequest), [GH\_RepoSecret](/opengraph/extensions/github/nodes/gh_reposecret), [GH\_EnvironmentSecret](/opengraph/extensions/github/nodes/gh_environmentsecret), [GH\_SecretScanningAlert](/opengraph/extensions/github/nodes/gh_secretscanningalert) * Traversable: ❌ ## General Information The non-traversable GH\_Contains edge represents structural containment within the GitHub resource hierarchy. The organization serves as the top-level container for users, teams, repositories, roles, secrets, app installations, and personal access tokens. Repositories contain their own repo-level secrets, and environments contain environment-scoped secrets. This edge is created by the collector to establish the organizational hierarchy of GitHub resources and is not traversable because containment alone does not imply privilege escalation. ```mermaid theme={null} graph LR node1("GH_Organization SpecterOps") node2("GH_User alice") node3("GH_Team engineering") node4("GH_Repository GitHound") node5("GH_RepoSecret DEPLOY_KEY") node1 -- GH_Contains --> node2 node1 -- GH_Contains --> node3 node1 -- GH_Contains --> node4 node4 -- GH_Contains --> node5 ``` # GH_ConvertIssuesToDiscussions Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_convertissuestodiscussions [Repository] Repo role can convert issues to discussions Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ConvertIssuesToDiscussions edge represents a role's ability to convert issues to discussions, moving them from the issue tracker to the discussions forum. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ConvertIssuesToDiscussions --> repo ``` # GH_CreateDiscussionCategory Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_creatediscussioncategory [Repository] Repo role can create discussion categories Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_CreateDiscussionCategory edge represents a role's ability to create new discussion categories. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_CreateDiscussionCategory --> repo ``` # GH_CreateRepository Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_createrepository [Organization] Org role can create repositories in the organization Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_CreateRepository edge represents that a role has the ability to create new repositories within the organization. This permission is available to Owners and custom organization roles that have been granted the repository creation permission. Creating repositories can introduce new attack surface to an organization, as each new repository is a potential vector for code execution through GitHub Actions workflows, secret exposure, and supply chain attacks. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_CreateRepository --> node2 ``` # GH_CreateSoloMergeQueueEntry Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_createsolomergequeueentry Repo role can create solo merge queue entries Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_CreateSoloMergeQueueEntry edge represents a role's ability to create solo merge queue entries, effectively bypassing the merge queue by merging independently of other queued changes. This permission is available to Admin roles and custom roles that have been granted this specific permission. Solo merge queue entries skip the batching and ordering guarantees of the merge queue, allowing changes to land without waiting for or being tested alongside other pending merges. This can circumvent the integration testing benefits that merge queues provide. ```mermaid theme={null} graph LR user1("GH_User carol") adminRole("GH_RepoRole GitHound\admin") customRole("GH_RepoRole GitHound\release_manager") repo("GH_Repository GitHound") user1 -- GH_HasRole --> customRole adminRole -- GH_CreateSoloMergeQueueEntry --> repo customRole -- GH_CreateSoloMergeQueueEntry --> repo ``` # GH_CreateTag Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_createtag [Repository] Repo role can create tags and releases Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_CreateTag edge represents a role's ability to create tags and releases. This permission is available to Maintain and Admin roles and custom roles that have been granted this specific permission. Creating tags can trigger CI/CD workflows and publish release artifacts. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\maintain") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_CreateTag --> repo ``` # GH_CreateTeam Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_createteam [Organization] Org role can create teams in the organization Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_CreateTeam edge represents that a role has the ability to create teams within the organization. Teams are the primary mechanism for granting groups of users access to repositories, so team creation is a stepping stone to broader access. This edge is created by the collector when enumerating organization role permissions, and its security significance lies in the fact that a newly created team can be granted repository access and then populated with controlled accounts. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_CreateTeam --> node2 ``` # GH_DeleteAlertsCodeScanning Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_deletealertscodescanning [Repository] Repo role can delete code scanning alerts Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_DeleteAlertsCodeScanning edge represents a role's ability to delete code scanning alerts from the repository. This permission is available to Admin roles and custom roles that have been granted this specific permission. Deleting code scanning alerts can obscure security vulnerabilities that have been detected in the codebase, which is significant from an audit and compliance perspective. An attacker with this permission could suppress evidence of vulnerabilities they have introduced. ```mermaid theme={null} graph LR user1("GH_User alice") adminRole("GH_RepoRole GitHound\admin") repo("GH_Repository GitHound") user1 -- GH_HasRole --> adminRole adminRole -- GH_DeleteAlertsCodeScanning --> repo ``` # GH_DeleteDiscussion Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_deletediscussion [Repository] Repo role can delete discussions Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_DeleteDiscussion edge represents a role's ability to delete discussions. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_DeleteDiscussion --> repo ``` # GH_DeleteDiscussionComment Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_deletediscussioncomment [Repository] Repo role can delete discussion comments Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_DeleteDiscussionComment edge represents a role's ability to delete discussion comments authored by any user. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_DeleteDiscussionComment --> repo ``` # GH_DeleteIssue Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_deleteissue [Repository] Repo role can delete issues Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_DeleteIssue edge represents a role's ability to delete issues permanently. Deleted issues cannot be recovered. This permission is available to Admin roles and custom roles that have been granted this specific permission. Deleting issues can destroy audit trails and remove evidence of security discussions or vulnerability reports. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\admin") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_DeleteIssue --> repo ``` # GH_DeleteTag Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_deletetag [Repository] Repo role can delete tags and releases Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_DeleteTag edge represents a role's ability to delete tags and releases. This permission is available to Admin roles and custom roles that have been granted this specific permission. Deleting tags can break downstream dependency references and remove published artifacts. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\admin") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_DeleteTag --> repo ``` # GH_DependsOn Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_dependson [Workflow] Job must run after another job (needs: dependency) — ordering only, not an access path Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_WorkflowJob](/opengraph/extensions/github/nodes/gh_workflowjob) * Destination: [GH\_WorkflowJob](/opengraph/extensions/github/nodes/gh_workflowjob) * Traversable: ❌ ## General Information The non-traversable GH\_DependsOn edge represents a `needs:` dependency between two jobs in the same workflow. This edge captures execution order constraints. The source job will not start until the destination job completes successfully. This edge is non-traversable because it represents sequencing only, not an access or privilege path. # GH_DeploysTo Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_deploysto [Workflow] Job deploys to a GitHub Environment — GH_WorkflowJob → GH_Environment Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_WorkflowJob](/opengraph/extensions/github/nodes/gh_workflowjob) * Destination: [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) * Traversable: ❌ ## General Information The non-traversable GH\_DeploysTo edge links a workflow job to the GitHub Environment it targets via the `environment:` key. This edge records which jobs deploy to which environments. Environments can gate deployments with protection rules and can expose environment-scoped secrets. # GH_EditCategoryOnDiscussion Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_editcategoryondiscussion [Repository] Repo role can change the category of a discussion Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_EditCategoryOnDiscussion edge represents a role's ability to change the category of a discussion, moving it between categories. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_EditCategoryOnDiscussion --> repo ``` # GH_EditDiscussionCategory Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_editdiscussioncategory [Repository] Repo role can edit discussion categories Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_EditDiscussionCategory edge represents a role's ability to edit discussion categories to reorganize discussion classification. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_EditDiscussionCategory --> repo ``` # GH_EditDiscussionComment Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_editdiscussioncomment [Repository] Repo role can edit discussion comments Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_EditDiscussionComment edge represents a role's ability to edit discussion comments authored by any user. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_EditDiscussionComment --> repo ``` # GH_EditRepoAnnouncementBanners Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_editrepoannouncementbanners [Repository] Repo role can edit repository announcement banners Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_EditRepoAnnouncementBanners edge represents a role's ability to edit repository announcement banners displayed to visitors. This permission is available to Maintain and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\maintain") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_EditRepoAnnouncementBanners --> repo ``` # GH_EditRepoCustomPropertiesValues Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_editrepocustompropertiesvalues [Repository] Repo role can edit custom property values on the repository Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_EditRepoCustomPropertiesValues edge represents a role's ability to edit custom property values on the repository. This permission is available to Admin roles and custom roles that have been granted this specific permission. Custom properties are organization-defined metadata fields on repositories that can be used for classification, compliance tagging, or policy enforcement via rulesets. Modifying custom property values could alter which organization-level rulesets apply to the repository, potentially bypassing security controls that are scoped by property-based targeting. ```mermaid theme={null} graph LR user1("GH_User alice") adminRole("GH_RepoRole GitHound\admin") repo("GH_Repository GitHound") user1 -- GH_HasRole --> adminRole adminRole -- GH_EditRepoCustomPropertiesValues --> repo adminRole -- GH_AdminTo --> repo ``` # GH_EditRepoMetadata Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_editrepometadata [Repository] Repo role can edit repository metadata Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_EditRepoMetadata edge represents a role's ability to edit repository metadata including description, homepage URL, and visibility settings. This permission is available to Maintain and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\maintain") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_EditRepoMetadata --> repo ``` # GH_EditRepoProtections Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_editrepoprotections Repo role can edit branch protection rules Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_EditRepoProtections edge represents a role's ability to edit or remove branch protection rules on the repository. This permission is available to Admin roles and custom roles that have been granted this specific permission. Modifying a branch protection rule is an indirect bypass -- removing or weakening protections opens the branch to direct push or unreviewed merges, making this a high-severity permission from a security perspective. Attack paths that include this edge can escalate to full branch write access by first disabling protections. ```mermaid theme={null} graph LR user1("GH_User bob") adminRole("GH_RepoRole GitHound\admin") repo("GH_Repository GitHound") user1 -- GH_HasRole --> adminRole adminRole -- GH_EditRepoProtections --> repo adminRole -- GH_AdminTo --> repo ``` # GH_HasBaseRole Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_hasbaserole Role inherits permissions from another role Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Traversable: ✅ ## General Information The traversable GH\_HasBaseRole edge represents role inheritance within the GitHub permission hierarchy. Org roles inherit down to all-repo roles (e.g., Owners inherits to all\_repo\_admin), and custom roles inherit from their base roles (e.g., a custom\_role inherits from write). This edge is traversable because it extends permissions through the role hierarchy, meaning a principal with a higher-level role implicitly holds all inherited lower-level roles. ```mermaid theme={null} graph LR orgOwners("GH_OrgRole SpecterOps\\Owners") orgMembers("GH_OrgRole SpecterOps\\Members") allRepoAdmin("GH_RepoRole SpecterOps\\all_repo_admin") allRepoRead("GH_RepoRole SpecterOps\\all_repo_read") customRole("GH_RepoRole GitHound\\security_reviewer") writeRole("GH_RepoRole GitHound\\write") orgOwners -- GH_HasBaseRole --> allRepoAdmin orgMembers -- GH_HasBaseRole --> allRepoRead customRole -- GH_HasBaseRole --> writeRole ``` # GH_HasBranch Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_hasbranch Repository has this branch Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Destination: [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) * Traversable: ❌ ## General Information The non-traversable GH\_HasBranch edge represents the relationship between a repository and its branches. This edge links each collected branch to its parent repository. It is a structural edge that provides the foundation for understanding branch-level protections and access controls. While not traversable itself, it connects repositories to branches where traversable edges like [GH\_CanWriteBranch](/opengraph/extensions/github/edges/gh_canwritebranch) and [GH\_CanEditProtection](/opengraph/extensions/github/edges/gh_caneditprotection) model the effective access. ```mermaid theme={null} graph LR node1("GH_Repository GitHound") node2("GH_Branch main") node3("GH_Branch develop") node4("GH_Branch feature/auth") node1 -- GH_HasBranch --> node2 node1 -- GH_HasBranch --> node3 node1 -- GH_HasBranch --> node4 ``` # GH_HasEnvironment Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_hasenvironment Repository or branch has/can deploy to this environment Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) * Destination: [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) * Traversable: ❌ ## General Information The non-traversable GH\_HasEnvironment edge represents the relationship between a repository or branch and its deployment environments. This edge links environments to the repositories that define them and to the branches that are allowed to deploy to them (via deployment branch policies). Environments are security-relevant because they can gate access to secrets and cloud credentials, and their deployment branch policies control which branches can trigger deployments. ```mermaid theme={null} graph LR node1("GH_Repository GitHound") node2("GH_Environment production") node3("GH_Environment staging") node4("GH_Branch main") node1 -- GH_HasEnvironment --> node2 node1 -- GH_HasEnvironment --> node3 node4 -- GH_HasEnvironment --> node2 ``` # GH_HasExternalIdentity Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_hasexternalidentity SAML identity provider has this external identity Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_SamlIdentityProvider](/opengraph/extensions/github/nodes/gh_samlidentityprovider) * Destination: [GH\_ExternalIdentity](/opengraph/extensions/github/nodes/gh_externalidentity) * Traversable: ❌ ## General Information The non-traversable GH\_HasExternalIdentity edge represents the relationship between a SAML identity provider and the external identities (SSO users) it manages. This edge links each external identity to the SAML provider that authenticated it. External identities are a key component in cross-platform attack path analysis because they bridge the gap between corporate identity providers and GitHub user accounts via the [GH\_MapsToUser](/opengraph/extensions/github/edges/gh_mapstouser) edge. Enumerating external identities reveals which corporate users have linked GitHub accounts and enables mapping from IdP compromise to GitHub access. ```mermaid theme={null} graph LR node1("GH_SamlIdentityProvider entra-id-sso") node2("GH_ExternalIdentity alice\@specterops.io") node3("GH_ExternalIdentity bob\@specterops.io") node4("GH_User alice") node1 -- GH_HasExternalIdentity --> node2 node1 -- GH_HasExternalIdentity --> node3 node2 -- GH_MapsToUser --> node4 ``` # GH_HasJob Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_hasjob [Workflow] Workflow contains this job — GH_Workflow → GH_WorkflowJob Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_Workflow](/opengraph/extensions/github/nodes/gh_workflow) * Destination: [GH\_WorkflowJob](/opengraph/extensions/github/nodes/gh_workflowjob) * Traversable: ❌ ## General Information The traversable GH\_HasJob edge links a workflow to each of its jobs. This edge is the primary structural link for walking from a workflow definition into its execution units. Because jobs can declare environments and permissions, traversing this edge enables analysts to reason about what a workflow can do and where it can deploy. # GH_HasMember Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_hasmember Enterprise or organization has this user as a member Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Destination: [GH\_User](/opengraph/extensions/github/nodes/gh_user) * Traversable: ❌ ## General Information The non-traversable GH\_HasMember edge represents the relationship between a GitHub organization and a user who is a member of that scope. This edge records membership as directory context rather than as an access path. Being listed as a member does not by itself describe what the user can do, only that the user belongs to the organization. Membership is still security-relevant because it defines the population from which roles, team assignments, and token approvals are drawn. # GH_HasPersonalAccessToken Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_haspersonalaccesstoken User owns this personal access token that has been granted access to the organization Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_User](/opengraph/extensions/github/nodes/gh_user) * Destination: [GH\_PersonalAccessToken](/opengraph/extensions/github/nodes/gh_personalaccesstoken) * Traversable: ❌ ## General Information The non-traversable GH\_HasPersonalAccessToken edge represents the relationship between a user and their fine-grained personal access tokens that have been granted access to the organization. This edge links each approved token back to the user who created it. Fine-grained personal access tokens are security-significant because they provide programmatic access to organization resources with specific scoped permissions. Tracking token ownership is essential for understanding which users have standing API access and for identifying tokens that may need revocation. ```mermaid theme={null} graph LR node1("GH_User alice") node2("GH_PersonalAccessToken ci-deploy-token") node3("GH_PersonalAccessToken read-only-audit") node4("GH_User bob") node5("GH_PersonalAccessToken automation-token") node1 -- GH_HasPersonalAccessToken --> node2 node1 -- GH_HasPersonalAccessToken --> node3 node4 -- GH_HasPersonalAccessToken --> node5 ``` # GH_HasPersonalAccessTokenRequest Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_haspersonalaccesstokenrequest User has a pending personal access token request for the organization Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_User](/opengraph/extensions/github/nodes/gh_user) * Destination: [GH\_PersonalAccessTokenRequest](/opengraph/extensions/github/nodes/gh_personalaccesstokenrequest) * Traversable: ❌ ## General Information The non-traversable GH\_HasPersonalAccessTokenRequest edge represents the relationship between a user and their pending personal access token requests awaiting organizational approval. This edge links each pending token request back to the user who submitted it. Pending token requests are security-relevant because they represent access that may soon be granted, and reviewing them helps administrators understand what permissions users are requesting before approval. Organizations that require approval for fine-grained PATs will have these requests queued until an administrator acts on them. ```mermaid theme={null} graph LR node1("GH_User alice") node2("GH_PersonalAccessTokenRequest deploy-request") node3("GH_User bob") node4("GH_PersonalAccessTokenRequest admin-access-request") node1 -- GH_HasPersonalAccessTokenRequest --> node2 node3 -- GH_HasPersonalAccessTokenRequest --> node4 ``` # GH_HasRole Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_hasrole User or team has a role assignment (org role, team role, or repo role) Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) * Destination: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole), [GH\_TeamRole](/opengraph/extensions/github/nodes/gh_teamrole) * Traversable: ✅ ## General Information The traversable GH\_HasRole edge represents the assignment of a user or team to a specific role within the organization, repository, or team. This is the primary edge for connecting identities to their permissions and serves as the foundation of all access paths in the GitHub permission model. Because role assignment is the starting point for determining what a principal can do, this edge is traversable and critical for attack path analysis. ```mermaid theme={null} graph LR user1("GH_User alice") user2("GH_User bob") team1("GH_Team security-team") orgRole("GH_OrgRole SpecterOps\\Owners") repoRole("GH_RepoRole GitHound\\write") teamRole("GH_TeamRole security-team\\maintainer") user1 -- GH_HasRole --> orgRole user2 -- GH_HasRole --> repoRole team1 -- GH_HasRole --> repoRole user1 -- GH_HasRole --> teamRole ``` # GH_HasSamlIdentityProvider Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_hassamlidentityprovider Organization has this SAML identity provider configured Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Destination: [GH\_SamlIdentityProvider](/opengraph/extensions/github/nodes/gh_samlidentityprovider) * Traversable: ❌ ## General Information The non-traversable GH\_HasSamlIdentityProvider edge represents the relationship between an organization and its SAML identity provider configuration. This edge links an organization to the SAML SSO provider used for authentication and user provisioning. SAML identity providers are a critical security component because they establish the trust boundary between an external identity provider (such as Entra ID or Okta) and the GitHub organization. Understanding this relationship is essential for mapping cross-platform attack paths where compromise of the identity provider could lead to access within the GitHub organization. ```mermaid theme={null} graph LR node1("GH_Organization SpecterOps") node2("GH_SamlIdentityProvider entra-id-sso") node3("GH_ExternalIdentity alice\@specterops.io") node1 -- GH_HasSamlIdentityProvider --> node2 node2 -- GH_HasExternalIdentity --> node3 ``` # GH_HasSecret Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_hassecret Repository or environment has access to this secret Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) * Destination: [GH\_OrgSecret](/opengraph/extensions/github/nodes/gh_orgsecret), [GH\_RepoSecret](/opengraph/extensions/github/nodes/gh_reposecret), [GH\_EnvironmentSecret](/opengraph/extensions/github/nodes/gh_environmentsecret) * Traversable: ✅ ## General Information The traversable GH\_HasSecret edge represents the relationship between a repository or environment and the secrets accessible within that context. This edge shows which secrets are available in which scopes. Repositories can have access to both organization-level secrets (scoped to selected repositories) and repository-level secrets, while environments contain their own environment-scoped secrets. This edge is traversable because any principal that can push code to a repository (via [GH\_CanWriteBranch](/opengraph/extensions/github/edges/gh_canwritebranch) or [GH\_CanCreateBranch](/opengraph/extensions/github/edges/gh_cancreatebranch)) can write a workflow that exfiltrates the secret values at runtime, making this a meaningful link in attack path analysis. ```mermaid theme={null} graph LR node1("GH_Repository GitHound") node2("GH_OrgSecret NPM_TOKEN") node3("GH_RepoSecret DEPLOY_KEY") node4("GH_Environment production") node5("GH_EnvironmentSecret AWS_SECRET_KEY") node1 -- GH_HasSecret --> node2 node1 -- GH_HasSecret --> node3 node4 -- GH_HasSecret --> node5 ``` # GH_HasStep Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_hasstep [Workflow] Job contains this step — GH_WorkflowJob → GH_WorkflowStep Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_WorkflowJob](/opengraph/extensions/github/nodes/gh_workflowjob) * Destination: [GH\_WorkflowStep](/opengraph/extensions/github/nodes/gh_workflowstep) * Traversable: ❌ ## General Information The traversable GH\_HasStep edge links a job to each of its steps in execution order. This edge enables analysts to enumerate all actions and shell commands executed by a job, including which secrets and variables each step consumes. # GH_HasVariable Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_hasvariable Repository has access to this variable (org-level or repo-level) Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Destination: [GH\_OrgVariable](/opengraph/extensions/github/nodes/gh_orgvariable), [GH\_RepoVariable](/opengraph/extensions/github/nodes/gh_repovariable) * Traversable: ✅ ## General Information The traversable GH\_HasVariable edge represents the relationship between a repository and the variables accessible within that context. This edge shows which variables are available in which scopes. Repositories can have access to both organization-level variables (scoped by visibility to all, private, or selected repositories) and repository-level variables defined directly on the repo. This edge is traversable because any principal that can push code to a repository (via [GH\_CanWriteBranch](/opengraph/extensions/github/edges/gh_canwritebranch) or [GH\_CanCreateBranch](/opengraph/extensions/github/edges/gh_cancreatebranch)) can write a workflow that reads variable values at runtime, and variables may contain configuration data useful for lateral movement such as deployment URLs, service names, or environment identifiers. ```mermaid theme={null} graph LR node1("GH_Repository GitHound") node2("GH_OrgVariable ENVIRONMENT_URL") node3("GH_RepoVariable NODE_VERSION") node1 -- GH_HasVariable --> node2 node1 -- GH_HasVariable --> node3 ``` # GH_HasWorkflow Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_hasworkflow Repository has this workflow Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Destination: [GH\_Workflow](/opengraph/extensions/github/nodes/gh_workflow) * Traversable: ❌ ## General Information The non-traversable GH\_HasWorkflow edge represents the relationship between a repository and its GitHub Actions workflows. This edge links each discovered workflow definition to its parent repository. Workflows are significant from a security perspective because they can execute arbitrary code with repository permissions, access secrets, and assume cloud identities. This structural edge enables analysts to enumerate which workflows exist in a given repository. ```mermaid theme={null} graph LR node1("GH_Repository GitHound") node2("GH_Workflow ci.yml") node3("GH_Workflow deploy.yml") node4("GH_Repository BloodHound") node5("GH_Workflow release.yml") node1 -- GH_HasWorkflow --> node2 node1 -- GH_HasWorkflow --> node3 node4 -- GH_HasWorkflow --> node5 ``` # GH_InstalledAs Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_installedas GitHub App is installed as this app installation on an organization Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_App](/opengraph/extensions/github/nodes/gh_app) * Destination: [GH\_AppInstallation](/opengraph/extensions/github/nodes/gh_appinstallation) * Traversable: ✅ ## General Information The traversable GH\_InstalledAs edge links a GitHub App to its installation within the organization. This edge is traversable because it connects the app definition to its active installation, which determines the specific set of repositories and permissions the app has been granted. Understanding the relationship between an app and its installation is essential for tracing how app-level permissions translate into repository access. ```mermaid theme={null} graph LR app("GH_App dependabot") install("GH_AppInstallation dependabot#12345") repo1("GH_Repository GitHound") repo2("GH_Repository BloodHound") app -- GH_InstalledAs --> install install -- GH_CanAccess --> repo1 install -- GH_CanAccess --> repo2 ``` # GH_InviteMember Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_invitemember [Organization] Org role can invite members to the organization Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_InviteMember edge represents that a role has the ability to invite new members to the organization. This permission is typically restricted to Owners, as inviting members expands the organization's trust boundary by granting new users access to internal resources. An attacker with this permission could invite a controlled account to gain persistent access to the organization's repositories, teams, and secrets. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_InviteMember --> node2 ``` # GH_JumpMergeQueue Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_jumpmergequeue Repo role can jump the merge queue Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_JumpMergeQueue edge represents a role's ability to jump ahead of other entries in the merge queue. This permission is available to Admin roles and custom roles that have been granted this specific permission. Merge queues enforce an ordered sequence of CI checks and merges; jumping the queue allows a principal to prioritize their changes ahead of others. While less severe than bypassing protections entirely, this permission can be used to accelerate the landing of malicious changes before other queued entries are reviewed or tested. ```mermaid theme={null} graph LR user1("GH_User bob") adminRole("GH_RepoRole GitHound\admin") repo("GH_Repository GitHound") user1 -- GH_HasRole --> adminRole adminRole -- GH_JumpMergeQueue --> repo adminRole -- GH_AdminTo --> repo ``` # GH_ManageDeployKeys Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_managedeploykeys [Repository] Repo role can manage deploy keys Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ManageDeployKeys edge represents a role's ability to create, modify, and delete deploy keys for the repository. This permission is available to Admin roles and custom roles that have been granted this specific permission. Deploy keys provide SSH-based access to the repository, and a deploy key with write access can push commits directly without going through the GitHub web interface or API authentication. Managing deploy keys is security-significant because it enables the creation of persistent, credential-based access that operates outside the normal user authentication flow. ```mermaid theme={null} graph LR user1("GH_User alice") adminRole("GH_RepoRole GitHound\admin") repo("GH_Repository GitHound") user1 -- GH_HasRole --> adminRole adminRole -- GH_ManageDeployKeys --> repo adminRole -- GH_AdminTo --> repo ``` # GH_ManageDiscussionBadges Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_managediscussionbadges [Repository] Repo role can manage discussion badges Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ManageDiscussionBadges edge represents a role's ability to manage discussion badges used to highlight discussion participants. This permission is available to Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\write") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ManageDiscussionBadges --> repo ``` # GH_ManageOrganizationWebhooks Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_manageorganizationwebhooks [Organization] Org role can manage organization webhooks Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_ManageOrganizationWebhooks edge represents that a role has the ability to manage organization-level webhooks. This edge is dynamically generated from custom organization role permissions discovered by the collector. Webhooks can be configured to send event data to external endpoints, making this permission significant for security because an attacker could create or modify webhooks to exfiltrate repository data, commit contents, or issue details to an attacker-controlled server, or use them as a persistence mechanism. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_ManageOrganizationWebhooks --> node2 ``` # GH_ManageRepoSecurityProducts Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_managereposecurityproducts Repo role can manage repo-level security products Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ManageRepoSecurityProducts edge represents a role's ability to manage repository-specific security product settings. This permission is available to Admin roles and custom roles that have been granted this specific permission. Unlike the broader [GH\_ManageSecurityProducts](/opengraph/extensions/github/edges/gh_managesecurityproducts) permission, this edge is scoped to repository-level security configuration such as repository-specific scanning settings and alert management. Disabling repository-level security products can create blind spots in vulnerability detection for the specific repository. ```mermaid theme={null} graph LR user1("GH_User alice") adminRole("GH_RepoRole GitHound\admin") customRole("GH_RepoRole GitHound\security_admin") repo("GH_Repository GitHound") user1 -- GH_HasRole --> customRole adminRole -- GH_ManageRepoSecurityProducts --> repo customRole -- GH_ManageRepoSecurityProducts --> repo ``` # GH_ManageSecurityProducts Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_managesecurityproducts Repo role can manage security products Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ManageSecurityProducts edge represents a role's ability to manage security product settings on the repository. This permission is available to Admin roles and custom roles that have been granted this specific permission. Managing security products allows enabling or disabling features such as secret scanning, code scanning, and Dependabot alerts. An attacker with this permission could disable security features to prevent detection of vulnerabilities or leaked secrets, making this a high-severity permission for security posture management. ```mermaid theme={null} graph LR user1("GH_User bob") adminRole("GH_RepoRole GitHound\admin") customRole("GH_RepoRole GitHound\security_admin") repo("GH_Repository GitHound") user1 -- GH_HasRole --> adminRole adminRole -- GH_ManageSecurityProducts --> repo customRole -- GH_ManageSecurityProducts --> repo ``` # GH_ManageSettingsMergeTypes Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_managesettingsmergetypes [Repository] Repo role can manage allowed merge types Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ManageSettingsMergeTypes edge represents a role's ability to configure allowed merge types (merge commit, squash, rebase) on the repository. This permission is available to Maintain and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\maintain") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ManageSettingsMergeTypes --> repo ``` # GH_ManageSettingsPages Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_managesettingspages [Repository] Repo role can manage GitHub Pages settings Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ManageSettingsPages edge represents a role's ability to manage GitHub Pages settings including enabling, disabling, and configuring the source. This permission is available to Maintain and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\maintain") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ManageSettingsPages --> repo ``` # GH_ManageSettingsProjects Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_managesettingsprojects [Repository] Repo role can manage project settings Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ManageSettingsProjects edge represents a role's ability to manage project board settings on the repository. This permission is available to Maintain and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\maintain") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ManageSettingsProjects --> repo ``` # GH_ManageSettingsWiki Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_managesettingswiki [Repository] Repo role can manage wiki settings Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ManageSettingsWiki edge represents a role's ability to enable or disable the repository wiki. This permission is available to Maintain and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\maintain") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ManageSettingsWiki --> repo ``` # GH_ManageTopics Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_managetopics [Repository] Repo role can manage repository topics Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ManageTopics edge represents a role's ability to manage repository topics used for discovery and classification. This permission is available to Maintain and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\maintain") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ManageTopics --> repo ``` # GH_ManageWebhooks Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_managewebhooks [Repository] Repo role can manage repository webhooks Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ManageWebhooks edge represents a role's ability to create, modify, and delete repository-level webhooks. This permission is available to Admin roles and custom roles that have been granted this specific permission. Webhooks can exfiltrate repository events and code changes to external endpoints, making this a security-sensitive permission. An attacker with this permission could configure a webhook to receive push event payloads containing commit diffs, effectively creating a covert channel for data exfiltration. ```mermaid theme={null} graph LR user1("GH_User carol") adminRole("GH_RepoRole GitHound\admin") customRole("GH_RepoRole GitHound\integrations_manager") repo("GH_Repository GitHound") user1 -- GH_HasRole --> adminRole adminRole -- GH_ManageWebhooks --> repo customRole -- GH_ManageWebhooks --> repo ``` # GH_MapsToUser Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_mapstouser External identity maps to a GitHub user or identity provider user Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_ExternalIdentity](/opengraph/extensions/github/nodes/gh_externalidentity) * Destination: [GH\_User](/opengraph/extensions/github/nodes/gh_user) * Traversable: ❌ ## General Information The non-traversable GH\_MapsToUser edge maps an external identity (provisioned via SAML or SCIM) to a GitHub user within the organization, or to an external IdP user (such as [AZUser](/resources/nodes/az-user), [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), or [PingOneUser](https://github.com/andyrobbins/PingOneHound?tab=readme-ov-file#schema)) in hybrid graph scenarios. This edge represents identity correlation rather than an attack path, connecting a user's external IdP account to their GitHub account for visibility into federated identity mappings. ```mermaid theme={null} graph LR extId1("GH_ExternalIdentity alice\@specterops.io") extId2("GH_ExternalIdentity bob\@specterops.io") user1("GH_User alice") user2("GH_User bob") extId1 -- GH_MapsToUser --> user1 extId2 -- GH_MapsToUser --> user2 ``` # GH_MarkAsDuplicate Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_markasduplicate [Repository] Repo role can mark issues or pull requests as duplicates Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_MarkAsDuplicate edge represents a role's ability to mark issues or pull requests as duplicates. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_MarkAsDuplicate --> repo ``` # GH_MemberOf Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_memberof Team role is a member of a team, or team is a nested member of a parent team Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_TeamRole](/opengraph/extensions/github/nodes/gh_teamrole), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) * Destination: [GH\_Team](/opengraph/extensions/github/nodes/gh_team) * Traversable: ✅ ## General Information The traversable GH\_MemberOf edge represents team membership, linking a team role to its parent team or a child team to a parent team in nested team hierarchies. This edge is traversable because team membership extends access transitively -- a user who holds a role in a child team inherits the repository permissions of all ancestor teams in the nesting hierarchy, making it a key component of attack path analysis. ```mermaid theme={null} graph LR teamRole1("GH_TeamRole security-team\\maintainer") teamRole2("GH_TeamRole appsec-team\\member") childTeam("GH_Team appsec-team") parentTeam("GH_Team security-team") teamRole1 -- GH_MemberOf --> parentTeam teamRole2 -- GH_MemberOf --> childTeam childTeam -- GH_MemberOf --> parentTeam ``` # GH_OrgBypassCodeScanningDismissalRequests Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_orgbypasscodescanningdismissalrequests [Organization] Org role can bypass code scanning dismissal requests Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_OrgBypassCodeScanningDismissalRequests edge represents that a role can bypass code scanning dismissal requests at the organization level. This edge is dynamically generated from custom organization role permissions discovered by the collector. This permission allows suppressing code scanning security findings without the standard review process, which is significant because an attacker could use it to hide vulnerabilities or malicious code patterns that would otherwise be flagged by automated scanning tools. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_OrgBypassCodeScanningDismissalRequests --> node2 ``` # GH_OrgBypassSecretScanningClosureRequests Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_orgbypasssecretscanningclosurerequests [Organization] Org role can bypass secret scanning closure requests Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_OrgBypassSecretScanningClosureRequests edge represents that a role can bypass secret scanning closure requests at the organization level. This edge is dynamically generated from custom organization role permissions discovered by the collector. This permission allows closing secret scanning alerts without going through the standard review and approval process, which is significant because an attacker could use it to suppress alerts about leaked credentials and prevent incident response teams from being notified. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_OrgBypassSecretScanningClosureRequests --> node2 ``` # GH_OrgReviewAndManageSecretScanningBypassRequests Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_orgreviewandmanagesecretscanningbypassrequests [Organization] Org role can review and manage secret scanning bypass requests Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_OrgReviewAndManageSecretScanningBypassRequests edge represents that a role can review and manage secret scanning push protection bypass requests at the organization level. This edge is dynamically generated from custom organization role permissions discovered by the collector. Push protection prevents secrets from being committed to repositories, and bypass requests allow developers to override this protection for specific commits. An attacker with this permission could approve their own or an accomplice's bypass requests, allowing secrets to be committed to repositories without triggering push protection blocks. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_OrgReviewAndManageSecretScanningBypassRequests --> node2 ``` # GH_OrgReviewAndManageSecretScanningClosureRequests Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_orgreviewandmanagesecretscanningclosurerequests [Organization] Org role can review and manage secret scanning closure requests Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_OrgReviewAndManageSecretScanningClosureRequests edge represents that a role can review and manage secret scanning alert closure requests at the organization level. This edge is dynamically generated from custom organization role permissions discovered by the collector. Alert closure requests are part of the workflow for closing secret scanning alerts, and this permission controls who can approve or deny those requests. An attacker with this permission could approve closure requests to suppress alerts about actively leaked credentials, undermining the organization's secret scanning remediation process. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_OrgReviewAndManageSecretScanningClosureRequests --> node2 ``` # GH_Owns Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_owns Organization owns a repository Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ✅ ## General Information The traversable GH\_Owns edge represents that an organization owns a repository. This edge establishes the foundation of the access control model by linking repositories to their owning organization. It is traversable because repository ownership is a critical relationship for understanding how organizational permissions cascade down to repository-level access, making it essential for attack path analysis. ```mermaid theme={null} graph LR node1("GH_Organization SpecterOps") node2("GH_Repository GitHound") node3("GH_Repository BloodHound") node4("GH_Repository Nemesis") node1 -- GH_Owns --> node2 node1 -- GH_Owns --> node3 node1 -- GH_Owns --> node4 ``` # GH_ProtectedBy Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_protectedby Branch protection rule protects this branch Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_BranchProtectionRule](/opengraph/extensions/github/nodes/gh_branchprotectionrule) * Destination: [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) * Traversable: ❌ ## General Information The non-traversable GH\_ProtectedBy edge represents that a branch protection rule applies to a specific branch. This edge links protection rules to the branches they govern. Understanding which protections apply to a branch is critical for determining the effective access model — protections such as required reviews, status checks, and push restrictions directly impact who can modify a branch. This edge is consumed by the computed branch-access edges to determine effective push access; the computed [GH\_CanWriteBranch](/opengraph/extensions/github/edges/gh_canwritebranch) and [GH\_CanEditProtection](/opengraph/extensions/github/edges/gh_caneditprotection) edges carry traversability instead. ```mermaid theme={null} graph LR node1("GH_Repository GitHound") node2("GH_Branch main") node3("GH_BranchProtectionRule main-protection") node4("GH_Branch develop") node5("GH_BranchProtectionRule develop-protection") node1 -- GH_HasBranch --> node2 node1 -- GH_HasBranch --> node4 node3 -- GH_ProtectedBy --> node2 node5 -- GH_ProtectedBy --> node4 ``` # GH_PushProtectedBranch Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_pushprotectedbranch [Repository] Repo role can push to branches with push restrictions. Not affected by enforce_admins. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_PushProtectedBranch edge represents a role's ability to push directly to branches that are protected by push restrictions. This permission is available to Admin and Maintain roles. This edge bypasses the push gate of branch protection, allowing direct commits to protected branches without going through the pull request workflow. Unlike merge gate bypasses (such as [GH\_BypassBranchProtection](/opengraph/extensions/github/edges/gh_bypassbranchprotection)), this push gate bypass is NOT suppressed by the `enforce_admins` setting on the branch protection rule, making it a particularly potent permission. ```mermaid theme={null} graph LR user1("GH_User bob") maintainRole("GH_RepoRole GitHound\maintain") adminRole("GH_RepoRole GitHound\admin") repo("GH_Repository GitHound") user1 -- GH_HasRole --> maintainRole maintainRole -- GH_PushProtectedBranch --> repo adminRole -- GH_PushProtectedBranch --> repo ``` # GH_ReadCodeScanning Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_readcodescanning [Repository] Repo role can read code scanning results Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ReadCodeScanning edge represents a role's ability to read code scanning analysis results and alerts. This permission is available to Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. Code scanning alerts may reveal exploitable vulnerabilities in the codebase. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\write") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ReadCodeScanning --> repo ``` # GH_ReadOrganizationActionsUsageMetrics Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_readorganizationactionsusagemetrics [Organization] Org role can read Actions usage metrics Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_ReadOrganizationActionsUsageMetrics edge represents that a role can read GitHub Actions usage metrics for the organization. This edge is dynamically generated from custom organization role permissions discovered by the collector. Usage metrics provide visibility into workflow execution patterns, runner utilization, and billing data across the organization. While this is primarily an informational permission, it can reveal which repositories have active CI/CD pipelines and the scale of automation in use. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_ReadOrganizationActionsUsageMetrics --> node2 ``` # GH_ReadOrganizationCustomOrgRole Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_readorganizationcustomorgrole [Organization] Org role can read custom org role definitions Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_ReadOrganizationCustomOrgRole edge represents that a role can read custom organization role definitions. This edge is dynamically generated from custom organization role permissions discovered by the collector. Reading custom org role definitions allows a user to enumerate the permissions granted to each custom role, which provides reconnaissance value for understanding the organization's access control model and identifying roles with elevated privileges. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_ReadOrganizationCustomOrgRole --> node2 ``` # GH_ReadOrganizationCustomRepoRole Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_readorganizationcustomreporole [Organization] Org role can read custom repo role definitions Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_ReadOrganizationCustomRepoRole edge represents that a role can read custom repository role definitions. This edge is dynamically generated from custom organization role permissions discovered by the collector. Reading custom repo role definitions allows a user to enumerate the permissions granted to each custom repository role, which provides reconnaissance value for understanding repository-level access controls and identifying roles that grant elevated repository permissions. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_ReadOrganizationCustomRepoRole --> node2 ``` # GH_ReadRepoContents Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_readrepocontents [Repository] Repo role can read repository contents Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ReadRepoContents edge represents a role's ability to read repository contents including source code, issues, and pull requests. This is the base level of repository access, available to all roles at the Read permission level and above (Read, Triage, Write, Maintain, Admin). ```mermaid theme={null} graph LR user1("GH_User alice") readRole("GH_RepoRole GitHound\read") writeRole("GH_RepoRole GitHound\write") adminRole("GH_RepoRole GitHound\admin") repo("GH_Repository GitHound") user1 -- GH_HasRole --> readRole readRole -- GH_ReadRepoContents -.-> repo writeRole -- GH_ReadRepoContents -.-> repo adminRole -- GH_ReadRepoContents -.> repo ``` # GH_RemoveAssignee Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_removeassignee [Repository] Repo role can remove assignees from issues and pull requests Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_RemoveAssignee edge represents a role's ability to remove assignees from issues and pull requests. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_RemoveAssignee --> repo ``` # GH_RemoveLabel Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_removelabel [Repository] Repo role can remove labels from issues and pull requests Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_RemoveLabel edge represents a role's ability to remove labels from issues and pull requests. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_RemoveLabel --> repo ``` # GH_ReopenDiscussion Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_reopendiscussion [Repository] Repo role can reopen discussions Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ReopenDiscussion edge represents a role's ability to reopen closed discussions to allow further replies. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ReopenDiscussion --> repo ``` # GH_ReopenIssue Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_reopenissue [Repository] Repo role can reopen closed issues Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ReopenIssue edge represents a role's ability to reopen closed issues. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ReopenIssue --> repo ``` # GH_ReopenPullRequest Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_reopenpullrequest [Repository] Repo role can reopen closed pull requests Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ReopenPullRequest edge represents a role's ability to reopen closed pull requests. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ReopenPullRequest --> repo ``` # GH_RequestPrReview Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_requestprreview [Repository] Repo role can request pull request reviews Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_RequestPrReview edge represents a role's ability to request pull request reviews from specific users or teams. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_RequestPrReview --> repo ``` # GH_ResolveDependabotAlerts Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_resolvedependabotalerts [Repository] Repo role can resolve Dependabot alerts Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ResolveDependabotAlerts edge represents a role's ability to dismiss or resolve Dependabot alerts. This permission is available to Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. An attacker could dismiss valid alerts to suppress vulnerability warnings and prevent remediation. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\write") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ResolveDependabotAlerts --> repo ``` # GH_ResolveSecretScanningAlerts Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_resolvesecretscanningalerts [Organization] Org role can resolve secret scanning alerts Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_ResolveSecretScanningAlerts edge represents that a role can resolve (close) secret scanning alerts at the organization level. This edge is dynamically generated from custom organization role permissions discovered by the collector. Resolving a secret scanning alert marks a leaked secret as addressed, which removes it from active monitoring dashboards. An attacker with this permission could suppress alerts about leaked credentials to prevent incident response teams from detecting and rotating compromised secrets. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_ResolveSecretScanningAlerts --> node2 ``` # GH_RestrictionsCanPush Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_restrictionscanpush User or team is allowed to push to branches protected by this rule Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) * Destination: [GH\_BranchProtectionRule](/opengraph/extensions/github/nodes/gh_branchprotectionrule) * Traversable: ❌ ## General Information The non-traversable GH\_RestrictionsCanPush edge represents a per-actor allowance that grants push access through push restrictions on a branch protection rule. This edge identifies specific users or teams that are permitted to push to the protected branch even when push restrictions are active. This is security-relevant because push restrictions limit who can directly push to a branch, and actors with this allowance bypass that control. Unlike [GH\_BypassPullRequestAllowances](/opengraph/extensions/github/edges/gh_bypasspullrequestallowances), this allowance is NOT suppressed by `enforce_admins` — listed actors retain push access regardless of admin enforcement settings. ```mermaid theme={null} graph LR user1("GH_User deploy-bot") team1("GH_Team platform-eng") bpr1("GH_BranchProtectionRule release/*") user1 -- GH_RestrictionsCanPush --> bpr1 team1 -- GH_RestrictionsCanPush --> bpr1 ``` # GH_RunOrgMigration Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_runorgmigration [Repository] Repo role can run organization migrations Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_RunOrgMigration edge represents a role's ability to run organization migrations on the repository. This permission is available to Admin roles and custom roles that have been granted this specific permission. Organization migrations export repository data including source code, issues, and pull requests, which can be used to transfer repository contents to another organization. This permission is security-relevant because it enables bulk data export from the repository. ```mermaid theme={null} graph LR user1("GH_User carol") adminRole("GH_RepoRole GitHound\admin") customRole("GH_RepoRole GitHound\migration_operator") repo("GH_Repository GitHound") user1 -- GH_HasRole --> adminRole adminRole -- GH_RunOrgMigration --> repo customRole -- GH_RunOrgMigration --> repo ``` # GH_SetInteractionLimits Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_setinteractionlimits [Repository] Repo role can set interaction limits on the repository Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_SetInteractionLimits edge represents a role's ability to set temporary interaction limits to restrict who can comment, open issues, or create pull requests. This permission is available to Maintain and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\maintain") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_SetInteractionLimits --> repo ``` # GH_SetIssueType Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_setissuetype [Repository] Repo role can set issue types Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_SetIssueType edge represents a role's ability to set issue types. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_SetIssueType --> repo ``` # GH_SetMilestone Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_setmilestone [Repository] Repo role can set milestones on issues and pull requests Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_SetMilestone edge represents a role's ability to set milestones on issues and pull requests. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_SetMilestone --> repo ``` # GH_SetSocialPreview Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_setsocialpreview [Repository] Repo role can set the repository social preview image Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_SetSocialPreview edge represents a role's ability to set the repository social preview image shown in link previews. This permission is available to Maintain and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\maintain") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_SetSocialPreview --> repo ``` # GH_SyncedTo Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_syncedto External identity (Azure, Okta, PingOne) is synced to this GitHub user via SSO/SCIM Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [AZUser](/resources/nodes/az-user), [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [PingOneUser](https://github.com/andyrobbins/PingOneHound?tab=readme-ov-file#schema) * Destination: [GH\_User](/opengraph/extensions/github/nodes/gh_user) * Traversable: ✅ ## General Information The traversable GH\_SyncedTo edge is a hybrid edge that maps an external IdP user to a GitHub user based on SCIM provisioning. This edge represents a confirmed identity linkage between an external identity provider and GitHub. It is traversable because compromising the IdP account provides a verified path to the corresponding GitHub account, making it a critical edge for cross-system attack path analysis. This edge enables analysts to trace access from enterprise identity providers like Azure AD, Okta, or PingOne into the GitHub environment. ```mermaid theme={null} graph LR azUser("AZUser alice\@specterops.io") oktaUser("Okta_User bob\@specterops.io") ghUser1("GH_User alice") ghUser2("GH_User bob") azUser -- GH_SyncedTo --> ghUser1 oktaUser -- GH_SyncedTo --> ghUser2 ``` # GH_ToggleDiscussionAnswer Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_togglediscussionanswer [Repository] Repo role can toggle discussion answers Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ToggleDiscussionAnswer edge represents a role's ability to mark or unmark a discussion comment as the accepted answer. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ToggleDiscussionAnswer --> repo ``` # GH_ToggleDiscussionCommentMinimize Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_togglediscussioncommentminimize [Repository] Repo role can minimize discussion comments Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ToggleDiscussionCommentMinimize edge represents a role's ability to minimize or restore discussion comments, hiding them from default view. This permission is available to Triage, Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\triage") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ToggleDiscussionCommentMinimize --> repo ``` # GH_TransferRepository Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_transferrepository [Organization] Org role can transfer repositories Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_TransferRepository edge represents that a role has the ability to transfer repositories to or from the organization. This permission is typically restricted to Owners, as transferring a repository can move it outside of the organization's security controls, branch protection rules, and audit logging. An attacker with this permission could transfer a repository to an organization they control, effectively exfiltrating the codebase and its associated secrets. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_TransferRepository --> node2 ``` # GH_UsesSecret Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_usessecret [Workflow] Step references a secret by name — GH_WorkflowStep → GH_RepoSecret / GH_OrgSecret (name match) Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_WorkflowStep](/opengraph/extensions/github/nodes/gh_workflowstep) * Destination: [GH\_RepoSecret](/opengraph/extensions/github/nodes/gh_reposecret), [GH\_OrgSecret](/opengraph/extensions/github/nodes/gh_orgsecret) * Traversable: ❌ ## General Information The traversable GH\_UsesSecret edge links a workflow step to the secret it references via a `${{ secrets.NAME }}` expression. This edge reveals which secrets a step can access at runtime, enabling analysts to trace the blast radius of a compromised workflow. ### Matching strategy Edges use `match_by: property` with two matchers to disambiguate between secrets with the same name across repositories: * **[GH\_RepoSecret](/opengraph/extensions/github/nodes/gh_reposecret)** is matched by `name` + `repository_id`. * **[GH\_OrgSecret](/opengraph/extensions/github/nodes/gh_orgsecret)** is matched by `name` + `environmentid`. This means one `${{ secrets.MY_SECRET }}` expression in a workflow can produce up to two GH\_UsesSecret edges. ### Context property The edge carries a `context` property indicating where the reference was found: * `with` — inside a `with:` input block of a `uses:` action step * `env` — inside the step's `env:` block * `run` — inline within a `run:` shell script # GH_UsesVariable Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_usesvariable [Workflow] Step references a variable by name — GH_WorkflowStep → GH_RepoVariable / GH_OrgVariable (name match) Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_WorkflowStep](/opengraph/extensions/github/nodes/gh_workflowstep) * Destination: [GH\_RepoVariable](/opengraph/extensions/github/nodes/gh_repovariable), [GH\_OrgVariable](/opengraph/extensions/github/nodes/gh_orgvariable) * Traversable: ❌ ## General Information The non-traversable GH\_UsesVariable edge links a workflow step to the variable it references via a `${{ vars.NAME }}` expression. This edge maps variable consumption within workflows. Unlike secrets, variable values are readable via the API, making them lower sensitivity, but they can still influence workflow behavior. ### Matching strategy Edges use `match_by: property` with two matchers to disambiguate between variables with the same name across repositories: * **[GH\_RepoVariable](/opengraph/extensions/github/nodes/gh_repovariable)** is matched by `name` + `repository_id`. * **[GH\_OrgVariable](/opengraph/extensions/github/nodes/gh_orgvariable)** is matched by `name` + `environmentid`. This means one `${{ vars.MY_VAR }}` expression can produce up to two GH\_UsesVariable edges. ### Context property The edge carries a `context` property indicating where the reference was found: * `with` — inside a `with:` input block of a `uses:` action step * `env` — inside the step's `env:` block * `run` — inline within a `run:` shell script # GH_ValidToken Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_validtoken Secret scanning alert contains a valid, active token belonging to this user Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_SecretScanningAlert](/opengraph/extensions/github/nodes/gh_secretscanningalert) * Destination: [GH\_User](/opengraph/extensions/github/nodes/gh_user) * Traversable: ✅ ## General Information The traversable GH\_ValidToken edge represents a secret scanning alert that contains a valid, active GitHub Personal Access Token belonging to a specific user. This edge is only emitted when the alert's state is `open`, the secret type is `github_personal_access_token`, and the token is confirmed valid by calling the GitHub API. This edge is traversable because possessing the leaked token grants the ability to act as the token's owner, effectively compromising that user's identity and all permissions granted to the token. ```mermaid theme={null} graph LR node1("GH_SecretScanningAlert #42") node2("GH_User jdoe") node1 -- GH_ValidToken --> node2 ``` # GH_ViewDependabotAlerts Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_viewdependabotalerts [Repository] Repo role can view Dependabot alerts Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ViewDependabotAlerts edge represents a role's ability to view Dependabot security alerts, which reveal known vulnerabilities in the repository's dependencies. This permission is available to Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. This information could be used to identify and exploit unpatched vulnerabilities. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\write") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_ViewDependabotAlerts --> repo ``` # GH_ViewSecretScanningAlerts Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_viewsecretscanningalerts [Repository] Role can view secret scanning alerts Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_ViewSecretScanningAlerts edge represents that a role can view secret scanning alerts at the organization or repository level. This edge is dynamically generated from custom role permissions discovered by the collector. Secret scanning alerts may reveal details about leaked credentials, including partial or full secret values and the locations where they were detected. This makes the permission significant for security because an attacker with access to view these alerts could harvest exposed credentials for use in lateral movement or privilege escalation. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node3("GH_RepoRole GitHound\\write") node4("GH_Repository GitHound") node1 -- GH_ViewSecretScanningAlerts --> node2 node3 -- GH_ViewSecretScanningAlerts --> node4 ``` # GH_WriteCodeScanning Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_writecodescanning [Repository] Repo role can upload code scanning results Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_WriteCodeScanning edge represents a role's ability to upload code scanning analysis results. This permission is available to Write, Maintain, and Admin roles and custom roles that have been granted this specific permission. An attacker could upload falsified SARIF results to suppress real alerts or inject misleading findings. ```mermaid theme={null} graph LR user1("GH_User alice") role("GH_RepoRole GitHound\\write") repo("GH_Repository GitHound") user1 -- GH_HasRole --> role role -- GH_WriteCodeScanning --> repo ``` # GH_WriteOrganizationActionsSecrets Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_writeorganizationactionssecrets [Organization] Org role can write Actions secrets Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_WriteOrganizationActionsSecrets edge represents that a role can write organization-level GitHub Actions secrets. This edge is dynamically generated from custom organization role permissions discovered by the collector. Organization-level secrets are available to workflows across multiple repositories and often contain credentials for external systems such as cloud providers, package registries, and deployment targets. An attacker with this permission could overwrite existing secrets to inject malicious credentials or create new secrets to facilitate lateral movement. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_WriteOrganizationActionsSecrets --> node2 ``` # GH_WriteOrganizationActionsSettings Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_writeorganizationactionssettings [Organization] Org role can write Actions settings Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_WriteOrganizationActionsSettings edge represents that a role can modify organization-level GitHub Actions settings. This edge is dynamically generated from custom organization role permissions discovered by the collector. These settings control which actions are allowed to run within the organization and the default permissions granted to the `GITHUB_TOKEN` in workflows. An attacker with this permission could weaken restrictions to allow untrusted third-party actions or elevate default token permissions to enable write access across repositories. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_WriteOrganizationActionsSettings --> node2 ``` # GH_WriteOrganizationActionsVariables Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_writeorganizationactionsvariables [Organization] Org role can write Actions variables Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_WriteOrganizationActionsVariables edge represents that a role can write organization-level GitHub Actions variables. This edge is dynamically generated from custom organization role permissions discovered by the collector. Organization-level variables are available to workflows across multiple repositories and often contain configuration values such as environment URLs, feature flags, and service endpoints. An attacker with this permission could overwrite existing variables to redirect workflows to malicious endpoints or alter application behavior. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_WriteOrganizationActionsVariables --> node2 ``` # GH_WriteOrganizationCustomOrgRole Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_writeorganizationcustomorgrole [Organization] Org role can write custom org role definitions Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ✅ ## General Information The traversable GH\_WriteOrganizationCustomOrgRole edge represents that a role can create or modify custom organization role definitions. This edge is dynamically generated from custom organization role permissions discovered by the collector. Modifying organization role definitions can escalate privileges because an attacker could add permissions to an existing custom role that is already assigned to their account, including setting the base\_role to inherit all\_repo\_admin. Since this permission can only belong to custom organization roles, the user necessarily holds the role they can modify — guaranteeing a self-escalation path. This makes it a Tier Zero privilege escalation vector. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_WriteOrganizationCustomOrgRole --> node2 ``` # GH_WriteOrganizationCustomRepoRole Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_writeorganizationcustomreporole [Organization] Org role can write custom repo role definitions Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_WriteOrganizationCustomRepoRole edge represents that a role can create or modify custom repository role definitions. This edge is dynamically generated from custom organization role permissions discovered by the collector. Modifying repository role definitions can escalate privileges because an attacker could add permissions such as admin access, bypass branch protections, or secret management to a custom repo role that is already assigned to their account. This makes it a high-impact permission for gaining elevated access to repositories across the organization. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_WriteOrganizationCustomRepoRole --> node2 ``` # GH_WriteOrganizationNetworkConfigurations Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_writeorganizationnetworkconfigurations [Organization] Org role can write network configurations Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) * Traversable: ❌ ## General Information The non-traversable GH\_WriteOrganizationNetworkConfigurations edge represents that a role can modify organization network configurations. This edge is dynamically generated from custom organization role permissions discovered by the collector. Network configurations control how GitHub-hosted runners connect to private resources such as internal APIs, databases, and cloud services. An attacker with this permission could modify network settings to route runner traffic through attacker-controlled infrastructure or grant runners access to previously isolated network segments. ```mermaid theme={null} graph LR node1("GH_OrgRole SpecterOps\\Owners") node2("GH_Organization SpecterOps") node1 -- GH_WriteOrganizationNetworkConfigurations --> node2 ``` # GH_WriteRepoContents Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_writerepocontents [Repository] Repo role can write repository contents Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_WriteRepoContents edge represents a role's ability to push commits to the repository. This permission is available to Write, Maintain, and Admin roles. Pushing code can modify application behavior and introduce vulnerabilities, making this a security-significant edge. However, this edge represents only the raw permission; actual branch push capability is determined by the computed [GH\_CanWriteBranch](/opengraph/extensions/github/edges/gh_canwritebranch) edge, which factors in branch protection rules and push restrictions. ```mermaid theme={null} graph LR user1("GH_User bob") writeRole("GH_RepoRole GitHound\write") maintainRole("GH_RepoRole GitHound\maintain") adminRole("GH_RepoRole GitHound\admin") repo("GH_Repository GitHound") user1 -- GH_HasRole --> writeRole writeRole -- GH_WriteRepoContents --> repo maintainRole -- GH_WriteRepoContents --> repo adminRole -- GH_WriteRepoContents --> repo ``` # GH_WriteRepoPullRequests Source: https://bloodhound.specterops.io/opengraph/extensions/github/edges/gh_writerepopullrequests [Repository] Repo role can create and merge pull requests Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) * Destination: [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) * Traversable: ❌ ## General Information The non-traversable GH\_WriteRepoPullRequests edge represents a role's ability to create and merge pull requests in the repository. This permission is available to Write, Maintain, and Admin roles. Pull request merge access is security-significant because merging code into protected branches is a common vector for introducing unauthorized changes; however, actual merge capability on protected branches is further governed by branch protection rules and required reviews. ```mermaid theme={null} graph LR user1("GH_User carol") writeRole("GH_RepoRole GitHound\write") adminRole("GH_RepoRole GitHound\admin") repo("GH_Repository GitHound") user1 -- GH_HasRole --> writeRole writeRole -- GH_WriteRepoPullRequests --> repo adminRole -- GH_WriteRepoPullRequests --> repo ``` # Getting Started Source: https://bloodhound.specterops.io/opengraph/extensions/github/getting-started Learn how to get started with the GitHub OpenGraph extension in BloodHound. Applies to BloodHound Enterprise and CE ## Prerequisites Full OpenGraph support requires a PostgreSQL graph database and one of the following editions: * BloodHound Enterprise (uses PostgreSQL by default) * BloodHound Community v8.0.0+ (requires changing to a [PostgreSQL database](/get-started/custom-installation#postgresql)) While many OpenGraph features may work on a Neo4j database, there are functional and performance limitations (see the [OpenGraph FAQ](/opengraph/faq#why-is-it-taking-so-long-to-ingest-opengraph-data)). For full support, migrate to a PostgreSQL database. The OpenGraph Extension Management feature must be enabled before you can manage extensions. Enable this feature on the **Administration** > **Early Access Features** page. ## Install the Extension ### Optional Schemas If your uses SCIM, upload the [bh-scim-extension.json](https://github.com/SpecterOps/bloodhound-scim-extension/blob/main/bh-scim-extension.json) schema as well. This schema provides a shared model for provisioned users and groups across cloud identity providers and applications. If is connected to other BloodHound-supported data sources in your environment, such as , make sure the corresponding schema is installed too. In BloodHound Enterprise v9.3.0 and later, some extensions (such as GitHub, Jamf, and Okta) are pre-installed. Upload any companion schemas that are not already installed. Doing so ensures those cross-platform relationships are modeled correctly in BloodHound. ## Import Cypher Queries ## Collect and Upload Data ## Configure Privilege Zones Read the [Tier Zero Classification](/opengraph/extensions/github/tier-zero) page to understand the rationale behind the Tier Zero Privilege Zone rules. # Mitigating Controls Source: https://bloodhound.specterops.io/opengraph/extensions/github/mitigating-controls Branch protection analysis and attack path mitigation for GitHub organizations Applies to BloodHound Enterprise and CE This document provides empirically verified analysis of how GitHub branch protection rules interact with two key attack paths in the GitHub extension graph model. All findings were validated through systematic testing against live GitHub repositories. ## Attack Paths ### 1. Secret Exfiltration via Workflow Creation A user with write access ([GH\_WriteRepoContents](/opengraph/extensions/github/edges/gh_writerepocontents)) to a repository can: 1. Create a **new branch** in the repository 2. Push a **workflow file** (`.github/workflows/*.yml`) to that branch with `on: push` trigger 3. The workflow executes automatically on push 4. The workflow can access **all repo-level and org-level secrets** ([GH\_HasSecret](/opengraph/extensions/github/edges/gh_hassecret)) available to the repository 5. The workflow exfiltrates the secrets (e.g., via HTTP request to an attacker-controlled server) **Graph path:** `(:GH_User)-[:GH_HasRole|GH_HasBaseRole|GH_MemberOf*1..]->(:GH_RepoRole)-[:GH_WriteRepoContents]->(repo:GH_Repository)-[:GH_HasSecret]->(:GH_RepoSecret|:GH_OrgSecret)` PR reviews do **not** prevent this attack because they only gate merging, not pushing to new branches. The attacker never needs to merge anything. ### 2. Supply Chain Attack via Direct Push A user with write access to a repository can push directly to the default branch (e.g., `main`, `master`), injecting a backdoor into released software. **Graph path:** `(:GH_User)-[:GH_HasRole|GH_HasBaseRole|GH_MemberOf*1..]->(:GH_RepoRole)-[:GH_WriteRepoContents]->(repo:GH_Repository)` ## Branch Protection Settings | Setting | GraphQL Field | BPR Property | Effect | | ------------------ | -------------------------- | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | Require PR reviews | `requiresApprovingReviews` | `required_pull_request_reviews` | Blocks direct pushes to **existing** protected branches (merge-gate control) | | Restrict pushes | `restrictsPushes` | `push_restrictions` | Restricts who can push to matching branches (push-gate control) | | Block creations | `blocksCreations` | `blocks_creations` | Restricts creation of new branches matching the pattern. **Requires `push_restrictions`**; silently reverts to `false` otherwise | | Lock branch | `lockBranch` | `lock_branch` | Makes the branch completely read-only (merge-gate control) | | Enforce for admins | `isAdminEnforced` | `enforce_admins` | Enforces merge-gate controls for admins and users with `bypass_branch_protection` | | Allow force pushes | `allowsForcePushes` | `allows_force_pushes` | Controls whether force pushes are allowed; does **not** grant push access | ### Setting Dependencies * `blocks_creations` requires `push_restrictions` to be `true`. If `push_restrictions` is `false`, the GitHub API accepts the mutation but silently reverts `blocks_creations` to `false`. * `allows_force_pushes` only controls whether history rewrites are permitted. It does not bypass any access controls. ## Merge-Gate vs. Push-Gate Controls Branch protection settings fall into two distinct categories based on *what they control* and *how they can be bypassed*. This distinction is critical because each category has a completely different set of bypass mechanisms, and `enforce_admins` only affects one category. ### Merge-Gate Controls Govern whether changes can be merged or committed to a protected branch. They enforce code review and read-only policies. | Setting | Property | What it blocks | | ------------------ | ------------------------------- | ----------------------------------------------------------------------------------- | | Require PR reviews | `required_pull_request_reviews` | Direct pushes to existing protected branches — forces changes through pull requests | | Lock branch | `lock_branch` | All changes to the branch — makes it completely read-only | Merge-gate controls are bypassed by `bypass_branch_protection` and `bypassPullRequestAllowances`. They **are** enforced by `enforce_admins`. ### Push-Gate Controls Govern *who is authorized* to push to matching branches. They are an access control layer that restricts push operations to an explicit allowlist. | Setting | Property | What it blocks | | --------------- | ------------------- | ---------------------------------------------------------------------------- | | Restrict pushes | `push_restrictions` | Pushes from anyone not in the `pushAllowances` list | | Block creations | `blocks_creations` | Creation of new branches matching the pattern (requires `push_restrictions`) | Push-gate controls are bypassed by `push_protected_branch`, admin access, and `pushAllowances`. They are **NOT** enforced by `enforce_admins`. A common misconfiguration is enabling `enforce_admins` and assuming all protections are enforced. In reality, `enforce_admins` only enforces merge-gate controls. Admin users and users with `push_protected_branch` can still bypass push restrictions regardless of the `enforce_admins` setting. ## Bypass Mechanisms There are seven mechanisms that can bypass branch protection rules. They fall into two categories based on which type of control they bypass. ### Merge-Gate Bypasses | Mechanism | Scope | Edge/Property | Blocked by `enforce_admins`? | | ------------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------- | ---------------------------- | | `bypass_branch_protection` permission | Repo-wide (via custom role) | [GH\_BypassBranchProtection](/opengraph/extensions/github/edges/gh_bypassbranchprotection) | **Yes** | | `bypassPullRequestAllowances` | Per-rule (specific users/teams) | [GH\_BypassPullRequestAllowances](/opengraph/extensions/github/edges/gh_bypasspullrequestallowances) | Not tested (likely yes) | `bypassPullRequestAllowances` is **narrower** than `bypass_branch_protection`. It only bypasses PR review requirements, not lock branch. ### Push-Gate Bypasses | Mechanism | Scope | Edge/Property | Blocked by `enforce_admins`? | | ---------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------- | | `push_protected_branch` permission | Repo-wide (via custom role) | [GH\_PushProtectedBranch](/opengraph/extensions/github/edges/gh_pushprotectedbranch) | **No** | | Admin access | Repo-wide (built-in role) | [GH\_AdminTo](/opengraph/extensions/github/edges/gh_adminto) | **No** | | `pushAllowances` | Per-rule (specific users/teams) | [GH\_RestrictionsCanPush](/opengraph/extensions/github/edges/gh_restrictionscanpush) | Not tested (likely no) | ### Other Bypasses | Mechanism | Effect | Edge | | ---------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------ | | `edit_repo_protections` permission | Can remove/modify protection rules entirely, then push | [GH\_EditRepoProtections](/opengraph/extensions/github/edges/gh_editrepoprotections) | ## Complete Test Results ### Test Series 1: New Branch Creation (Secret Exfiltration Path) Can a user with write access create a new branch and push a workflow? | Test | PR Reviews | Push Restrictions | Blocks Creations (`*`) | Result | | ---- | :--------: | :---------------: | :--------------------: | ------------------------------------------------------ | | 1 | On | Off | Off | **Succeeded** — new branch created | | 2 | On | On | Off | **Succeeded** — new branch created | | 3 | On | On | On | **Blocked** | | 4 | Off | On | On | **Blocked** | | 5 | Off | Off | On (silently ignored) | **Succeeded** — `blocks_creations` reverted to `false` | **Conclusion:** The **only** branch protection configuration that blocks the secret exfiltration attack is `push_restrictions` + `blocks_creations` on a `*` pattern rule. ### Test Series 2: Push to Existing Protected Branch (Supply Chain Path) Can a user with write access push directly to `master`? | Test | Protection Config | Result | Error Message | | ---- | ------------------------------------------ | ------------- | --------------------------------------------- | | 1 | PR reviews only | **Blocked** | "Changes must be made through a pull request" | | 2 | Push restrictions (user NOT in allowances) | **Blocked** | "You're not authorized to push" | | 3 | Push restrictions (user IN allowances) | **Succeeded** | — | | 4 | Lock branch | **Blocked** | "Cannot change this locked branch" | **Conclusion:** Any **one** of PR reviews, push restrictions (without allowance), or lock branch is sufficient to block direct pushes to an existing protected branch. ### Test Series 3: `bypass_branch_protection` Permission | Test | Protection Config | Result | | ---- | ------------------------------------------ | --------------------------------------------- | | 3.1 | PR reviews | **Bypassed** ("Bypassed rule violations") | | 3.2 | Push restrictions (not in allowances) | **Blocked** ("You're not authorized to push") | | 3.3 | Lock branch | **Bypassed** ("Bypassed rule violations") | | 3.4 | Push restrictions + blocks creations (`*`) | **Blocked** ("You're not authorized to push") | **Conclusion:** `bypass_branch_protection` bypasses merge-gate controls (PR reviews, lock branch) but NOT push-gate controls (`push_restrictions`). ### Test Series 4: `push_protected_branch` Permission | Test | Protection Config | Result | | ---- | ------------------------------------------ | ----------------------------------------------------------- | | 4.1 | PR reviews | **Blocked** ("Changes must be made through a pull request") | | 4.2 | Push restrictions (not in allowances) | **Bypassed** | | 4.3 | Lock branch | **Blocked** ("Cannot change this locked branch") | | 4.4 | Push restrictions + blocks creations (`*`) | **Bypassed** (new branch created) | **Conclusion:** `push_protected_branch` bypasses push-gate controls (`push_restrictions`, `blocks_creations`) but NOT merge-gate controls (PR reviews, lock branch). It is the **exact complement** of `bypass_branch_protection`. ### Test Series 5: `enforce_admins` Interaction | Test | Protection | Actor | Result | | ---- | ------------------------------------------ | --------------------------- | ----------------------------------------------- | | 5.1 | PR reviews | `bypass_branch_protection` | **Blocked** (enforce\_admins suppresses bypass) | | 5.2 | Lock branch | `bypass_branch_protection` | **Blocked** (enforce\_admins suppresses bypass) | | 5.3 | Push restrictions | `push_protected_branch` | **Bypassed** (enforce\_admins has no effect) | | 5.4 | Push restrictions + blocks creations (`*`) | Admin (enforce\_admins OFF) | **Bypassed** | | 5.5 | Push restrictions + blocks creations (`*`) | Admin (enforce\_admins ON) | **Bypassed** (enforce\_admins has no effect) | **Conclusion:** `enforce_admins` only enforces merge-gate controls. It suppresses `bypass_branch_protection` but has **no effect** on `push_protected_branch` or admin push access. ### Test Series 6: `allows_force_pushes` | Test | Protection Config | Result | | ---- | -------------------------------------- | ------------------------------------------------ | | 6.1 | Lock branch + force push allowed | **Blocked** ("Cannot change this locked branch") | | 6.2 | Push restrictions + force push allowed | **Blocked** ("You're not authorized to push") | **Conclusion:** `allows_force_pushes` is not a bypass mechanism. It only controls whether force pushes (history rewrites) are permitted for users who already have push access. ### Test Series 7: Both Permissions Combined User with both `bypass_branch_protection` and `push_protected_branch`: | Test | Protection Config | Result | | ---- | ------------------------------------------ | --------------------------------- | | 7.1 | PR reviews | **Bypassed** | | 7.2 | Push restrictions | **Bypassed** | | 7.3 | Lock branch | **Bypassed** | | 7.4 | Push restrictions + blocks creations (`*`) | **Bypassed** (new branch created) | **Conclusion:** Both permissions combined provide full bypass capability, equivalent to admin access. ### Test Series 8: `bypassPullRequestAllowances` (Per-Rule Edge) User in `bypassPullRequestAllowances` list (regular write access, no custom role): | Test | Protection Config | Result | | ---- | ----------------- | ------------------------------------------------ | | 8.1 | PR reviews | **Bypassed** ("Bypassed rule violations") | | 8.2 | Lock branch | **Blocked** ("Cannot change this locked branch") | **Conclusion:** `bypassPullRequestAllowances` is narrower than the `bypass_branch_protection` permission. It only bypasses PR review requirements, not lock branch. ## Summary Matrix | Protection | Regular Write | `bypass_branch_protection` | `push_protected_branch` | Both | Admin | `bypassPRAllowances` | `pushAllowances` | | -------------------------- | :-----------: | :------------------------: | :---------------------: | :----------: | :-----------: | :------------------: | :--------------: | | PR reviews | Blocked | **Bypassed** | Blocked | **Bypassed** | N/T | **Bypassed** | N/T | | Push restrictions | Blocked | Blocked | **Bypassed** | **Bypassed** | **Bypassed** | N/T | **Bypassed** | | Lock branch | Blocked | **Bypassed** | Blocked | **Bypassed** | N/T | Blocked | N/T | | Blocks creations (`*`) | Blocked | Blocked | **Bypassed** | **Bypassed** | **Bypassed** | N/T | N/T | | **enforce\_admins effect** | — | **Suppressed** | **No effect** | — | **No effect** | N/T | N/T | N/T = Not tested (not applicable to that control type) ## Effective Mitigating Controls ### For Secret Exfiltration (Write → New Branch → Workflow → Secrets) The attack requires creating a new branch. This is only blocked when **all** of the following are true: 1. A [GH\_BranchProtectionRule](/opengraph/extensions/github/nodes/gh_branchprotectionrule) exists with `pattern` = `*` 2. `push_restrictions` = `true` 3. `blocks_creations` = `true` **However**, even with this control in place, the following actors can still exfiltrate secrets: * Users with `push_protected_branch` permission ([GH\_PushProtectedBranch](/opengraph/extensions/github/edges/gh_pushprotectedbranch)) * Users with admin access ([GH\_AdminTo](/opengraph/extensions/github/edges/gh_adminto)) — **cannot** be mitigated by `enforce_admins` * Users in `pushAllowances` for the `*` rule ([GH\_RestrictionsCanPush](/opengraph/extensions/github/edges/gh_restrictionscanpush)) * Users with `edit_repo_protections` permission ([GH\_EditRepoProtections](/opengraph/extensions/github/edges/gh_editrepoprotections)) — can remove the rule * Users with both `bypass_branch_protection` and `push_protected_branch` ### For Supply Chain Attack (Write → Push to Default Branch) Any **one** of the following protections is sufficient to block direct pushes to an existing branch: * `required_pull_request_reviews` = `true` * `push_restrictions` = `true` (and attacker not in `pushAllowances`) * `lock_branch` = `true` **Bypass vectors per protection type:** | Protection | Bypassed by | | ----------------- | -------------------------------------------------------------------------------------------- | | PR reviews | `bypass_branch_protection`, `bypassPullRequestAllowances` (both blocked by `enforce_admins`) | | Push restrictions | `push_protected_branch`, admin, `pushAllowances` (none blocked by `enforce_admins`) | | Lock branch | `bypass_branch_protection` (blocked by `enforce_admins`) | # GH_App Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_app A GitHub App definition representing the registered application. The app owner controls the private key used to generate installation tokens. Applies to BloodHound Enterprise and CE Represents a GitHub App definition — the registered application entity. The app owner holds the private key that can generate installation access tokens for **every** [GH\_AppInstallation](/opengraph/extensions/github/nodes/gh_appinstallation) of this app. If the private key is compromised, all installations across all organizations are affected. App definitions are retrieved via the public `GET /apps/{app_slug}` endpoint (no authentication required) after discovering unique app slugs from the organization's app installations. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges No inbound edges are defined by the GitHub extension for this node. ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | -------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ----------- | | [GH\_InstalledAs](/opengraph/extensions/github/edges/gh_installedas) | [GH\_AppInstallation](/opengraph/extensions/github/nodes/gh_appinstallation) | ✅ | ## Properties | Property Name | Data Type | Description | | -------------------- | --------- | ----------------------------------------------------------------------- | | objectid | string | Composite identifier: `A_kwHOABLL_s4ABJ8V`. | | id | integer | The GitHub App's numeric ID. | | name | string | The display name of the app. | | slug | string | The app's URL-friendly slug identifier. | | client\_id | string | The app's OAuth client ID. | | node\_id | string | The app's GraphQL node ID. | | description | string | The app's description. | | external\_url | string | The app's external homepage URL. | | html\_url | string | URL to the app's GitHub page. | | owner\_login | string | The login of the user or organization that owns the app. | | owner\_node\_id | string | The node\_id of the user or organization that owns the app. | | owner\_type | string | The type of the owner (e.g., `User`, `Organization`). | | created\_at | datetime | When the app was created. | | updated\_at | datetime | When the app was last updated. | | permissions | string | JSON string of the default permissions the app requests. | | events | string | JSON string of the default webhook events the app subscribes to. | | installations\_count | integer | The total number of installations of this app across all organizations. | ## Diagram ```mermaid theme={null} flowchart TD GH_App[fa:fa-cube GH_App] GH_AppInstallation[fa:fa-plug GH_AppInstallation] GH_Repository[fa:fa-box-archive GH_Repository] GH_Organization[fa:fa-building GH_Organization] GH_App -->|GH_InstalledAs| GH_AppInstallation GH_Organization -.->|GH_Contains| GH_AppInstallation GH_AppInstallation -.->|GH_CanAccess| GH_Repository ``` # GH_AppInstallation Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_appinstallation A GitHub App installed on the organization with specific permissions and repository access Applies to BloodHound Enterprise and CE Represents a GitHub App installed on an organization. App installations have specific permissions and can be scoped to all repositories or a selection of repositories. The permissions granted to the app are captured as a JSON string in the properties. Each installation is linked to its parent [GH\_App](/opengraph/extensions/github/nodes/gh_app) via a [GH\_InstalledAs](/opengraph/extensions/github/edges/gh_installedas) edge. For installations with `repository_selection` set to `all`, [GH\_CanAccess](/opengraph/extensions/github/edges/gh_canaccess) edges are created to every repository in the organization. For installations with `repository_selection` set to `selected`, repository-level edges cannot be enumerated with a PAT (requires app installation token authentication). ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_InstalledAs](/opengraph/extensions/github/edges/gh_installedas) | [GH\_App](/opengraph/extensions/github/nodes/gh_app) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------- | ------------------------------------------------------------------ | ----------- | | [GH\_CanAccess](/opengraph/extensions/github/edges/gh_canaccess) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | ## Properties | Property Name | Data Type | Description | | --------------------- | --------- | ----------------------------------------------------------------------------------------------------- | | objectid | string | Composite identifier: `Iv23liPgjiu18oXLM2q7`. | | id | integer | The GitHub installation ID. | | name | string | The app's slug identifier. | | environment\_name | string | The name of the environment (GitHub organization) where the app is installed. | | environmentid | string | The node\_id of the environment (GitHub organization). | | repositories\_url | string | API URL to list repositories accessible to this installation. | | app\_id | integer | The GitHub App's numeric ID (shared across all installations of the same app). | | app\_slug | string | The app's URL-friendly slug identifier. | | repository\_selection | string | Whether the app has access to `all` repositories or `selected` repositories. | | access\_tokens\_url | string | API URL to create installation access tokens. | | target\_type | string | The target type of the installation (e.g., `Organization`). | | description | string | The app's description. | | html\_url | string | URL to the app's GitHub page. | | created\_at | datetime | When the app was installed. | | updated\_at | datetime | When the installation was last updated. | | suspended\_at | datetime | When the installation was suspended, if applicable. | | permissions | string | JSON string of the permissions granted to the app (e.g., `{"contents": "read", "metadata": "read"}`). | | events | string | JSON string of the webhook events the app subscribes to. | ## Diagram ```mermaid theme={null} flowchart TD GH_App[fa:fa-cube GH_App] GH_Organization[fa:fa-building GH_Organization] GH_AppInstallation[fa:fa-plug GH_AppInstallation] GH_Repository[fa:fa-box-archive GH_Repository] GH_App -->|GH_InstalledAs| GH_AppInstallation GH_Organization -.->|GH_Contains| GH_AppInstallation GH_AppInstallation -.->|GH_CanAccess| GH_Repository ``` # GH_Branch Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_branch A named reference in a repository representing a line of development Applies to BloodHound Enterprise and CE Represents a Git branch within a repository. Branch nodes capture basic branch information and whether the branch is protected. Protection rule details are stored in separate [GH\_BranchProtectionRule](/opengraph/extensions/github/nodes/gh_branchprotectionrule) nodes, linked via [GH\_ProtectedBy](/opengraph/extensions/github/edges/gh_protectedby) edges. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [GH\_CanEditProtection](/opengraph/extensions/github/edges/gh_caneditprotection) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ✅ | | [GH\_CanWriteBranch](/opengraph/extensions/github/edges/gh_canwritebranch) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole), [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) | ✅ | | [GH\_HasBranch](/opengraph/extensions/github/edges/gh_hasbranch) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ProtectedBy](/opengraph/extensions/github/edges/gh_protectedby) | [GH\_BranchProtectionRule](/opengraph/extensions/github/nodes/gh_branchprotectionrule) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ----------- | | [GH\_CanAssumeIdentity](/opengraph/extensions/github/edges/gh_canassumeidentity) | [AZFederatedIdentityCredential](/resources/nodes/az-federated-identity-credential), `AWSRole` | ✅ | | [GH\_HasEnvironment](/opengraph/extensions/github/edges/gh_hasenvironment) | [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | ## Properties | Property Name | Data Type | Description | | ----------------- | --------- | ------------------------------------------------------------------------------ | | objectid | string | A unique identifier for the branch: `REF_kwDOMuFnXLNyZWZzL2hlYWRzL0NhblB1c2gz` | | name | string | The fully qualified branch name (e.g., `repo\main`). | | short\_name | string | The branch reference name (e.g., `main`). | | node\_id | string | Same as objectid. | | environment\_name | string | The name of the environment (GitHub organization). | | environmentid | string | The node\_id of the environment (GitHub organization). | | protected | boolean | Whether the branch has a protection rule. | ## Diagram ```mermaid theme={null} flowchart TD GH_Branch[fa:fa-code-branch GH_Branch] GH_Repository[fa:fa-box-archive GH_Repository] GH_RepoRole[fa:fa-user-tie GH_RepoRole] GH_BranchProtectionRule[fa:fa-shield GH_BranchProtectionRule] GH_Environment[fa:fa-leaf GH_Environment] GH_User[fa:fa-user GH_User] GH_Team[fa:fa-user-group GH_Team] AZFederatedIdentityCredential[fa:fa-id-card AZFederatedIdentityCredential] GH_Repository -.->|GH_HasBranch| GH_Branch GH_BranchProtectionRule -.->|GH_ProtectedBy| GH_Branch GH_Branch -.->|GH_HasEnvironment| GH_Environment GH_Branch -->|GH_CanAssumeIdentity| AZFederatedIdentityCredential GH_RepoRole -->|GH_CanWriteBranch| GH_Branch GH_RepoRole -->|GH_CanEditProtection| GH_Branch GH_User -->|GH_CanWriteBranch| GH_Branch GH_Team -->|GH_CanWriteBranch| GH_Branch ``` # GH_BranchProtectionRule Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_branchprotectionrule A branch protection rule that applies to one or more branches via pattern matching Applies to BloodHound Enterprise and CE Represents a branch protection rule configured on a GitHub repository. Protection rules define requirements that must be met before changes can be merged to matching branches, such as required reviews, status checks, and restrictions on who can push. A single protection rule can apply to multiple branches via pattern matching (e.g., `main`, `release/*`). ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_BypassPullRequestAllowances](/opengraph/extensions/github/edges/gh_bypasspullrequestallowances) | [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) | ❌ | | [GH\_RestrictionsCanPush](/opengraph/extensions/github/edges/gh_restrictionscanpush) | [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | -------------------------------------------------------------------- | ---------------------------------------------------------- | ----------- | | [GH\_ProtectedBy](/opengraph/extensions/github/edges/gh_protectedby) | [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) | ❌ | ## Properties | Property Name | Data Type | Description | | ---------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | objectid | string | The GitHub node ID of the branch protection rule: `BPR_kwDOMuFnXM4DPZQt` | | name | string | Display name (e.g., `repo\main`). | | id | string | Same as objectid. | | environment\_name | string | The GitHub organization login name. | | environmentid | string | The GitHub organization node ID. | | pattern | string | The branch name pattern this rule applies to (e.g., `main`, `release/*`). | | enforce\_admins | boolean | Whether branch protection rules are enforced for administrators. | | lock\_branch | boolean | Whether the branch is locked (read-only). | | blocks\_creations | boolean | Whether creating branches matching this pattern is restricted. Only effective when `push_restrictions` is also `true`; silently reverts to `false` otherwise. | | required\_pull\_request\_reviews | boolean | Whether pull request reviews are required before merging. | | required\_approving\_review\_count | integer | The number of approving reviews required. | | require\_code\_owner\_reviews | boolean | Whether reviews from code owners are required. | | require\_last\_push\_approval | boolean | Whether the last push must be approved by someone other than the pusher. | | push\_restrictions | boolean | Whether push access is restricted to specific users/teams. | | requires\_status\_checks | boolean | Whether status checks must pass before merging. | | requires\_strict\_status\_checks | boolean | Whether branches must be up to date with the base branch before merging. | | dismisses\_stale\_reviews | boolean | Whether new commits dismiss previously approved reviews. | | allows\_force\_pushes | boolean | Whether force pushes are allowed to matching branches. | | allows\_deletions | boolean | Whether matching branches can be deleted. | ## Diagram ```mermaid theme={null} flowchart TD GH_BranchProtectionRule[fa:fa-shield GH_BranchProtectionRule] GH_Branch[fa:fa-code-branch GH_Branch] GH_User[fa:fa-user GH_User] GH_Team[fa:fa-user-group GH_Team] GH_User -.->|GH_BypassPullRequestAllowances| GH_BranchProtectionRule GH_Team -.->|GH_BypassPullRequestAllowances| GH_BranchProtectionRule GH_User -.->|GH_RestrictionsCanPush| GH_BranchProtectionRule GH_Team -.->|GH_RestrictionsCanPush| GH_BranchProtectionRule GH_BranchProtectionRule -.->|GH_ProtectedBy| GH_Branch ``` ## Security Considerations Branch protection rules are critical security controls. Key settings to review: * **enforce\_admins**: Enforces merge-gate controls (PR reviews, lock branch) for admins and users with `bypass_branch_protection`. Does **not** enforce push-gate controls (`push_restrictions`) for admins or users with `push_protected_branch`. * **required\_pull\_request\_reviews**: Blocks direct pushes to existing protected branches. Bypassed by [GH\_BypassBranchProtection](/opengraph/extensions/github/edges/gh_bypassbranchprotection) and [GH\_BypassPullRequestAllowances](/opengraph/extensions/github/edges/gh_bypasspullrequestallowances) (both suppressed by `enforce_admins`). * **push\_restrictions**: Restricts who can push. Bypassed by [GH\_PushProtectedBranch](/opengraph/extensions/github/edges/gh_pushprotectedbranch), [GH\_AdminTo](/opengraph/extensions/github/edges/gh_adminto), and [GH\_RestrictionsCanPush](/opengraph/extensions/github/edges/gh_restrictionscanpush) (none suppressed by `enforce_admins`). * **blocks\_creations**: Restricts new branch creation when `push_restrictions` is also `true`. Same bypass vectors as `push_restrictions`. Silently reverts to `false` if `push_restrictions` is disabled. * **lock\_branch**: Makes branch read-only. Bypassed by [GH\_BypassBranchProtection](/opengraph/extensions/github/edges/gh_bypassbranchprotection) (suppressed by `enforce_admins`). * **require\_code\_owner\_reviews**: If `false`, changes to critical paths may not require owner approval. * **allows\_force\_pushes**: Controls whether history rewrites are allowed. Does **not** grant push access — it is not a bypass mechanism. * **allows\_deletions**: If `true`, branches can be deleted (potentially losing code). ### Secret Exfiltration Mitigation The only branch protection configuration that blocks the write-access → workflow → secrets exfiltration attack path is `push_restrictions` + `blocks_creations` on a `*` pattern rule. However, users with [GH\_PushProtectedBranch](/opengraph/extensions/github/edges/gh_pushprotectedbranch), [GH\_AdminTo](/opengraph/extensions/github/edges/gh_adminto), [GH\_RestrictionsCanPush](/opengraph/extensions/github/edges/gh_restrictionscanpush), or [GH\_EditRepoProtections](/opengraph/extensions/github/edges/gh_editrepoprotections) can bypass this control. For complete analysis, see [Mitigating Controls](/opengraph/extensions/github/mitigating-controls). ### Identifying Bypass Actors Use these edges to identify users and teams with elevated branch permissions: * [GH\_BypassPullRequestAllowances](/opengraph/extensions/github/edges/gh_bypasspullrequestallowances) — can bypass PR requirements on a specific rule (PR reviews only) * [GH\_RestrictionsCanPush](/opengraph/extensions/github/edges/gh_restrictionscanpush) — can push despite push restrictions on a specific rule * [GH\_BypassBranchProtection](/opengraph/extensions/github/edges/gh_bypassbranchprotection) — repo-wide bypass of merge-gate controls (PR reviews + lock branch) * [GH\_PushProtectedBranch](/opengraph/extensions/github/edges/gh_pushprotectedbranch) — repo-wide bypass of push-gate controls (push restrictions + blocks creations) * [GH\_EditRepoProtections](/opengraph/extensions/github/edges/gh_editrepoprotections) — can remove/modify protection rules entirely # GH_Environment Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_environment A GitHub Actions deployment environment with protection rules and deployment branch policies Applies to BloodHound Enterprise and CE Represents a GitHub Actions deployment environment configured on a repository. Environments can have protection rules including required reviewers, wait timers, and deployment branch policies. When custom branch policies are configured, the environment is connected to specific branches; otherwise, it is connected directly to the repository. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [GH\_HasEnvironment](/opengraph/extensions/github/edges/gh_hasenvironment) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_CanAssumeIdentity](/opengraph/extensions/github/edges/gh_canassumeidentity) | [AZFederatedIdentityCredential](/resources/nodes/az-federated-identity-credential), `AWSRole` | ✅ | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole), [GH\_TeamRole](/opengraph/extensions/github/nodes/gh_teamrole), [GH\_OrgSecret](/opengraph/extensions/github/nodes/gh_orgsecret), [GH\_AppInstallation](/opengraph/extensions/github/nodes/gh_appinstallation), [GH\_PersonalAccessToken](/opengraph/extensions/github/nodes/gh_personalaccesstoken), [GH\_PersonalAccessTokenRequest](/opengraph/extensions/github/nodes/gh_personalaccesstokenrequest), [GH\_RepoSecret](/opengraph/extensions/github/nodes/gh_reposecret), [GH\_EnvironmentSecret](/opengraph/extensions/github/nodes/gh_environmentsecret), [GH\_SecretScanningAlert](/opengraph/extensions/github/nodes/gh_secretscanningalert) | ❌ | | [GH\_HasSecret](/opengraph/extensions/github/edges/gh_hassecret) | [GH\_OrgSecret](/opengraph/extensions/github/nodes/gh_orgsecret), [GH\_RepoSecret](/opengraph/extensions/github/nodes/gh_reposecret), [GH\_EnvironmentSecret](/opengraph/extensions/github/nodes/gh_environmentsecret) | ✅ | ## Properties | Property Name | Data Type | Description | | ------------------- | --------- | ----------------------------------------------------------------------------- | | objectid | string | The GitHub `node_id` of the environment, used as the unique graph identifier. | | id | integer | The numeric GitHub ID of the environment. | | node\_id | string | The GitHub node ID. Redundant with objectid. | | name | string | The fully qualified environment name (e.g., `repoName\production`). | | short\_name | string | The environment's display name (e.g., `production`, `staging`). | | can\_admins\_bypass | boolean | Whether repository administrators can bypass environment protection rules. | | environment\_name | string | The name of the environment (GitHub organization) | | environmentid | string | The node\_id of the environment (GitHub organization) | | repository\_name | string | The full name of the containing repository. | | repository\_id | string | The ID of the containing repository. | ## Diagram ```mermaid theme={null} flowchart TD GH_Environment[fa:fa-leaf GH_Environment] GH_Repository[fa:fa-box-archive GH_Repository] GH_Branch[fa:fa-code-branch GH_Branch] GH_EnvironmentSecret[fa:fa-lock GH_EnvironmentSecret] GH_EnvironmentVariable[fa:fa-lock-open GH_EnvironmentVariable] AZFederatedIdentityCredential[fa:fa-id-card AZFederatedIdentityCredential] GH_Repository -.->|GH_HasEnvironment| GH_Environment GH_Branch -.->|GH_HasEnvironment| GH_Environment GH_Environment -.->|GH_Contains| GH_EnvironmentSecret GH_Environment -.->|GH_Contains| GH_EnvironmentVariable GH_Environment -->|GH_HasSecret| GH_EnvironmentSecret GH_Environment -->|GH_CanAssumeIdentity| AZFederatedIdentityCredential ``` # GH_EnvironmentSecret Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_environmentsecret An environment-level GitHub Actions secret scoped to a specific deployment environment Applies to BloodHound Enterprise and CE Represents an environment-level GitHub Actions secret. These secrets are scoped to a specific deployment environment and are only available to workflow jobs that reference that environment. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_HasSecret](/opengraph/extensions/github/edges/gh_hassecret) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ✅ | ### Outbound Edges No outbound edges are defined by the GitHub extension for this node. ## Properties | Property Name | Data Type | Description | | ----------------------------- | --------- | --------------------------------------------------------------------------------- | | objectid | string | A deterministic ID in the format `GH_EnvironmentSecret_{envNodeId}_{secretName}`. | | id | string | Same as objectid. | | name | string | The name of the secret. | | environment\_name | string | The name of the environment (GitHub organization) | | environmentid | string | The node\_id of the environment (GitHub organization) | | deployment\_environment\_name | string | The name of the containing deployment environment. | | deployment\_environmentid | string | The node\_id of the containing deployment environment. | | created\_at | datetime | When the secret was created. | | updated\_at | datetime | When the secret was last updated. | ## Diagram ```mermaid theme={null} flowchart TD GH_Environment[fa:fa-leaf GH_Environment] GH_EnvironmentSecret[fa:fa-lock GH_EnvironmentSecret] GH_Environment -.->|GH_Contains| GH_EnvironmentSecret ``` # GH_EnvironmentVariable Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_environmentvariable An environment-level GitHub Actions variable scoped to a specific deployment environment. Unlike secrets, variable values are readable. Applies to BloodHound Enterprise and CE Represents an environment-level GitHub Actions variable. These variables are scoped to a specific deployment environment and are only available to workflow jobs that reference that environment. Unlike secrets, variable values are readable via the API. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges No inbound edges are defined by the GitHub extension for this node. ### Outbound Edges No outbound edges are defined by the GitHub extension for this node. ## Properties | Property Name | Data Type | Description | | ----------------------------- | --------- | ------------------------------------------------------------------------------------- | | objectid | string | A deterministic ID in the format `GH_EnvironmentVariable_{envNodeId}_{variableName}`. | | id | string | Same as objectid. | | name | string | The name of the variable. | | environment\_name | string | The name of the environment (GitHub organization). | | environmentid | string | The node\_id of the environment (GitHub organization). | | repository\_name | string | The name of the containing repository. | | repository\_id | string | The node\_id of the containing repository. | | deployment\_environment\_name | string | The name of the containing deployment environment. | | deployment\_environmentid | string | The node\_id of the containing deployment environment. | | value | string | The plaintext value of the variable. | | created\_at | datetime | When the variable was created. | | updated\_at | datetime | When the variable was last updated. | ## Diagram ```mermaid theme={null} flowchart TD GH_Environment[fa:fa-leaf GH_Environment] GH_EnvironmentVariable[fa:fa-lock-open GH_EnvironmentVariable] GH_Environment -.->|GH_Contains| GH_EnvironmentVariable ``` # GH_ExternalIdentity Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_externalidentity An external identity from a SAML/SCIM provider linked to a GitHub user for SSO authentication Applies to BloodHound Enterprise and CE Represents an external identity from a SAML or SCIM identity provider that is linked to a GitHub user. External identities map corporate user accounts (from providers like Okta, Azure AD, etc.) to GitHub user accounts, enabling single sign-on authentication. Each external identity can have both SAML and SCIM identity attributes. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ----------- | | [GH\_HasExternalIdentity](/opengraph/extensions/github/edges/gh_hasexternalidentity) | [GH\_SamlIdentityProvider](/opengraph/extensions/github/nodes/gh_samlidentityprovider) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ------------------------------------------------------------------ | ------------------------------------------------------ | ----------- | | [GH\_MapsToUser](/opengraph/extensions/github/edges/gh_mapstouser) | [GH\_User](/opengraph/extensions/github/nodes/gh_user) | ❌ | ## Properties | Property Name | Data Type | Description | | ---------------------------- | --------- | -------------------------------------------------------- | | objectid | string | The GraphQL ID of the external identity. | | node\_id | string | The GraphQL ID of the external identity. | | name | string | Same as objectid. | | guid | string | The GUID of the external identity. | | environmentid | string | The GraphQL ID of the environment (GitHub organization). | | environment\_name | string | The name of the environment (GitHub organization). | | saml\_identity\_family\_name | string | The family name from the SAML identity. | | saml\_identity\_given\_name | string | The given name from the SAML identity. | | saml\_identity\_name\_id | string | The SAML NameID attribute. | | saml\_identity\_username | string | The username from the SAML identity. | | scim\_identity\_family\_name | string | The family name from the SCIM identity. | | scim\_identity\_given\_name | string | The given name from the SCIM identity. | | scim\_identity\_username | string | The username from the SCIM identity. | | github\_username | string | The GitHub login of the linked user. | | github\_user\_id | string | The GraphQL ID of the linked GitHub user. | ## Diagram ```mermaid theme={null} flowchart TD GH_SamlIdentityProvider[fa:fa-id-badge GH_SamlIdentityProvider] GH_ExternalIdentity[fa:fa-arrows-left-right GH_ExternalIdentity] GH_User[fa:fa-user GH_User] AZUser[fa:fa-user AZUser] Okta_User[fa:fa-user Okta_User] PingOneUser[fa:fa-user PingOneUser] GH_SamlIdentityProvider -.->|GH_HasExternalIdentity| GH_ExternalIdentity GH_ExternalIdentity -.->|GH_MapsToUser| GH_User GH_ExternalIdentity -.->|GH_MapsToUser| AZUser GH_ExternalIdentity -.->|GH_MapsToUser| Okta_User GH_ExternalIdentity -.->|GH_MapsToUser| PingOneUser ``` # GH_Organization Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_organization A GitHub Organization—top-level container for repositories, teams, and settings Applies to BloodHound Enterprise and CE Represents a GitHub organization. This is the root node of the graph and serves as the primary container for all other nodes. Organization-level settings such as default repository permissions, Actions configuration, and security features are captured as properties on this node. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_AddCollaborator](/opengraph/extensions/github/edges/gh_addcollaborator) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_CreateRepository](/opengraph/extensions/github/edges/gh_createrepository) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_CreateTeam](/opengraph/extensions/github/edges/gh_createteam) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_InviteMember](/opengraph/extensions/github/edges/gh_invitemember) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_ManageOrganizationWebhooks](/opengraph/extensions/github/edges/gh_manageorganizationwebhooks) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_OrgBypassCodeScanningDismissalRequests](/opengraph/extensions/github/edges/gh_orgbypasscodescanningdismissalrequests) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_OrgBypassSecretScanningClosureRequests](/opengraph/extensions/github/edges/gh_orgbypasssecretscanningclosurerequests) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_OrgReviewAndManageSecretScanningBypassRequests](/opengraph/extensions/github/edges/gh_orgreviewandmanagesecretscanningbypassrequests) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_OrgReviewAndManageSecretScanningClosureRequests](/opengraph/extensions/github/edges/gh_orgreviewandmanagesecretscanningclosurerequests) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_ReadOrganizationActionsUsageMetrics](/opengraph/extensions/github/edges/gh_readorganizationactionsusagemetrics) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_ReadOrganizationCustomOrgRole](/opengraph/extensions/github/edges/gh_readorganizationcustomorgrole) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_ReadOrganizationCustomRepoRole](/opengraph/extensions/github/edges/gh_readorganizationcustomreporole) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_ResolveSecretScanningAlerts](/opengraph/extensions/github/edges/gh_resolvesecretscanningalerts) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_TransferRepository](/opengraph/extensions/github/edges/gh_transferrepository) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_ViewSecretScanningAlerts](/opengraph/extensions/github/edges/gh_viewsecretscanningalerts) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_WriteOrganizationActionsSecrets](/opengraph/extensions/github/edges/gh_writeorganizationactionssecrets) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_WriteOrganizationActionsSettings](/opengraph/extensions/github/edges/gh_writeorganizationactionssettings) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_WriteOrganizationActionsVariables](/opengraph/extensions/github/edges/gh_writeorganizationactionsvariables) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_WriteOrganizationCustomOrgRole](/opengraph/extensions/github/edges/gh_writeorganizationcustomorgrole) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ✅ | | [GH\_WriteOrganizationCustomRepoRole](/opengraph/extensions/github/edges/gh_writeorganizationcustomreporole) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | | [GH\_WriteOrganizationNetworkConfigurations](/opengraph/extensions/github/edges/gh_writeorganizationnetworkconfigurations) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole), [GH\_TeamRole](/opengraph/extensions/github/nodes/gh_teamrole), [GH\_OrgSecret](/opengraph/extensions/github/nodes/gh_orgsecret), [GH\_AppInstallation](/opengraph/extensions/github/nodes/gh_appinstallation), [GH\_PersonalAccessToken](/opengraph/extensions/github/nodes/gh_personalaccesstoken), [GH\_PersonalAccessTokenRequest](/opengraph/extensions/github/nodes/gh_personalaccesstokenrequest), [GH\_RepoSecret](/opengraph/extensions/github/nodes/gh_reposecret), [GH\_EnvironmentSecret](/opengraph/extensions/github/nodes/gh_environmentsecret), [GH\_SecretScanningAlert](/opengraph/extensions/github/nodes/gh_secretscanningalert) | ❌ | | [GH\_HasSamlIdentityProvider](/opengraph/extensions/github/edges/gh_hassamlidentityprovider) | [GH\_SamlIdentityProvider](/opengraph/extensions/github/nodes/gh_samlidentityprovider) | ❌ | | [GH\_Owns](/opengraph/extensions/github/edges/gh_owns) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ✅ | ## Properties | Property Name | Data Type | Description | | ------------------------------------------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | objectid | string | The GitHub `node_id` of the organization, used as the unique graph identifier. | | id | integer | The numeric GitHub ID of the organization. | | name | string | The organization's login handle, used as the display name. | | login | string | The organization's login handle (URL slug). | | node\_id | string | The GitHub GraphQL node ID. Redundant with objectid. | | description | string | The organization's description. | | org\_name | string | The organization's display name (from the `name` field in the GitHub API). | | company | string | The company associated with the organization. | | blog | string | The organization's blog URL. | | location | string | The organization's location. | | email | string | The organization's public email address. | | is\_verified | boolean | Whether the organization's domain is verified by GitHub. | | has\_organization\_projects | boolean | Whether the organization has projects enabled. | | has\_repository\_projects | boolean | Whether repository projects are enabled. | | public\_repos | integer | Number of public repositories in the organization. | | public\_gists | integer | Number of public gists. | | followers | integer | Number of followers the organization has. | | following | integer | Number of accounts the organization is following. | | html\_url | string | URL to the organization's GitHub profile page. | | created\_at | datetime | When the organization was created. | | updated\_at | datetime | When the organization was last updated. | | type | string | The account type (e.g., `Organization`). | | total\_private\_repos | integer | Total number of private repositories. | | owned\_private\_repos | integer | Number of private repositories owned directly by the organization. | | private\_gists | integer | Number of private gists. | | collaborators | integer | Number of outside collaborators across the organization. | | default\_repository\_permission | string | Default permission level granted to members on all repositories (e.g., `read`, `write`, `admin`, `none`). Used to associate the Members org role with the appropriate `all_repo_*` role node. | | members\_can\_create\_repositories | boolean | Whether members can create repositories. | | two\_factor\_requirement\_enabled | boolean | Whether two-factor authentication is required for all members. | | members\_can\_create\_public\_repositories | boolean | Whether members can create public repositories. | | members\_can\_create\_private\_repositories | boolean | Whether members can create private repositories. | | members\_can\_create\_internal\_repositories | boolean | Whether members can create internal repositories. | | members\_can\_create\_pages | boolean | Whether members can create GitHub Pages sites. | | members\_can\_fork\_private\_repositories | boolean | Whether members can fork private repositories. | | web\_commit\_signoff\_required | boolean | Whether web-based commits require sign-off. | | deploy\_keys\_enabled\_for\_repositories | string | Which repositories allow deploy keys. | | members\_can\_delete\_repositories | boolean | Whether members can delete repositories. | | members\_can\_change\_repo\_visibility | boolean | Whether members can change repository visibility. | | members\_can\_invite\_outside\_collaborators | boolean | Whether members can invite outside collaborators. | | members\_can\_delete\_issues | boolean | Whether members can delete issues. | | display\_commenter\_full\_name\_setting\_enabled | boolean | Whether commenter full names are displayed. | | readers\_can\_create\_discussions | boolean | Whether readers can create discussions. | | members\_can\_create\_teams | boolean | Whether members can create teams. | | members\_can\_view\_dependency\_insights | boolean | Whether members can view dependency insights. | | default\_repository\_branch | string | The default branch name for new repositories. | | members\_can\_create\_public\_pages | boolean | Whether members can create public GitHub Pages sites. | | members\_can\_create\_private\_pages | boolean | Whether members can create private GitHub Pages sites. | | advanced\_security\_enabled\_for\_new\_repositories | boolean | Whether GitHub Advanced Security is automatically enabled for new repositories. | | dependabot\_alerts\_enabled\_for\_new\_repositories | boolean | Whether Dependabot alerts are enabled for new repositories. | | dependabot\_security\_updates\_enabled\_for\_new\_repositories | boolean | Whether Dependabot security updates are enabled for new repositories. | | dependency\_graph\_enabled\_for\_new\_repositories | boolean | Whether the dependency graph is enabled for new repositories. | | secret\_scanning\_enabled\_for\_new\_repositories | boolean | Whether secret scanning is enabled for new repositories. | | secret\_scanning\_push\_protection\_enabled\_for\_new\_repositories | boolean | Whether secret scanning push protection is enabled for new repositories. | | secret\_scanning\_push\_protection\_custom\_link\_enabled | boolean | Whether a custom link is enabled for secret scanning push protection. | | secret\_scanning\_push\_protection\_custom\_link | boolean | The custom link for secret scanning push protection. | | secret\_scanning\_validity\_checks\_enabled | boolean | Whether secret scanning validity checks are enabled. | | actions\_enabled\_repositories | string | Which repositories have GitHub Actions enabled: `all`, `selected`, or `none`. | | actions\_allowed\_actions | string | Which Actions are allowed to run: `all`, `local_only`, or `selected`. | | actions\_sha\_pinning\_required | boolean | Whether SHA pinning is required for GitHub Actions. | ## Diagram ```mermaid theme={null} flowchart TD GH_Organization[fa:fa-building GH_Organization] GH_Repository[fa:fa-box-archive GH_Repository] GH_OrgSecret[fa:fa-lock GH_OrgSecret] GH_SamlIdentityProvider[fa:fa-id-badge GH_SamlIdentityProvider] GH_OrgRole[fa:fa-user-tie GH_OrgRole] GH_Organization -.->|GH_Owns| GH_Repository GH_PersonalAccessToken[fa:fa-key GH_PersonalAccessToken] GH_PersonalAccessTokenRequest[fa:fa-key GH_PersonalAccessTokenRequest] GH_Organization -.->|GH_Contains| GH_OrgSecret GH_Organization -.->|GH_HasSamlIdentityProvider| GH_SamlIdentityProvider GH_Organization -.->|GH_Contains| GH_PersonalAccessToken GH_Organization -.->|GH_Contains| GH_PersonalAccessTokenRequest GH_OrgRole -.->|GH_ManageOrganizationWebhooks| GH_Organization GH_OrgRole -.->|GH_OrgBypassCodeScanningDismissalRequests| GH_Organization GH_OrgRole -.->|GH_OrgBypassSecretScanningClosureRequests| GH_Organization GH_OrgRole -.->|GH_CreateRepository| GH_Organization GH_OrgRole -.->|GH_InviteMember| GH_Organization GH_OrgRole -.->|GH_AddCollaborator| GH_Organization GH_OrgRole -.->|GH_CreateTeam| GH_Organization GH_OrgRole -.->|GH_TransferRepository| GH_Organization ``` # GH_OrgRole Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_orgrole The role a user has at the organization level (e.g., admin, member) Applies to BloodHound Enterprise and CE Represents an organization-level role such as Owner, Member, or a custom organization role. Org roles define what permissions a user or team has at the organization level. The Owner and Member roles are default (built-in), while custom roles inherit from a base role and can have additional permissions. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_HasBaseRole](/opengraph/extensions/github/edges/gh_hasbaserole) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ✅ | | [GH\_HasRole](/opengraph/extensions/github/edges/gh_hasrole) | [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [GH\_AddCollaborator](/opengraph/extensions/github/edges/gh_addcollaborator) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_CanReadSecretScanningAlert](/opengraph/extensions/github/edges/gh_canreadsecretscanningalert) | [GH\_SecretScanningAlert](/opengraph/extensions/github/nodes/gh_secretscanningalert) | ✅ | | [GH\_CreateRepository](/opengraph/extensions/github/edges/gh_createrepository) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_CreateTeam](/opengraph/extensions/github/edges/gh_createteam) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_HasBaseRole](/opengraph/extensions/github/edges/gh_hasbaserole) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ✅ | | [GH\_InviteMember](/opengraph/extensions/github/edges/gh_invitemember) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_ManageOrganizationWebhooks](/opengraph/extensions/github/edges/gh_manageorganizationwebhooks) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_OrgBypassCodeScanningDismissalRequests](/opengraph/extensions/github/edges/gh_orgbypasscodescanningdismissalrequests) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_OrgBypassSecretScanningClosureRequests](/opengraph/extensions/github/edges/gh_orgbypasssecretscanningclosurerequests) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_OrgReviewAndManageSecretScanningBypassRequests](/opengraph/extensions/github/edges/gh_orgreviewandmanagesecretscanningbypassrequests) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_OrgReviewAndManageSecretScanningClosureRequests](/opengraph/extensions/github/edges/gh_orgreviewandmanagesecretscanningclosurerequests) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_ReadOrganizationActionsUsageMetrics](/opengraph/extensions/github/edges/gh_readorganizationactionsusagemetrics) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_ReadOrganizationCustomOrgRole](/opengraph/extensions/github/edges/gh_readorganizationcustomorgrole) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_ReadOrganizationCustomRepoRole](/opengraph/extensions/github/edges/gh_readorganizationcustomreporole) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_ResolveSecretScanningAlerts](/opengraph/extensions/github/edges/gh_resolvesecretscanningalerts) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_TransferRepository](/opengraph/extensions/github/edges/gh_transferrepository) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_ViewSecretScanningAlerts](/opengraph/extensions/github/edges/gh_viewsecretscanningalerts) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_WriteOrganizationActionsSecrets](/opengraph/extensions/github/edges/gh_writeorganizationactionssecrets) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_WriteOrganizationActionsSettings](/opengraph/extensions/github/edges/gh_writeorganizationactionssettings) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_WriteOrganizationActionsVariables](/opengraph/extensions/github/edges/gh_writeorganizationactionsvariables) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_WriteOrganizationCustomOrgRole](/opengraph/extensions/github/edges/gh_writeorganizationcustomorgrole) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ✅ | | [GH\_WriteOrganizationCustomRepoRole](/opengraph/extensions/github/edges/gh_writeorganizationcustomreporole) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | | [GH\_WriteOrganizationNetworkConfigurations](/opengraph/extensions/github/edges/gh_writeorganizationnetworkconfigurations) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | ## Properties | Property Name | Data Type | Description | | ----------------- | --------- | ---------------------------------------------------------------------------------------- | | objectid | string | A deterministic ID derived from the organization ID and role name. | | name | string | The fully qualified role name (e.g., `OrgName\Owners`). | | id | string | Same as objectid. | | short\_name | string | The short display name of the role (e.g., `Owners`, `Members`, or the custom role name). | | type | string | `default` for built-in roles (Owner, Member) or `custom` for custom organization roles. | | environment\_name | string | The name of the environment (GitHub organization). | | environmentid | string | The node\_id of the environment (GitHub organization). | ## Diagram ```mermaid theme={null} flowchart TD GH_OrgRole[fa:fa-user-tie GH_OrgRole] GH_User[fa:fa-user GH_User] GH_Team[fa:fa-user-group GH_Team] GH_Organization[fa:fa-building GH_Organization] GH_RepoRole[fa:fa-user-tie GH_RepoRole] GH_SecretScanningAlert[fa:fa-key GH_SecretScanningAlert] GH_User -->|GH_HasRole| GH_OrgRole GH_Team -->|GH_HasRole| GH_OrgRole GH_OrgRole -->|GH_HasBaseRole| GH_OrgRole GH_OrgRole -.->|GH_ManageOrganizationWebhooks| GH_Organization GH_OrgRole -.->|GH_OrgBypassCodeScanningDismissalRequests| GH_Organization GH_OrgRole -.->|GH_OrgBypassSecretScanningClosureRequests| GH_Organization GH_OrgRole -.->|GH_CreateRepository| GH_Organization GH_OrgRole -.->|GH_InviteMember| GH_Organization GH_OrgRole -.->|GH_AddCollaborator| GH_Organization GH_OrgRole -.->|GH_CreateTeam| GH_Organization GH_OrgRole -.->|GH_TransferRepository| GH_Organization GH_OrgRole -->|GH_HasBaseRole| GH_RepoRole GH_OrgRole -->|GH_CanReadSecretScanningAlert| GH_SecretScanningAlert ``` # GH_OrgSecret Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_orgsecret An organization-level GitHub Actions secret that can be scoped to all, private, or selected repositories Applies to BloodHound Enterprise and CE Represents an organization-level GitHub Actions secret. Organization secrets can be scoped to all repositories, only private/internal repositories, or a specific set of selected repositories. The visibility property determines how [GH\_HasSecret](/opengraph/extensions/github/edges/gh_hassecret) edges are resolved to repository nodes. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_HasSecret](/opengraph/extensions/github/edges/gh_hassecret) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ✅ | ### Outbound Edges No outbound edges are defined by the GitHub extension for this node. ## Properties | Property Name | Data Type | Description | | ----------------- | --------- | ------------------------------------------------------------------------------------------------------------------------- | | objectid | string | A deterministic ID in the format `GH_OrgSecret_{orgNodeId}_{secretName}`. | | id | string | Same as objectid. | | name | string | The name of the secret. | | environment\_name | string | The name of the environment (GitHub organization). | | environmentid | string | The node\_id of the environment (GitHub organization). | | created\_at | datetime | When the secret was created. | | updated\_at | datetime | When the secret was last updated. | | visibility | string | The secret's visibility scope: `all` (all repos), `private` (private and internal repos), or `selected` (specific repos). | ## Diagram ```mermaid theme={null} flowchart TD GH_OrgSecret[fa:fa-lock GH_OrgSecret] GH_Organization[fa:fa-building GH_Organization] GH_Repository[fa:fa-box-archive GH_Repository] GH_Organization -.->|GH_Contains| GH_OrgSecret GH_Repository -->|GH_HasSecret| GH_OrgSecret ``` # GH_OrgVariable Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_orgvariable An organization-level GitHub Actions variable that can be scoped to all, private, or selected repositories. Unlike secrets, variable values are readable. Applies to BloodHound Enterprise and CE Represents an organization-level GitHub Actions variable. Organization variables can be scoped to all repositories, only private/internal repositories, or a specific set of selected repositories. The visibility property determines how [GH\_HasVariable](/opengraph/extensions/github/edges/gh_hasvariable) edges are resolved to repository nodes. Unlike secrets, variable values are readable via the API. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------- | ------------------------------------------------------------------ | ----------- | | [GH\_HasVariable](/opengraph/extensions/github/edges/gh_hasvariable) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ✅ | ### Outbound Edges No outbound edges are defined by the GitHub extension for this node. ## Properties | Property Name | Data Type | Description | | ----------------- | --------- | --------------------------------------------------------------------------------------------------------------------------- | | objectid | string | A deterministic ID in the format `GH_OrgVariable_{orgNodeId}_{variableName}`. | | id | string | Same as objectid. | | name | string | The name of the variable. | | environment\_name | string | The name of the environment (GitHub organization). | | environmentid | string | The node\_id of the environment (GitHub organization). | | value | string | The plaintext value of the variable. | | created\_at | datetime | When the variable was created. | | updated\_at | datetime | When the variable was last updated. | | visibility | string | The variable's visibility scope: `all` (all repos), `private` (private and internal repos), or `selected` (specific repos). | ## Diagram ```mermaid theme={null} flowchart TD GH_OrgVariable[fa:fa-lock-open GH_OrgVariable] GH_Organization[fa:fa-building GH_Organization] GH_Repository[fa:fa-box-archive GH_Repository] GH_Organization -.->|GH_Contains| GH_OrgVariable GH_Repository -->|GH_HasVariable| GH_OrgVariable ``` # GH_PersonalAccessToken Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_personalaccesstoken A fine-grained personal access token granted access to organization resources Applies to BloodHound Enterprise and CE Represents a fine-grained personal access token that has been granted access to organization resources. PATs are linked to their owning user, the organization, and the repositories they can access. The permissions granted to the token are captured as a JSON string in the properties. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_HasPersonalAccessToken](/opengraph/extensions/github/edges/gh_haspersonalaccesstoken) | [GH\_User](/opengraph/extensions/github/nodes/gh_user) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------- | ------------------------------------------------------------------ | ----------- | | [GH\_CanAccess](/opengraph/extensions/github/edges/gh_canaccess) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | ## Properties | Property Name | Data Type | Description | | --------------------- | --------- | -------------------------------------------------------------------------------------------------- | | objectid | string | Deterministic Base64-encoded identifier, used as the unique graph identifier. | | id | string | The deterministic identifier (same as objectid). | | name | string | The user-assigned display name of the token. | | environment\_name | string | The name of the environment (GitHub organization) where the token has access. | | environmentid | string | The node\_id of the environment (GitHub organization). | | owner\_login | string | The login handle of the user who owns the token. | | owner\_id | integer | The numeric GitHub ID of the token owner. | | owner\_node\_id | string | The GraphQL node ID of the token owner. | | token\_id | integer | Unique identifier of the user's token, found in audit logs and organization settings. | | token\_name | string | The user-assigned display name of the token. | | token\_expired | boolean | Whether the token has expired. | | token\_expires\_at | string | ISO 8601 timestamp of when the token expires. | | token\_last\_used\_at | string | ISO 8601 timestamp of when the token was last used. | | repository\_selection | string | Whether the token has access to `all`, `subset`, or `none` of the organization's repositories. | | access\_granted\_at | string | ISO 8601 timestamp of when access was granted to the organization. | | permissions | string | JSON string of the permissions granted to the token (e.g., `{"organization":{},"repository":{}}`). | ## Diagram ```mermaid theme={null} flowchart TD GH_PersonalAccessToken[fa:fa-key GH_PersonalAccessToken] GH_User[fa:fa-user GH_User] GH_Organization[fa:fa-building GH_Organization] GH_Repository[fa:fa-box-archive GH_Repository] GH_User -.->|GH_HasPersonalAccessToken| GH_PersonalAccessToken GH_Organization -.->|GH_Contains| GH_PersonalAccessToken GH_PersonalAccessToken -.->|GH_CanAccess| GH_Repository ``` # GH_PersonalAccessTokenRequest Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_personalaccesstokenrequest A pending request from an organization member to access organization resources with a fine-grained personal access token Applies to BloodHound Enterprise and CE Represents a pending request from an organization member to access organization resources with a fine-grained personal access token. PAT requests are linked to their owning user and the organization. The requested permissions are captured as a JSON string in the properties. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_HasPersonalAccessTokenRequest](/opengraph/extensions/github/edges/gh_haspersonalaccesstokenrequest) | [GH\_User](/opengraph/extensions/github/nodes/gh_user) | ❌ | ### Outbound Edges No outbound edges are defined by the GitHub extension for this node. ## Properties | Property Name | Data Type | Description | | --------------------- | --------- | --------------------------------------------------------------------------------------------- | | objectid | string | Deterministic Base64-encoded identifier, used as the unique graph identifier. | | id | string | The deterministic identifier (same as objectid). | | name | string | The user-assigned display name of the token. | | environment\_name | string | The name of the environment (GitHub organization) where access is being requested. | | environmentid | string | The node\_id of the environment (GitHub organization). | | owner\_login | string | The login handle of the user who submitted the request. | | owner\_id | integer | The numeric GitHub ID of the requester. | | owner\_node\_id | string | The GraphQL node ID of the requester. | | token\_id | integer | Unique identifier of the user's token, found in audit logs. | | token\_name | string | The user-assigned display name of the token. | | token\_expired | boolean | Whether the token has expired. | | token\_expires\_at | string | ISO 8601 timestamp of when the token expires. | | token\_last\_used\_at | string | ISO 8601 timestamp of when the token was last used. | | repository\_selection | string | Whether the request targets `all`, `subset`, or `none` of the organization's repositories. | | reason | string | The rationale provided by the requester for the access request. | | created\_at | string | ISO 8601 timestamp of when the request was submitted. | | permissions | string | JSON string of the permissions being requested (e.g., `{"organization":{},"repository":{}}`). | ## Diagram ```mermaid theme={null} flowchart TD GH_PersonalAccessTokenRequest[fa:fa-key GH_PersonalAccessTokenRequest] GH_User[fa:fa-user GH_User] GH_Organization[fa:fa-building GH_Organization] GH_User -.->|GH_HasPersonalAccessTokenRequest| GH_PersonalAccessTokenRequest GH_Organization -.->|GH_Contains| GH_PersonalAccessTokenRequest ``` # GH_RepoRole Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_reporole The permission granted to a user or team on a repository (e.g., admin, write, read) Applies to BloodHound Enterprise and CE Represents a repository-level permission role. Each repository has five default roles (Read, Write, Admin, Triage, Maintain) plus any custom repository roles defined at the organization level. Repo roles define what actions a user or team can perform on a specific repository. Default roles form an inheritance hierarchy (Triage -> Read, Maintain -> Write, Admin includes all), and custom roles inherit from one of the base roles. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_HasBaseRole](/opengraph/extensions/github/edges/gh_hasbaserole) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ✅ | | [GH\_HasRole](/opengraph/extensions/github/edges/gh_hasrole) | [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [GH\_AddAssignee](/opengraph/extensions/github/edges/gh_addassignee) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_AddLabel](/opengraph/extensions/github/edges/gh_addlabel) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_AdminTo](/opengraph/extensions/github/edges/gh_adminto) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_BypassBranchProtection](/opengraph/extensions/github/edges/gh_bypassbranchprotection) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_CanCreateBranch](/opengraph/extensions/github/edges/gh_cancreatebranch) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ✅ | | [GH\_CanEditProtection](/opengraph/extensions/github/edges/gh_caneditprotection) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) | ✅ | | [GH\_CanReadSecretScanningAlert](/opengraph/extensions/github/edges/gh_canreadsecretscanningalert) | [GH\_SecretScanningAlert](/opengraph/extensions/github/nodes/gh_secretscanningalert) | ✅ | | [GH\_CanWriteBranch](/opengraph/extensions/github/edges/gh_canwritebranch) | [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) | ✅ | | [GH\_CloseDiscussion](/opengraph/extensions/github/edges/gh_closediscussion) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_CloseIssue](/opengraph/extensions/github/edges/gh_closeissue) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ClosePullRequest](/opengraph/extensions/github/edges/gh_closepullrequest) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ConvertIssuesToDiscussions](/opengraph/extensions/github/edges/gh_convertissuestodiscussions) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_CreateDiscussionCategory](/opengraph/extensions/github/edges/gh_creatediscussioncategory) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_CreateSoloMergeQueueEntry](/opengraph/extensions/github/edges/gh_createsolomergequeueentry) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_CreateTag](/opengraph/extensions/github/edges/gh_createtag) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_DeleteAlertsCodeScanning](/opengraph/extensions/github/edges/gh_deletealertscodescanning) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_DeleteDiscussion](/opengraph/extensions/github/edges/gh_deletediscussion) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_DeleteDiscussionComment](/opengraph/extensions/github/edges/gh_deletediscussioncomment) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_DeleteIssue](/opengraph/extensions/github/edges/gh_deleteissue) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_DeleteTag](/opengraph/extensions/github/edges/gh_deletetag) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_EditCategoryOnDiscussion](/opengraph/extensions/github/edges/gh_editcategoryondiscussion) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_EditDiscussionCategory](/opengraph/extensions/github/edges/gh_editdiscussioncategory) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_EditDiscussionComment](/opengraph/extensions/github/edges/gh_editdiscussioncomment) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_EditRepoAnnouncementBanners](/opengraph/extensions/github/edges/gh_editrepoannouncementbanners) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_EditRepoCustomPropertiesValues](/opengraph/extensions/github/edges/gh_editrepocustompropertiesvalues) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_EditRepoMetadata](/opengraph/extensions/github/edges/gh_editrepometadata) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_EditRepoProtections](/opengraph/extensions/github/edges/gh_editrepoprotections) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_HasBaseRole](/opengraph/extensions/github/edges/gh_hasbaserole) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ✅ | | [GH\_JumpMergeQueue](/opengraph/extensions/github/edges/gh_jumpmergequeue) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ManageDeployKeys](/opengraph/extensions/github/edges/gh_managedeploykeys) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ManageDiscussionBadges](/opengraph/extensions/github/edges/gh_managediscussionbadges) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ManageRepoSecurityProducts](/opengraph/extensions/github/edges/gh_managereposecurityproducts) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ManageSecurityProducts](/opengraph/extensions/github/edges/gh_managesecurityproducts) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ManageSettingsMergeTypes](/opengraph/extensions/github/edges/gh_managesettingsmergetypes) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ManageSettingsPages](/opengraph/extensions/github/edges/gh_managesettingspages) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ManageSettingsProjects](/opengraph/extensions/github/edges/gh_managesettingsprojects) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ManageSettingsWiki](/opengraph/extensions/github/edges/gh_managesettingswiki) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ManageTopics](/opengraph/extensions/github/edges/gh_managetopics) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ManageWebhooks](/opengraph/extensions/github/edges/gh_managewebhooks) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_MarkAsDuplicate](/opengraph/extensions/github/edges/gh_markasduplicate) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_PushProtectedBranch](/opengraph/extensions/github/edges/gh_pushprotectedbranch) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ReadCodeScanning](/opengraph/extensions/github/edges/gh_readcodescanning) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ReadRepoContents](/opengraph/extensions/github/edges/gh_readrepocontents) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_RemoveAssignee](/opengraph/extensions/github/edges/gh_removeassignee) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_RemoveLabel](/opengraph/extensions/github/edges/gh_removelabel) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ReopenDiscussion](/opengraph/extensions/github/edges/gh_reopendiscussion) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ReopenIssue](/opengraph/extensions/github/edges/gh_reopenissue) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ReopenPullRequest](/opengraph/extensions/github/edges/gh_reopenpullrequest) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_RequestPrReview](/opengraph/extensions/github/edges/gh_requestprreview) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ResolveDependabotAlerts](/opengraph/extensions/github/edges/gh_resolvedependabotalerts) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_RunOrgMigration](/opengraph/extensions/github/edges/gh_runorgmigration) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_SetInteractionLimits](/opengraph/extensions/github/edges/gh_setinteractionlimits) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_SetIssueType](/opengraph/extensions/github/edges/gh_setissuetype) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_SetMilestone](/opengraph/extensions/github/edges/gh_setmilestone) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_SetSocialPreview](/opengraph/extensions/github/edges/gh_setsocialpreview) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ToggleDiscussionAnswer](/opengraph/extensions/github/edges/gh_togglediscussionanswer) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ToggleDiscussionCommentMinimize](/opengraph/extensions/github/edges/gh_togglediscussioncommentminimize) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ViewDependabotAlerts](/opengraph/extensions/github/edges/gh_viewdependabotalerts) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_ViewSecretScanningAlerts](/opengraph/extensions/github/edges/gh_viewsecretscanningalerts) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_WriteCodeScanning](/opengraph/extensions/github/edges/gh_writecodescanning) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_WriteRepoContents](/opengraph/extensions/github/edges/gh_writerepocontents) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | | [GH\_WriteRepoPullRequests](/opengraph/extensions/github/edges/gh_writerepopullrequests) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | ## Properties | Property Name | Data Type | Description | | ----------------- | --------- | ------------------------------------------------------------------------------------------------ | | objectid | string | A deterministic ID derived from the repo node\_id and role name. | | name | string | The fully qualified role name (e.g., `repoName\read`). | | id | string | Same as objectid. | | short\_name | string | The short role name (e.g., `read`, `write`, `admin`, `triage`, `maintain`, or custom role name). | | type | string | `default` for built-in roles or `custom` for custom repository roles. | | environment\_name | string | The name of the environment (GitHub organization). | | environmentid | string | The node\_id of the environment (GitHub organization). | | repository\_name | string | The name of the repository this role belongs to. | | repository\_id | string | The node\_id of the repository this role belongs to. | ## Diagram ```mermaid theme={null} flowchart TD GH_RepoRole[fa:fa-user-tie GH_RepoRole] GH_Repository[fa:fa-box-archive GH_Repository] GH_Branch[fa:fa-code-branch GH_Branch] GH_BranchProtectionRule[fa:fa-shield GH_BranchProtectionRule] GH_User[fa:fa-user GH_User] GH_Team[fa:fa-user-group GH_Team] GH_OrgRole[fa:fa-user-tie GH_OrgRole] GH_SecretScanningAlert[fa:fa-key GH_SecretScanningAlert] GH_RepoRole -.->|GH_ReadRepoContents| GH_Repository GH_RepoRole -.->|GH_WriteRepoContents| GH_Repository GH_RepoRole -.->|GH_AdminTo| GH_Repository GH_RepoRole -.->|GH_ViewSecretScanningAlerts| GH_Repository GH_RepoRole -.->|GH_BypassBranchProtection| GH_Repository GH_RepoRole -.->|GH_EditRepoProtections| GH_Repository %% Note: Additional non-traversable permission edges (issue triage, discussions, settings) omitted for readability. GH_RepoRole -.->|GH_ReadCodeScanning| GH_Repository GH_RepoRole -.->|GH_WriteCodeScanning| GH_Repository GH_RepoRole -.->|GH_ViewDependabotAlerts| GH_Repository GH_RepoRole -.->|GH_ResolveDependabotAlerts| GH_Repository GH_RepoRole -.->|GH_DeleteIssue| GH_Repository GH_RepoRole -.->|GH_CreateTag| GH_Repository GH_RepoRole -.->|GH_DeleteTag| GH_Repository GH_RepoRole -->|GH_HasBaseRole| GH_RepoRole GH_RepoRole -->|GH_CanEditProtection| GH_Repository GH_RepoRole -->|GH_CanEditProtection| GH_Branch GH_RepoRole -->|GH_CanWriteBranch| GH_Branch GH_RepoRole -->|GH_CanCreateBranch| GH_Repository GH_RepoRole -->|GH_CanReadSecretScanningAlert| GH_SecretScanningAlert GH_User -->|GH_HasRole| GH_RepoRole GH_Team -->|GH_HasRole| GH_RepoRole GH_OrgRole -->|GH_HasBaseRole| GH_RepoRole ``` # GH_RepoSecret Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_reposecret A repository-level GitHub Actions secret accessible only to workflows in that repository Applies to BloodHound Enterprise and CE Represents a repository-level GitHub Actions secret. These are secrets defined directly on a specific repository and are only accessible to workflows running in that repository. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_HasSecret](/opengraph/extensions/github/edges/gh_hassecret) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ✅ | ### Outbound Edges No outbound edges are defined by the GitHub extension for this node. ## Properties | Property Name | Data Type | Description | | ----------------- | --------- | ---------------------------------------------------------------------- | | objectid | string | A deterministic ID in the format `GHSecret_{repoNodeId}_{secretName}`. | | id | string | Same as objectid. | | name | string | The name of the secret. | | environment\_name | string | The name of the environment (GitHub organization). | | environmentid | string | The node\_id of the environment (GitHub organization). | | repository\_name | string | The name of the containing repository. | | repository\_id | string | The node\_id of the containing repository. | | created\_at | datetime | When the secret was created. | | updated\_at | datetime | When the secret was last updated. | | visibility | string | The secret's visibility scope. | ## Diagram ```mermaid theme={null} flowchart TD GH_RepoSecret[fa:fa-lock GH_RepoSecret] GH_Repository[fa:fa-box-archive GH_Repository] GH_Repository -.->|GH_Contains| GH_RepoSecret GH_Repository -->|GH_HasSecret| GH_RepoSecret ``` # GH_Repository Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_repository A code repository in an organization, containing files, issues, and other resources Applies to BloodHound Enterprise and CE Represents a GitHub repository within the organization. Repository nodes capture metadata about the repo including visibility, Actions enablement status, and security configuration. Repository role nodes ([GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole)) are created alongside each repository to represent the permission levels available. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_AddAssignee](/opengraph/extensions/github/edges/gh_addassignee) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_AddLabel](/opengraph/extensions/github/edges/gh_addlabel) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_AdminTo](/opengraph/extensions/github/edges/gh_adminto) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_BypassBranchProtection](/opengraph/extensions/github/edges/gh_bypassbranchprotection) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_CanAccess](/opengraph/extensions/github/edges/gh_canaccess) | [GH\_PersonalAccessToken](/opengraph/extensions/github/nodes/gh_personalaccesstoken), [GH\_AppInstallation](/opengraph/extensions/github/nodes/gh_appinstallation) | ❌ | | [GH\_CanCreateBranch](/opengraph/extensions/github/edges/gh_cancreatebranch) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole), [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) | ✅ | | [GH\_CanEditProtection](/opengraph/extensions/github/edges/gh_caneditprotection) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ✅ | | [GH\_CloseDiscussion](/opengraph/extensions/github/edges/gh_closediscussion) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_CloseIssue](/opengraph/extensions/github/edges/gh_closeissue) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ClosePullRequest](/opengraph/extensions/github/edges/gh_closepullrequest) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_ConvertIssuesToDiscussions](/opengraph/extensions/github/edges/gh_convertissuestodiscussions) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_CreateDiscussionCategory](/opengraph/extensions/github/edges/gh_creatediscussioncategory) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_CreateSoloMergeQueueEntry](/opengraph/extensions/github/edges/gh_createsolomergequeueentry) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_CreateTag](/opengraph/extensions/github/edges/gh_createtag) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_DeleteAlertsCodeScanning](/opengraph/extensions/github/edges/gh_deletealertscodescanning) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_DeleteDiscussion](/opengraph/extensions/github/edges/gh_deletediscussion) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_DeleteDiscussionComment](/opengraph/extensions/github/edges/gh_deletediscussioncomment) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_DeleteIssue](/opengraph/extensions/github/edges/gh_deleteissue) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_DeleteTag](/opengraph/extensions/github/edges/gh_deletetag) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_EditCategoryOnDiscussion](/opengraph/extensions/github/edges/gh_editcategoryondiscussion) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_EditDiscussionCategory](/opengraph/extensions/github/edges/gh_editdiscussioncategory) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_EditDiscussionComment](/opengraph/extensions/github/edges/gh_editdiscussioncomment) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_EditRepoAnnouncementBanners](/opengraph/extensions/github/edges/gh_editrepoannouncementbanners) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_EditRepoCustomPropertiesValues](/opengraph/extensions/github/edges/gh_editrepocustompropertiesvalues) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_EditRepoMetadata](/opengraph/extensions/github/edges/gh_editrepometadata) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_EditRepoProtections](/opengraph/extensions/github/edges/gh_editrepoprotections) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_JumpMergeQueue](/opengraph/extensions/github/edges/gh_jumpmergequeue) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ManageDeployKeys](/opengraph/extensions/github/edges/gh_managedeploykeys) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ManageDiscussionBadges](/opengraph/extensions/github/edges/gh_managediscussionbadges) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ManageRepoSecurityProducts](/opengraph/extensions/github/edges/gh_managereposecurityproducts) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ManageSecurityProducts](/opengraph/extensions/github/edges/gh_managesecurityproducts) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ManageSettingsMergeTypes](/opengraph/extensions/github/edges/gh_managesettingsmergetypes) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ManageSettingsPages](/opengraph/extensions/github/edges/gh_managesettingspages) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ManageSettingsProjects](/opengraph/extensions/github/edges/gh_managesettingsprojects) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ManageSettingsWiki](/opengraph/extensions/github/edges/gh_managesettingswiki) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ManageTopics](/opengraph/extensions/github/edges/gh_managetopics) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ManageWebhooks](/opengraph/extensions/github/edges/gh_managewebhooks) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_MarkAsDuplicate](/opengraph/extensions/github/edges/gh_markasduplicate) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_Owns](/opengraph/extensions/github/edges/gh_owns) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ✅ | | [GH\_PushProtectedBranch](/opengraph/extensions/github/edges/gh_pushprotectedbranch) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ReadCodeScanning](/opengraph/extensions/github/edges/gh_readcodescanning) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ReadRepoContents](/opengraph/extensions/github/edges/gh_readrepocontents) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_RemoveAssignee](/opengraph/extensions/github/edges/gh_removeassignee) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_RemoveLabel](/opengraph/extensions/github/edges/gh_removelabel) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ReopenDiscussion](/opengraph/extensions/github/edges/gh_reopendiscussion) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ReopenIssue](/opengraph/extensions/github/edges/gh_reopenissue) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ReopenPullRequest](/opengraph/extensions/github/edges/gh_reopenpullrequest) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_RequestPrReview](/opengraph/extensions/github/edges/gh_requestprreview) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ResolveDependabotAlerts](/opengraph/extensions/github/edges/gh_resolvedependabotalerts) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_RunOrgMigration](/opengraph/extensions/github/edges/gh_runorgmigration) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_SetInteractionLimits](/opengraph/extensions/github/edges/gh_setinteractionlimits) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_SetIssueType](/opengraph/extensions/github/edges/gh_setissuetype) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_SetMilestone](/opengraph/extensions/github/edges/gh_setmilestone) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_SetSocialPreview](/opengraph/extensions/github/edges/gh_setsocialpreview) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ToggleDiscussionAnswer](/opengraph/extensions/github/edges/gh_togglediscussionanswer) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ToggleDiscussionCommentMinimize](/opengraph/extensions/github/edges/gh_togglediscussioncommentminimize) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ViewDependabotAlerts](/opengraph/extensions/github/edges/gh_viewdependabotalerts) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_ViewSecretScanningAlerts](/opengraph/extensions/github/edges/gh_viewsecretscanningalerts) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_WriteCodeScanning](/opengraph/extensions/github/edges/gh_writecodescanning) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_WriteRepoContents](/opengraph/extensions/github/edges/gh_writerepocontents) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | | [GH\_WriteRepoPullRequests](/opengraph/extensions/github/edges/gh_writerepopullrequests) | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_CanAssumeIdentity](/opengraph/extensions/github/edges/gh_canassumeidentity) | [AZFederatedIdentityCredential](/resources/nodes/az-federated-identity-credential), `AWSRole` | ✅ | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole), [GH\_TeamRole](/opengraph/extensions/github/nodes/gh_teamrole), [GH\_OrgSecret](/opengraph/extensions/github/nodes/gh_orgsecret), [GH\_AppInstallation](/opengraph/extensions/github/nodes/gh_appinstallation), [GH\_PersonalAccessToken](/opengraph/extensions/github/nodes/gh_personalaccesstoken), [GH\_PersonalAccessTokenRequest](/opengraph/extensions/github/nodes/gh_personalaccesstokenrequest), [GH\_RepoSecret](/opengraph/extensions/github/nodes/gh_reposecret), [GH\_EnvironmentSecret](/opengraph/extensions/github/nodes/gh_environmentsecret), [GH\_SecretScanningAlert](/opengraph/extensions/github/nodes/gh_secretscanningalert) | ❌ | | [GH\_HasBranch](/opengraph/extensions/github/edges/gh_hasbranch) | [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) | ❌ | | [GH\_HasEnvironment](/opengraph/extensions/github/edges/gh_hasenvironment) | [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_HasSecret](/opengraph/extensions/github/edges/gh_hassecret) | [GH\_OrgSecret](/opengraph/extensions/github/nodes/gh_orgsecret), [GH\_RepoSecret](/opengraph/extensions/github/nodes/gh_reposecret), [GH\_EnvironmentSecret](/opengraph/extensions/github/nodes/gh_environmentsecret) | ✅ | | [GH\_HasVariable](/opengraph/extensions/github/edges/gh_hasvariable) | [GH\_OrgVariable](/opengraph/extensions/github/nodes/gh_orgvariable), [GH\_RepoVariable](/opengraph/extensions/github/nodes/gh_repovariable) | ✅ | | [GH\_HasWorkflow](/opengraph/extensions/github/edges/gh_hasworkflow) | [GH\_Workflow](/opengraph/extensions/github/nodes/gh_workflow) | ❌ | ## Properties | Property Name | Data Type | Description | | ------------------------------ | --------- | ---------------------------------------------------------------------------- | | objectid | string | The GitHub `node_id` of the repository, used as the unique graph identifier. | | id | integer | The numeric GitHub ID of the repository. | | node\_id | string | The GitHub GraphQL node ID. Redundant with objectid. | | name | string | The repository name. | | full\_name | string | The fully qualified name (e.g., `org/repo`). | | environment\_name | string | The name of the environment (GitHub organization). | | environmentid | string | The node\_id of the environment (GitHub organization). | | owner\_id | integer | The numeric ID of the repository owner. | | owner\_node\_id | string | The node\_id of the repository owner. | | owner\_name | string | The login of the repository owner. | | private | boolean | Whether the repository is private. | | visibility | string | The visibility level: `public`, `private`, or `internal`. | | html\_url | string | URL to the repository on GitHub. | | description | string | The repository description. | | created\_at | datetime | When the repository was created. | | updated\_at | datetime | When the repository was last updated. | | pushed\_at | datetime | When the repository last had a push. | | archived | boolean | Whether the repository is archived. | | disabled | boolean | Whether the repository is disabled. | | open\_issues\_count | integer | Number of open issues. | | allow\_forking | boolean | Whether forking is allowed. | | web\_commit\_signoff\_required | boolean | Whether web-based commits require sign-off. | | forks | integer | Number of forks. | | open\_issues | integer | Number of open issues (includes pull requests). | | watchers | integer | Number of watchers. | | default\_branch | string | The name of the default branch (e.g., `main`). | | actions\_enabled | boolean | Whether GitHub Actions is enabled for this repository. | | secret\_scanning | string | Status of secret scanning (e.g., `enabled`, `disabled`). | ## Diagram ```mermaid theme={null} flowchart TD GH_Repository[fa:fa-box-archive GH_Repository] GH_Organization[fa:fa-building GH_Organization] GH_Branch[fa:fa-code-branch GH_Branch] GH_Workflow[fa:fa-cogs GH_Workflow] GH_Environment[fa:fa-leaf GH_Environment] GH_OrgSecret[fa:fa-lock GH_OrgSecret] GH_RepoSecret[fa:fa-lock GH_RepoSecret] GH_OrgVariable[fa:fa-lock-open GH_OrgVariable] GH_RepoVariable[fa:fa-lock-open GH_RepoVariable] GH_SecretScanningAlert[fa:fa-key GH_SecretScanningAlert] GH_RepoRole[fa:fa-user-tie GH_RepoRole] AZFederatedIdentityCredential[fa:fa-id-card AZFederatedIdentityCredential] GH_PersonalAccessToken[fa:fa-key GH_PersonalAccessToken] GH_Organization -->|GH_Owns| GH_Repository GH_Repository -.->|GH_HasBranch| GH_Branch GH_Repository -.->|GH_HasWorkflow| GH_Workflow GH_Repository -.->|GH_HasEnvironment| GH_Environment GH_Repository -->|GH_HasSecret| GH_OrgSecret GH_Repository -->|GH_HasSecret| GH_RepoSecret GH_Repository -->|GH_HasVariable| GH_OrgVariable GH_Repository -->|GH_HasVariable| GH_RepoVariable GH_Repository -.->|GH_Contains| GH_RepoSecret GH_Repository -.->|GH_Contains| GH_RepoVariable GH_Repository -.->|GH_Contains| GH_SecretScanningAlert GH_RepoRole -.->|GH_ReadRepoContents| GH_Repository GH_RepoRole -.->|GH_WriteRepoContents| GH_Repository GH_RepoRole -.->|GH_AdminTo| GH_Repository GH_RepoRole -.->|GH_BypassBranchProtection| GH_Repository GH_RepoRole -.->|GH_EditRepoProtections| GH_Repository GH_RepoRole -.->|GH_ViewSecretScanningAlerts| GH_Repository %% Note: Additional non-traversable permission edges (issue triage, discussions, settings) omitted for readability. GH_RepoRole -.->|GH_ReadCodeScanning| GH_Repository GH_RepoRole -.->|GH_WriteCodeScanning| GH_Repository GH_RepoRole -.->|GH_ViewDependabotAlerts| GH_Repository GH_RepoRole -.->|GH_ResolveDependabotAlerts| GH_Repository GH_RepoRole -.->|GH_DeleteIssue| GH_Repository GH_RepoRole -.->|GH_CreateTag| GH_Repository GH_RepoRole -.->|GH_DeleteTag| GH_Repository GH_RepoRole -->|GH_CanCreateBranch| GH_Repository GH_RepoRole -->|GH_CanEditProtection| GH_Repository GH_Repository -->|GH_CanAssumeIdentity| AZFederatedIdentityCredential ``` # GH_RepoVariable Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_repovariable A repository-level GitHub Actions variable accessible only to workflows in that repository. Unlike secrets, variable values are readable. Applies to BloodHound Enterprise and CE Represents a repository-level GitHub Actions variable. These are variables defined directly on a specific repository and are only accessible to workflows running in that repository. Unlike secrets, variable values are readable via the API. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------- | ------------------------------------------------------------------ | ----------- | | [GH\_HasVariable](/opengraph/extensions/github/edges/gh_hasvariable) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ✅ | ### Outbound Edges No outbound edges are defined by the GitHub extension for this node. ## Properties | Property Name | Data Type | Description | | ----------------- | --------- | --------------------------------------------------------------------------- | | objectid | string | A deterministic ID in the format `GH_Variable_{repoNodeId}_{variableName}`. | | id | string | Same as objectid. | | name | string | The name of the variable. | | environment\_name | string | The name of the environment (GitHub organization). | | environmentid | string | The node\_id of the environment (GitHub organization). | | repository\_name | string | The name of the containing repository. | | repository\_id | string | The node\_id of the containing repository. | | value | string | The plaintext value of the variable. | | created\_at | datetime | When the variable was created. | | updated\_at | datetime | When the variable was last updated. | ## Diagram ```mermaid theme={null} flowchart TD GH_RepoVariable[fa:fa-lock-open GH_RepoVariable] GH_Repository[fa:fa-box-archive GH_Repository] GH_Repository -.->|GH_Contains| GH_RepoVariable GH_Repository -->|GH_HasVariable| GH_RepoVariable ``` # GH_SamlIdentityProvider Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_samlidentityprovider A SAML identity provider configured for the organization, enabling SSO Applies to BloodHound Enterprise and CE Represents a SAML identity provider configured for the organization. This node captures the SAML SSO configuration details and serves as the parent container for external identity mappings. Through external identities, it enables linking GitHub users to their corporate identities in the identity provider. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ----------- | | [GH\_HasSamlIdentityProvider](/opengraph/extensions/github/edges/gh_hassamlidentityprovider) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ----------- | | [GH\_HasExternalIdentity](/opengraph/extensions/github/edges/gh_hasexternalidentity) | [GH\_ExternalIdentity](/opengraph/extensions/github/nodes/gh_externalidentity) | ❌ | ## Properties | Property Name | Data Type | Description | | ---------------------- | --------- | ---------------------------------------------------------- | | objectid | string | The GraphQL ID of the SAML identity provider. | | name | string | Same as objectid. | | node\_id | string | Same as objectid. | | environment\_name | string | The name of the environment (GitHub organization). | | environmentid | string | The GraphQL ID of the environment (GitHub organization). | | foreign\_environmentid | string | The ID of the foreign environment linked to this provider. | | digest\_method | string | The digest method used by the SAML provider. | | idp\_certificate | string | The identity provider's X.509 certificate. | | issuer | string | The SAML issuer URL. | | signature\_method | string | The signature method used by the SAML provider. | | sso\_url | string | The SAML single sign-on URL. | ## Diagram ```mermaid theme={null} flowchart TD GH_Organization[fa:fa-building GH_Organization] GH_SamlIdentityProvider[fa:fa-id-badge GH_SamlIdentityProvider] GH_ExternalIdentity[fa:fa-arrows-left-right GH_ExternalIdentity] GH_Organization -.->|GH_HasSamlIdentityProvider| GH_SamlIdentityProvider GH_SamlIdentityProvider -.->|GH_HasExternalIdentity| GH_ExternalIdentity ``` # GH_SecretScanningAlert Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_secretscanningalert A GitHub Advanced Security alert indicating a secret was accidentally committed to a repository Applies to BloodHound Enterprise and CE Represents a GitHub secret scanning alert detected in a repository. Secret scanning alerts are raised when GitHub detects a known secret pattern (such as an API key, token, or credential) committed to a repository. The alert captures the secret type, validity status, and current resolution state. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_CanReadSecretScanningAlert](/opengraph/extensions/github/edges/gh_canreadsecretscanningalert) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | ✅ | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ------------------------------------------------------------------ | ------------------------------------------------------ | ----------- | | [GH\_ValidToken](/opengraph/extensions/github/edges/gh_validtoken) | [GH\_User](/opengraph/extensions/github/nodes/gh_user) | ✅ | ## Properties | Property Name | Data Type | Description | | --------------------------- | --------- | ---------------------------------------------------------------------------------------------- | | objectid | string | A deterministic Base64-encoded ID derived from the organization, repository, and alert number. | | id | string | Same as objectid. | | name | string | The alert number. | | repository\_name | string | The name of the repository where the secret was detected. | | repository\_id | string | The node\_id of the repository. | | repository\_url | string | The HTML URL of the repository. | | secret\_type | string | The type of secret detected (e.g., `github_personal_access_token`, `aws_access_key_id`). | | secret\_type\_display\_name | string | A human-readable name for the secret type. | | validity | string | The validity status of the detected secret (e.g., `active`, `inactive`, `unknown`). | | state | string | The alert state (e.g., `open`, `resolved`). | | created\_at | datetime | When the alert was created. | | updated\_at | datetime | When the alert was last updated. | | url | string | The HTML URL to view the alert on GitHub. | ## Diagram ```mermaid theme={null} flowchart TD GH_Repository[fa:fa-box-archive GH_Repository] GH_SecretScanningAlert[fa:fa-key GH_SecretScanningAlert] GH_User[fa:fa-user GH_User] GH_OrgRole[fa:fa-user-tie GH_OrgRole] GH_RepoRole[fa:fa-user-tie GH_RepoRole] GH_Repository -.->|GH_Contains| GH_SecretScanningAlert GH_SecretScanningAlert -->|GH_ValidToken| GH_User GH_OrgRole -->|GH_CanReadSecretScanningAlert| GH_SecretScanningAlert GH_RepoRole -->|GH_CanReadSecretScanningAlert| GH_SecretScanningAlert ``` # GH_Team Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_team A team within an organization, grouping users for shared access and collaboration Applies to BloodHound Enterprise and CE Represents a GitHub team within the organization. Teams can have parent-child relationships, contain members with different roles (Member, Maintainer), and be assigned to repository roles. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_AddMember](/opengraph/extensions/github/edges/gh_addmember) | [GH\_TeamRole](/opengraph/extensions/github/nodes/gh_teamrole) | ✅ | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_MemberOf](/opengraph/extensions/github/edges/gh_memberof) | [GH\_TeamRole](/opengraph/extensions/github/nodes/gh_teamrole), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_BypassPullRequestAllowances](/opengraph/extensions/github/edges/gh_bypasspullrequestallowances) | [GH\_BranchProtectionRule](/opengraph/extensions/github/nodes/gh_branchprotectionrule) | ❌ | | [GH\_CanCreateBranch](/opengraph/extensions/github/edges/gh_cancreatebranch) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ✅ | | [GH\_CanWriteBranch](/opengraph/extensions/github/edges/gh_canwritebranch) | [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) | ✅ | | [GH\_HasRole](/opengraph/extensions/github/edges/gh_hasrole) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole), [GH\_TeamRole](/opengraph/extensions/github/nodes/gh_teamrole) | ✅ | | [GH\_MemberOf](/opengraph/extensions/github/edges/gh_memberof) | [GH\_Team](/opengraph/extensions/github/nodes/gh_team) | ✅ | | [GH\_RestrictionsCanPush](/opengraph/extensions/github/edges/gh_restrictionscanpush) | [GH\_BranchProtectionRule](/opengraph/extensions/github/nodes/gh_branchprotectionrule) | ❌ | ## Properties | Property Name | Data Type | Description | | ----------------- | --------- | ------------------------------------------------------------------------- | | objectid | string | The GitHub GraphQL `id` of the team, used as the unique graph identifier. | | name | string | The team's display name, derived from the slug property. | | id | string | The GraphQL ID of the team. | | node\_id | string | The GitHub node ID. Redundant with objectid. | | slug | string | The team's URL-safe slug identifier. | | description | string | The team's description. | | privacy | string | The team's privacy level (e.g., `visible`, `secret`). | | permission | string | The team's default permission on repositories. | | environment\_name | string | The name of the environment (GitHub organization). | | environmentid | string | The node\_id of the environment (GitHub organization). | ## Diagram ```mermaid theme={null} flowchart TD GH_Team[fa:fa-user-group GH_Team] GH_OrgRole[fa:fa-user-tie GH_OrgRole] GH_RepoRole[fa:fa-user-tie GH_RepoRole] GH_TeamRole[fa:fa-user-tie GH_TeamRole] GH_Branch[fa:fa-code-branch GH_Branch] GH_BranchProtectionRule[fa:fa-shield GH_BranchProtectionRule] GH_Repository[fa:fa-box-archive GH_Repository] GH_Team -->|GH_MemberOf| GH_Team GH_Team -->|GH_HasRole| GH_OrgRole GH_Team -->|GH_HasRole| GH_RepoRole GH_Team -.->|GH_BypassPullRequestAllowances| GH_BranchProtectionRule GH_Team -.->|GH_RestrictionsCanPush| GH_BranchProtectionRule GH_Team -->|GH_CanWriteBranch| GH_Branch GH_Team -->|GH_CanCreateBranch| GH_Repository GH_TeamRole -->|GH_MemberOf| GH_Team GH_TeamRole -->|GH_AddMember| GH_Team ``` # GH_TeamRole Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_teamrole The role a user has within a team (e.g., maintainer, member) Applies to BloodHound Enterprise and CE Represents a role within a GitHub team. Each team has two built-in roles: Member and Maintainer. Maintainers can add and remove team members. Team roles connect users to teams and transitively to any repository roles assigned to the team. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_HasRole](/opengraph/extensions/github/edges/gh_hasrole) | [GH\_User](/opengraph/extensions/github/nodes/gh_user), [GH\_Team](/opengraph/extensions/github/nodes/gh_team) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------- | ------------------------------------------------------ | ----------- | | [GH\_AddMember](/opengraph/extensions/github/edges/gh_addmember) | [GH\_Team](/opengraph/extensions/github/nodes/gh_team) | ✅ | | [GH\_MemberOf](/opengraph/extensions/github/edges/gh_memberof) | [GH\_Team](/opengraph/extensions/github/nodes/gh_team) | ✅ | ## Properties | Property Name | Data Type | Description | | ----------------- | --------- | ------------------------------------------------------------------------------------ | | objectid | string | A deterministic ID derived from the team ID and role name (e.g., `{teamId}_member`). | | name | string | The fully qualified role name (e.g., `TeamSlug\member`). | | id | string | Same as objectid. | | short\_name | string | The short role name: `member` or `maintainer`. | | type | string | Always `default` for team roles. | | environment\_name | string | The name of the environment (GitHub organization). | | environmentid | string | The node\_id of the environment (GitHub organization). | ## Diagram ```mermaid theme={null} flowchart TD GH_TeamRole[fa:fa-user-tie GH_TeamRole] GH_User[fa:fa-user GH_User] GH_Team[fa:fa-user-group GH_Team] GH_User -->|GH_HasRole| GH_TeamRole GH_TeamRole -->|GH_MemberOf| GH_Team GH_TeamRole -->|GH_AddMember| GH_Team ``` # GH_User Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_user An individual GitHub user account Applies to BloodHound Enterprise and CE Represents a GitHub user who is a member of the organization. Users are associated with organization roles (Owner or Member) and can be assigned to repository roles and team roles. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository), [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_MapsToUser](/opengraph/extensions/github/edges/gh_mapstouser) | [GH\_ExternalIdentity](/opengraph/extensions/github/nodes/gh_externalidentity) | ❌ | | [GH\_SyncedTo](/opengraph/extensions/github/edges/gh_syncedto) | [AZUser](/resources/nodes/az-user), [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [PingOneUser](https://github.com/andyrobbins/PingOneHound?tab=readme-ov-file#schema) | ✅ | | [GH\_ValidToken](/opengraph/extensions/github/edges/gh_validtoken) | [GH\_SecretScanningAlert](/opengraph/extensions/github/nodes/gh_secretscanningalert) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_BypassPullRequestAllowances](/opengraph/extensions/github/edges/gh_bypasspullrequestallowances) | [GH\_BranchProtectionRule](/opengraph/extensions/github/nodes/gh_branchprotectionrule) | ❌ | | [GH\_CanCreateBranch](/opengraph/extensions/github/edges/gh_cancreatebranch) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ✅ | | [GH\_CanWriteBranch](/opengraph/extensions/github/edges/gh_canwritebranch) | [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) | ✅ | | [GH\_HasPersonalAccessToken](/opengraph/extensions/github/edges/gh_haspersonalaccesstoken) | [GH\_PersonalAccessToken](/opengraph/extensions/github/nodes/gh_personalaccesstoken) | ❌ | | [GH\_HasPersonalAccessTokenRequest](/opengraph/extensions/github/edges/gh_haspersonalaccesstokenrequest) | [GH\_PersonalAccessTokenRequest](/opengraph/extensions/github/nodes/gh_personalaccesstokenrequest) | ❌ | | [GH\_HasRole](/opengraph/extensions/github/edges/gh_hasrole) | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole), [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole), [GH\_TeamRole](/opengraph/extensions/github/nodes/gh_teamrole) | ✅ | | [GH\_RestrictionsCanPush](/opengraph/extensions/github/edges/gh_restrictionscanpush) | [GH\_BranchProtectionRule](/opengraph/extensions/github/nodes/gh_branchprotectionrule) | ❌ | ## Properties | Property Name | Data Type | Description | | ----------------- | --------- | ---------------------------------------------------------------------- | | objectid | string | The GitHub `node_id` of the user, used as the unique graph identifier. | | name | string | The user's display name, derived from the login property. | | login | string | The user's GitHub login handle. | | company | string | The company listed on the user's profile. | | email | string | The user's public email address. | | full\_name | string | The user's full name from their profile. | | id | integer | The numeric GitHub ID of the user. | | node\_id | string | The GitHub GraphQL node ID. Redundant with objectid. | | environment\_name | string | The name of the environment (GitHub organization) the user belongs to. | | environmentid | string | The node\_id of the environment (GitHub organization). | ## Diagram ```mermaid theme={null} flowchart TD GH_User[fa:fa-user GH_User] GH_OrgRole[fa:fa-user-tie GH_OrgRole] GH_RepoRole[fa:fa-user-tie GH_RepoRole] GH_TeamRole[fa:fa-user-tie GH_TeamRole] GH_Branch[fa:fa-code-branch GH_Branch] GH_ExternalIdentity[fa:fa-arrows-left-right GH_ExternalIdentity] AZUser[fa:fa-user AZUser] Okta_User[fa:fa-user Okta_User] PingOneUser[fa:fa-user PingOneUser] GH_PersonalAccessToken[fa:fa-key GH_PersonalAccessToken] GH_PersonalAccessTokenRequest[fa:fa-key GH_PersonalAccessTokenRequest] GH_BranchProtectionRule[fa:fa-shield GH_BranchProtectionRule] GH_Repository[fa:fa-box-archive GH_Repository] GH_User -->|GH_HasRole| GH_OrgRole GH_User -->|GH_HasRole| GH_TeamRole GH_User -->|GH_HasRole| GH_RepoRole GH_User -.->|GH_BypassPullRequestAllowances| GH_BranchProtectionRule GH_User -.->|GH_RestrictionsCanPush| GH_BranchProtectionRule GH_User -->|GH_CanWriteBranch| GH_Branch GH_User -->|GH_CanCreateBranch| GH_Repository GH_ExternalIdentity -.->|GH_MapsToUser| GH_User AZUser -->|GH_SyncedTo| GH_User Okta_User -->|GH_SyncedTo| GH_User PingOneUser -->|GH_SyncedTo| GH_User ``` # GH_Workflow Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_workflow A GitHub Actions workflow defined in a repository Applies to BloodHound Enterprise and CE Represents a GitHub Actions workflow defined in a repository. Workflow nodes capture the workflow definition metadata including its file path, state, containing repository, and the full YAML contents of the workflow file. Only repositories with GitHub Actions enabled are queried for workflows. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------- | ------------------------------------------------------------------ | ----------- | | [GH\_HasWorkflow](/opengraph/extensions/github/edges/gh_hasworkflow) | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | ❌ | ### Outbound Edges No outbound edges are defined by the GitHub extension for this node. ## Properties | Property Name | Data Type | Description | | ----------------- | --------- | ---------------------------------------------------------------------------- | | objectid | string | The GitHub `node_id` of the workflow, used as the unique graph identifier. | | name | string | The fully qualified workflow name (e.g., `repoName\CI Build`). | | short\_name | string | The workflow's display name. | | node\_id | string | The GitHub GraphQL node ID. Redundant with objectid. | | environment\_name | string | The name of the environment (GitHub organization). | | environmentid | string | The node\_id of the environment (GitHub organization). | | repository\_name | string | The full name of the containing repository. | | repository\_id | string | The node\_id of the containing repository. | | path | string | The file path of the workflow definition (e.g., `.github/workflows/ci.yml`). | | state | string | The workflow state (e.g., `active`, `disabled_manually`). | | url | string | The API URL for the workflow. | | html\_url | string | The GitHub web URL for the workflow file. | | branch | string | The branch where the workflow file was found. | | contents | string | The full YAML contents of the workflow file, downloaded from the repository. | ## Diagram ```mermaid theme={null} flowchart TD GH_Workflow[fa:fa-cogs GH_Workflow] GH_Repository[fa:fa-box-archive GH_Repository] GH_Repository -.->|GH_HasWorkflow| GH_Workflow ``` # GH_WorkflowJob Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_workflowjob A job within a GitHub Actions workflow, with a runner, permissions, and an ordered list of steps Applies to BloodHound Enterprise and CE Represents a single job within a GitHub Actions workflow. Jobs are the top-level execution units of a workflow. They run on a runner, hold a set of steps, and can declare permissions, environments, and dependencies on other jobs. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------- | -------------------------------------------------------------------- | ----------- | | [GH\_DependsOn](/opengraph/extensions/github/edges/gh_dependson) | [GH\_WorkflowJob](/opengraph/extensions/github/nodes/gh_workflowjob) | ❌ | | [GH\_HasJob](/opengraph/extensions/github/edges/gh_hasjob) | [GH\_Workflow](/opengraph/extensions/github/nodes/gh_workflow) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ------------------------------------------------------------------------ | ---------------------------------------------------------------------- | ----------- | | [GH\_CallsWorkflow](/opengraph/extensions/github/edges/gh_callsworkflow) | [GH\_Workflow](/opengraph/extensions/github/nodes/gh_workflow) | ❌ | | [GH\_DependsOn](/opengraph/extensions/github/edges/gh_dependson) | [GH\_WorkflowJob](/opengraph/extensions/github/nodes/gh_workflowjob) | ❌ | | [GH\_DeploysTo](/opengraph/extensions/github/edges/gh_deploysto) | [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | ❌ | | [GH\_HasStep](/opengraph/extensions/github/edges/gh_hasstep) | [GH\_WorkflowStep](/opengraph/extensions/github/nodes/gh_workflowstep) | ❌ | ## Diagram ```mermaid theme={null} flowchart TD GH_Workflow[fa:fa-cogs GH_Workflow] GH_WorkflowJob1[fa:fa-gear GH_WorkflowJob] GH_WorkflowJob2[fa:fa-gear GH_WorkflowJob] GH_WorkflowStep[fa:fa-shoe-prints GH_WorkflowStep] GH_Environment[fa:fa-leaf GH_Environment] GH_Workflow -.->|GH_HasJob| GH_WorkflowJob1 GH_WorkflowJob1 -.->|GH_DependsOn| GH_WorkflowJob2 GH_WorkflowJob1 -.->|GH_HasStep| GH_WorkflowStep GH_WorkflowJob1 -.->|GH_DeploysTo| GH_Environment ``` # GH_WorkflowStep Source: https://bloodhound.specterops.io/opengraph/extensions/github/nodes/gh_workflowstep A single step within a GitHub Actions job — either a uses: action reference or a run: shell command Applies to BloodHound Enterprise and CE Represents a single step within a GitHub Actions job. A step is either a `uses:` action reference or a `run:` shell command. Steps are the leaf nodes of the workflow execution tree and are the primary location where secrets and variables are consumed. ## Edges The tables below list edges defined by the GitHub extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ------------------------------------------------------------ | -------------------------------------------------------------------- | ----------- | | [GH\_HasStep](/opengraph/extensions/github/edges/gh_hasstep) | [GH\_WorkflowJob](/opengraph/extensions/github/nodes/gh_workflowjob) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [GH\_UsesSecret](/opengraph/extensions/github/edges/gh_usessecret) | [GH\_RepoSecret](/opengraph/extensions/github/nodes/gh_reposecret), [GH\_OrgSecret](/opengraph/extensions/github/nodes/gh_orgsecret) | ❌ | | [GH\_UsesVariable](/opengraph/extensions/github/edges/gh_usesvariable) | [GH\_RepoVariable](/opengraph/extensions/github/nodes/gh_repovariable), [GH\_OrgVariable](/opengraph/extensions/github/nodes/gh_orgvariable) | ❌ | ## Diagram ```mermaid theme={null} flowchart TD GH_WorkflowJob[fa:fa-gear GH_WorkflowJob] GH_WorkflowStep[fa:fa-shoe-prints GH_WorkflowStep] GH_RepoSecret[fa:fa-lock GH_RepoSecret] GH_OrgVariable[fa:fa-sliders GH_OrgVariable] GH_WorkflowJob -.->|GH_HasStep| GH_WorkflowStep GH_WorkflowStep -.->|GH_UsesSecret| GH_RepoSecret GH_WorkflowStep -.->|GH_UsesVariable| GH_OrgVariable ``` # Overview Source: https://bloodhound.specterops.io/opengraph/extensions/github/overview Learn about the GitHub OpenGraph extension for BloodHound. Applies to BloodHound Enterprise and CE The GitHub extension is an OpenGraph extension for [GitHub](https://github.com/) that enables BloodHound to model GitHub organizations, identities, repositories, workflows, secrets, roles, and related relationships as graph data. It adds GitHub-specific [nodes](/opengraph/extensions/github/schema#nodes), [edges](/opengraph/extensions/github/schema#edges), [Cypher queries](/opengraph/extensions/github/queries), and [Privilege Zone rules](/opengraph/extensions/github/privilege-zone-rules) to help security professionals visualize and analyze their GitHub configurations in BloodHound. In BloodHound Enterprise v9.3.0 and later, GitHub is supported as a pre-installed extension. Use [OpenGraph Extension Management](/opengraph/extensions/manage) to verify the installed version or upload a newer supported schema manually. ## GitHub Attack Paths GitHub is a highly valuable target for attackers in the modern enterprise. The privileged actions required to administer repositories, manage secrets, and control CI/CD pipelines allow elevated access to source code, cloud environments, and connected infrastructure, and complicate the jobs of defensive teams trying to differentiate benign and malicious actions. Compromising a GitHub organization can provide attackers with a wide range of access to laterally move across repositories, exfiltrate sensitive code and secrets, tamper with CI/CD pipelines, and pivot to connected cloud environments via OIDC federation. Example GitHub graph Our research on GitHub attack paths is still ongoing. ## Available Collectors The GitHub extension supports two collector paths: * [OpenHound GitHub collector](/openhound/collectors/github/overview): The SpecterOps-supported GitHub collector. This is the primary documented path for collecting GitHub data for BloodHound. * [GitHound collector](https://github.com/SpecterOps/GitHound): An alternative GitHub collector that also targets the GitHub extension schema. ## Community Please join us in the `#github-og` channel of the [BloodHound Community Slack](https://slack.specterops.io/) workspace if you want to chat about attack paths in GitHub. You are also welcome to open an issue or pull request on [GitHub](https://github.com/SpecterOps/openhound-github). ## Related Pages * [Getting started](/opengraph/extensions/github/getting-started) * [Schema reference](/opengraph/extensions/github/schema) * [Cypher queries](/opengraph/extensions/github/queries) * [Privilege Zone rules](/opengraph/extensions/github/privilege-zone-rules) * [OpenHound GitHub collector overview](/openhound/collectors/github/overview) # Privilege Zone Rules Source: https://bloodhound.specterops.io/opengraph/extensions/github/privilege-zone-rules GitHub extension Privilege Zone rules Applies to BloodHound Enterprise and CE The following Privilege Zone rules can be imported into BloodHound to group nodes for Cypher query analysis and BloodHound Enterprise finding generation. This file is automatically generated from the [JSON Privilege Zone rule files](https://github.com/SpecterOps/openhound-github/tree/main/extension/privilege_zone_rules). ## Tier Zero All-Repo Admin Role The synthetic all\_repo\_admin role grants admin access to every repository in the organization. This role is inherited by the owners role via GH\_HasBaseRole and cascades admin permissions including branch protection editing, secret access, and deploy key management to all repositories. Zone: Tier Zero ```cypher theme={null} MATCH (n:GH_OrgRole) WHERE n.name CONTAINS 'ALL_REPO_ADMIN' RETURN n ``` This rule is defined in the [t0-all-repo-admin-role.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/privilege_zone_rules/t0-all-repo-admin-role.json) file. ## Tier Zero App Installations (All Repositories) GitHub App installations scoped to all repositories in the organization that have at least one write permission. A compromised app credential grants write access to every repository. Installations with only read permissions are excluded — they pose a data exfiltration risk but do not grant control over the organization. Zone: Tier Zero ```cypher theme={null} MATCH (n:GH_AppInstallation {repository_selection:'all'}) WHERE n.permissions CONTAINS '"write"' RETURN n ``` This rule is defined in the [t0-app-installations-all-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/privilege_zone_rules/t0-app-installations-all-repos.json) file. ## Tier Zero Apps (All-Repository Installations) GitHub App definitions whose installations have write access to all repositories. The app owner controls the private key that can generate tokens for any installation. Compromise of the app's private key grants write access to every repository in organizations where it is installed. Apps whose installations have only read permissions are excluded. Zone: Tier Zero ```cypher theme={null} MATCH (n:GH_App)-[:GH_InstalledAs]->(i:GH_AppInstallation {repository_selection:'all'}) WHERE i.permissions CONTAINS '"write"' RETURN n ``` This rule is defined in the [t0-apps-all-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/privilege_zone_rules/t0-apps-all-repos.json) file. ## Tier Zero External Identities (Owner-Mapped) External identities from SAML/SCIM providers that map to GitHub users holding the owners role. Compromise of these external identities in the identity provider grants organizational owner access to GitHub via SSO. Zone: Tier Zero ```cypher theme={null} MATCH (n:GH_ExternalIdentity)-[:GH_MapsToUser]->(:GH_User)-[:GH_HasRole]->(:GH_OrgRole {short_name:'owners'}) RETURN n ``` This rule is defined in the [t0-external-identities-owners.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/privilege_zone_rules/t0-external-identities-owners.json) file. ## Tier Zero Organizations GitHub organizations are the root trust boundary for all repositories, teams, users, and settings. Compromise of the organization grants full administrative control over all contained assets. Zone: Tier Zero ```cypher theme={null} MATCH (n:GH_Organization) RETURN n ``` This rule is defined in the [t0-organizations.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/privilege_zone_rules/t0-organizations.json) file. ## Tier Zero Owner Users Users who hold the organization owners role have full administrative control over the GitHub organization. Compromise of any owner account grants control over all repositories, secrets, SSO configuration, and cloud identities. Zone: Tier Zero ```cypher theme={null} MATCH (n:GH_User)-[:GH_HasRole]->(:GH_OrgRole {short_name:'owners'}) RETURN n ``` This rule is defined in the [t0-owner-users.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/privilege_zone_rules/t0-owner-users.json) file. ## Tier Zero Owners Role The owners organization role grants full administrative control including all repository admin, member management, SSO configuration, app management, and billing. Owners inherit all\_repo\_admin, cascading admin access to every repository, secret, environment, and cloud identity in the organization. Zone: Tier Zero ```cypher theme={null} MATCH (n:GH_OrgRole {short_name:'owners'}) RETURN n ``` This rule is defined in the [t0-owners-role.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/privilege_zone_rules/t0-owners-role.json) file. ## Tier Zero PATs (All Repositories) Fine-grained personal access tokens scoped to all repositories in the organization that have at least one write permission. A single compromised token grants write access to every repository. PATs with only read permissions are excluded — they pose a data exfiltration risk but do not grant control over the organization. Zone: Tier Zero ```cypher theme={null} MATCH (n:GH_PersonalAccessToken {repository_selection:'all'}) WHERE n.permissions CONTAINS '"write"' RETURN n ``` This rule is defined in the [t0-pats-all-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/privilege_zone_rules/t0-pats-all-repos.json) file. ## Tier Zero Privilege Escalation Roles Custom organization roles with write\_organization\_custom\_org\_role permission can modify organization role definitions, including setting the base\_role to inherit all\_repo\_admin. Since this permission only exists on custom organization roles, the holder can escalate the role they already hold — a guaranteed self-escalation path to full organizational control. Zone: Tier Zero ```cypher theme={null} MATCH (n:GH_OrgRole)-[:GH_WriteOrganizationCustomOrgRole]->(:GH_Organization) RETURN n ``` This rule is defined in the [t0-privilege-escalation-roles.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/privilege_zone_rules/t0-privilege-escalation-roles.json) file. ## Tier Zero Privilege Escalation Users Users who hold custom organization roles with write\_organization\_custom\_org\_role permission. These users can modify organization role definitions — including the role they hold — to set the base\_role to all\_repo\_admin, granting themselves admin access to every repository in the organization. Zone: Tier Zero ```cypher theme={null} MATCH (n:GH_User)-[:GH_HasRole|GH_HasBaseRole*1..]->(:GH_OrgRole)-[:GH_WriteOrganizationCustomOrgRole]->(:GH_Organization) RETURN n ``` This rule is defined in the [t0-privilege-escalation-users.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/privilege_zone_rules/t0-privilege-escalation-users.json) file. ## Tier Zero SAML Identity Providers SAML identity providers control authentication for all organization members via SSO. Compromise of the identity provider grants the ability to impersonate any user, including organization owners, by manipulating SAML assertions or resetting credentials. Zone: Tier Zero ```cypher theme={null} MATCH (n:GH_SamlIdentityProvider) RETURN n ``` This rule is defined in the [t0-saml-identity-providers.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/privilege_zone_rules/t0-saml-identity-providers.json) file. # Cypher Queries Source: https://bloodhound.specterops.io/opengraph/extensions/github/queries GitHub extension Cypher queries Applies to BloodHound Enterprise and CE The following custom Cypher queries can be imported into BloodHound to enhance visibility. This file is automatically generated from the [JSON query files](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches). ## Actions SHA Pinning Not Required Finds organizations that do not require SHA pinning for GitHub Actions. Without pinning, actions referenced by tag can be silently replaced with malicious versions. ```cypher theme={null} MATCH (org:GH_Organization {actions_sha_pinning_required: false}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [actions-sha-pinning-not-required.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/actions-sha-pinning-not-required.json) file. ## Active Leaked Secrets Finds secret scanning alerts that are both unresolved and confirmed active. These are valid, usable credentials committed to source code and represent an immediate compromise risk. ```cypher theme={null} MATCH p=(:GH_Repository)-[:GH_Contains]->(alert:GH_SecretScanningAlert {state: 'open', validity: 'active'}) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [active-leaked-secrets.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/active-leaked-secrets.json) file. ## Advanced Security Disabled for New Repositories Finds organizations where GitHub Advanced Security is not automatically enabled for new repositories. New repositories will lack code scanning, secret scanning, and other GHAS features. ```cypher theme={null} MATCH (org:GH_Organization {advanced_security_enabled_for_new_repositories: false}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [advanced-security-disabled-new-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/advanced-security-disabled-new-repos.json) file. ## All GitHub Actions Allowed Finds organizations that allow all GitHub Actions to run, including third-party actions from the marketplace. This creates supply chain risk if a malicious or compromised action is used. ```cypher theme={null} MATCH (org:GH_Organization {actions_allowed_actions: 'all'}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [all-actions-allowed.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/all-actions-allowed.json) file. ## App Installations with Access to All Repositories Finds GitHub App installations that have access to every repository in the organization. A compromised app credential would affect all repositories. ```cypher theme={null} MATCH (app:GH_AppInstallation {repository_selection: 'all'}) RETURN app LIMIT 1000 ``` This query can be imported into BloodHound from the [app-installations-all-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/app-installations-all-repos.json) file. ## Branch Protection Rules - Admins Not Enforced Finds branch protection rules where administrators can bypass all protections. Admins can push directly, skip reviews, and override status checks on these branches. ```cypher theme={null} MATCH p=(:GH_BranchProtectionRule {enforce_admins: false})-[:GH_ProtectedBy]->(:GH_Branch) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [branch-protection-admins-not-enforced.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/branch-protection-admins-not-enforced.json) file. ## Branch Protection Rules - Deletions Allowed Finds protected branches that can be deleted. Branch deletion can result in loss of code and removal of audit history. ```cypher theme={null} MATCH p=(:GH_BranchProtectionRule {allows_deletions: true})-[:GH_ProtectedBy]->(:GH_Branch) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [branch-protection-deletions-allowed.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/branch-protection-deletions-allowed.json) file. ## Branch Protection Rules - Force Pushes Allowed Finds branches where force pushes are allowed. Force pushes can rewrite commit history, potentially hiding malicious changes or destroying audit trails. ```cypher theme={null} MATCH p=(:GH_BranchProtectionRule {allows_force_pushes: true})-[:GH_ProtectedBy]->(:GH_Branch) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [branch-protection-force-pushes.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/branch-protection-force-pushes.json) file. ## Branch Protection Rules - No Code Owner Reviews Finds branches where code owner reviews are not required. Changes to security-critical paths can be merged without authorization from the designated code owners. ```cypher theme={null} MATCH p=(:GH_BranchProtectionRule {require_code_owner_reviews: false})-[:GH_ProtectedBy]->(:GH_Branch) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [branch-protection-no-code-owner-reviews.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/branch-protection-no-code-owner-reviews.json) file. ## Branch Protection Rules - No Pull Request Reviews Required Finds branches where pull request reviews are not required. Code can be merged directly without peer review, increasing the risk of undetected vulnerabilities or malicious changes. ```cypher theme={null} MATCH p=(:GH_BranchProtectionRule {required_pull_request_reviews: false})-[:GH_ProtectedBy]->(:GH_Branch) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [branch-protection-no-pr-reviews.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/branch-protection-no-pr-reviews.json) file. ## Branch Protection Rules - No Status Checks Required Finds branches where CI/CD status checks are not required before merging. Code with failing tests or security scans can be merged into protected branches. ```cypher theme={null} MATCH p=(:GH_BranchProtectionRule {requires_status_checks: false})-[:GH_ProtectedBy]->(:GH_Branch) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [branch-protection-no-status-checks.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/branch-protection-no-status-checks.json) file. ## Branch Protection Rules - Self-Approval Allowed Finds branches where the author of the last push can approve their own pull request. This allows a single person to both write and approve code changes. ```cypher theme={null} MATCH p=(:GH_BranchProtectionRule {require_last_push_approval: false})-[:GH_ProtectedBy]->(:GH_Branch) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [branch-protection-self-approval.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/branch-protection-self-approval.json) file. ## Branch Protection Rules - Stale Reviews Not Dismissed Finds branches where stale reviews are not dismissed when new commits are pushed. An attacker could get a review approved, then push additional malicious commits that inherit the stale approval. ```cypher theme={null} MATCH p=(:GH_BranchProtectionRule {dismisses_stale_reviews: false})-[:GH_ProtectedBy]->(:GH_Branch) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [branch-protection-stale-reviews.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/branch-protection-stale-reviews.json) file. ## Users Who Can Bypass Pull Request Requirements Finds users and teams that can bypass pull request review requirements on protected branches. These actors can merge code without any reviews. ```cypher theme={null} MATCH p=(actor)-[:GH_BypassPullRequestAllowances]->(rule:GH_BranchProtectionRule)-[:GH_ProtectedBy]->(branch:GH_Branch) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [bypass-pr-requirements.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/bypass-pr-requirements.json) file. ## Dangerous Branch Permissions Identifies users with dangerous branch permissions in a GitHub organization, including bypass allowances on protection rules. ```cypher theme={null} MATCH p=(:GH_User)-[:GH_HasRole|GH_HasBaseRole|GH_MemberOf*1..]->(:GH_RepoRole)-[:GH_PushProtectedBranch|GH_BypassBranchProtection]-(r:GH_Repository) MATCH p1=(:GH_User)-[:GH_BypassPullRequestAllowances|GH_RestrictionsCanPush]->(rule:GH_BranchProtectionRule)-[:GH_ProtectedBy]->(b:GH_Branch) RETURN p,p1 LIMIT 1000 ``` This query can be imported into BloodHound from the [dangerous-branch-perms.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/dangerous-branch-perms.json) file. ## Organizations with default repository permission Returns organizations that have a default repository permission other than 'none'. ```cypher theme={null} MATCH (o:GH_Organization) WHERE o.default_repository_permission <> 'none' RETURN o LIMIT 1000 ``` This query can be imported into BloodHound from the [default-repository-permissions.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/default-repository-permissions.json) file. ## \[Demo] SSO Round-Trip: Azure/Okta → GitHub → Cloud Identity The cloud-to-cloud pivot through GitHub: a compromised Azure or Okta identity syncs to a GitHub user via SSO. That GitHub user has write access to repositories configured with OIDC federation to Azure workload identities. The attacker pivots from one cloud identity through GitHub into a completely different Azure identity — crossing cloud boundaries twice in a single attack chain. ```cypher theme={null} MATCH p1=(extUser)-[:SyncedToGHUser]->(ghUser:GH_User) MATCH p2=(ghUser)-[:GH_HasRole|GH_HasBaseRole|GH_MemberOf*1..]->(:GH_RepoRole)-[:GH_WriteRepoContents]->(:GH_Repository)-[:GH_CanAssumeIdentity]->(cred:AZFederatedIdentityCredential) RETURN p1, p2 LIMIT 1000 ``` This query can be imported into BloodHound from the [demo-sso-to-cloud-round-trip.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/demo-sso-to-cloud-round-trip.json) file. ## Dependabot Alerts Disabled for New Repositories Finds organizations where Dependabot alerts are not enabled for new repositories. Vulnerable dependencies in new repositories will go undetected. ```cypher theme={null} MATCH (org:GH_Organization {dependabot_alerts_enabled_for_new_repositories: false}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [dependabot-alerts-disabled-new-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/dependabot-alerts-disabled-new-repos.json) file. ## Dependabot Security Updates Disabled for New Repositories Finds organizations where Dependabot security update PRs are not enabled for new repositories. Known vulnerable dependencies will not receive automated fix PRs. ```cypher theme={null} MATCH (org:GH_Organization {dependabot_security_updates_enabled_for_new_repositories: false}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [dependabot-updates-disabled-new-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/dependabot-updates-disabled-new-repos.json) file. ## Dependency Graph Disabled for New Repositories Finds organizations where the dependency graph is not enabled for new repositories. Without the dependency graph, transitive dependency vulnerabilities cannot be tracked. ```cypher theme={null} MATCH (org:GH_Organization {dependency_graph_enabled_for_new_repositories: false}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [dependency-graph-disabled-new-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/dependency-graph-disabled-new-repos.json) file. ## Environments Where Admins Can Bypass Protections Finds deployment environments where administrators can bypass protection rules such as required reviewers and wait timers. Admins can deploy to these environments without any approval. ```cypher theme={null} MATCH p=(:GH_Repository)-[:GH_HasEnvironment]->(env:GH_Environment {can_admins_bypass: true}) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [environments-admin-bypass.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/environments-admin-bypass.json) file. ## Expired Personal Access Tokens Finds expired personal access tokens that still exist. Expired tokens should be cleaned up to reduce credential inventory and audit noise. ```cypher theme={null} MATCH p=(:GH_User)-[:GH_HasPersonalAccessToken]->(token:GH_PersonalAccessToken {token_expired: true}) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [expired-pats.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/expired-pats.json) file. ## External Identities Without SCIM Provisioning Finds external identities that lack SCIM synchronization. Without SCIM, user deprovisioning in the identity provider will not automatically revoke GitHub access. ```cypher theme={null} MATCH (ei:GH_ExternalIdentity) WHERE ei.scim_identity_username = '' RETURN ei LIMIT 1000 ``` This query can be imported into BloodHound from the [external-identities-without-scim.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/external-identities-without-scim.json) file. ## GitHub-to-Azure Identity Assumptions Finds GitHub entities (repositories, branches, environments) that can assume Azure identities via OIDC federation. Verify that each trust relationship is intentional and scoped appropriately. ```cypher theme={null} MATCH p=(src)-[:GH_CanAssumeIdentity]->(cred:AZFederatedIdentityCredential) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [github-to-azure-identity.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/github-to-azure-identity.json) file. ## Global Repo Permissions Returns all users who hold a global repository permission role (i.e., roles that are not default). ```cypher theme={null} MATCH p=(:GH_User)-[:GH_HasBaseRole|GH_HasRole|GH_MemberOf*1..3]->(role:GH_OrgRole) WHERE role.short_name CONTAINS 'all_repo_' RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [global-repo-perms.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/global-repo-perms.json) file. ## External Identities Returns all external identities (e.g., Azure or Okta users) that are associated with GitHub users. ```cypher theme={null} MATCH p=(s)-[]->(d:GH_User) WHERE s:AZUser OR s:Okta_User RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [hybrid-identities.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/hybrid-identities.json) file. ## Members Can Change Repository Visibility Finds organizations where members can change repository visibility. This allows any member to make a private repository public, potentially exposing source code and secrets. ```cypher theme={null} MATCH (org:GH_Organization {members_can_change_repo_visibility: true}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [members-can-change-repo-visibility.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/members-can-change-repo-visibility.json) file. ## Members Can Create GitHub Pages Finds organizations where members can create GitHub Pages sites. Pages can be used to host phishing content, data exfiltration endpoints, or other malicious resources. ```cypher theme={null} MATCH (org:GH_Organization {members_can_create_pages: true}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [members-can-create-pages.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/members-can-create-pages.json) file. ## Members Can Create Public Repositories Finds organizations where members can create internet-facing public repositories. This increases the risk of accidental exposure of proprietary code or secrets. ```cypher theme={null} MATCH (org:GH_Organization {members_can_create_public_repositories: true}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [members-can-create-public-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/members-can-create-public-repos.json) file. ## Members Can Delete Repositories Finds organizations where members can delete repositories. This poses a risk of accidental or malicious destruction of code and audit history. ```cypher theme={null} MATCH (org:GH_Organization {members_can_delete_repositories: true}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [members-can-delete-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/members-can-delete-repos.json) file. ## Members Can Fork Private Repositories Finds organizations where members can fork private repositories to personal accounts. Forked copies leave organizational control and oversight. ```cypher theme={null} MATCH (org:GH_Organization {members_can_fork_private_repositories: true}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [members-can-fork-private-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/members-can-fork-private-repos.json) file. ## Members Can Invite Outside Collaborators Finds organizations where any member can invite external users. This can lead to unauthorized third-party access to repositories without centralized oversight. ```cypher theme={null} MATCH (org:GH_Organization {members_can_invite_outside_collaborators: true}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [members-can-invite-outside-collaborators.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/members-can-invite-outside-collaborators.json) file. ## Organization Owners Returns all users who hold the organization owners role. ```cypher theme={null} MATCH p=(:GH_User)-[:GH_HasRole]->(:GH_OrgRole {short_name:'owners'}) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [org-owners.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/org-owners.json) file. ## Organizations without 2FA Returns organizations that do not require two-factor authentication. ```cypher theme={null} MATCH (o:GH_Organization) WHERE o.two_factor_requirement_enabled = false RETURN o LIMIT 1000 ``` This query can be imported into BloodHound from the [orgs-without-2fa.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/orgs-without-2fa.json) file. ## PATs with Access to All Repositories Finds fine-grained personal access tokens scoped to all repositories. A single compromised token grants access to every repository in the organization. ```cypher theme={null} MATCH p=(:GH_User)-[:GH_HasPersonalAccessToken]->(token:GH_PersonalAccessToken {repository_selection: 'all'}) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [pats-all-repo-access.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/pats-all-repo-access.json) file. ## Pending PAT Requests Finds pending fine-grained personal access token requests awaiting approval. Review these to ensure requested permissions are appropriate before granting access. ```cypher theme={null} MATCH p=(:GH_User)-[:GH_HasPersonalAccessTokenRequest]->(req:GH_PersonalAccessTokenRequest) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [pending-pat-requests.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/pending-pat-requests.json) file. ## Private Repositories with Forking Allowed Finds private repositories that allow forking. Forked copies of private repositories can leave organizational governance and visibility. ```cypher theme={null} MATCH (repo:GH_Repository {visibility: 'private', allow_forking: true}) RETURN repo LIMIT 1000 ``` This query can be imported into BloodHound from the [private-repos-forking-allowed.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/private-repos-forking-allowed.json) file. ## Privileged Custom Org Roles Returns all custom organization roles that are privileged (i.e., have permissions that are not default) ```cypher theme={null} MATCH p=(role:GH_OrgRole {type:'custom'})-[r]->(dest) WHERE dest:GH_Organization OR dest:GH_OrgRole RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [privileged-custom-org-roles.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/privileged-custom-org-roles.json) file. ## Privileged Hybrid Identities Returns all hybrid identities (e.g., Azure or Okta users) that are associated with GitHub users who hold the organization owners role. ```cypher theme={null} MATCH p=()-[:GH_SyncedTo]->(:GH_User)-[:GH_HasRole]->(:GH_OrgRole {short_name:'owners'}) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [privileged-hybrid-identities.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/privileged-hybrid-identities.json) file. ## Public Repositories Returns all public repositories. ```cypher theme={null} MATCH (repo:GH_Repository {private: false}) RETURN repo LIMIT 1000 ``` This query can be imported into BloodHound from the [public-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/public-repos.json) file. ## Secret Scanning Push Protection Disabled for New Repositories Finds organizations where push protection is not enabled for new repositories. Without push protection, secrets can be committed without being blocked before they reach the repository. ```cypher theme={null} MATCH (org:GH_Organization {secret_scanning_push_protection_enabled_for_new_repositories: false}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [push-protection-disabled-new-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/push-protection-disabled-new-repos.json) file. ## Users Who Can Push to Protected Branches Finds users and teams that are allowed to push directly to protected branches when push restrictions are enabled. These actors bypass the normal pull request workflow. ```cypher theme={null} MATCH p=(actor)-[:GH_RestrictionsCanPush]->(rule:GH_BranchProtectionRule)-[:GH_ProtectedBy]->(branch:GH_Branch) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [push-to-protected-branches.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/push-to-protected-branches.json) file. ## Repositories with Secret Scanning Disabled Finds repositories where secret scanning is disabled. Committed credentials in these repositories will not be detected by GitHub. ```cypher theme={null} MATCH (repo:GH_Repository {secret_scanning: 'disabled'}) RETURN repo LIMIT 1000 ``` This query can be imported into BloodHound from the [repos-secret-scanning-disabled.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/repos-secret-scanning-disabled.json) file. ## Repos Vulnerable to Workflow Secret Exfiltration Secrets reachable by users who can create new branches. The GH\_CanCreateBranch edge accounts for branch protection rules, push restrictions, blocks\_creations settings, and all bypass mechanisms (admin, push\_protected\_branch, pushAllowances). Edges emit from RepoRole in the common case; per-actor edges from User/Team are only present when per-rule allowances grant additional access beyond the role. ```cypher theme={null} MATCH p1=(:GH_User)-[:GH_HasRole|GH_HasBaseRole|GH_MemberOf*1..]->(:GH_RepoRole)-[:GH_CanCreateBranch]->(repo:GH_Repository)-[:GH_HasSecret]->(s) WHERE (s:GH_RepoSecret OR s:GH_OrgSecret) OPTIONAL MATCH p2=(repo)<-[:GH_CanCreateBranch]-(:GH_User) OPTIONAL MATCH p3=(repo)<-[:GH_CanCreateBranch]-(:GH_Team)<-[:GH_HasRole|GH_MemberOf|GH_AddMember*1..]-(:GH_User) RETURN p1, p2, p3 LIMIT 1000 ``` This query can be imported into BloodHound from the [repos-vulnerable-to-workflow-secret-exfil.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/repos-vulnerable-to-workflow-secret-exfil.json) file. ## Repository Workflows Returns all repository workflows ```cypher theme={null} MATCH p=(:GH_Repository)-[:GH_HasWorkflow]->(:GH_Workflow) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [repository-workflows.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/repository-workflows.json) file. ## SAML Configuration Mapping Finds SAML Identity Providers, their external identities, and mapped users. ```cypher theme={null} MATCH p=(OIP:GH_SamlIdentityProvider)-[:GH_HasExternalIdentity]->(EI:GH_ExternalIdentity) MATCH p1=(OIP)<-[:GH_HasSamlIdentityProvider]-(:GH_Organization) MATCH p2=(EI)-[:GH_MapsToUser]->() RETURN p,p1,p2 LIMIT 1000 ``` This query can be imported into BloodHound from the [saml-configuration.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/saml-configuration.json) file. ## Secret Scanning Alerts Returns all repositories that have secret scanning alerts. ```cypher theme={null} MATCH p=(repo:GH_Repository)-[:GH_Contains]->(:GH_SecretScanningAlert {state:'open'}) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [secret-scanning-alerts.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/secret-scanning-alerts.json) file. ## Secret Scanning Disabled for New Repositories Finds organizations where secret scanning is not automatically enabled for new repositories. New repositories will not detect committed credentials until manually enabled. ```cypher theme={null} MATCH (org:GH_Organization {secret_scanning_enabled_for_new_repositories: false}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [secret-scanning-disabled-new-repos.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/secret-scanning-disabled-new-repos.json) file. ## Secrets Reachable by User Returns all repo and org secrets reachable by users through write access. Users with write access can create GitHub Actions workflows to access secrets. ```cypher theme={null} MATCH p=(:GH_User)-[:GH_HasRole|GH_HasBaseRole|GH_MemberOf*1..]->(:GH_RepoRole)-[:GH_WriteRepoContents]->(:GH_Repository)-[:GH_HasSecret]->(s) WHERE s:GH_RepoSecret OR s:GH_OrgSecret RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [secrets-reachable-by-user.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/secrets-reachable-by-user.json) file. ## Team Membership Admins Returns all users who hold the maintainer role over a team, this also represents team nesting. ```cypher theme={null} MATCH p=(:GH_User)-[:GH_HasRole]->(:GH_TeamRole)-[:GH_AddMember]->(team:GH_Team) MATCH p1=(team)<-[:GH_MemberOf]-(:GH_Team)<-[:GH_AddMember]-(:GH_TeamRole)<-[:GH_HasRole]-(:GH_User) RETURN p,p1 LIMIT 1000 ``` This query can be imported into BloodHound from the [team-membership-admin.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/team-membership-admin.json) file. ## Team Structure Returns the structure of teams within organizations, including team roles and their members. ```cypher theme={null} MATCH p=(:GH_User)-[:GH_HasRole]->(:GH_TeamRole)-[:GH_MemberOf*1..]->(:GH_Team) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [team-structure.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/team-structure.json) file. ## Unprotected Branches Returns all unprotected branches in repositories. ```cypher theme={null} MATCH p=(repo:GH_Repository)-[:GH_HasBranch]-(:GH_Branch {protected: false}) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [unprotected-branches.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/unprotected-branches.json) file. ## Repositories with Workflows and Unprotected Default Branch Returns all repositories that have GitHub Actions workflows and an unprotected default branch. This means that users with GH\_WriteRepoContents to the Repository can overwrite or change the workflow. ```cypher theme={null} MATCH p=(repo:GH_Repository)-[:GH_HasWorkflow]->(:GH_Workflow) MATCH p1=(repo)-[:GH_HasBranch]->(branch:GH_Branch) WHERE repo.default_branch = branch.short_name AND branch.protected = false RETURN p1 LIMIT 1000 ``` This query can be imported into BloodHound from the [unprotected-default-branch-with-workflow.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/unprotected-default-branch-with-workflow.json) file. ## Unprotected Default Branches Returns all default branches in repositories that are not protected. ```cypher theme={null} MATCH p=(repo:GH_Repository)-[:GH_HasBranch]-(branch:GH_Branch {protected: false}) WHERE repo.default_branch = branch.short_name RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [unprotected-default-branches.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/unprotected-default-branches.json) file. ## Web Commit Signoff Not Required Finds organizations that do not require sign-off for web-based commits. Without signoff, commit attribution cannot be verified. ```cypher theme={null} MATCH (org:GH_Organization {web_commit_signoff_required: false}) RETURN org LIMIT 1000 ``` This query can be imported into BloodHound from the [web-commit-signoff-not-required.json](https://github.com/SpecterOps/openhound-github/tree/main/extension/saved_searches/web-commit-signoff-not-required.json) file. # Schema Source: https://bloodhound.specterops.io/opengraph/extensions/github/schema GitHub extension definition schema Applies to BloodHound Enterprise and CE ## Metadata **Name:** SOGitHub
**Display Name:** GitHub Extension (by SpecterOps)
**Version:** v1.2.3
**Namespace:** GH
**Environment Kind:** GH\_Organization
**Source Kind:** GitHub This file is automatically generated from the [extension definition schema file](https://github.com/SpecterOps/openhound-github/blob/main/extension/schema.json). ## Nodes | Icon | Node Kind | Display Name | | ------------------------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------ | | GH_App | [GH\_App](/opengraph/extensions/github/nodes/gh_app) | GitHub App | | GH_AppInstallation | [GH\_AppInstallation](/opengraph/extensions/github/nodes/gh_appinstallation) | GitHub App Installation | | GH_Branch | [GH\_Branch](/opengraph/extensions/github/nodes/gh_branch) | GitHub Branch | | GH_BranchProtectionRule | [GH\_BranchProtectionRule](/opengraph/extensions/github/nodes/gh_branchprotectionrule) | GitHub Branch Protection Rule | | GH_Environment | [GH\_Environment](/opengraph/extensions/github/nodes/gh_environment) | GitHub Environment | | GH_EnvironmentSecret | [GH\_EnvironmentSecret](/opengraph/extensions/github/nodes/gh_environmentsecret) | GitHub Environment Secret | | GH_EnvironmentVariable | [GH\_EnvironmentVariable](/opengraph/extensions/github/nodes/gh_environmentvariable) | GitHub Environment Variable | | GH_ExternalIdentity | [GH\_ExternalIdentity](/opengraph/extensions/github/nodes/gh_externalidentity) | GitHub External Identity | | GH_Organization | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | GitHub Organization | | GH_OrgRole | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | GitHub Org Role | | GH_OrgSecret | [GH\_OrgSecret](/opengraph/extensions/github/nodes/gh_orgsecret) | GitHub Org Secret | | GH_OrgVariable | [GH\_OrgVariable](/opengraph/extensions/github/nodes/gh_orgvariable) | GitHub Org Variable | | GH_PersonalAccessToken | [GH\_PersonalAccessToken](/opengraph/extensions/github/nodes/gh_personalaccesstoken) | GitHub Personal Access Token | | GH_PersonalAccessTokenRequest | [GH\_PersonalAccessTokenRequest](/opengraph/extensions/github/nodes/gh_personalaccesstokenrequest) | GitHub Personal Access Token Request | | GH_RepoRole | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) | GitHub Repo Role | | GH_RepoSecret | [GH\_RepoSecret](/opengraph/extensions/github/nodes/gh_reposecret) | GitHub Repo Secret | | GH_Repository | [GH\_Repository](/opengraph/extensions/github/nodes/gh_repository) | GitHub Repository | | GH_RepoVariable | [GH\_RepoVariable](/opengraph/extensions/github/nodes/gh_repovariable) | GitHub Repo Variable | | GH_SamlIdentityProvider | [GH\_SamlIdentityProvider](/opengraph/extensions/github/nodes/gh_samlidentityprovider) | GitHub SAML Identity Provider | | GH_SecretScanningAlert | [GH\_SecretScanningAlert](/opengraph/extensions/github/nodes/gh_secretscanningalert) | GitHub Secret Scanning Alert | | GH_Team | [GH\_Team](/opengraph/extensions/github/nodes/gh_team) | GitHub Team | | GH_TeamRole | [GH\_TeamRole](/opengraph/extensions/github/nodes/gh_teamrole) | GitHub Team Role | | GH_User | [GH\_User](/opengraph/extensions/github/nodes/gh_user) | GitHub User | | GH_Workflow | [GH\_Workflow](/opengraph/extensions/github/nodes/gh_workflow) | GitHub Workflow | | GH_WorkflowJob | [GH\_WorkflowJob](/opengraph/extensions/github/nodes/gh_workflowjob) | GitHub Workflow Job | | GH_WorkflowStep | [GH\_WorkflowStep](/opengraph/extensions/github/nodes/gh_workflowstep) | GitHub Workflow Step | ## Edges | Relationship Kind | Traversable | Description | | -------------------------------------------------------------------------------------------------------------------------------------------- | :---------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [GH\_AddAssignee](/opengraph/extensions/github/edges/gh_addassignee) | ❌ | \[Repository] Repo role can assign users to issues and pull requests | | [GH\_AddCollaborator](/opengraph/extensions/github/edges/gh_addcollaborator) | ❌ | \[Organization] Org role can add outside collaborators | | [GH\_AddLabel](/opengraph/extensions/github/edges/gh_addlabel) | ❌ | \[Repository] Repo role can add labels to issues and pull requests | | [GH\_AddMember](/opengraph/extensions/github/edges/gh_addmember) | ✅ | Team role can add members to the team (maintainer privilege) | | [GH\_AdminTo](/opengraph/extensions/github/edges/gh_adminto) | ❌ | \[Repository] Repo role has admin access to the repository. | | [GH\_BypassBranchProtection](/opengraph/extensions/github/edges/gh_bypassbranchprotection) | ❌ | \[Repository] Repo role can bypass merge-gate branch protections (PR reviews, lock branch). Suppressed by enforce\_admins. | | [GH\_BypassPullRequestAllowances](/opengraph/extensions/github/edges/gh_bypasspullrequestallowances) | ❌ | User or team can bypass pull request requirements on a branch protection rule | | [GH\_CallsWorkflow](/opengraph/extensions/github/edges/gh_callsworkflow) | ❌ | \[Workflow] Job calls a reusable workflow — GH\_WorkflowJob → GH\_Workflow | | [GH\_CanAccess](/opengraph/extensions/github/edges/gh_canaccess) | ❌ | Personal access token or app installation can access this repository or organization | | [GH\_CanAssumeIdentity](/opengraph/extensions/github/edges/gh_canassumeidentity) | ✅ | Repository can assume this cloud identity via OIDC federation (Azure workload identity or AWS IAM role) | | [GH\_CanCreateBranch](/opengraph/extensions/github/edges/gh_cancreatebranch) | ✅ | \[Repository - Computed] Role can create new branches in this repository (unprotected branches that bypass the merge gate) | | [GH\_CanEditProtection](/opengraph/extensions/github/edges/gh_caneditprotection) | ✅ | \[Repository - Computed] Repo role can modify or remove branch protection rules for the repository/branch (computed from GH\_EditRepoProtections + GH\_ProtectedBy) | | [GH\_CanPwnRequest](/opengraph/extensions/github/edges/gh_canpwnrequest) | ✅ | \[Computed] Repo role can exploit a pwn-requestable workflow to execute arbitrary code with the target's secrets and permissions — GH\_RepoRole → GH\_Repository / GH\_Branch | | [GH\_CanReadSecretScanningAlert](/opengraph/extensions/github/edges/gh_canreadsecretscanningalert) | ✅ | \[Computed] Role can read secret scanning alerts (computed from GH\_ViewSecretScanningAlerts permission + GH\_Contains) | | [GH\_CanWriteBranch](/opengraph/extensions/github/edges/gh_canwritebranch) | ✅ | \[Repository - Computed] Role can push to this branch after evaluating branch protection rules, push restrictions, and bypass allowances | | [GH\_CloseDiscussion](/opengraph/extensions/github/edges/gh_closediscussion) | ❌ | \[Repository] Repo role can close discussions | | [GH\_CloseIssue](/opengraph/extensions/github/edges/gh_closeissue) | ❌ | \[Repository] Repo role can close issues | | [GH\_ClosePullRequest](/opengraph/extensions/github/edges/gh_closepullrequest) | ❌ | \[Repository] Repo role can close pull requests | | [GH\_Contains](/opengraph/extensions/github/edges/gh_contains) | ❌ | Container relationship for organizational hierarchy (org contains secrets/variables, repo contains secrets/variables, environment contains secrets/variables) | | [GH\_ConvertIssuesToDiscussions](/opengraph/extensions/github/edges/gh_convertissuestodiscussions) | ❌ | \[Repository] Repo role can convert issues to discussions | | [GH\_CreateDiscussionCategory](/opengraph/extensions/github/edges/gh_creatediscussioncategory) | ❌ | \[Repository] Repo role can create discussion categories | | [GH\_CreateRepository](/opengraph/extensions/github/edges/gh_createrepository) | ❌ | \[Organization] Org role can create repositories in the organization | | [GH\_CreateSoloMergeQueueEntry](/opengraph/extensions/github/edges/gh_createsolomergequeueentry) | ❌ | Repo role can create solo merge queue entries | | [GH\_CreateTag](/opengraph/extensions/github/edges/gh_createtag) | ❌ | \[Repository] Repo role can create tags and releases | | [GH\_CreateTeam](/opengraph/extensions/github/edges/gh_createteam) | ❌ | \[Organization] Org role can create teams in the organization | | [GH\_DeleteAlertsCodeScanning](/opengraph/extensions/github/edges/gh_deletealertscodescanning) | ❌ | \[Repository] Repo role can delete code scanning alerts | | [GH\_DeleteDiscussion](/opengraph/extensions/github/edges/gh_deletediscussion) | ❌ | \[Repository] Repo role can delete discussions | | [GH\_DeleteDiscussionComment](/opengraph/extensions/github/edges/gh_deletediscussioncomment) | ❌ | \[Repository] Repo role can delete discussion comments | | [GH\_DeleteIssue](/opengraph/extensions/github/edges/gh_deleteissue) | ❌ | \[Repository] Repo role can delete issues | | [GH\_DeleteTag](/opengraph/extensions/github/edges/gh_deletetag) | ❌ | \[Repository] Repo role can delete tags and releases | | [GH\_DependsOn](/opengraph/extensions/github/edges/gh_dependson) | ❌ | \[Workflow] Job must run after another job (needs: dependency) — ordering only, not an access path | | [GH\_DeploysTo](/opengraph/extensions/github/edges/gh_deploysto) | ❌ | \[Workflow] Job deploys to a GitHub Environment — GH\_WorkflowJob → GH\_Environment | | [GH\_EditCategoryOnDiscussion](/opengraph/extensions/github/edges/gh_editcategoryondiscussion) | ❌ | \[Repository] Repo role can change the category of a discussion | | [GH\_EditDiscussionCategory](/opengraph/extensions/github/edges/gh_editdiscussioncategory) | ❌ | \[Repository] Repo role can edit discussion categories | | [GH\_EditDiscussionComment](/opengraph/extensions/github/edges/gh_editdiscussioncomment) | ❌ | \[Repository] Repo role can edit discussion comments | | [GH\_EditRepoAnnouncementBanners](/opengraph/extensions/github/edges/gh_editrepoannouncementbanners) | ❌ | \[Repository] Repo role can edit repository announcement banners | | [GH\_EditRepoCustomPropertiesValues](/opengraph/extensions/github/edges/gh_editrepocustompropertiesvalues) | ❌ | \[Repository] Repo role can edit custom property values on the repository | | [GH\_EditRepoMetadata](/opengraph/extensions/github/edges/gh_editrepometadata) | ❌ | \[Repository] Repo role can edit repository metadata | | [GH\_EditRepoProtections](/opengraph/extensions/github/edges/gh_editrepoprotections) | ❌ | Repo role can edit branch protection rules | | [GH\_HasBaseRole](/opengraph/extensions/github/edges/gh_hasbaserole) | ✅ | Role inherits permissions from another role | | [GH\_HasBranch](/opengraph/extensions/github/edges/gh_hasbranch) | ❌ | Repository has this branch | | [GH\_HasEnvironment](/opengraph/extensions/github/edges/gh_hasenvironment) | ❌ | Repository or branch has/can deploy to this environment | | [GH\_HasExternalIdentity](/opengraph/extensions/github/edges/gh_hasexternalidentity) | ❌ | SAML identity provider has this external identity | | [GH\_HasJob](/opengraph/extensions/github/edges/gh_hasjob) | ❌ | \[Workflow] Workflow contains this job — GH\_Workflow → GH\_WorkflowJob | | [GH\_HasMember](/opengraph/extensions/github/edges/gh_hasmember) | ❌ | Enterprise or organization has this user as a member | | [GH\_HasPersonalAccessToken](/opengraph/extensions/github/edges/gh_haspersonalaccesstoken) | ❌ | User owns this personal access token that has been granted access to the organization | | [GH\_HasPersonalAccessTokenRequest](/opengraph/extensions/github/edges/gh_haspersonalaccesstokenrequest) | ❌ | User has a pending personal access token request for the organization | | [GH\_HasRole](/opengraph/extensions/github/edges/gh_hasrole) | ✅ | User or team has a role assignment (org role, team role, or repo role) | | [GH\_HasSamlIdentityProvider](/opengraph/extensions/github/edges/gh_hassamlidentityprovider) | ❌ | Organization has this SAML identity provider configured | | [GH\_HasSecret](/opengraph/extensions/github/edges/gh_hassecret) | ✅ | Repository or environment has access to this secret | | [GH\_HasStep](/opengraph/extensions/github/edges/gh_hasstep) | ❌ | \[Workflow] Job contains this step — GH\_WorkflowJob → GH\_WorkflowStep | | [GH\_HasVariable](/opengraph/extensions/github/edges/gh_hasvariable) | ✅ | Repository has access to this variable (org-level or repo-level) | | [GH\_HasWorkflow](/opengraph/extensions/github/edges/gh_hasworkflow) | ❌ | Repository has this workflow | | [GH\_InstalledAs](/opengraph/extensions/github/edges/gh_installedas) | ✅ | GitHub App is installed as this app installation on an organization | | [GH\_InviteMember](/opengraph/extensions/github/edges/gh_invitemember) | ❌ | \[Organization] Org role can invite members to the organization | | [GH\_JumpMergeQueue](/opengraph/extensions/github/edges/gh_jumpmergequeue) | ❌ | Repo role can jump the merge queue | | [GH\_ManageDeployKeys](/opengraph/extensions/github/edges/gh_managedeploykeys) | ❌ | \[Repository] Repo role can manage deploy keys | | [GH\_ManageDiscussionBadges](/opengraph/extensions/github/edges/gh_managediscussionbadges) | ❌ | \[Repository] Repo role can manage discussion badges | | [GH\_ManageOrganizationWebhooks](/opengraph/extensions/github/edges/gh_manageorganizationwebhooks) | ❌ | \[Organization] Org role can manage organization webhooks | | [GH\_ManageRepoSecurityProducts](/opengraph/extensions/github/edges/gh_managereposecurityproducts) | ❌ | Repo role can manage repo-level security products | | [GH\_ManageSecurityProducts](/opengraph/extensions/github/edges/gh_managesecurityproducts) | ❌ | Repo role can manage security products | | [GH\_ManageSettingsMergeTypes](/opengraph/extensions/github/edges/gh_managesettingsmergetypes) | ❌ | \[Repository] Repo role can manage allowed merge types | | [GH\_ManageSettingsPages](/opengraph/extensions/github/edges/gh_managesettingspages) | ❌ | \[Repository] Repo role can manage GitHub Pages settings | | [GH\_ManageSettingsProjects](/opengraph/extensions/github/edges/gh_managesettingsprojects) | ❌ | \[Repository] Repo role can manage project settings | | [GH\_ManageSettingsWiki](/opengraph/extensions/github/edges/gh_managesettingswiki) | ❌ | \[Repository] Repo role can manage wiki settings | | [GH\_ManageTopics](/opengraph/extensions/github/edges/gh_managetopics) | ❌ | \[Repository] Repo role can manage repository topics | | [GH\_ManageWebhooks](/opengraph/extensions/github/edges/gh_managewebhooks) | ❌ | \[Repository] Repo role can manage repository webhooks | | [GH\_MapsToUser](/opengraph/extensions/github/edges/gh_mapstouser) | ❌ | External identity maps to a GitHub user or identity provider user | | [GH\_MarkAsDuplicate](/opengraph/extensions/github/edges/gh_markasduplicate) | ❌ | \[Repository] Repo role can mark issues or pull requests as duplicates | | [GH\_MemberOf](/opengraph/extensions/github/edges/gh_memberof) | ✅ | Team role is a member of a team, or team is a nested member of a parent team | | [GH\_OrgBypassCodeScanningDismissalRequests](/opengraph/extensions/github/edges/gh_orgbypasscodescanningdismissalrequests) | ❌ | \[Organization] Org role can bypass code scanning dismissal requests | | [GH\_OrgBypassSecretScanningClosureRequests](/opengraph/extensions/github/edges/gh_orgbypasssecretscanningclosurerequests) | ❌ | \[Organization] Org role can bypass secret scanning closure requests | | [GH\_OrgReviewAndManageSecretScanningBypassRequests](/opengraph/extensions/github/edges/gh_orgreviewandmanagesecretscanningbypassrequests) | ❌ | \[Organization] Org role can review and manage secret scanning bypass requests | | [GH\_OrgReviewAndManageSecretScanningClosureRequests](/opengraph/extensions/github/edges/gh_orgreviewandmanagesecretscanningclosurerequests) | ❌ | \[Organization] Org role can review and manage secret scanning closure requests | | [GH\_Owns](/opengraph/extensions/github/edges/gh_owns) | ✅ | Organization owns a repository | | [GH\_ProtectedBy](/opengraph/extensions/github/edges/gh_protectedby) | ❌ | Branch protection rule protects this branch | | [GH\_PushProtectedBranch](/opengraph/extensions/github/edges/gh_pushprotectedbranch) | ❌ | \[Repository] Repo role can push to branches with push restrictions. Not affected by enforce\_admins. | | [GH\_ReadCodeScanning](/opengraph/extensions/github/edges/gh_readcodescanning) | ❌ | \[Repository] Repo role can read code scanning results | | [GH\_ReadOrganizationActionsUsageMetrics](/opengraph/extensions/github/edges/gh_readorganizationactionsusagemetrics) | ❌ | \[Organization] Org role can read Actions usage metrics | | [GH\_ReadOrganizationCustomOrgRole](/opengraph/extensions/github/edges/gh_readorganizationcustomorgrole) | ❌ | \[Organization] Org role can read custom org role definitions | | [GH\_ReadOrganizationCustomRepoRole](/opengraph/extensions/github/edges/gh_readorganizationcustomreporole) | ❌ | \[Organization] Org role can read custom repo role definitions | | [GH\_ReadRepoContents](/opengraph/extensions/github/edges/gh_readrepocontents) | ❌ | \[Repository] Repo role can read repository contents | | [GH\_RemoveAssignee](/opengraph/extensions/github/edges/gh_removeassignee) | ❌ | \[Repository] Repo role can remove assignees from issues and pull requests | | [GH\_RemoveLabel](/opengraph/extensions/github/edges/gh_removelabel) | ❌ | \[Repository] Repo role can remove labels from issues and pull requests | | [GH\_ReopenDiscussion](/opengraph/extensions/github/edges/gh_reopendiscussion) | ❌ | \[Repository] Repo role can reopen discussions | | [GH\_ReopenIssue](/opengraph/extensions/github/edges/gh_reopenissue) | ❌ | \[Repository] Repo role can reopen closed issues | | [GH\_ReopenPullRequest](/opengraph/extensions/github/edges/gh_reopenpullrequest) | ❌ | \[Repository] Repo role can reopen closed pull requests | | [GH\_RequestPrReview](/opengraph/extensions/github/edges/gh_requestprreview) | ❌ | \[Repository] Repo role can request pull request reviews | | [GH\_ResolveDependabotAlerts](/opengraph/extensions/github/edges/gh_resolvedependabotalerts) | ❌ | \[Repository] Repo role can resolve Dependabot alerts | | [GH\_ResolveSecretScanningAlerts](/opengraph/extensions/github/edges/gh_resolvesecretscanningalerts) | ❌ | \[Organization] Org role can resolve secret scanning alerts | | [GH\_RestrictionsCanPush](/opengraph/extensions/github/edges/gh_restrictionscanpush) | ❌ | User or team is allowed to push to branches protected by this rule | | [GH\_RunOrgMigration](/opengraph/extensions/github/edges/gh_runorgmigration) | ❌ | \[Repository] Repo role can run organization migrations | | [GH\_SetInteractionLimits](/opengraph/extensions/github/edges/gh_setinteractionlimits) | ❌ | \[Repository] Repo role can set interaction limits on the repository | | [GH\_SetIssueType](/opengraph/extensions/github/edges/gh_setissuetype) | ❌ | \[Repository] Repo role can set issue types | | [GH\_SetMilestone](/opengraph/extensions/github/edges/gh_setmilestone) | ❌ | \[Repository] Repo role can set milestones on issues and pull requests | | [GH\_SetSocialPreview](/opengraph/extensions/github/edges/gh_setsocialpreview) | ❌ | \[Repository] Repo role can set the repository social preview image | | [GH\_SyncedTo](/opengraph/extensions/github/edges/gh_syncedto) | ✅ | External identity (Azure, Okta, PingOne) is synced to this GitHub user via SSO/SCIM | | [GH\_ToggleDiscussionAnswer](/opengraph/extensions/github/edges/gh_togglediscussionanswer) | ❌ | \[Repository] Repo role can toggle discussion answers | | [GH\_ToggleDiscussionCommentMinimize](/opengraph/extensions/github/edges/gh_togglediscussioncommentminimize) | ❌ | \[Repository] Repo role can minimize discussion comments | | [GH\_TransferRepository](/opengraph/extensions/github/edges/gh_transferrepository) | ❌ | \[Organization] Org role can transfer repositories | | [GH\_UsesSecret](/opengraph/extensions/github/edges/gh_usessecret) | ❌ | \[Workflow] Step references a secret by name — GH\_WorkflowStep → GH\_RepoSecret / GH\_OrgSecret (name match) | | [GH\_UsesVariable](/opengraph/extensions/github/edges/gh_usesvariable) | ❌ | \[Workflow] Step references a variable by name — GH\_WorkflowStep → GH\_RepoVariable / GH\_OrgVariable (name match) | | [GH\_ValidToken](/opengraph/extensions/github/edges/gh_validtoken) | ✅ | Secret scanning alert contains a valid, active token belonging to this user | | [GH\_ViewDependabotAlerts](/opengraph/extensions/github/edges/gh_viewdependabotalerts) | ❌ | \[Repository] Repo role can view Dependabot alerts | | [GH\_ViewSecretScanningAlerts](/opengraph/extensions/github/edges/gh_viewsecretscanningalerts) | ❌ | \[Repository] Role can view secret scanning alerts | | [GH\_WriteCodeScanning](/opengraph/extensions/github/edges/gh_writecodescanning) | ❌ | \[Repository] Repo role can upload code scanning results | | [GH\_WriteOrganizationActionsSecrets](/opengraph/extensions/github/edges/gh_writeorganizationactionssecrets) | ❌ | \[Organization] Org role can write Actions secrets | | [GH\_WriteOrganizationActionsSettings](/opengraph/extensions/github/edges/gh_writeorganizationactionssettings) | ❌ | \[Organization] Org role can write Actions settings | | [GH\_WriteOrganizationActionsVariables](/opengraph/extensions/github/edges/gh_writeorganizationactionsvariables) | ❌ | \[Organization] Org role can write Actions variables | | [GH\_WriteOrganizationCustomOrgRole](/opengraph/extensions/github/edges/gh_writeorganizationcustomorgrole) | ✅ | \[Organization] Org role can write custom org role definitions | | [GH\_WriteOrganizationCustomRepoRole](/opengraph/extensions/github/edges/gh_writeorganizationcustomreporole) | ❌ | \[Organization] Org role can write custom repo role definitions | | [GH\_WriteOrganizationNetworkConfigurations](/opengraph/extensions/github/edges/gh_writeorganizationnetworkconfigurations) | ❌ | \[Organization] Org role can write network configurations | | [GH\_WriteRepoContents](/opengraph/extensions/github/edges/gh_writerepocontents) | ❌ | \[Repository] Repo role can write repository contents | | [GH\_WriteRepoPullRequests](/opengraph/extensions/github/edges/gh_writerepopullrequests) | ❌ | \[Repository] Repo role can create and merge pull requests | # Tier Zero Classification Source: https://bloodhound.specterops.io/opengraph/extensions/github/tier-zero Tier Zero asset classification for GitHub organizations Applies to BloodHound Enterprise and CE Tier Zero (T0) identifies assets whose compromise grants control over the entire GitHub organization or the ability to compromise everything else. This is analogous to Active Directory Tier Zero, where Domain Controllers and Domain Admins are T0 because their compromise means full domain compromise. In GitHub, T0 classification serves two purposes: 1. **Defensive prioritization** — T0 assets should receive the highest level of protection, monitoring, and access review. 2. **Attack path analysis** — any attack path that reaches a T0 asset represents a critical finding, regardless of the number of hops. ## The Two Dimensions of GitHub Tier Zero ### Control Plane — Organizational Authority Control plane T0 assets can reshape the access model itself. They don't just have access to resources — they control *who* has access and *how* access is granted. This includes: * **Organization administration** — managing members, teams, billing, security settings * **SSO/SCIM configuration** — controlling authentication for all org members * **Role definition** — creating or modifying custom organization and repository roles * **App management** — installing or configuring GitHub Apps with arbitrary permissions An actor with control plane authority can grant themselves (or anyone else) any level of access, making them effectively omnipotent within the organization. ### Data Plane — Universal Repository Access Data plane T0 assets have or cascade to admin access on every repository in the organization. Through the graph, this means they can reach: * Every secret (via [GH\_HasSecret](/opengraph/extensions/github/edges/gh_hassecret)) * Every branch (via [GH\_HasBranch](/opengraph/extensions/github/edges/gh_hasbranch) → [GH\_CanWriteBranch](/opengraph/extensions/github/edges/gh_canwritebranch)) * Every environment (via [GH\_HasEnvironment](/opengraph/extensions/github/edges/gh_hasenvironment)) * Every cloud identity (via [GH\_CanAssumeIdentity](/opengraph/extensions/github/edges/gh_canassumeidentity)) The `all_repo_admin` synthetic role is the primary mechanism: the owners org role inherits it via [GH\_HasBaseRole](/opengraph/extensions/github/edges/gh_hasbaserole), and it fans out via [GH\_AdminTo](/opengraph/extensions/github/edges/gh_adminto), [GH\_WriteRepoContents](/opengraph/extensions/github/edges/gh_writerepocontents), [GH\_BypassBranchProtection](/opengraph/extensions/github/edges/gh_bypassbranchprotection), etc. to every repository. ## T0 Asset Categories ### Always T0 | Asset | Node Kind | Identifying Property | Dimension | Rationale | | ---------------------- | -------------------------------------------------------------------------------------- | ---------------------------------- | --------- | ------------------------------------------------------- | | Organization | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | All instances | Control | Root trust boundary for all contained assets | | Owners role | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | `short_name:'owners'` | Both | Full administrative control + inherits `all_repo_admin` | | All-repo admin role | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) | `name` ends with `/all_repo_admin` | Data | Admin access to every repository in the org | | SAML identity provider | [GH\_SamlIdentityProvider](/opengraph/extensions/github/nodes/gh_samlidentityprovider) | All instances | Control | Controls SSO authentication; can impersonate any user | ### T0 by Relationship | Asset | Condition | Dimension | Rationale | | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -------------------------------------------------------------------------------------------------- | | Owner users | [GH\_User](/opengraph/extensions/github/nodes/gh_user) → [GH\_HasRole](/opengraph/extensions/github/edges/gh_hasrole) → [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) `{owners}` | Both | Identity with full org control | | Privilege escalation roles | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) → [GH\_WriteOrganizationCustomOrgRole](/opengraph/extensions/github/edges/gh_writeorganizationcustomorgrole) → [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization) | Control | Can modify org role definitions to set base\_role to all\_repo\_admin — guaranteed self-escalation | | Privilege escalation users | [GH\_User](/opengraph/extensions/github/nodes/gh_user) → role chain → above roles | Control | Can escalate the org role they hold to gain full organizational control | | External identities (owner-mapped) | [GH\_ExternalIdentity](/opengraph/extensions/github/nodes/gh_externalidentity) → [GH\_MapsToUser](/opengraph/extensions/github/edges/gh_mapstouser) → owner [GH\_User](/opengraph/extensions/github/nodes/gh_user) | Control | IdP identity of an org owner; compromising it grants owner access via SSO | | App installations (all repos, write) | [GH\_AppInstallation](/opengraph/extensions/github/nodes/gh_appinstallation) `{repository_selection:'all'}` + write permissions | Data | App credential with write access to every repository | | Apps (all-repo installations, write) | [GH\_App](/opengraph/extensions/github/nodes/gh_app) → [GH\_InstalledAs](/opengraph/extensions/github/edges/gh_installedas) → all-repo [GH\_AppInstallation](/opengraph/extensions/github/nodes/gh_appinstallation) with write permissions | Data | App private key can generate write tokens for every repository | | PATs (all repos, write) | [GH\_PersonalAccessToken](/opengraph/extensions/github/nodes/gh_personalaccesstoken) `{repository_selection:'all'}` + write permissions | Data | Single token with write access to every repository | ### Explicitly Not T0 | Asset | Rationale | | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | Individual repositories | Even critical ones — T0 is about universal control, not single-resource importance | | [GH\_OrgRole](/opengraph/extensions/github/nodes/gh_orgrole) (members) | Default role with limited, non-administrative permissions | | Team maintainer roles | Scoped to one team's repositories, not org-wide | | [GH\_RepoRole](/opengraph/extensions/github/nodes/gh_reporole) (admin on single repo) | Single-repository scope, not universal | | Secret scanning alerts | Attack paths *to* T0, not T0 themselves | | Individual secrets or variables | Resources protected by T0, not T0 themselves | | Read-only all-repo apps/PATs | Data exfiltration risk but no write control — visibility without the ability to modify | | `write_organization_custom_repo_role` roles | Manages custom repo roles, but the holder may not hold those repo roles — no guaranteed self-escalation | ## Classification Rules The classification rules are located in the [`extension/privilege_zone_rules`](https://github.com/SpecterOps/openhound-github/tree/main/extension/privilege_zone_rules) directory of the OpenHound collector repository. Each rule is a Cypher query that returns nodes to be tagged as Tier Zero. See [Privilege Zone Rules](/opengraph/extensions/github/privilege-zone-rules) for the full list of queries. | Rule | File | Category | | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | -------- | | [Organizations](/opengraph/extensions/github/privilege-zone-rules#tier-zero-organizations) | `t0-organizations.json` | Control | | [Owners Role](/opengraph/extensions/github/privilege-zone-rules#tier-zero-owners-role) | `t0-owners-role.json` | Control | | [Owner Users](/opengraph/extensions/github/privilege-zone-rules#tier-zero-owner-users) | `t0-owner-users.json` | Control | | [All-Repo Admin Role](/opengraph/extensions/github/privilege-zone-rules#tier-zero-all-repo-admin-role) | `t0-all-repo-admin-role.json` | Data | | [SAML Identity Providers](/opengraph/extensions/github/privilege-zone-rules#tier-zero-saml-identity-providers) | `t0-saml-identity-providers.json` | Control | | [Privilege Escalation Roles](/opengraph/extensions/github/privilege-zone-rules#tier-zero-privilege-escalation-roles) | `t0-privilege-escalation-roles.json` | Control | | [Privilege Escalation Users](/opengraph/extensions/github/privilege-zone-rules#tier-zero-privilege-escalation-users) | `t0-privilege-escalation-users.json` | Control | | [External Identities (Owner-Mapped)](/opengraph/extensions/github/privilege-zone-rules#tier-zero-external-identities-owner-mapped) | `t0-external-identities-owners.json` | Control | | [App Installations (All Repos)](/opengraph/extensions/github/privilege-zone-rules#tier-zero-app-installations-all-repositories) | `t0-app-installations-all-repos.json` | Data | | [Apps (All-Repo Installations)](/opengraph/extensions/github/privilege-zone-rules#tier-zero-apps-all-repository-installations) | `t0-apps-all-repos.json` | Data | | [PATs (All Repos)](/opengraph/extensions/github/privilege-zone-rules#tier-zero-pats-all-repositories) | `t0-pats-all-repos.json` | Data | # jamf_AdminTo Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_adminto Represents full administrative control over the target and all resources controlled by the target. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ✅ ## General Information The traversable jamf\_AdminTo edge represents full administrative control over the Jamf Pro tenant. This edge is created when an account has "Full Access" access level and "Administrator" privilege set, granting complete control over all resources in the tenant. ```mermaid theme={null} graph LR A("jamf_Account john.admin") B("jamf_Tenant CorpJamfPro") C("jamf_Account it.superadmin") D("jamf_DisabledAccount former.admin") A -- jamf_AdminTo --> B C -- jamf_AdminTo --> B D -- jamf_AdminTo --> B ``` # jamf_AdminToSite Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_admintosite The source has administrative control over the site and all resources controlled by the site. This includes creating policies that impact resources of the site, send or clear MDM commands, remotely administer site devices and computers, create computer objects for the site. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) * Destination: [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) * Traversable: ✅ ## General Information The traversable jamf\_AdminToSite edge represents administrative control over a specific Jamf Pro site. This edge is created when an account or group has "Site Access" access level and "Administrator" privilege set, granting control over all resources within that site including creating policies, managing devices, and administering site computers. ```mermaid theme={null} graph LR A("jamf_Account site.manager") B("jamf_Site Engineering") C("jamf_Account location.admin") D("jamf_Site Finance") E("jamf_Group SiteAdmins") A -- jamf_AdminToSite --> B C -- jamf_AdminToSite --> D E -- jamf_AdminToSite --> B ``` # jamf_AssignedUser Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_assigneduser Represents the user assignment relationship on a jamf-managed computer. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) * Destination: [jamf\_ComputerUser](/opengraph/extensions/jamf/nodes/jamf_computeruser) * Traversable: ✅ ## General Information The traversable jamf\_AssignedUser edge represents the user assignment relationship on a Jamf-managed computer. The specified user is assigned to the source computer, establishing the physical access relationship between a device and its primary user. ```mermaid theme={null} graph LR A("jamf_Computer MacBook-Pro-01") B("jamf_ComputerUser jane.doe") C("jamf_Computer iMac-Design-02") D("jamf_ComputerUser bob.smith") A -- jamf_AssignedUser --> B C -- jamf_AssignedUser --> D ``` # jamf_AZMatchedEmail Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_azmatchedemail Represents a cross-platform identity correlation where the Jamf principal's email attribute matches an Azure AD account's email. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_ComputerUser](/opengraph/extensions/jamf/nodes/jamf_computeruser) * Destination: [AZUser](/resources/nodes/az-user) * Traversable: ❌ ## General Information The non-traversable jamf\_AZMatchedEmail edge represents a cross-platform identity correlation created during post-processing. When the Jamf principal's email attribute matches an Azure AD account's email, this edge links the identities across environments. ```mermaid theme={null} graph LR A("jamf_Account john.admin") B("AzureAD_User john.admin\@contoso.com") C("jamf_ComputerUser bob.smith") D("AzureAD_User bob.smith\@contoso.com") E("jamf_DisabledAccount bob.former") F("AzureAD_User bob\@former.localhost") A -- jamf_AZMatchedEmail --> B C -- jamf_AZMatchedEmail --> D E -- jamf_AZMatchedEmail --> F ``` # jamf_Contains Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_contains Represents a structural containment relationship where the source node contains the target resource. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant), [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) * Destination: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer), [jamf\_ComputerUser](/opengraph/extensions/jamf/nodes/jamf_computeruser), [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient), [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration) * Traversable: ✅ ## General Information The traversable jamf\_Contains edge represents a structural containment relationship where the source node contains the target resource. The Jamf tenant contains all top-level resources, while sites contain resources scoped to that site. Resources not assigned to a specific site are contained directly by the tenant. ComputerUser nodes are only contained by sites or tenant indirectly through their parent computer, not directly by Contains edges. ```mermaid theme={null} graph LR A("jamf_Tenant CorpJamfPro") B("jamf_Site NYC-Office") C("jamf_Computer MacBook-Pro-01") D("jamf_Account admin.user") E("jamf_Group IT-Staff") A -- jamf_Contains --> B A -- jamf_Contains --> D A -- jamf_Contains --> E B -- jamf_Contains --> C ``` # jamf_Create_API_Client_and_Assign_Role Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_create_api_client_and_assign_role Represents a privilege escalation path where the source possesses 'Create API Integrations' permission and at least one role exists allowing the creation of new API clients to assume existing role permissions. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ✅ ## General Information The traversable jamf\_Create\_API\_Client\_and\_Assign\_Role edge represents a privilege escalation path. The source possesses 'Create API Integrations' permission and at least one role exists, allowing creation of new API clients that assume existing role permissions and retrieving credentials for authentication. ```mermaid theme={null} graph LR A("jamf_Account platform.eng") B("jamf_Tenant CorpJamfPro") C("jamf_Group DevOps-Team") D("jamf_ApiClient service-mesh") A -- jamf_Create_API_Client_and_Assign_Role --> B C -- jamf_Create_API_Client_and_Assign_Role --> B D -- jamf_Create_API_Client_and_Assign_Role --> B ``` # jamf_Create_API_Client_and_Create_Role Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_create_api_client_and_create_role Represents a combined privilege escalation path, where the source possesses the 'Create API Integrations' and 'Create API Roles' permissions, that allow the creation of new API clients with any permissions in newly assigned roles and retrieving API client credentials to authenticate. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ✅ ## General Information The traversable jamf\_Create\_API\_Client\_and\_Create\_Role edge represents a combined privilege escalation path. The source possesses both 'Create API Integrations' and 'Create API Roles' permissions, allowing creation of new API clients with any permissions in newly assigned roles and retrieving credentials to authenticate with those permissions. ```mermaid theme={null} graph LR A("jamf_Account dev.ops") B("jamf_Tenant CorpJamfPro") C("jamf_Group API-Managers") D("jamf_ApiClient platform-client") A -- jamf_Create_API_Client_and_Create_Role --> B C -- jamf_Create_API_Client_and_Create_Role --> B D -- jamf_Create_API_Client_and_Create_Role --> B ``` # jamf_Create_API_Client_and_Update_Role Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_create_api_client_and_update_role Represents a combined privilege escalation path where the source possesses 'Create API Integrations' and 'Update API Roles' permissions and at least one API role exists allowing the creation of new API clients to assume roles, modifying the permissions of existing roles, and retrieving API client credentials. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ✅ ## General Information The traversable jamf\_Create\_API\_Client\_and\_Update\_Role edge represents a combined privilege escalation path. The source possesses 'Create API Integrations' and 'Update API Roles' permissions and at least one API role exists, allowing creation of new API clients, modifying existing role permissions, and retrieving credentials for authentication. ```mermaid theme={null} graph LR A("jamf_Account Dev Ops Admin") B("jamf_Tenant CorpJamfPro") C("jamf_Group Integration Team") D("jamf_ApiClient Integration Runner") A -- jamf_Create_API_Client_and_Update_Role --> B C -- jamf_Create_API_Client_and_Update_Role --> B D -- jamf_Create_API_Client_and_Update_Role --> B ``` # jamf_CreateAccounts Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_createaccounts Represents possession of the 'Create Accounts' JSS Object permission which allows creating new accounts, including administrators, as well as creating new groups with any permissions. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ✅ ## General Information The traversable jamf\_CreateAccounts edge represents possession of the 'Create Accounts' JSS Object permission which allows creating new accounts, including administrators, as well as creating new groups with any permissions and defining Jamf accounts assigned to them. ```mermaid theme={null} graph LR A("jamf_Account account.provisoner") B("jamf_Tenant CorpJamfPro") C("jamf_Group AccountManagers") D("jamf_ApiClient onboarding-service") A -- jamf_CreateAccounts --> B C -- jamf_CreateAccounts --> B D -- jamf_CreateAccounts --> B ``` # jamf_CreateAPIRoles Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_createapiroles Represents the ability to create API roles in the Jamf tenant. Non-traversable because creating roles without the ability to create or update API integrations does not provide a credential retrieval mechanism. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ❌ ## General Information The non-traversable jamf\_CreateAPIRoles edge represents the ability to create API roles in the Jamf tenant. This edge is non-traversable on its own because creating roles without the ability to create or update API integrations does not enable privilege escalation. ```mermaid theme={null} graph LR A("jamf_Account API Architect") B("jamf_Tenant CorpJamfPro") C("jamf_Group API Admins") D("jamf_ApiClient Integration Tool") A -- jamf_CreateAPIRoles --> B C -- jamf_CreateAPIRoles --> B D -- jamf_CreateAPIRoles --> B ``` # jamf_CreateComputerExtensions Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_createcomputerextensions Represents the ability to create computer extension attributes which can execute code on all computers in the Jamf tenant. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) * Traversable: ✅ ## General Information The traversable jamf\_CreateComputerExtensions edge represents the ability to create computer extension attributes that execute code on all computers in the Jamf tenant. Extension attributes can run scripts during inventory collection, providing a code execution vector. ```mermaid theme={null} graph LR A("jamf_Account it.engineer") B("jamf_Computer MacBook-Pro-01") C("jamf_Computer iMac-Finance-02") D("jamf_ApiClient inventory-client") A -- jamf_CreateComputerExtensions --> B A -- jamf_CreateComputerExtensions --> C D -- jamf_CreateComputerExtensions --> B D -- jamf_CreateComputerExtensions --> C ``` # jamf_CreatePolicies Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_createpolicies Represents possession of the 'Create Policies' JSSObject privilege allowing code execution on target computers. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) * Traversable: ✅ ## General Information The traversable jamf\_CreatePolicies edge represents possession of the 'Create Policies' privilege allowing code execution on target computers. This is a critical attack path as policies can execute commands, run scripts, and deploy packages to managed computers. ```mermaid theme={null} graph LR A("jamf_Account it.engineer") B("jamf_Computer MacBook-Pro-01") C("jamf_Computer iMac-Design-03") D("jamf_Group PolicyAdmins") A -- jamf_CreatePolicies --> B A -- jamf_CreatePolicies --> C D -- jamf_CreatePolicies --> B D -- jamf_CreatePolicies --> C ``` # jamf_MatchedEmail Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_matchedemail Represents an identity correlation where the Jamf computer user's email attribute matches the Jamf account's email. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_ComputerUser](/opengraph/extensions/jamf/nodes/jamf_computeruser) * Destination: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount) * Traversable: ✅ ## General Information The traversable jamf\_MatchedEmail edge represents an identity correlation where the Jamf computer user's email attribute matches the Jamf account's email, indicating they are likely the same person. This links physical device access to Jamf administrative privileges. ```mermaid theme={null} graph LR A("jamf_ComputerUser jsmith\@corp.com") B("jamf_Account jsmith\@corp.com") C("jamf_ComputerUser jdoe\@corp.com") D("jamf_DisabledAccount jdoe\@corp.com") A -- jamf_MatchedEmail --> B C -- jamf_MatchedEmail --> D ``` # jamf_MatchedName Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_matchedname Represents an identity correlation where the Jamf computer user's displayname matches the Jamf account's name or displayname. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_ComputerUser](/opengraph/extensions/jamf/nodes/jamf_computeruser) * Destination: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount) * Traversable: ✅ ## General Information The traversable jamf\_MatchedName edge represents an identity correlation where the Jamf computer user's displayname matches the Jamf account's name or displayname. This links physical device access to Jamf administrative privileges. ```mermaid theme={null} graph LR A("jamf_ComputerUser jdoe") B("jamf_Account jdoe") C("jamf_ComputerUser bsmith") D("jamf_DisabledAccount bsmith") A -- jamf_MatchedName --> B C -- jamf_MatchedName --> D ``` # jamf_MemberOf Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_memberof Represents group membership where the source inherits the group's permissions and assignments. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount) * Destination: [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) * Traversable: ✅ ## General Information The traversable jamf\_MemberOf edge represents group membership. The source account is a member of the destination group and inherits the group's permissions. This is a standard identity relationship edge. ```mermaid theme={null} graph LR A("jamf_Account John Admin") B("jamf_Group IT-Admins") C("jamf_DisabledAccount Jane Support") D("jamf_Group HelpDesk") A -- jamf_MemberOf --> B C -- jamf_MemberOf --> D ``` # jamf_Okta_Same_Device Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_okta_same_device Represents a hybrid cross-platform device correlation where the Jamf Pro registered computer's UDID matches the registered device UDID in Okta. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) * Destination: [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device) * Traversable: ✅ ## General Information The traversable jamf\_Okta\_Same\_Device edge represents a hybrid cross-platform device correlation where the Jamf Pro registered computer's UDID matches the registered device UDID in Okta. This edge links the Jamf device graph to the corresponding Okta device. ```mermaid theme={null} graph LR A("jamf_Computer MacBook-Pro-01:UDID-ABCD1234") B("Okta_Device MacBook-Pro-01:UDID-ABCD1234") A -- jamf_Okta_Same_Device --> B ``` # jamf_ScriptsNonTraversable Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_scriptsnontraversable Represents the ability to create or update scripts on the target. This edge is non-traversable because script creation/modification alone does not enable code execution. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ❌ ## General Information The non-traversable jamf\_ScriptsNonTraversable edge represents the ability to create or update scripts on the target. This edge is non-traversable because script creation/modification alone does not enable code execution — a policy must also be configured to run the script. ```mermaid theme={null} graph LR A("jamf_Account Script Admin") B("jamf_Tenant CorpJamfPro") C("jamf_Group Script Writers") D("jamf_ApiClient script-deployer") A -- jamf_ScriptsNonTraversable --> B C -- jamf_ScriptsNonTraversable --> B D -- jamf_ScriptsNonTraversable --> B ``` # jamf_SSO_Login Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_sso_login Represents the ability of an SSO identity provider to authenticate as and inherit the privileges of Jamf accounts and groups. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration) * Destination: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) * Traversable: ✅ ## General Information The traversable jamf\_SSO\_Login edge represents the ability of an SSO identity provider to authenticate as and inherit the privileges of Jamf accounts and groups. SSO sources can map attributes to authenticate as any target principal, making the SSO integration a high-value Tier 0 target. ```mermaid theme={null} graph LR A("jamf_SSOIntegration Okta-SAML") B("jamf_Account John Admin") C("jamf_Group IT-Admins") D("jamf_DisabledAccount John Old") A -- jamf_SSO_Login --> B A -- jamf_SSO_Login --> C A -- jamf_SSO_Login --> D ``` # jamf_Update_API_Client_and_Assign_Role Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_update_api_client_and_assign_role Represents posession of the 'Update API Integrations' permission and at least one role has been created in the tenant. Combined these allow updating existing API clients to assume the permissions of existing roles. Non-traversable because these permissions alone cannot retrieve API client credentials. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ❌ ## General Information The non-traversable jamf\_Update\_API\_Client\_and\_Assign\_Role edge represents a combined permission where the source can update existing API clients and assign existing roles. This is non-traversable because Jamf accounts and groups cannot retrieve API client credentials without the 'Create API Integration' permission. ```mermaid theme={null} graph LR A("jamf_Account Steve New") B("jamf_Tenant CorpJamfPro") C("jamf_Group API-Governance") D("jamf_DisabledAccount Steve Old") A -- jamf_Update_API_Client_and_Assign_Role --> B C -- jamf_Update_API_Client_and_Assign_Role --> B D -- jamf_Update_API_Client_and_Assign_Role --> B ``` # jamf_Update_API_Client_and_Create_Roles Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_update_api_client_and_create_roles Represents combined possession of 'Update API Integrations' and 'Create API Roles' permissions and at least one API client exists in the tenant allowing updates of existing API clients and assigning new roles created with any included permissions. Non-traversable because these permissions alone cannot retrieve API client credentials. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ❌ ## General Information The non-traversable jamf\_Update\_API\_Client\_and\_Create\_Roles edge represents a combined permission where the source can update existing API clients and create new API roles. This is non-traversable because Jamf accounts and groups cannot retrieve API client credentials without the 'Create API Integration' permission. ```mermaid theme={null} graph LR A("jamf_Account role.designer") B("jamf_Tenant CorpJamfPro") C("jamf_Group Platform-Admins") D("jamf_ApiClient Workflow API Client") A -- jamf_Update_API_Client_and_Create_Roles --> B C -- jamf_Update_API_Client_and_Create_Roles --> B D -- jamf_Update_API_Client_and_Create_Roles --> B ``` # jamf_Update_API_Client_and_Update_Roles Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_update_api_client_and_update_roles Represents combined possession of 'Update API Integrations' and 'Update API Roles' permissions and at least one Api Client and Role exist in the tenant allowing updates of existing API clients with any permissions by updating existing roles. Non-traversable because these permissions alone cannot retrieve API client credentials. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ❌ ## General Information The non-traversable jamf\_Update\_API\_Client\_and\_Update\_Roles edge represents a combined permission where the source can update existing API clients and update existing API roles. This is non-traversable because Jamf accounts and groups cannot retrieve API client credentials without the 'Create API Integration' permission. ```mermaid theme={null} graph LR A("jamf_Account Dave API Management") B("jamf_Tenant CorpJamfPro") C("jamf_Group API-Governance") D("jamf_DisabledAccount Former Dev Chris") A -- jamf_Update_API_Client_and_Update_Roles --> B C -- jamf_Update_API_Client_and_Update_Roles --> B D -- jamf_Update_API_Client_and_Update_Roles --> B ``` # jamf_Update_Recurring_Scripts Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_update_recurring_scripts Represents a code execution path where the source has 'Update Scripts' JSSObject permission and there are scripts configured to run repeatedly on target computers via enabled policies allowing code execution. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) * Traversable: ✅ ## General Information The traversable jamf\_Update\_Recurring\_Scripts edge represents a code execution path where the source has 'Update Scripts' permission and there are scripts configured to run repeatedly on target computers via enabled policies. The source can modify these scripts to execute arbitrary code on the in-scope computers at the next policy execution cycle. ```mermaid theme={null} graph LR A("jamf_Account Script Admin") B("jamf_Computer MacBook-Pro-01") C("jamf_DisabledAccount Old Script Admin") D("jamf_Group Script Managers") E("jamf_ApiClient Script Automation") F("jamf_DisabledApiClient Legacy Script Automation") A -- jamf_Update_Recurring_Scripts --> B C -- jamf_Update_Recurring_Scripts --> B D -- jamf_Update_Recurring_Scripts --> B E -- jamf_Update_Recurring_Scripts --> B F -- jamf_Update_Recurring_Scripts --> B ``` # jamf_Update_Roles_Assigned_To_Self Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_update_roles_assigned_to_self Represents an API client possessing the 'Update API Roles' permission which allows updating existing API roles with any permissions, including roles assigned to itself. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ✅ ## General Information The traversable jamf\_Update\_Roles\_Assigned\_To\_Self edge represents an API client possessing the 'Update API Roles' privilege which allows updating existing API roles with any permissions, including roles assigned to itself. Since the source already has an assigned role, it can escalate its own privileges by modifying its own role definitions. ```mermaid theme={null} graph LR A("jamf_ApiClient self-service-client") B("jamf_Tenant CorpJamfPro") C("jamf_DisabledApiClient legacy-scheduler") D("jamf_ApiClient automation-runner") A -- jamf_Update_Roles_Assigned_To_Self --> B D -- jamf_Update_Roles_Assigned_To_Self --> B C -- jamf_Update_Roles_Assigned_To_Self --> B ``` # jamf_Update_Self_and_Assign_Roles Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_update_self_and_assign_roles Represents an API client that possesses 'Update API Integrations' permission and at least one role exists, allowing the client to assume the permissions of existing roles. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ✅ ## General Information The traversable jamf\_Update\_Self\_and\_Assign\_Roles edge represents an API client that possesses 'Update API Integrations' permission and at least one role exists. This allows the client to update itself to assume the permissions of existing roles. Traversable because the source is already an authenticated API client. ```mermaid theme={null} graph LR A("jamf_ApiClient monitoring-agent") B("jamf_Tenant CorpJamfPro") C("jamf_DisabledApiClient retired-service") A -- jamf_Update_Self_and_Assign_Roles --> B C -- jamf_Update_Self_and_Assign_Roles --> B ``` # jamf_Update_Self_and_Create_Roles Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_update_self_and_create_roles Represents an API client that possesses 'Update API Integrations' and 'Create API Roles' permissions, allowing the client to assign new roles with any included permissions. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ✅ ## General Information The traversable jamf\_Update\_Self\_and\_Create\_Roles edge represents an API client that possesses 'Update API Integrations' and 'Create API Roles' permissions. This allows the client to update itself or other API clients and assign new roles with any included permissions. Traversable because the source is already an authenticated API client. ```mermaid theme={null} graph LR A("jamf_ApiClient bootstrap-client") B("jamf_Tenant CorpJamfPro") C("jamf_DisabledApiClient old-bootstrap") A -- jamf_Update_Self_and_Create_Roles --> B C -- jamf_Update_Self_and_Create_Roles --> B ``` # jamf_Update_Self_and_Update_Roles Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_update_self_and_update_roles Represents an API client that possesses 'Update API Integrations' and 'Update API Roles' permissions and at least one role exists, allowing the client to assign any permissions by modifying existing roles. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ✅ ## General Information The traversable jamf\_Update\_Self\_and\_Update\_Roles edge represents an API client that possesses 'Update API Integrations' and 'Update API Roles' permissions and at least one role exists. This allows the client to update itself or other API clients to assign any permissions by modifying existing roles. Traversable because the source is already an authenticated API client. ```mermaid theme={null} graph LR A("jamf_ApiClient Pipeline Agent") B("jamf_Tenant CorpJamfPro") C("jamf_DisabledApiClient Old Pipeline Agent") A -- jamf_Update_Self_and_Update_Roles --> B C -- jamf_Update_Self_and_Update_Roles --> B ``` # jamf_UpdateAccounts Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_updateaccounts Represents possession of the 'Update Accounts' JSS Object permission which allows altering the passwords, enabled status, permissions, and memberships of existing accounts or groups. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ✅ ## General Information The traversable jamf\_UpdateAccounts edge represents possession of the 'Update Accounts' JSS Object permission which allows altering the permissions of existing accounts or groups. If the source is a local Jamf account, they can grant themselves additional permissions, grant permissions to other accounts, modify group memberships, enable disabled accounts, and reset passwords of any Jamf account. ```mermaid theme={null} graph LR A("jamf_Account Help Desk") B("jamf_Tenant CorpJamfPro") C("jamf_Group AccountManagers") D("jamf_ApiClient nscim-provisioner") A -- jamf_UpdateAccounts --> B C -- jamf_UpdateAccounts --> B D -- jamf_UpdateAccounts --> B ``` # jamf_UpdateAPIRoles Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_updateapiroles Represents the ability to update existing API roles in the Jamf tenant. Non-traversable because modifying roles without the ability to create or update API clients does not provide a credential retrieval mechanism. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) * Destination: [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) * Traversable: ❌ ## General Information The non-traversable jamf\_UpdateAPIRoles edge represents the ability to update existing API roles in the Jamf tenant. This edge is non-traversable for Jamf accounts and groups because modifying roles without the ability to create or update API clients does not provide a credential retrieval mechanism. ```mermaid theme={null} graph LR A("jamf_Account Role Manager") B("jamf_Tenant CorpJamfPro") C("jamf_Group Security-Team") D("jamf_DisabledAccount Former RBAC Admin") A -- jamf_UpdateAPIRoles --> B C -- jamf_UpdateAPIRoles --> B D -- jamf_UpdateAPIRoles --> B ``` # jamf_UpdateComputerExtensions Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_updatecomputerextensions Represents the ability to update existing computer extension attributes and at least one extension attribute exists, allowing execution of code on all computers in the Jamf tenant during inventory collection. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) * Traversable: ✅ ## General Information The traversable jamf\_UpdateComputerExtensions edge represents the ability to update existing computer extension attributes and at least one extension attribute exists, allowing execution of code on all computers in the Jamf tenant during inventory collection. ```mermaid theme={null} graph LR A("jamf_Account it.support") B("jamf_Computer MacBook-Pro-07") C("jamf_Computer Mac-Studio-01") D("jamf_Group ExtAttrEditors") A -- jamf_UpdateComputerExtensions --> B A -- jamf_UpdateComputerExtensions --> C D -- jamf_UpdateComputerExtensions --> B D -- jamf_UpdateComputerExtensions --> C ``` # jamf_UpdatePolicies Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/edges/jamf_updatepolicies Represents possession of the 'Update Policies' JSSObject privilege and at least one policy already exists in the tenant, allowing modification of existing policies for code execution on target computers. Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) * Destination: [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) * Traversable: ✅ ## General Information The traversable jamf\_UpdatePolicies edge represents possession of the 'Update Policies' privilege and at least one policy already exists in the tenant, allowing modification of existing policies for code execution on target computers. ```mermaid theme={null} graph LR A("jamf_Account Help Desk") B("jamf_Computer MacBook-Air-05") C("jamf_ApiClient policy-manager") D("jamf_Group Policy Admins") A -- jamf_UpdatePolicies --> B D -- jamf_UpdatePolicies --> B C -- jamf_UpdatePolicies --> B ``` # Getting Started Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/getting-started Learn how to get started with the Jamf OpenGraph extension in BloodHound. Applies to BloodHound Enterprise and CE ## Prerequisites Full OpenGraph support requires a PostgreSQL graph database and one of the following editions: * BloodHound Enterprise (uses PostgreSQL by default) * BloodHound Community v8.0.0+ (requires changing to a [PostgreSQL database](/get-started/custom-installation#postgresql)) While many OpenGraph features may work on a Neo4j database, there are functional and performance limitations (see the [OpenGraph FAQ](/opengraph/faq#why-is-it-taking-so-long-to-ingest-opengraph-data)). For full support, migrate to a PostgreSQL database. The OpenGraph Extension Management feature must be enabled before you can manage extensions. Enable this feature on the **Administration** > **Early Access Features** page. ## Install the Extension ### Optional Schemas If is connected to other BloodHound-supported data sources in your environment, such as , make sure the corresponding schema is installed too. In BloodHound Enterprise v9.3.0 and later, some extensions (such as GitHub, Jamf, and Okta) are pre-installed. Upload any companion schemas that are not already installed. Doing so ensures those cross-platform relationships are modeled correctly in BloodHound. ## Import Cypher Queries ## Collect and Upload Data ## Configure Privilege Zones # jamf_Account Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/nodes/jamf_account Represents an enabled Jamf Pro local or directory account. Accounts are identity principals that hold permissions and can perform actions within the Jamf Pro environment. Applies to BloodHound Enterprise and CE Represents an enabled Jamf Pro local or directory account. Accounts are the primary identity principals that hold permissions and can perform actions within the Jamf Pro environment. ## Created by `process_account_nodes` in `lib/preprocess.py` ## Edges The tables below list edges defined by the Jamf extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [jamf\_Contains](/opengraph/extensions/jamf/edges/jamf_contains) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant), [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) | ✅ | | [jamf\_MatchedEmail](/opengraph/extensions/jamf/edges/jamf_matchedemail) | [jamf\_ComputerUser](/opengraph/extensions/jamf/nodes/jamf_computeruser) | ✅ | | [jamf\_MatchedName](/opengraph/extensions/jamf/edges/jamf_matchedname) | [jamf\_ComputerUser](/opengraph/extensions/jamf/nodes/jamf_computeruser) | ✅ | | [jamf\_SSO\_Login](/opengraph/extensions/jamf/edges/jamf_sso_login) | [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration) | ✅ | | [jamf\_Update\_SSO\_Settings](/opengraph/extensions/jamf/edges/jamf_update_sso_settings) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [jamf\_AdminTo](/opengraph/extensions/jamf/edges/jamf_adminto) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_AdminToSite](/opengraph/extensions/jamf/edges/jamf_admintosite) | [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) | ✅ | | [jamf\_AZMatchedEmail](/opengraph/extensions/jamf/edges/jamf_azmatchedemail) | [AZUser](/resources/nodes/az-user) | ❌ | | [jamf\_Create\_API\_Client\_and\_Assign\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_assign_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Create\_API\_Client\_and\_Create\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_create_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Create\_API\_Client\_and\_Update\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_update_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_CreateAccounts](/opengraph/extensions/jamf/edges/jamf_createaccounts) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_CreateAPIRoles](/opengraph/extensions/jamf/edges/jamf_createapiroles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_CreateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_createcomputerextensions) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_CreatePolicies](/opengraph/extensions/jamf/edges/jamf_createpolicies) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_MemberOf](/opengraph/extensions/jamf/edges/jamf_memberof) | [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | ✅ | | [jamf\_ScriptsNonTraversable](/opengraph/extensions/jamf/edges/jamf_scriptsnontraversable) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_API\_Client\_and\_Assign\_Role](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_assign_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_API\_Client\_and\_Create\_Roles](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_create_roles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_API\_Client\_and\_Update\_Roles](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_update_roles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_Recurring\_Scripts](/opengraph/extensions/jamf/edges/jamf_update_recurring_scripts) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_Update\_SSO\_Settings](/opengraph/extensions/jamf/edges/jamf_update_sso_settings) | [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration), [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | ✅ | | [jamf\_UpdateAccounts](/opengraph/extensions/jamf/edges/jamf_updateaccounts) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_UpdateAPIRoles](/opengraph/extensions/jamf/edges/jamf_updateapiroles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_UpdateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_updatecomputerextensions) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_UpdatePolicies](/opengraph/extensions/jamf/edges/jamf_updatepolicies) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | ## Properties | Property Name | Data Type | Description | | ------------------------- | --------- | ----------------------------------------------------- | | displayname | string | Full name of the account holder | | privilege\_set | string | Privilege set assigned (Administrator, Custom, etc.) | | objectid | string | Unique identifier for the Account | | name | string | Username of the account | | email | string | Email address associated with the account | | site\_id | integer | ID of the site the account is assigned to | | access\_level | string | Access level (Full Access, Site Access, Group Access) | | enabled | string | Whether the account is enabled | | tier | integer | Security tier classification (0 for administrators) | | local\_account | boolean | Whether this is a local Jamf account (not directory) | | privileges\_jss\_objects | string\[] | JSS Object permissions granted to the account | | privileges\_jss\_actions | string\[] | JSS Action permissions granted | | privileges\_jss\_settings | string\[] | JSS Settings permissions granted | | groups | integer | Group assignment indicator | ## Relationship Diagram > **Note:** Some non-traversable edges have been omitted for clarity. The diagram shows all traversable edges and structurally important non-traversable edges. Omitted edges include: `jamf_Update_API_Client_and_Update_Roles`, `jamf_Update_API_Client_and_Create_Roles`, `jamf_Update_API_Client_and_Assign_Role`, `jamf_CreateAPIRoles`, and `jamf_UpdateAPIRoles`. ```mermaid theme={null} flowchart TD Account[fa:fa-circle-user jamf_Account] Tenant[fa:fa-cloud jamf_Tenant] Site[fa:fa-circle-nodes jamf_Site] Computer[fa:fa-display jamf_Computer] Group[fa:fa-people-group jamf_Group] ComputerUser[fa:fa-circle-user jamf_ComputerUser] SSOIntegration[fa:fa-address-card jamf_SSOIntegration] Account -->|jamf_AdminTo| Tenant Account -->|jamf_AdminToSite| Site Account -->|jamf_UpdateAccounts| Tenant Account -->|jamf_CreateAccounts| Tenant Account -->|jamf_CreatePolicies| Computer Account -->|jamf_UpdatePolicies| Computer Account -->|jamf_MemberOf| Group Account -->|jamf_CreateComputerExtensions| Computer Account -->|jamf_UpdateComputerExtensions| Computer Account -->|jamf_Create_API_Client_and_Create_Role| Tenant Account -->|jamf_Create_API_Client_and_Update_Role| Tenant Account -->|jamf_Create_API_Client_and_Assign_Role| Tenant Account -->|jamf_Update_Recurring_Scripts| Computer Account -.->|jamf_ScriptsNonTraversable| Tenant Account -.->|jamf_ScriptsNonTraversable| Site Tenant -->|jamf_Contains| Account Site -->|jamf_Contains| Account ComputerUser -->|jamf_MatchedEmail| Account ComputerUser -->|jamf_MatchedName| Account SSOIntegration -->|jamf_SSO_Login| Account style Account fill:#0098BB,stroke:#333,stroke-width:3px,color:#000 style Tenant fill:#00C08D,stroke:#333,stroke-width:1px,color:#000 style Site fill:#D67500,stroke:#333,stroke-width:1px,color:#000 style Computer fill:#D6001C,stroke:#333,stroke-width:1px,color:#fff style Group fill:#F0FC03,stroke:#333,stroke-width:1px,color:#000 style ComputerUser fill:#FC03A5,stroke:#333,stroke-width:1px,color:#000 style SSOIntegration fill:#FFFFFF,stroke:#333,stroke-width:1px,color:#000 ``` # jamf_ApiClient Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/nodes/jamf_apiclient Represents an enabled Jamf Pro API client integration. API clients authenticate via OAuth client credentials and hold permissions through assigned API roles. They can perform programmatic actions holding the same permissions as accounts and groups and cannot be scoped to sites. Applies to BloodHound Enterprise and CE Represents an enabled Jamf Pro API client integration. API clients authenticate via OAuth client credentials and hold permissions through assigned API roles. They can perform programmatic actions including policy management, script operations, and self-modification. ## Created by `process_api_client_nodes` in `lib/preprocess.py` ## Edges The tables below list edges defined by the Jamf extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ----------- | | [jamf\_Contains](/opengraph/extensions/jamf/edges/jamf_contains) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant), [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [jamf\_Create\_API\_Client\_and\_Assign\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_assign_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Create\_API\_Client\_and\_Create\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_create_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Create\_API\_Client\_and\_Update\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_update_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_CreateAccounts](/opengraph/extensions/jamf/edges/jamf_createaccounts) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_CreateAPIRoles](/opengraph/extensions/jamf/edges/jamf_createapiroles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_CreateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_createcomputerextensions) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_CreatePolicies](/opengraph/extensions/jamf/edges/jamf_createpolicies) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_ScriptsNonTraversable](/opengraph/extensions/jamf/edges/jamf_scriptsnontraversable) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_Recurring\_Scripts](/opengraph/extensions/jamf/edges/jamf_update_recurring_scripts) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_Update\_Roles\_Assigned\_To\_Self](/opengraph/extensions/jamf/edges/jamf_update_roles_assigned_to_self) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Update\_Self\_and\_Assign\_Roles](/opengraph/extensions/jamf/edges/jamf_update_self_and_assign_roles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Update\_Self\_and\_Create\_Roles](/opengraph/extensions/jamf/edges/jamf_update_self_and_create_roles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Update\_Self\_and\_Update\_Roles](/opengraph/extensions/jamf/edges/jamf_update_self_and_update_roles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Update\_SSO\_Settings](/opengraph/extensions/jamf/edges/jamf_update_sso_settings) | [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration), [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | ✅ | | [jamf\_UpdateAccounts](/opengraph/extensions/jamf/edges/jamf_updateaccounts) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_UpdateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_updatecomputerextensions) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_UpdatePolicies](/opengraph/extensions/jamf/edges/jamf_updatepolicies) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | ## Properties | Property Name | Data Type | Description | | --------------------- | --------- | --------------------------------------------------- | | display\_name | string | Display name of the API client | | name | string | Name of the API client | | enabled | boolean | Whether the API client is enabled | | authorization\_scopes | string\[] | API roles assigned to this client | | privileges | string\[] | Resolved list of all privileges from assigned roles | | tier | integer | Security tier classification | ## Relationship Diagram > **Note:** Some non-traversable edges have been omitted for clarity. The diagram shows all traversable edges and structurally important non-traversable edges. ```mermaid theme={null} flowchart TD ApiClient[fa:fa-user-gear jamf_ApiClient] Tenant[fa:fa-cloud jamf_Tenant] Computer[fa:fa-display jamf_Computer] ApiClient -->|jamf_UpdateAccounts| Tenant ApiClient -->|jamf_CreateAccounts| Tenant ApiClient -->|jamf_CreatePolicies| Computer ApiClient -->|jamf_UpdatePolicies| Computer ApiClient -->|jamf_CreateComputerExtensions| Computer ApiClient -->|jamf_UpdateComputerExtensions| Computer ApiClient -->|jamf_Create_API_Client_and_Create_Role| Tenant ApiClient -->|jamf_Create_API_Client_and_Update_Role| Tenant ApiClient -->|jamf_Create_API_Client_and_Assign_Role| Tenant ApiClient -->|jamf_Update_Self_and_Update_Roles| Tenant ApiClient -->|jamf_Update_Self_and_Create_Roles| Tenant ApiClient -->|jamf_Update_Self_and_Assign_Roles| Tenant ApiClient -->|jamf_Update_Roles_Assigned_To_Self| Tenant ApiClient -->|jamf_Update_Recurring_Scripts| Computer ApiClient -.->|jamf_CreateAPIRoles| Tenant ApiClient -.->|jamf_ScriptsNonTraversable| Tenant Tenant -->|jamf_Contains| ApiClient style ApiClient fill:#8803FC,stroke:#333,stroke-width:3px,color:#fff style Tenant fill:#00C08D,stroke:#333,stroke-width:1px,color:#000 style Computer fill:#D6001C,stroke:#333,stroke-width:1px,color:#fff ``` # jamf_Computer Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/nodes/jamf_computer Represents a computer managed by Jamf Pro, commonly macOS. Computers are the primary target resources for policy execution, script deployment, and MDM management commands. Applies to BloodHound Enterprise and CE Represents a computer managed by Jamf Pro. Computers are the primary target resources for policy execution, script deployment, and MDM management commands. ## Created by `process_computer_nodes` in `lib/preprocess.py` ## Edges The tables below list edges defined by the Jamf extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [jamf\_Contains](/opengraph/extensions/jamf/edges/jamf_contains) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant), [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) | ✅ | | [jamf\_CreateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_createcomputerextensions) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | | [jamf\_CreatePolicies](/opengraph/extensions/jamf/edges/jamf_createpolicies) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | | [jamf\_Update\_Recurring\_Scripts](/opengraph/extensions/jamf/edges/jamf_update_recurring_scripts) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | | [jamf\_UpdateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_updatecomputerextensions) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | | [jamf\_UpdatePolicies](/opengraph/extensions/jamf/edges/jamf_updatepolicies) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ----------- | | [jamf\_AssignedUser](/opengraph/extensions/jamf/edges/jamf_assigneduser) | [jamf\_ComputerUser](/opengraph/extensions/jamf/nodes/jamf_computeruser) | ✅ | | [jamf\_Okta\_Same\_Device](/opengraph/extensions/jamf/edges/jamf_okta_same_device) | [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device) | ✅ | ## Properties | Property Name | Data Type | Description | | -------------------------- | --------- | -------------------------------------- | | displayname | string | Display name of the computer | | name | string | Computer name | | objectid | string | Unique identifier (UDID) | | managed | boolean | Whether the computer is managed | | make | string | Hardware manufacturer | | mdm\_capable | boolean | Whether the computer supports MDM | | model | string | Hardware model | | enrolled\_via\_dep | boolean | Enrolled via Device Enrollment Program | | user\_approved\_enrollment | boolean | Whether enrollment was user-approved | | user\_approved\_mdm | boolean | Whether MDM was user-approved | | device\_aad\_infos | string | Azure AD device information | | site\_id | integer | ID of the site the computer belongs to | | sitename | string | Name of the site | | username | string | Assigned username | | email\_address | string | Assigned user email | | os\_name | string | Operating system name | | os\_version | string | Operating system version | | os\_build | string | Operating system build | | serial\_number | string | Hardware serial number | | udid | string | Unique Device Identifier | | uuid | string | Universal Unique Identifier | | supervised | boolean | Whether the device is supervised | | sip\_status | string | System Integrity Protection status | | firewall\_enabled | boolean | Whether the firewall is enabled | | gatekeeper\_status | string | Gatekeeper status | | ip\_address | string | IP address | | is\_apple\_silicon | boolean | Whether the device uses Apple Silicon | | last\_contact\_time\_utc | datetime | Last check-in time | | jamf\_version | string | Jamf agent version | | filevault2\_users | string | FileVault 2 enabled users | | local\_accounts | string | Local user accounts | | tier | integer | Security tier classification | ## Relationship Diagram ```mermaid theme={null} flowchart TD Computer[fa:fa-display jamf_Computer] Tenant[fa:fa-cloud jamf_Tenant] Site[fa:fa-circle-nodes jamf_Site] Account[fa:fa-circle-user jamf_Account] DisabledAccount[fa:fa-circle-user jamf_DisabledAccount] Group[fa:fa-people-group jamf_Group] ApiClient[fa:fa-user-gear jamf_ApiClient] DisabledApiClient[fa:fa-user-gear jamf_DisabledApiClient] ComputerUser[fa:fa-circle-user jamf_ComputerUser] Okta_Device[fa:fa-mobile-screen Okta_Device] Computer -->|jamf_AssignedUser| ComputerUser Computer -->|jamf_Okta_Same_Device| Okta_Device Tenant -->|jamf_Contains| Computer Site -->|jamf_Contains| Computer Account -->|jamf_CreatePolicies| Computer DisabledAccount -->|jamf_CreatePolicies| Computer Group -->|jamf_CreatePolicies| Computer ApiClient -->|jamf_CreatePolicies| Computer DisabledApiClient -->|jamf_CreatePolicies| Computer Account -->|jamf_UpdatePolicies| Computer Group -->|jamf_UpdatePolicies| Computer ApiClient -->|jamf_UpdatePolicies| Computer Account -->|jamf_CreateComputerExtensions| Computer Group -->|jamf_CreateComputerExtensions| Computer ApiClient -->|jamf_CreateComputerExtensions| Computer Account -->|jamf_UpdateComputerExtensions| Computer Group -->|jamf_UpdateComputerExtensions| Computer ApiClient -->|jamf_UpdateComputerExtensions| Computer Account -->|jamf_Update_Recurring_Scripts| Computer Group -->|jamf_Update_Recurring_Scripts| Computer ApiClient -->|jamf_Update_Recurring_Scripts| Computer style Computer fill:#D6001C,stroke:#333,stroke-width:3px,color:#fff style Tenant fill:#00C08D,stroke:#333,stroke-width:1px,color:#000 style Site fill:#D67500,stroke:#333,stroke-width:1px,color:#000 style Account fill:#0098BB,stroke:#333,stroke-width:1px,color:#000 style DisabledAccount fill:#909090,stroke:#333,stroke-width:1px,color:#000 style Group fill:#F0FC03,stroke:#333,stroke-width:1px,color:#000 style ApiClient fill:#8803FC,stroke:#333,stroke-width:1px,color:#fff style DisabledApiClient fill:#909090,stroke:#333,stroke-width:1px,color:#000 style ComputerUser fill:#FC03A5,stroke:#333,stroke-width:1px,color:#000 style Okta_Device fill:#007DC1,stroke:#333,stroke-width:1px,color:#fff ``` # jamf_ComputerUser Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/nodes/jamf_computeruser Represents a user assigned to a jamf-managed computer. Computer users are derived from the location/user assignment on the computer record. Applies to BloodHound Enterprise and CE Represents a user assigned to a Jamf-managed computer. Computer users are derived from the location/user assignment on the computer record and serve as identity pivot nodes for linking physical device access to Jamf account permissions. ## Created by `process_assigned_user_nodes` in `lib/preprocess.py` ## Edges The tables below list edges defined by the Jamf extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- | ----------- | | [jamf\_AssignedUser](/opengraph/extensions/jamf/edges/jamf_assigneduser) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_Contains](/opengraph/extensions/jamf/edges/jamf_contains) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant), [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [jamf\_AZMatchedEmail](/opengraph/extensions/jamf/edges/jamf_azmatchedemail) | [AZUser](/resources/nodes/az-user) | ❌ | | [jamf\_MatchedEmail](/opengraph/extensions/jamf/edges/jamf_matchedemail) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount) | ✅ | | [jamf\_MatchedName](/opengraph/extensions/jamf/edges/jamf_matchedname) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount) | ✅ | ## Properties | Property Name | Data Type | Description | | ------------- | --------- | ------------------------------------------- | | displayname | string | Display name of the user | | name | string | Username or email of the user | | email | string | Email address of the user | | objectid | string | Unique identifier for the Computer User | | computer | string | ID of the computer this user is assigned to | | tier | integer | Security tier classification | ## Relationship Diagram ```mermaid theme={null} flowchart TD ComputerUser[fa:fa-circle-user jamf_ComputerUser] Account[fa:fa-circle-user jamf_Account] DisabledAccount[fa:fa-circle-user jamf_DisabledAccount] Computer[fa:fa-display jamf_Computer] ComputerUser -->|jamf_MatchedEmail| Account ComputerUser -->|jamf_MatchedEmail| DisabledAccount ComputerUser -->|jamf_MatchedName| Account ComputerUser -->|jamf_MatchedName| DisabledAccount Computer -->|jamf_AssignedUser| ComputerUser style ComputerUser fill:#FC03A5,stroke:#333,stroke-width:3px,color:#000 style Account fill:#0098BB,stroke:#333,stroke-width:1px,color:#000 style DisabledAccount fill:#909090,stroke:#333,stroke-width:1px,color:#000 style Computer fill:#D6001C,stroke:#333,stroke-width:1px,color:#fff ``` # jamf_DisabledAccount Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/nodes/jamf_disabledaccount Represents a disabled Jamf Pro account. Disabled accounts retain their permission configuration but cannot actively authenticate. If re-enabled, they regain all assigned privileges. Applies to BloodHound Enterprise and CE Represents a disabled Jamf Pro account. Disabled accounts retain their permission configuration but cannot actively authenticate. If re-enabled, they regain all assigned privileges. > This node shares its property set with [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account). The difference is that the account's `enabled` property is set to "Disabled". ## Created by `process_account_nodes` in `lib/preprocess.py` ## Edges The tables below list edges defined by the Jamf extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [jamf\_Contains](/opengraph/extensions/jamf/edges/jamf_contains) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant), [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) | ✅ | | [jamf\_MatchedEmail](/opengraph/extensions/jamf/edges/jamf_matchedemail) | [jamf\_ComputerUser](/opengraph/extensions/jamf/nodes/jamf_computeruser) | ✅ | | [jamf\_MatchedName](/opengraph/extensions/jamf/edges/jamf_matchedname) | [jamf\_ComputerUser](/opengraph/extensions/jamf/nodes/jamf_computeruser) | ✅ | | [jamf\_SSO\_Login](/opengraph/extensions/jamf/edges/jamf_sso_login) | [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration) | ✅ | | [jamf\_Update\_SSO\_Settings](/opengraph/extensions/jamf/edges/jamf_update_sso_settings) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [jamf\_AdminTo](/opengraph/extensions/jamf/edges/jamf_adminto) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_AdminToSite](/opengraph/extensions/jamf/edges/jamf_admintosite) | [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) | ✅ | | [jamf\_AZMatchedEmail](/opengraph/extensions/jamf/edges/jamf_azmatchedemail) | [AZUser](/resources/nodes/az-user) | ❌ | | [jamf\_Create\_API\_Client\_and\_Assign\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_assign_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Create\_API\_Client\_and\_Create\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_create_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Create\_API\_Client\_and\_Update\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_update_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_CreateAccounts](/opengraph/extensions/jamf/edges/jamf_createaccounts) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_CreateAPIRoles](/opengraph/extensions/jamf/edges/jamf_createapiroles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_CreateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_createcomputerextensions) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_CreatePolicies](/opengraph/extensions/jamf/edges/jamf_createpolicies) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_MemberOf](/opengraph/extensions/jamf/edges/jamf_memberof) | [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | ✅ | | [jamf\_ScriptsNonTraversable](/opengraph/extensions/jamf/edges/jamf_scriptsnontraversable) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_API\_Client\_and\_Assign\_Role](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_assign_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_API\_Client\_and\_Create\_Roles](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_create_roles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_API\_Client\_and\_Update\_Roles](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_update_roles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_Recurring\_Scripts](/opengraph/extensions/jamf/edges/jamf_update_recurring_scripts) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_Update\_SSO\_Settings](/opengraph/extensions/jamf/edges/jamf_update_sso_settings) | [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration), [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | ✅ | | [jamf\_UpdateAccounts](/opengraph/extensions/jamf/edges/jamf_updateaccounts) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_UpdateAPIRoles](/opengraph/extensions/jamf/edges/jamf_updateapiroles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_UpdateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_updatecomputerextensions) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_UpdatePolicies](/opengraph/extensions/jamf/edges/jamf_updatepolicies) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | ## Properties | Property Name | Data Type | Description | | ------------------------- | --------- | ----------------------------------------------------- | | displayname | string | Full name of the account holder | | privilege\_set | string | Privilege set assigned (Administrator, Custom, etc.) | | objectid | string | Unique identifier for the Account | | name | string | Username of the account | | email | string | Email address associated with the account | | site\_id | integer | ID of the site the account is assigned to | | access\_level | string | Access level (Full Access, Site Access, Group Access) | | enabled | string | Whether the account is enabled (always "Disabled") | | tier | integer | Security tier classification (0 for administrators) | | local\_account | boolean | Whether this is a local Jamf account (not directory) | | privileges\_jss\_objects | string\[] | JSS Object permissions granted to the account | | privileges\_jss\_actions | string\[] | JSS Action permissions granted | | privileges\_jss\_settings | string\[] | JSS Settings permissions granted | | groups | integer | Group assignment indicator | ## Relationship Diagram > **Note:** Some non-traversable edges have been omitted for clarity. The diagram shows all traversable edges and structurally important non-traversable edges. Omitted edges include: `jamf_Update_API_Client_and_Update_Roles`, `jamf_Update_API_Client_and_Create_Roles`, `jamf_Update_API_Client_and_Assign_Role`, `jamf_CreateAPIRoles`, and `jamf_UpdateAPIRoles`. ```mermaid theme={null} flowchart TD DisabledAccount[fa:fa-circle-user jamf_DisabledAccount] Tenant[fa:fa-cloud jamf_Tenant] Site[fa:fa-circle-nodes jamf_Site] Computer[fa:fa-display jamf_Computer] Group[fa:fa-people-group jamf_Group] ComputerUser[fa:fa-circle-user jamf_ComputerUser] SSOIntegration[fa:fa-address-card jamf_SSOIntegration] DisabledAccount -->|jamf_AdminTo| Tenant DisabledAccount -->|jamf_AdminToSite| Site DisabledAccount -->|jamf_UpdateAccounts| Tenant DisabledAccount -->|jamf_CreateAccounts| Tenant DisabledAccount -->|jamf_CreatePolicies| Computer DisabledAccount -->|jamf_UpdatePolicies| Computer DisabledAccount -->|jamf_MemberOf| Group DisabledAccount -->|jamf_CreateComputerExtensions| Computer DisabledAccount -->|jamf_UpdateComputerExtensions| Computer DisabledAccount -->|jamf_Create_API_Client_and_Create_Role| Tenant DisabledAccount -->|jamf_Create_API_Client_and_Update_Role| Tenant DisabledAccount -->|jamf_Create_API_Client_and_Assign_Role| Tenant DisabledAccount -->|jamf_Update_Recurring_Scripts| Computer DisabledAccount -.->|jamf_ScriptsNonTraversable| Tenant DisabledAccount -.->|jamf_ScriptsNonTraversable| Site Tenant -->|jamf_Contains| DisabledAccount Site -->|jamf_Contains| DisabledAccount ComputerUser -->|jamf_MatchedEmail| DisabledAccount ComputerUser -->|jamf_MatchedName| DisabledAccount SSOIntegration -->|jamf_SSO_Login| DisabledAccount style DisabledAccount fill:#909090,stroke:#333,stroke-width:3px,color:#000 style Tenant fill:#00C08D,stroke:#333,stroke-width:1px,color:#000 style Site fill:#D67500,stroke:#333,stroke-width:1px,color:#000 style Computer fill:#D6001C,stroke:#333,stroke-width:1px,color:#fff style Group fill:#F0FC03,stroke:#333,stroke-width:1px,color:#000 style ComputerUser fill:#FC03A5,stroke:#333,stroke-width:1px,color:#000 style SSOIntegration fill:#FFFFFF,stroke:#333,stroke-width:1px,color:#000 ``` # jamf_DisabledApiClient Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/nodes/jamf_disabledapiclient Represents a disabled Jamf Pro API client integration. Disabled API clients retain their role assignments but cannot authenticate. If re-enabled, they regain all assigned permissions. Applies to BloodHound Enterprise and CE Represents a disabled Jamf Pro API client integration. Disabled API clients retain their role assignments but cannot authenticate. If re-enabled, they regain all assigned permissions. > This node shares its property set with [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient). The difference is that the client's `enabled` property is false. ## Created by `process_api_client_nodes` in `lib/preprocess.py` ## Edges The tables below list edges defined by the Jamf extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ----------- | | [jamf\_Contains](/opengraph/extensions/jamf/edges/jamf_contains) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant), [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [jamf\_Create\_API\_Client\_and\_Assign\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_assign_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Create\_API\_Client\_and\_Create\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_create_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Create\_API\_Client\_and\_Update\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_update_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_CreateAccounts](/opengraph/extensions/jamf/edges/jamf_createaccounts) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_CreateAPIRoles](/opengraph/extensions/jamf/edges/jamf_createapiroles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_CreateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_createcomputerextensions) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_CreatePolicies](/opengraph/extensions/jamf/edges/jamf_createpolicies) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_ScriptsNonTraversable](/opengraph/extensions/jamf/edges/jamf_scriptsnontraversable) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_Recurring\_Scripts](/opengraph/extensions/jamf/edges/jamf_update_recurring_scripts) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_Update\_Roles\_Assigned\_To\_Self](/opengraph/extensions/jamf/edges/jamf_update_roles_assigned_to_self) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Update\_Self\_and\_Assign\_Roles](/opengraph/extensions/jamf/edges/jamf_update_self_and_assign_roles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Update\_Self\_and\_Create\_Roles](/opengraph/extensions/jamf/edges/jamf_update_self_and_create_roles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Update\_Self\_and\_Update\_Roles](/opengraph/extensions/jamf/edges/jamf_update_self_and_update_roles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Update\_SSO\_Settings](/opengraph/extensions/jamf/edges/jamf_update_sso_settings) | [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration), [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | ✅ | | [jamf\_UpdateAccounts](/opengraph/extensions/jamf/edges/jamf_updateaccounts) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_UpdateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_updatecomputerextensions) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_UpdatePolicies](/opengraph/extensions/jamf/edges/jamf_updatepolicies) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | ## Properties | Property Name | Data Type | Description | | --------------------- | --------- | --------------------------------------------------- | | display\_name | string | Display name of the API client | | name | string | Name of the API client | | enabled | boolean | Whether the API client is enabled (always false) | | authorization\_scopes | string\[] | API roles assigned to this client | | privileges | string\[] | Resolved list of all privileges from assigned roles | | tier | integer | Security tier classification | ## Relationship Diagram > **Note:** Some non-traversable edges have been omitted for clarity. The diagram shows all traversable edges and structurally important non-traversable edges. ```mermaid theme={null} flowchart TD DisabledApiClient[fa:fa-user-gear jamf_DisabledApiClient] Tenant[fa:fa-cloud jamf_Tenant] Computer[fa:fa-display jamf_Computer] DisabledApiClient -->|jamf_UpdateAccounts| Tenant DisabledApiClient -->|jamf_CreateAccounts| Tenant DisabledApiClient -->|jamf_CreatePolicies| Computer DisabledApiClient -->|jamf_UpdatePolicies| Computer DisabledApiClient -->|jamf_CreateComputerExtensions| Computer DisabledApiClient -->|jamf_UpdateComputerExtensions| Computer DisabledApiClient -->|jamf_Create_API_Client_and_Create_Role| Tenant DisabledApiClient -->|jamf_Create_API_Client_and_Update_Role| Tenant DisabledApiClient -->|jamf_Create_API_Client_and_Assign_Role| Tenant DisabledApiClient -->|jamf_Update_Self_and_Update_Roles| Tenant DisabledApiClient -->|jamf_Update_Self_and_Create_Roles| Tenant DisabledApiClient -->|jamf_Update_Self_and_Assign_Roles| Tenant DisabledApiClient -->|jamf_Update_Roles_Assigned_To_Self| Tenant DisabledApiClient -->|jamf_Update_Recurring_Scripts| Computer DisabledApiClient -.->|jamf_CreateAPIRoles| Tenant DisabledApiClient -.->|jamf_ScriptsNonTraversable| Tenant Tenant -->|jamf_Contains| DisabledApiClient style DisabledApiClient fill:#909090,stroke:#333,stroke-width:3px,color:#000 style Tenant fill:#00C08D,stroke:#333,stroke-width:1px,color:#000 style Computer fill:#D6001C,stroke:#333,stroke-width:1px,color:#fff ``` # jamf_Group Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/nodes/jamf_group Represents a Jamf Pro account group. Groups aggregate accounts and hold shared permissions that are inherited by their members. Groups can have Full Access or Site Access privilege levels. Applies to BloodHound Enterprise and CE Represents a Jamf Pro account group. Groups aggregate accounts and hold shared permissions that are inherited by their members. Groups can have Full Access or Site Access privilege levels. ## Created by `process_group_nodes` in `lib/preprocess.py` ## Edges The tables below list edges defined by the Jamf extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [jamf\_Contains](/opengraph/extensions/jamf/edges/jamf_contains) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant), [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) | ✅ | | [jamf\_MemberOf](/opengraph/extensions/jamf/edges/jamf_memberof) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount) | ✅ | | [jamf\_SSO\_Login](/opengraph/extensions/jamf/edges/jamf_sso_login) | [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration) | ✅ | | [jamf\_Update\_SSO\_Settings](/opengraph/extensions/jamf/edges/jamf_update_sso_settings) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [jamf\_AdminToSite](/opengraph/extensions/jamf/edges/jamf_admintosite) | [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) | ✅ | | [jamf\_Create\_API\_Client\_and\_Assign\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_assign_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Create\_API\_Client\_and\_Create\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_create_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_Create\_API\_Client\_and\_Update\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_update_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_CreateAccounts](/opengraph/extensions/jamf/edges/jamf_createaccounts) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_CreateAPIRoles](/opengraph/extensions/jamf/edges/jamf_createapiroles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_CreateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_createcomputerextensions) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_CreatePolicies](/opengraph/extensions/jamf/edges/jamf_createpolicies) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_ScriptsNonTraversable](/opengraph/extensions/jamf/edges/jamf_scriptsnontraversable) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_API\_Client\_and\_Assign\_Role](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_assign_role) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_API\_Client\_and\_Create\_Roles](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_create_roles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_API\_Client\_and\_Update\_Roles](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_update_roles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_Update\_Recurring\_Scripts](/opengraph/extensions/jamf/edges/jamf_update_recurring_scripts) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_Update\_SSO\_Settings](/opengraph/extensions/jamf/edges/jamf_update_sso_settings) | [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration), [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | ✅ | | [jamf\_UpdateAccounts](/opengraph/extensions/jamf/edges/jamf_updateaccounts) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ✅ | | [jamf\_UpdateAPIRoles](/opengraph/extensions/jamf/edges/jamf_updateapiroles) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | ❌ | | [jamf\_UpdateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_updatecomputerextensions) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | | [jamf\_UpdatePolicies](/opengraph/extensions/jamf/edges/jamf_updatepolicies) | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | ✅ | ## Properties | Property Name | Data Type | Description | | ------------------------- | --------- | --------------------------------------------------------- | | displayname | string | Display name of the group | | privilege\_set | string | Privilege set assigned (Administrator, Custom, etc.) | | objectid | string | Unique identifier for the Group | | name | string | Name of the group | | site\_id | integer | ID of the site the group is assigned to | | access\_level | string | Access level (Full Access, Site Access) | | tier | integer | Security tier classification (0 for administrator groups) | | privileges\_jss\_objects | string\[] | JSS Object permissions granted to the group | | privileges\_jss\_actions | string\[] | JSS Action permissions granted | | privileges\_jss\_settings | string\[] | JSS Settings permissions granted | | members | string | Serialized list of group members | ## Relationship Diagram > **Note:** Some non-traversable edges have been omitted for clarity. The diagram shows all traversable edges and structurally important non-traversable edges. Omitted edges include: `jamf_Update_API_Client_and_Update_Roles`, `jamf_Update_API_Client_and_Create_Roles`, `jamf_Update_API_Client_and_Assign_Role`, `jamf_CreateAPIRoles`, and `jamf_UpdateAPIRoles`. ```mermaid theme={null} flowchart TD Group[fa:fa-people-group jamf_Group] Tenant[fa:fa-cloud jamf_Tenant] Site[fa:fa-circle-nodes jamf_Site] Computer[fa:fa-display jamf_Computer] Account[fa:fa-circle-user jamf_Account] DisabledAccount[fa:fa-circle-user jamf_DisabledAccount] SSOIntegration[fa:fa-address-card jamf_SSOIntegration] Group -->|jamf_AdminTo| Tenant Group -->|jamf_AdminToSite| Site Group -->|jamf_UpdateAccounts| Tenant Group -->|jamf_CreateAccounts| Tenant Group -->|jamf_CreatePolicies| Computer Group -->|jamf_UpdatePolicies| Computer Group -->|jamf_CreateComputerExtensions| Computer Group -->|jamf_UpdateComputerExtensions| Computer Group -->|jamf_Create_API_Client_and_Create_Role| Tenant Group -->|jamf_Create_API_Client_and_Update_Role| Tenant Group -->|jamf_Create_API_Client_and_Assign_Role| Tenant Group -->|jamf_Update_Recurring_Scripts| Computer Group -.->|jamf_ScriptsNonTraversable| Tenant Group -.->|jamf_ScriptsNonTraversable| Site Tenant -->|jamf_Contains| Group Site -->|jamf_Contains| Group Account -->|jamf_MemberOf| Group DisabledAccount -->|jamf_MemberOf| Group SSOIntegration -->|jamf_SSO_Login| Group style Group fill:#F0FC03,stroke:#333,stroke-width:3px,color:#000 style Tenant fill:#00C08D,stroke:#333,stroke-width:1px,color:#000 style Site fill:#D67500,stroke:#333,stroke-width:1px,color:#000 style Computer fill:#D6001C,stroke:#333,stroke-width:1px,color:#fff style Account fill:#0098BB,stroke:#333,stroke-width:1px,color:#000 style DisabledAccount fill:#909090,stroke:#333,stroke-width:1px,color:#000 style SSOIntegration fill:#FFFFFF,stroke:#333,stroke-width:1px,color:#000 ``` # jamf_Site Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/nodes/jamf_site Represents a Jamf Pro site. Sites are organizational containers that segment resources within a Jamf tenant. Accounts and resources can be scoped to specific sites, limiting their access and management boundaries. Applies to BloodHound Enterprise and CE Represents a Jamf Pro site. Sites are organizational containers that segment resources within a Jamf tenant. Accounts and resources can be scoped to specific sites, limiting their access and management boundaries. ## Created by `process_site_nodes` in `lib/preprocess.py` ## Edges The tables below list edges defined by the Jamf extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [jamf\_AdminToSite](/opengraph/extensions/jamf/edges/jamf_admintosite) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | ✅ | | [jamf\_Contains](/opengraph/extensions/jamf/edges/jamf_contains) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant), [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [jamf\_Contains](/opengraph/extensions/jamf/edges/jamf_contains) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer), [jamf\_ComputerUser](/opengraph/extensions/jamf/nodes/jamf_computeruser), [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient), [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration) | ✅ | ## Properties | Property Name | Data Type | Description | | ------------- | --------- | ------------------------------ | | name | string | Name of the site | | objectid | string | Unique identifier for the Site | | displayname | string | Display name of the site | | site\_id | integer | Jamf site ID | | tier | integer | Security tier classification | ## Relationship Diagram ```mermaid theme={null} flowchart TD Site[fa:fa-circle-nodes jamf_Site] Tenant[fa:fa-cloud jamf_Tenant] Account[fa:fa-circle-user jamf_Account] DisabledAccount[fa:fa-circle-user jamf_DisabledAccount] Group[fa:fa-people-group jamf_Group] Computer[fa:fa-display jamf_Computer] ComputerUser[fa:fa-circle-user jamf_ComputerUser] Site -->|jamf_Contains| Account Site -->|jamf_Contains| DisabledAccount Site -->|jamf_Contains| Group Site -->|jamf_Contains| Computer Site -->|jamf_Contains| ComputerUser Tenant -->|jamf_Contains| Site Account -->|jamf_AdminToSite| Site DisabledAccount -->|jamf_AdminToSite| Site Group -->|jamf_AdminToSite| Site Account -.->|jamf_ScriptsNonTraversable| Site DisabledAccount -.->|jamf_ScriptsNonTraversable| Site Group -.->|jamf_ScriptsNonTraversable| Site style Site fill:#D67500,stroke:#333,stroke-width:3px,color:#000 style Tenant fill:#00C08D,stroke:#333,stroke-width:1px,color:#000 style Account fill:#0098BB,stroke:#333,stroke-width:1px,color:#000 style DisabledAccount fill:#909090,stroke:#333,stroke-width:1px,color:#000 style Group fill:#F0FC03,stroke:#333,stroke-width:1px,color:#000 style Computer fill:#D6001C,stroke:#333,stroke-width:1px,color:#fff style ComputerUser fill:#FC03A5,stroke:#333,stroke-width:1px,color:#000 ``` # jamf_SSOIntegration Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/nodes/jamf_ssointegration Represents the Single Sign-On (SSO) integration configured in the Jamf Pro tenant. When enabled, the SSO provider can map attributes to authenticate as any Jamf account or group, making it a Tier 0 node with significant security implications. Applies to BloodHound Enterprise and CE Represents the Single Sign-On (SSO) integration configured in the Jamf Pro tenant. When enabled, the SSO provider can map attributes to authenticate as any Jamf account or group, making it a Tier 0 node with significant security implications. ## Created by `process_sso_node` in `lib/preprocess.py` ## Edges The tables below list edges defined by the Jamf extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [jamf\_Contains](/opengraph/extensions/jamf/edges/jamf_contains) | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant), [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) | ✅ | | [jamf\_Update\_SSO\_Settings](/opengraph/extensions/jamf/edges/jamf_update_sso_settings) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [jamf\_SSO\_Login](/opengraph/extensions/jamf/edges/jamf_sso_login) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | ✅ | ## Properties | Property Name | Data Type | Description | | ----------------------- | --------- | -------------------------------- | | sso\_enabled | boolean | Whether SSO is enabled | | idp\_url | string | Identity Provider URL | | idp\_provider\_type | string | Type of identity provider | | entity\_id | string | SAML entity ID | | group\_attribute\_name | string | Attribute name for group mapping | | group\_rdn\_key | string | RDN key for group lookups | | site\_id | string | Site ID (always "-1" for global) | | tier | integer | Security tier classification (0) | | name | string | Name of the SSO integration | | enrollment\_sso\_config | string | Enrollment SSO configuration | ## Relationship Diagram ```mermaid theme={null} flowchart TD SSOIntegration[fa:fa-address-card jamf_SSOIntegration] Account[fa:fa-circle-user jamf_Account] DisabledAccount[fa:fa-circle-user jamf_DisabledAccount] Group[fa:fa-people-group jamf_Group] Tenant[fa:fa-cloud jamf_Tenant] SSOIntegration -->|jamf_SSO_Login| Account SSOIntegration -->|jamf_SSO_Login| DisabledAccount SSOIntegration -->|jamf_SSO_Login| Group Tenant -->|jamf_Contains| SSOIntegration style SSOIntegration fill:#FFFFFF,stroke:#333,stroke-width:3px,color:#000 style Account fill:#0098BB,stroke:#333,stroke-width:1px,color:#000 style DisabledAccount fill:#909090,stroke:#333,stroke-width:1px,color:#000 style Group fill:#F0FC03,stroke:#333,stroke-width:1px,color:#000 style Tenant fill:#00C08D,stroke:#333,stroke-width:1px,color:#000 ``` # jamf_Tenant Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/nodes/jamf_tenant Represents the top-level Jamf Pro tenant environment. This is the root container node for all Jamf resources. Applies to BloodHound Enterprise and CE Represents the top-level Jamf Pro tenant environment. This is the root container node for all Jamf resources. ## Created by `prepare_graph` in `lib/preprocess.py` ## Edges The tables below list edges defined by the Jamf extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [jamf\_AdminTo](/opengraph/extensions/jamf/edges/jamf_adminto) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount) | ✅ | | [jamf\_Create\_API\_Client\_and\_Assign\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_assign_role) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | | [jamf\_Create\_API\_Client\_and\_Create\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_create_role) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | | [jamf\_Create\_API\_Client\_and\_Update\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_update_role) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | | [jamf\_CreateAccounts](/opengraph/extensions/jamf/edges/jamf_createaccounts) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | | [jamf\_CreateAPIRoles](/opengraph/extensions/jamf/edges/jamf_createapiroles) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ❌ | | [jamf\_ScriptsNonTraversable](/opengraph/extensions/jamf/edges/jamf_scriptsnontraversable) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ❌ | | [jamf\_Update\_API\_Client\_and\_Assign\_Role](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_assign_role) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | ❌ | | [jamf\_Update\_API\_Client\_and\_Create\_Roles](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_create_roles) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | ❌ | | [jamf\_Update\_API\_Client\_and\_Update\_Roles](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_update_roles) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | ❌ | | [jamf\_Update\_Roles\_Assigned\_To\_Self](/opengraph/extensions/jamf/edges/jamf_update_roles_assigned_to_self) | [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | | [jamf\_Update\_Self\_and\_Assign\_Roles](/opengraph/extensions/jamf/edges/jamf_update_self_and_assign_roles) | [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | | [jamf\_Update\_Self\_and\_Create\_Roles](/opengraph/extensions/jamf/edges/jamf_update_self_and_create_roles) | [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | | [jamf\_Update\_Self\_and\_Update\_Roles](/opengraph/extensions/jamf/edges/jamf_update_self_and_update_roles) | [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | | [jamf\_UpdateAccounts](/opengraph/extensions/jamf/edges/jamf_updateaccounts) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | ✅ | | [jamf\_UpdateAPIRoles](/opengraph/extensions/jamf/edges/jamf_updateapiroles) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [jamf\_Contains](/opengraph/extensions/jamf/edges/jamf_contains) | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount), [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group), [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer), [jamf\_ComputerUser](/opengraph/extensions/jamf/nodes/jamf_computeruser), [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site), [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient), [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient), [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration) | ✅ | ## Properties | Property Name | Data Type | Description | | ------------- | --------- | ------------------------------------------ | | name | string | Domain name of the Jamf Pro tenant | | type | string | Hosting type (cloud-hosted or on-premesis) | | objectid | string | Unique identifier matching the tenant name | | displayname | string | Display name of the Tenant | | tier | integer | Security tier classification | ## Relationship Diagram > **Note:** Some non-traversable edges have been omitted for clarity. The diagram shows all traversable edges and structurally important non-traversable edges. Omitted edges include: `jamf_Update_API_Client_and_Update_Roles`, `jamf_Update_API_Client_and_Create_Roles`, `jamf_Update_API_Client_and_Assign_Role`, `jamf_CreateAPIRoles`, and `jamf_UpdateAPIRoles`. ```mermaid theme={null} flowchart TD Tenant[fa:fa-cloud jamf_Tenant] Account[fa:fa-circle-user jamf_Account] DisabledAccount[fa:fa-circle-user jamf_DisabledAccount] Group[fa:fa-people-group jamf_Group] ApiClient[fa:fa-user-gear jamf_ApiClient] DisabledApiClient[fa:fa-user-gear jamf_DisabledApiClient] Tenant -->|jamf_Contains| Tenant Account -->|jamf_AdminTo| Tenant DisabledAccount -->|jamf_AdminTo| Tenant Account -->|jamf_UpdateAccounts| Tenant DisabledAccount -->|jamf_UpdateAccounts| Tenant Group -->|jamf_UpdateAccounts| Tenant ApiClient -->|jamf_UpdateAccounts| Tenant DisabledApiClient -->|jamf_UpdateAccounts| Tenant Account -->|jamf_CreateAccounts| Tenant DisabledAccount -->|jamf_CreateAccounts| Tenant Group -->|jamf_CreateAccounts| Tenant ApiClient -->|jamf_CreateAccounts| Tenant DisabledApiClient -->|jamf_CreateAccounts| Tenant Account -->|jamf_Create_API_Client_and_Create_Role| Tenant Group -->|jamf_Create_API_Client_and_Create_Role| Tenant ApiClient -->|jamf_Create_API_Client_and_Create_Role| Tenant Account -->|jamf_Create_API_Client_and_Update_Role| Tenant Group -->|jamf_Create_API_Client_and_Update_Role| Tenant ApiClient -->|jamf_Create_API_Client_and_Update_Role| Tenant Account -->|jamf_Create_API_Client_and_Assign_Role| Tenant Group -->|jamf_Create_API_Client_and_Assign_Role| Tenant ApiClient -->|jamf_Create_API_Client_and_Assign_Role| Tenant ApiClient -->|jamf_Update_Self_and_Update_Roles| Tenant DisabledApiClient -->|jamf_Update_Self_and_Update_Roles| Tenant ApiClient -->|jamf_Update_Self_and_Create_Roles| Tenant DisabledApiClient -->|jamf_Update_Self_and_Create_Roles| Tenant ApiClient -->|jamf_Update_Self_and_Assign_Roles| Tenant DisabledApiClient -->|jamf_Update_Self_and_Assign_Roles| Tenant ApiClient -->|jamf_Update_Roles_Assigned_To_Self| Tenant DisabledApiClient -->|jamf_Update_Roles_Assigned_To_Self| Tenant Account -.->|jamf_ScriptsNonTraversable| Tenant DisabledAccount -.->|jamf_ScriptsNonTraversable| Tenant Group -.->|jamf_ScriptsNonTraversable| Tenant style Tenant fill:#00C08D,stroke:#333,stroke-width:3px,color:#000 style Account fill:#0098BB,stroke:#333,stroke-width:1px,color:#000 style DisabledAccount fill:#909090,stroke:#333,stroke-width:1px,color:#000 style Group fill:#F0FC03,stroke:#333,stroke-width:1px,color:#000 style ApiClient fill:#8803FC,stroke:#333,stroke-width:1px,color:#fff style DisabledApiClient fill:#909090,stroke:#333,stroke-width:1px,color:#000 ``` # Overview Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/overview Learn about the Jamf OpenGraph extension for BloodHound. Applies to BloodHound Enterprise and CE The Jamf extension is an OpenGraph extension for [Jamf Pro](https://www.jamf.com/products/jamf-pro/) environments that enables BloodHound to model Jamf Pro users, groups, sites, scripts, API integrations, and related relationships as graph data. It adds Jamf-specific [nodes](/opengraph/extensions/jamf/schema#nodes), [edges](/opengraph/extensions/jamf/schema#edges), [Cypher queries](/opengraph/extensions/jamf/queries), and [Privilege Zone rules](/opengraph/extensions/jamf/privilege-zone-rules) to help security professionals visualize and analyze Jamf Pro configurations in BloodHound. In BloodHound Enterprise v9.3.0 and later, Jamf is supported as a pre-installed extension. Use [OpenGraph Extension Management](/opengraph/extensions/manage) to verify the installed version or upload a newer supported schema manually. The other main products in Jamf's portfolio are [Jamf Protect](https://www.jamf.com/products/jamf-protect/), [Jamf Account](https://learn.jamf.com/en-US/bundle/jamf-account-documentation/page/Jamf_Account_Documentation.html), [Jamf Now](https://www.jamf.com/products/jamf-now/), and [Jamf Connect](https://www.jamf.com/products/jamf-connect/). The Jamf extension does not currently support these products. ## Jamf Pro Attack Paths Jamf Pro is a highly valuable target in the modern enterprise. The privileged MDM actions required to administer Apple devices with Jamf Pro can provide elevated access to local devices and complicate the job of defensive teams trying to separate benign administrative behavior from attacker activity. Compromising a Jamf Pro tenant can provide attackers with a wide range of access to laterally move to Apple devices, exfiltrate information, lock or DOS devices, and more. Example Jamf graph SpecterOps has identified and exploited numerous Jamf Pro misconfigurations and blind spots during red team engagements and penetration tests in hardened macOS client environments. One such attack path is highlighted in the [State of Attack Path Management](https://specterops.io/wp-content/uploads/sites/3/2025/08/StateofAPM-2025_1037-0_Updated.pdf). Our research on Jamf attack paths is still ongoing. ## Available Collectors The Jamf extension supports two collector paths: * [OpenHound Jamf collector](/openhound/collectors/jamf/overview): The SpecterOps-supported Jamf collector. This is the primary documented path for collecting Jamf data for BloodHound. * [JamfHound collector](https://github.com/SpecterOps/JamfHound): An alternative Jamf collector that also targets the Jamf extension schema. ## Jamf Pro Trial Jamf Pro provides a [free trial](https://www.jamf.com/request-trial/) for organizations interested in testing their MDM capability. ## References We recommend reading the following posts and pages to learn more about potential Jamf Pro attack vectors: * [Lance Cain and Daniel Mayer (SpecterOps): Leveraging Jamf For Red Teaming in Enterprise Environments](https://i.blackhat.com/BH-USA-25/Presentations/USA-25-Cain-Mayer-Leveraging-Jamf-for-Red-Teaming.pdf) * [Video: Leveraging Jamf For Red Teaming in Enterprise Environments](https://www.youtube.com/watch?v=6TZD5Gb7z0c) * [Calum Hall and Luke Roberts (GitHub): Come to the Dark Side, We Have Apples | Turning macOS Management Evil](https://i.blackhat.com/USA21/Wednesday-Handouts/us-21-Come-To-The-Dark-Side-We-Have-Apples-Turning-MacOS-Management-Evil.pdf) * [(1nf1n1ty): macOS Red Teaming | Abusing MDMs](https://blog.1nf1n1ty.team/hacktricks/macos-hardening/macos-red-teaming#abusing-mdms) ## Research Tools Here are some interesting GitHub repositories related to Jamf Pro security research: * [Eve Jamf Post Exploitation Toolkit](https://github.com/RobotOperator/Eve) * [Typhon Mythic Agent](https://github.com/MythicAgents/typhon) * [Jamf-Attack-Toolkit](https://github.com/ReversecLabs/Jamf-Attack-Toolkit) ## Community Please join us in the `#jamf` channel of the [BloodHound Community Slack](https://slack.specterops.io/) workspace if you want to chat about attack paths in Jamf. You are also welcome to open an issue or pull request on [GitHub](https://github.com/SpecterOps/openhound-jamf). ## Related Pages * [Getting started](/opengraph/extensions/jamf/getting-started) * [Schema reference](/opengraph/extensions/jamf/schema) * [Cypher queries](/opengraph/extensions/jamf/queries) * [Privilege Zone rules](/opengraph/extensions/jamf/privilege-zone-rules) * [OpenHound Jamf collector overview](/openhound/collectors/jamf/overview) # Privilege Zone Rules Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/privilege-zone-rules Jamf extension Privilege Zone rules Applies to BloodHound Enterprise and CE The following Privilege Zone rules can be imported into BloodHound to group nodes for Cypher query analysis and BloodHound Enterprise finding generation. This file is automatically generated from the [JSON Privilege Zone rule files](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/privilege_zone_rules). ## Tenant Tenant nodes in Jamf Pro. Zone: Tier Zero ```cypher theme={null} MATCH (n:jamf_Tenant) RETURN n ``` This rule is defined in the [tenant.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/privilege_zone_rules/tenant.json) file. ## Tier Zero Principals Accounts and group principals with 'Full Access' administrator privileges in the tenant and 'SSO' configuration if enabled. Zone: Tier Zero ```cypher theme={null} MATCH (n) WHERE n.tier = 0 RETURN n ``` This rule is defined in the [tier0-principals.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/privilege_zone_rules/tier0-principals.json) file. # Cypher Queries Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/queries Jamf extension Cypher queries Applies to BloodHound Enterprise and CE The following custom Cypher queries can be imported into BloodHound to enhance visibility. This file is automatically generated from the [JSON query files](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches). ## Account Access by Name Filter to view access of a Jamf Account named or starting with 'LC' - increase the maximum edges to see more relationships (i.e. change 5 to 6 to see 1 more) ```cypher theme={null} MATCH p=(s:jamf_Account)-[*1..5]->(t) WHERE s.name STARTS WITH 'LC' RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_Account\_Access\_by\_Name.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_Account_Access_by_Name.json) file. ## Account to Account Attack Paths Display Jamf Accounts with Attack-Paths impacting other Jamf Accounts - increase the maximum edges to see more relationships (i.e. change 5 to 6 to see 1 more) ```cypher theme={null} MATCH p=(s:jamf_Account)-[*1..5]->(t:jamf_Account) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_Account\_to\_Account\_Attack\_Paths.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_Account_to_Account_Attack_Paths.json) file. ## Account to Tenant Edges Show edges from Jamf Accounts to the Jamf Tenant ```cypher theme={null} MATCH p=(s:jamf_Account)-[]->(t:jamf_Tenant) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_Account\_to\_Tenant\_Edges.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_Account_to_Tenant_Edges.json) file. ## All Account Paths View paths originating from Jamf Accounts with up to 4 edges - increase edges to see more ```cypher theme={null} MATCH p=(s:jamf_Account)-[*1..4]->(t) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_All\_Account\_Paths.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_All_Account_Paths.json) file. ## All Computers Get all Computers ```cypher theme={null} MATCH p=(s:jamf_Computer) RETURN p ``` This query can be imported into BloodHound from the [Jamf\_All\_Computers.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_All_Computers.json) file. ## All Groups Get Jamf Groups ```cypher theme={null} MATCH p=(s:jamf_Group) RETURN p ``` This query can be imported into BloodHound from the [Jamf\_All\_Groups.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_All_Groups.json) file. ## All Nodes and Edges Retrieve all nodes and edges where either a Jamf node has an inbound or outbound relationship, limits results to 1000 ```cypher theme={null} MATCH p=(s)-[]->(t) WHERE s.primarykind STARTS WITH 'jamf' OR t.primarykind STARTS WITH 'jamf' RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_All\_Nodes\_and\_Edges.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_All_Nodes_and_Edges.json) file. ## API Client Attack Paths to Tenant Display up to 4 edges in attack paths originating from Jamf API Clients with a matching name or name starting with DEMO targeting the tenant ```cypher theme={null} MATCH p=(s:jamf_ApiClient)-[*1..4]->(t:jamf_Tenant) WHERE s.name STARTS WITH 'DEMO' RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_API\_Client\_Attack\_Paths\_to\_Tenant.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_API_Client_Attack_Paths_to_Tenant.json) file. ## API Client Immediate Edges View immediate edges and impacted principals for Jamf API Clients ```cypher theme={null} MATCH p=(s:jamf_ApiClient)- [] ->(t) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_API\_Client\_Immediate\_Edges.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_API_Client_Immediate_Edges.json) file. ## Chained Targeted Filtering An example of chained targeted filtering with multiple conditions in series that creates multiple proprety filters such as restricting to nodes with specific strings in their name, kinds of nodes, and types of edge relationships existing between the nodes ```cypher theme={null} MATCH p=(s)-[r]->(t) WHERE s.name STARTS WITH 'TENANT_ADMIN' AND (t.name STARTS WITH 'UPDATE' OR t.name STARTS WITH 'SOL' OR t.name STARTS WITH 'JVM') AND (type(r) = 'jamf_UpdateAccounts' OR type(r) = 'jamf_CreateAccounts' OR type(r) = 'jamf_CreatePolicies' OR type(r) = 'jamf_AdminTo') OR s.primarykind = 'jamf_Account' AND (s.name IN ['EXAMPLE', 'REG', 'LCAIN']) AND type(r) = 'jamf_AdminTo' OR t.primarykind STARTS WITH 'jamf_Computer' AND s.primarykind = 'jamf_Account' AND s.name STARTS WITH 'AZURE' OR s.primarykind = 'jamf_Tenant' AND type(r) = 'jamf_Contains' AND (t.primarykind = 'jamf_Site' OR t.primarykind = 'jamf_Computer') OR (s.primarykind = 'jamf_Site' AND t.primarykind = 'jamf_Computer') RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_Chained\_Targeted\_Filtering.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_Chained_Targeted_Filtering.json) file. ## Expanded Tier 1 to Tier 0 Paths Expand the graph by one edge showing nodes with edges to Tier 1 nodes with edges to Tier 0 nodes ```cypher theme={null} MATCH p=(a) - [] -> (s)-[r]->(t) WHERE s.tier = 1 AND t.tier = 0 AND type(r) <> 'jamf_Contains' RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_Expanded\_Tier\_1\_to\_Tier\_0\_Paths.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_Expanded_Tier_1_to_Tier_0_Paths.json) file. ## Group Administrators Filtered Relationships Targeted Filtering that limits results to starting jamf\_Group nodes starting with 'TENANT' in the name and only show edges/relationships specified by r that are one of the three specified edges ```cypher theme={null} MATCH p=(s)-[r]->(t) WHERE s.name STARTS WITH 'TENANT' AND s.primarykind = 'jamf_Group' AND (t.name STARTS WITH 'UPDATE' OR t.name STARTS WITH 'SOL') AND (type(r) = 'jamf_UpdateAccounts' OR type(r) = 'jamf_CreateAccounts' OR type(r) = 'jamf_AdminTo') RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_Group\_Administrators\_Filtered\_Relationships.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_Group_Administrators_Filtered_Relationships.json) file. ## Group Administrators Targeted Edges Targeted Filtering Query, display nodes with edges between 'GROUP\_ADMINISTRATORS' and 'UPDATE' or 'GROUP\_ADMINISTRATORS' and other nodes that start with 'SOL' ```cypher theme={null} MATCH p=(s)-[]->(t) WHERE s.name STARTS WITH 'GROUP_ADMINISTRATORS' AND t.name STARTS WITH 'UPDATE' OR s.name STARTS WITH 'GROUP_ADMINISTRATORS' AND t.name STARTS WITH 'SOL' RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_Group\_Administrators\_Targeted\_Edges.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_Group_Administrators_Targeted_Edges.json) file. ## Group Edges to Accounts Get immediate edges impacting Jamf Accounts originating from Jamf Groups, swap jamfGroup for jamfTenant to see impact edges to the tenant from groups ```cypher theme={null} MATCH p=(s)-[]->(t:jamf_Account) WHERE s.primarykind ENDS WITH 'jamf_Group' RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_Group\_Edges\_to\_Accounts.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_Group_Edges_to_Accounts.json) file. ## Matched Email Edges Show nodes with the edge jamfMatchedEdmail ```cypher theme={null} MATCH p=(s)-[:jamf_MatchedEmail]->(t) RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_Matched\_Email\_Edges.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_Matched_Email_Edges.json) file. ## Tier 1 to Tier 0 Attack Paths Retrieve attack paths between Tier 1 nodes and Tier 0 nodes that are fully traversable - excludes tenant and site nodes as starting points ```cypher theme={null} MATCH p=(s)-[r*1..5]->(t) WHERE s.tier = 1 AND t.tier = 0 AND s.primarykind <> 'jamf_Tenant' AND s.primarykind <> 'jamf_Site' AND r.traversable = True RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_Tier\_1\_to\_Tier\_0\_Attack\_Paths.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_Tier_1_to_Tier_0_Attack_Paths.json) file. ## Tier 1 to Tier 0 Direct Edges Retrieve direct edges between Tier 1 nodes and Tier 0 nodes ```cypher theme={null} MATCH p=(s)-[]->(t) WHERE s.tier = 1 AND t.tier = 0 RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_Tier\_1\_to\_Tier\_0\_Direct\_Edges.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_Tier_1_to_Tier_0_Direct_Edges.json) file. ## Tier 1 to Tier 0 Without Contains Filter out jamf\_Contains edges from Tiered node query ```cypher theme={null} MATCH p=(s)-[r]->(t) WHERE s.tier = 1 AND t.tier = 0 AND type(r) <> 'jamf_Contains' RETURN p LIMIT 1000 ``` This query can be imported into BloodHound from the [Jamf\_Tier\_1\_to\_Tier\_0\_Without\_Contains.json](https://github.com/SpecterOps/openhound-jamf/tree/main/extension/saved_searches/Jamf_Tier_1_to_Tier_0_Without_Contains.json) file. # Schema Source: https://bloodhound.specterops.io/opengraph/extensions/jamf/schema Jamf extension schema definition Applies to BloodHound Enterprise and CE ## Metadata **Name:** SOJamf
**Display Name:** Jamf Extension (by SpecterOps)
**Version:** v1.1.2
**Namespace:** jamf
**Environment Kind:** jamf\_Tenant
**Source Kind:** jamf This file is automatically generated from the [extension schema definition file](https://github.com/SpecterOps/openhound-jamf/blob/main/extension/schema.json). ## Nodes | Icon | Node Kind | Display Name | | ------------------------------------ | ---------------------------------------------------------------------------------- | ------------------------ | | jamf_Account | [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account) | Jamf Account | | jamf_ApiClient | [jamf\_ApiClient](/opengraph/extensions/jamf/nodes/jamf_apiclient) | Jamf API Client | | jamf_Computer | [jamf\_Computer](/opengraph/extensions/jamf/nodes/jamf_computer) | Jamf Computer | | jamf_ComputerUser | [jamf\_ComputerUser](/opengraph/extensions/jamf/nodes/jamf_computeruser) | Jamf Computer User | | jamf_DisabledAccount | [jamf\_DisabledAccount](/opengraph/extensions/jamf/nodes/jamf_disabledaccount) | Jamf Disabled Account | | jamf_DisabledApiClient | [jamf\_DisabledApiClient](/opengraph/extensions/jamf/nodes/jamf_disabledapiclient) | Jamf Disabled API Client | | jamf_Group | [jamf\_Group](/opengraph/extensions/jamf/nodes/jamf_group) | Jamf Group | | jamf_Site | [jamf\_Site](/opengraph/extensions/jamf/nodes/jamf_site) | Jamf Site | | jamf_SSOIntegration | [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration) | Jamf SSO Integration | | jamf_Tenant | [jamf\_Tenant](/opengraph/extensions/jamf/nodes/jamf_tenant) | Jamf Tenant | ## Edges | Relationship Kind | Traversable | Description | | ------------------------------------------------------------------------------------------------------------------------- | :---------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [jamf\_AdminTo](/opengraph/extensions/jamf/edges/jamf_adminto) | ✅ | Represents full administrative control over the target and all resources controlled by the target. | | [jamf\_AdminToSite](/opengraph/extensions/jamf/edges/jamf_admintosite) | ✅ | The source has administrative control over the site and all resources controlled by the site. This includes creating policies that impact resources of the site, send or clear MDM commands, remotely administer site devices and computers, create computer objects for the site. | | [jamf\_AssignedUser](/opengraph/extensions/jamf/edges/jamf_assigneduser) | ✅ | Represents the user assignment relationship on a jamf-managed computer. | | [jamf\_AZMatchedEmail](/opengraph/extensions/jamf/edges/jamf_azmatchedemail) | ❌ | Represents a cross-platform identity correlation where the Jamf principal's email attribute matches an Azure AD account's email. | | [jamf\_Contains](/opengraph/extensions/jamf/edges/jamf_contains) | ✅ | Represents a structural containment relationship where the source node contains the target resource. | | [jamf\_Create\_API\_Client\_and\_Assign\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_assign_role) | ✅ | Represents a privilege escalation path where the source possesses 'Create API Integrations' permission and at least one role exists allowing the creation of new API clients to assume existing role permissions. | | [jamf\_Create\_API\_Client\_and\_Create\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_create_role) | ✅ | Represents a combined privilege escalation path, where the source possesses the 'Create API Integrations' and 'Create API Roles' permissions, that allow the creation of new API clients with any permissions in newly assigned roles and retrieving API client credentials to authenticate. | | [jamf\_Create\_API\_Client\_and\_Update\_Role](/opengraph/extensions/jamf/edges/jamf_create_api_client_and_update_role) | ✅ | Represents a combined privilege escalation path where the source possesses 'Create API Integrations' and 'Update API Roles' permissions and at least one API role exists allowing the creation of new API clients to assume roles, modifying the permissions of existing roles, and retrieving API client credentials. | | [jamf\_CreateAccounts](/opengraph/extensions/jamf/edges/jamf_createaccounts) | ✅ | Represents possession of the 'Create Accounts' JSS Object permission which allows creating new accounts, including administrators, as well as creating new groups with any permissions. | | [jamf\_CreateAPIRoles](/opengraph/extensions/jamf/edges/jamf_createapiroles) | ❌ | Represents the ability to create API roles in the Jamf tenant. Non-traversable because creating roles without the ability to create or update API integrations does not provide a credential retrieval mechanism. | | [jamf\_CreateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_createcomputerextensions) | ✅ | Represents the ability to create computer extension attributes which can execute code on all computers in the Jamf tenant. | | [jamf\_CreatePolicies](/opengraph/extensions/jamf/edges/jamf_createpolicies) | ✅ | Represents possession of the 'Create Policies' JSSObject privilege allowing code execution on target computers. | | [jamf\_MatchedEmail](/opengraph/extensions/jamf/edges/jamf_matchedemail) | ✅ | Represents an identity correlation where the Jamf computer user's email attribute matches the Jamf account's email. | | [jamf\_MatchedName](/opengraph/extensions/jamf/edges/jamf_matchedname) | ✅ | Represents an identity correlation where the Jamf computer user's displayname matches the Jamf account's name or displayname. | | [jamf\_MemberOf](/opengraph/extensions/jamf/edges/jamf_memberof) | ✅ | Represents group membership where the source inherits the group's permissions and assignments. | | [jamf\_Okta\_Same\_Device](/opengraph/extensions/jamf/edges/jamf_okta_same_device) | ✅ | Represents a hybrid cross-platform device correlation where the Jamf Pro registered computer's UDID matches the registered device UDID in Okta. | | [jamf\_ScriptsNonTraversable](/opengraph/extensions/jamf/edges/jamf_scriptsnontraversable) | ❌ | Represents the ability to create or update scripts on the target. This edge is non-traversable because script creation/modification alone does not enable code execution. | | [jamf\_SSO\_Login](/opengraph/extensions/jamf/edges/jamf_sso_login) | ✅ | Represents the ability of an SSO identity provider to authenticate as and inherit the privileges of Jamf accounts and groups. | | [jamf\_Update\_API\_Client\_and\_Assign\_Role](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_assign_role) | ❌ | Represents posession of the 'Update API Integrations' permission and at least one role has been created in the tenant. Combined these allow updating existing API clients to assume the permissions of existing roles. Non-traversable because these permissions alone cannot retrieve API client credentials. | | [jamf\_Update\_API\_Client\_and\_Create\_Roles](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_create_roles) | ❌ | Represents combined possession of 'Update API Integrations' and 'Create API Roles' permissions and at least one API client exists in the tenant allowing updates of existing API clients and assigning new roles created with any included permissions. Non-traversable because these permissions alone cannot retrieve API client credentials. | | [jamf\_Update\_API\_Client\_and\_Update\_Roles](/opengraph/extensions/jamf/edges/jamf_update_api_client_and_update_roles) | ❌ | Represents combined possession of 'Update API Integrations' and 'Update API Roles' permissions and at least one Api Client and Role exist in the tenant allowing updates of existing API clients with any permissions by updating existing roles. Non-traversable because these permissions alone cannot retrieve API client credentials. | | [jamf\_Update\_Recurring\_Scripts](/opengraph/extensions/jamf/edges/jamf_update_recurring_scripts) | ✅ | Represents a code execution path where the source has 'Update Scripts' JSSObject permission and there are scripts configured to run repeatedly on target computers via enabled policies allowing code execution. | | [jamf\_Update\_Roles\_Assigned\_To\_Self](/opengraph/extensions/jamf/edges/jamf_update_roles_assigned_to_self) | ✅ | Represents an API client possessing the 'Update API Roles' permission which allows updating existing API roles with any permissions, including roles assigned to itself. | | [jamf\_Update\_Self\_and\_Assign\_Roles](/opengraph/extensions/jamf/edges/jamf_update_self_and_assign_roles) | ✅ | Represents an API client that possesses 'Update API Integrations' permission and at least one role exists, allowing the client to assume the permissions of existing roles. | | [jamf\_Update\_Self\_and\_Create\_Roles](/opengraph/extensions/jamf/edges/jamf_update_self_and_create_roles) | ✅ | Represents an API client that possesses 'Update API Integrations' and 'Create API Roles' permissions, allowing the client to assign new roles with any included permissions. | | [jamf\_Update\_Self\_and\_Update\_Roles](/opengraph/extensions/jamf/edges/jamf_update_self_and_update_roles) | ✅ | Represents an API client that possesses 'Update API Integrations' and 'Update API Roles' permissions and at least one role exists, allowing the client to assign any permissions by modifying existing roles. | | [jamf\_Update\_SSO\_Settings](/opengraph/extensions/jamf/edges/jamf_update_sso_settings) | ✅ | Represents the ability to update or enable SSO settings in the tenant to change authentication to inherit the privileges of Jamf accounts and groups. | | [jamf\_UpdateAccounts](/opengraph/extensions/jamf/edges/jamf_updateaccounts) | ✅ | Represents possession of the 'Update Accounts' JSS Object permission which allows altering the passwords, enabled status, permissions, and memberships of existing accounts or groups. | | [jamf\_UpdateAPIRoles](/opengraph/extensions/jamf/edges/jamf_updateapiroles) | ❌ | Represents the ability to update existing API roles in the Jamf tenant. Non-traversable because modifying roles without the ability to create or update API clients does not provide a credential retrieval mechanism. | | [jamf\_UpdateComputerExtensions](/opengraph/extensions/jamf/edges/jamf_updatecomputerextensions) | ✅ | Represents the ability to update existing computer extension attributes and at least one extension attribute exists, allowing execution of code on all computers in the Jamf tenant during inventory collection. | | [jamf\_UpdatePolicies](/opengraph/extensions/jamf/edges/jamf_updatepolicies) | ✅ | Represents possession of the 'Update Policies' JSSObject privilege and at least one policy already exists in the tenant, allowing modification of existing policies for code execution on target computers. | # Manage Extensions Source: https://bloodhound.specterops.io/opengraph/extensions/manage Learn about extension components and management workflows, including initial setup and ongoing maintenance. Applies to BloodHound Enterprise and CE As described in the [OpenGraph Overview](/opengraph/overview), extensions that include an extension definition schema enrich collector-generated data payloads to produce structured graphs. Structured graphs enable enhanced features in BloodHound, such as pathfinding, findings, and metrics. Extensions that do not include an extension definition schema produce generic graphs. Extensions can include the following components: | Component | Description | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Extension definition schema** | A file that defines graph structure, including node types, edge types, traversability behavior, and visual configurations. Both BloodHound Community and BloodHound Enterprise use the same extension definition schema format. | | **Collector** | A tool (for example, OpenHound, AzureHound, or SharpHound) that authenticates to a third-party platform, collects the data of interest, and packages it into a standardized data payload that BloodHound can ingest. | | **Cypher saved queries** | Custom Cypher queries provided by extension developers. | | **Privilege Zone rules** | Custom rules provided by extension developers to categorize nodes into Privilege Zones based on their properties and relationships. | | **Findings** | Insights or observations provided by extension developers derived from the ingested data, which can be used to identify risk and remediation guidance. Findings are visible in BloodHound Enterprise only. | SpecterOps has developed several extensions that follow this model, including: Visualize and analyze your GitHub configurations in BloodHound. Visualize and analyze your Jamf configurations in BloodHound. Visualize and analyze your Okta configurations in BloodHound. Visualize SCIM-provisioned users and groups as nodes in BloodHound. Only users with the Administrator [role](/manage-bloodhound/auth/users-and-roles#user-role-definitions) can upload and delete extension definition schemas. Non-administrator users can view installed extensions, findings, and edges. ## Extension management models BloodHound uses several extension management models. Understanding the distinction helps you know which extensions BloodHound manages automatically and which schemas Administrators must manage manually. | Extension type | Examples | Management behavior | | -------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Built-in** | Active Directory, Azure | Included in BloodHound and BloodHound Enterprise. Administrators cannot delete these extensions. | | **Pre-installed** | GitHub, Jamf, Okta | Included in BloodHound Enterprise v9.3.0 and later. Administrators can verify, update, or delete them, but BloodHound Enterprise reapplies the latest versions at startup according to [automatic update rules](/opengraph/extensions/manage#automatic-updates). | | **Manually managed** | SCIM, custom, and community-built extensions | Administrators must upload and maintain these extensions manually. | ## Before you begin Complete the following steps before you verify or install an extension and upload structured graph data: Only users with the Administrator [role](/manage-bloodhound/auth/users-and-roles#user-role-definitions) can manage extensions. The OpenGraph Extension Management feature must be enabled before you can manage extensions. Enable this feature on the **Administration** > **Early Access Features** page. Support for extension-defined **Findings** in BloodHound Enterprise is a SpecterOps-managed feature. If it is not enabled in your environment, contact your account team for assistance. How you obtain extensions and collectors depends on your BloodHound edition and how they are distributed: * **BloodHound Community**: Users can download and use [publicly available](/opengraph/library) community-built extensions and collectors from GitHub repositories. * **BloodHound Enterprise**: In v9.3.0 and later, GitHub, Jamf, and Okta are supported as pre-installed extensions. Use the **OpenGraph Management** page to verify the installed version. You can still manually upload publicly available, companion, or custom extensions as needed. If you need a newer version of a pre-installed extension outside the standard BloodHound Enterprise release cycle, coordinate with your account team. Pre-installed extensions can include detailed findings and remediation guidance when that feature is enabled in your environment. Contact your account team for availability. After you obtain an extension and collector, review the prerequisites in the extension-specific setup documentation. For [OpenHound](/openhound/overview)-based collectors (GitHub, Jamf, and Okta), review edition-specific deployment information (Enterprise or Community) and the collector-specific documentation for details on permissions, platform API configuration, and deployment options. ## Workflow The workflow for generic and structured OpenGraph data is largely the same. The main difference is that structured graphs require an extension definition schema to be installed. An Administrator can ensure availability either by verifying a pre-installed extension or by uploading one manually. After that, users with read access can view installed extensions and use the resulting structured graph data. If changes are needed, only Administrators can upload or delete extension definition schemas. See [User Role Definitions](/manage-bloodhound/auth/users-and-roles#user-role-definitions) for a full breakdown of permissions. For [OpenHound](/openhound/overview) collectors (GitHub, Jamf, and Okta), upload behavior depends on both deployment model and BloodHound edition. Only BloodHound Enterprise can accept payloads directly through the API. BloodHound Community requires manual file upload. ### Initial setup The following diagram provides a high-level overview of the recommended workflow to prepare BloodHound for producing structured graphs from OpenGraph extensions. The initial setup workflow is not strictly linear and not all steps are required. For example, importing Saved Queries and creating extension-specific Privilege Zone rules are optional. For generic graphs, the workflow is minimal: users may optionally import Saved Queries (if any). Verifying or installing an extension definition schema and updating Privilege Zone rules is not required. ```mermaid theme={null} flowchart TB subgraph prepare[Configure and Run] a["Download extension
and collector"] a-->b["Configure
collector"] b-->c["Run
collector"] end subgraph upload[Verify or Install Extension] f["Verify or install extension
definition schema"] f-->g["Import Cypher
saved queries"] g-->h["Update Privilege
Zone rules"] end prepare-->upload ``` ### Operational cycle After initial setup, the following diagrams illustrate the recurring cycle of operations to keep extension data current. For OpenHound collectors, upload behavior depends on edition and runtime model: Enterprise can ingest through the API (often automatic when using a collector client), while Community requires manual file upload from locally run collector executables. The following diagrams illustrate the OpenHound workflow for both Enterprise and Community editions. **BloodHound Enterprise (containerized)** ```mermaid theme={null} sequenceDiagram autonumber box rgba(128, 128, 128, 0.14) Human actors actor Admin actor User end box rgba(96, 96, 96, 0.12) Systems participant CollectorClient as BHE Collector Client participant OpenHound as OpenHound Container participant TargetPlatform as Target Platform participant BloodHound end Admin->>CollectorClient: Run on demand or configure schedule CollectorClient->>OpenHound: Trigger collector job OpenHound->>TargetPlatform: Extract data TargetPlatform-->>OpenHound: Return source data OpenHound->>OpenHound: Normalize and load data OpenHound->>BloodHound: Auto-upload data payload BloodHound-->>CollectorClient: Ingest complete User->>BloodHound: Explore and analyze data ``` **BloodHound Community (CLI)** ```mermaid theme={null} sequenceDiagram autonumber box rgba(128, 128, 128, 0.14) Human actors actor Admin actor User end box rgba(96, 96, 96, 0.12) Systems participant OpenHound as OpenHound CLI participant TargetPlatform as Target Platform participant BloodHound end Admin->>OpenHound: Run openhound CLI OpenHound->>TargetPlatform: Extract data TargetPlatform-->>OpenHound: Return source data OpenHound->>OpenHound: Normalize and load data OpenHound-->>Admin: Generate data files locally Admin->>BloodHound: Upload data files BloodHound-->>Admin: Ingest complete User->>BloodHound: Explore and analyze data ``` ## Verify or install an extension Before you upload structured OpenGraph data, make sure BloodHound has the matching extension definition schema. In BloodHound Enterprise v9.3.0 and later, GitHub, Jamf, and Okta are supported as pre-installed extensions and usually only need verification. Administrators still manage custom or companion extension definition schemas, such as SCIM, manually. In BloodHound Community, Administrators manage all extension definition schemas manually. Only users with the Administrator [role](/manage-bloodhound/auth/users-and-roles#user-role-definitions) can install, update, and delete extension definition schemas. After the extension is installed, BloodHound produces structured graphs for data payloads that conform to the extension. In the left menu, click **Administration** > **OpenGraph Management**. Choose the path that matches your scenario: Confirm the extension appears in the list of active extensions. For example, confirm that BloodHound Enterprise lists the built-in Active Directory and Azure extensions and the pre-installed GitHub, Jamf, and Okta extensions. A screenshot of the OpenGraph Management page in BloodHound Enterprise showing the list of active extensions. 1. Click **Upload File** to open a file system dialog or drag and drop an extension definition schema file onto the canvas. 2. Click **Upload** to begin the schema installation and validation process. A screenshot showing the OpenGraph Management page with the Upload Schema Files dialog open, allowing the user to select a file and upload it. 3. Confirm the extension appears in the list of active extensions and that the version shown matches the schema you expect BloodHound to use. You may need to refresh the page to see the newly installed extension in the list of active extensions. A screenshot showing the OpenGraph Management page with the list of active extensions, highlighting the newly installed extension. ## Update an extension Collectors and extensions are versioned separately. To avoid compatibility issues, do not update collectors independently without confirming extension compatibility. Update collectors and extension definition schemas together whenever possible. To update an extension, upload the new version using the same process as installing a new extension. BloodHound validates the new extension definition schema and replaces the earlier version with the new one. ### Automatic updates In BloodHound Enterprise v9.3.0 and later, the pre-installed GitHub, Jamf, and Okta extensions follow automatic update rules. At application startup, BloodHound Enterprise compares each pre-installed extension with the embedded version included in the current release and keeps whichever version is newer: | System condition | System action | | ------------------------------------------------------ | ----------------------------- | | No installed version | Installs the embedded version | | Installed versions are older than the embedded version | Installs the embedded version | | Installed versions match the embedded version | No action taken | | Installed versions are newer than the embedded version | No action taken | If you want to update a pre-installed extension outside the standard BloodHound Enterprise release cycle, coordinate with your account team. You can manually install an older version of a pre-installed extension. However, this rollback does not persist across application restarts. At startup, BloodHound Enterprise reruns the automatic version check and reinstalls the embedded version if it is newer. Contact your account team if you need help maintaining a rollback across restarts. ## Delete an extension Deleting an extension removes the extension definition schema from BloodHound, but leaves the underlying data intact. Associated data reverts to generic graphs, which means structured graph capabilities are no longer available. You can still use node search and Cypher queries on the [Explore](/analyze-data/explore/search#search) page to explore the data. If you want to delete the data associated with an extension, you can do so separately on the **Database Management** page. To delete an extension, click the (trash) icon next to it in the list of active extensions and confirm the deletion in the prompt. You cannot delete built-in extensions (Active Directory and Azure). In BloodHound Enterprise, Administrators can delete pre-installed extensions (GitHub, Jamf, and Okta), but BloodHound Enterprise [reinstalls](#automatic-updates) them at the next application restart. ## Upload data After an Administrator installs an extension or verifies that an extension is present, users can upload data payloads that conform to the extension definition schema and take advantage of structured graph capabilities in BloodHound. For extensions that use OpenHound collectors (GitHub, Jamf, and Okta), AzureHound, or SharpHound, how data is uploaded depends on your BloodHound edition: * **BloodHound Enterprise**: The collector client can upload data directly through the API. In containerized deployments, upload is typically automatic. * **BloodHound Community**: After running the OpenHound, AzureHound, or SharpHound collector executables locally and generating data files, follow the manual upload steps below. For extensions that do not use OpenHound collectors, follow the manual upload steps below. Upload a data payload that conforms to the installed extension definition schema. 1. In the left menu, click **Quick Upload**. 2. Click the **Upload File** canvas to open a file system dialog or drag and drop the data payload file(s) onto the canvas. 3. Click **Upload** to begin the data ingestion and validation process. The file either uploads successfully or fails in the modal. You can then go to the [File Ingest](/collect-data/enterprise-collection/monitor#file-ingest) page to review ingest and analysis progress. Use the enhanced features enabled by the extension to explore and analyze your OpenGraph data in BloodHound. | Feature | Description | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Pathfinding | Use [Pathfinding](/analyze-data/explore/search#pathfinding) to identify attack paths and analyze traversable relationships across all platforms and environments, including built-in and extension-defined kinds. | | Saved queries | [Import](/analyze-data/explore/cypher-search#import-and-export) extension-specific saved queries so you can quickly run pre-defined Cypher queries on the **Explore** page. | | Privilege Zone rules | If your Administrator configured extension-specific Privilege Zone [rules](/analyze-data/privilege-zones/rules) during initial setup, BloodHound automatically assigns matching nodes to zones, giving you clearer prioritization and zone-aware analysis. | | Findings and remediation | When available, use findings and remediation information to prioritize and address issues in your environment. | # Okta_AddMember Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_addmember Ability to add or remove members in scoped Okta groups Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) * Traversable: ✅ ## General Information The traversable Okta\_AddMember edges represent custom role permissions that allow a principal (user, group, or application) to add or remove members in scoped Okta groups. These edges are created when a custom role includes the `okta.groups.members.manage` or `okta.groups.manage` permissions. ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") g1("Okta_Group Finance") g2("Okta_Group Tier 0 Admins") app1("Okta_Application Automation") u1 -- Okta_AddMember --> g1 app1 -- Okta_AddMember --> g2 ``` # Okta_AgentMemberOf Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_agentmemberof Membership of an Okta agent in an agent pool Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_Agent](/opengraph/extensions/okta/nodes/okta_agent) * Destination: [Okta\_AgentPool](/opengraph/extensions/okta/nodes/okta_agentpool) * Traversable: ✅ ## General Information Okta\_AgentMemberOf edges represent membership of an [Okta\_Agent](/opengraph/extensions/okta/nodes/okta_agent) in an [Okta\_AgentPool](/opengraph/extensions/okta/nodes/okta_agentpool). Active Directory Agent Pools and their agents can be visualized in BloodHound as follows: ```mermaid theme={null} graph LR ap1("Okta_AgentPool contoso.com") ap2("Okta_AgentPool adatum.com") a1("Okta_Agent CONTOSO-SRV1") a2("Okta_Agent CONTOSO-SRV2") a3("Okta_Agent ADATUM-SRV1") a1 -- Okta_AgentMemberOf --> ap1 a2 -- Okta_AgentMemberOf --> ap1 a3 -- Okta_AgentMemberOf --> ap2 ``` Traversable edges between [Okta\_AgentPool](/opengraph/extensions/okta/nodes/okta_agentpool) and AD Domain nodes are not modeled in the current version of the Okta BloodHound extension. Support for this is planned for a future release. # Okta_AgentPoolFor Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_agentpoolfor Relationship between an AD agent pool and its backing AD application Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_AgentPool](/opengraph/extensions/okta/nodes/okta_agentpool) * Destination: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Traversable: ✅ ## General Information Okta\_AgentPoolFor edges connect an AD [Okta\_AgentPool](/opengraph/extensions/okta/nodes/okta_agentpool) to the backing [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) used for directory integration. ```mermaid theme={null} graph TB subgraph Active Directory d1("Domain contoso.com") c1("Computer CONTOSO-SRV1$") c2("Computer CONTOSO-SRV2$") d1 -- Contains --> c1 d1 -- Contains --> c2 end subgraph Okta ap1("Okta_AgentPool contoso.com") a1("Okta_Agent CONTOSO-SRV1") a2("Okta_Agent CONTOSO-SRV2") app1("Okta_Application AD contoso.com") a1 -- Okta_AgentMemberOf --> ap1 a2 -- Okta_AgentMemberOf --> ap1 ap1 -- Okta_AgentPoolFor --> app1 end c1 -- Okta_HostsAgent --> a1 c2 -- Okta_HostsAgent --> a2 ``` # Okta_ApiTokenFor Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_apitokenfor User ownership of an Okta API token Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_ApiToken](/opengraph/extensions/okta/nodes/okta_apitoken) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Traversable: ✅ ## General Information The traversable Okta\_ApiTokenFor edges represent the API token assignments for users in Okta, represented by the [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) nodes: ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") u2("Okta_User steve\@contoso.com") t1("Okta_ApiToken Test App") t2("Okta_ApiToken Postman") t3("Okta_ApiToken Python Script") org("Okta_Organization contoso.okta.com") t1 -- Okta_ApiTokenFor --> u1 t2 -- Okta_ApiTokenFor --> u2 t3 -- Okta_ApiTokenFor --> u2 u2 -- Okta_SuperAdmin --> org ``` # Okta_AppAdmin Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_appadmin Application administrator role assignment Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) * Traversable: ✅ ## General Information The traversable Okta\_AppAdmin edges represent Application Administrator role assignments. Application Administrators can manage application configurations, user assignments, and provisioning settings for their assigned applications. ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") u2("Okta_User alice\@contoso.com") g1("Okta_Group Salesforce Admins") app1("Okta_Application GitHub") app2("Okta_Application Salesforce") is1("Okta_APIServiceIntegration Elastic Agent") u2 -- Okta_MemberOf --> g1 u1 -- Okta_AppAdmin --> app1 g1 -- Okta_AppAdmin --> app2 u1 -- Okta_AppAdmin --> is1 ``` # Okta_AppAssignment Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_appassignment Assignment of users or groups to an Okta application Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) * Destination: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Traversable: ❌ ## General Information Only users that are assigned to applications can access them. Users can be assigned to applications directly or indirectly through group memberships. The non-traversable Okta\_AppAssignment edges represent the application assignments for users and groups in Okta: ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") u2("Okta_User steve\@contoso.com") u3("Okta_User mary\@contoso.com") u4("Okta_User bob\@contoso.com") u5("Okta_User alice\@contoso.com") g1("Okta_Group Engineering") e("Okta_Group Everyone") a1("Okta_Application SalesForce") a2("Okta_Application GitHub") a3("Okta_Application VPN") e -. Okta_AppAssignment .-> a1 u1 -- Okta_MemberOf --> e u2 -- Okta_MemberOf --> e u3 -- Okta_MemberOf --> e u4 -- Okta_MemberOf --> e u3 -- Okta_MemberOf --> g1 u4 -- Okta_MemberOf --> g1 g1 -. Okta_AppAssignment .-> a2 u4 -. Okta_AppAssignment .-> a3 u5 -. Okta_AppAssignment .-> a3 ``` # Okta_Contains Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_contains Contains relationship between the Okta organization and its objects Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration), [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset), [Okta\_Role](/opengraph/extensions/okta/nodes/okta_role), [Okta\_CustomRole](/opengraph/extensions/okta/nodes/okta_customrole), [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment), [Okta\_Realm](/opengraph/extensions/okta/nodes/okta_realm), [Okta\_AgentPool](/opengraph/extensions/okta/nodes/okta_agentpool), [Okta\_IdentityProvider](/opengraph/extensions/okta/nodes/okta_identityprovider), [Okta\_AuthorizationServer](/opengraph/extensions/okta/nodes/okta_authorizationserver), [Okta\_Policy](/opengraph/extensions/okta/nodes/okta_policy) * Traversable: ✅ ## General Information The traversable Okta\_Contains edges represent the containment relationships between the organization and other entities in Okta. The organization node will have Okta\_Contains edges to all other nodes in the graph, with some exceptions. ```mermaid theme={null} graph LR org("Okta_Organization contoso.okta.com") user1("Okta_User john\@contoso.com") group1("Okta_Group IT") app1("Okta_Application GitHub") role1("Okta_Role Super Admin") device1("Okta_Device John's MacBook") realm1("Okta_Realm EU") cr1("Okta_CustomRole Help Desk") rs1("Okta_ResourceSet HR Resources") ap1("Okta_AgentPool AD Sync Pool") as1("Okta_AuthorizationServer Default Server") ip1("Okta_IdentityProvider Google IdP") is1("Okta_APIServiceIntegration Elastic Agent") p1("Okta_Policy Idp Discovery Policy") org -- Okta_Contains --> user1 org -- Okta_Contains --> group1 org -- Okta_Contains --> app1 org -- Okta_Contains --> role1 org -- Okta_Contains --> device1 org -- Okta_Contains --> cr1 org -- Okta_Contains --> realm1 org -- Okta_Contains --> rs1 org -- Okta_Contains --> ap1 org -- Okta_Contains --> as1 org -- Okta_Contains --> ip1 org -- Okta_Contains --> is1 org -- Okta_Contains --> p1 ``` # Okta_CreatorOf Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_creatorof Creator relationship for API service integrations Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) * Destination: [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) * Traversable: ❌ ## General Information The non-traversable Okta\_CreatorOf edges represent the creator relationships between API Service Integration instances and users in Okta: ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") u2("Okta_User steve\@contoso.com") is1("Okta_APIServiceIntegration Elastic Agent") is2("Okta_APIServiceIntegration Falcon Shield") u1 -. Okta_CreatorOf .-> is1 u2 -. Okta_CreatorOf .-> is2 ``` # Okta_DeviceOf Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_deviceof Ownership relationship between a device and its assigned user Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Traversable: ❌ ## General Information The non-traversable Okta\_DeviceOf edges represent the ownership relationships between users and devices in Okta: ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") u2("Okta_User steve\@contoso.com") d1("Okta_Device John's MacBook") d2("Okta_Device Steve's iPhone") d1 -. Okta_DeviceOf .-> u1 d1 -. Okta_DeviceOf .-> u2 d2 -. Okta_DeviceOf .-> u2 ``` # Okta_GroupAdmin Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_groupadmin Group administrator role assignment Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) * Traversable: ✅ ## General Information The traversable Okta\_GroupAdmin edges represent Group Administrator (also known as User Administrator) role assignments. Group Administrators can manage users and groups within their assigned scope. ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") u2("Okta_User alice\@contoso.com") g1("Okta_Group Marketing") u1 -- Okta_GroupAdmin --> u2 u1 -- Okta_GroupAdmin --> g1 u2 -- Okta_MemberOf --> g1 ``` Target group memberships are flattened when the assignment is evaluated. # Okta_GroupMembershipAdmin Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_groupmembershipadmin Group membership administrator role assignment Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) * Traversable: ✅ ## General Information The traversable Okta\_GroupMembershipAdmin edges represent Group Membership Administrator role assignments. Group Membership Administrators can add and remove members from groups within their assigned scope but cannot modify the groups themselves. ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") g1("Okta_Group Marketing") g2("Okta_Group Sales") u1 -- Okta_GroupMembershipAdmin --> g1 u1 -- Okta_GroupMembershipAdmin --> g2 ``` # Okta_GroupPull Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_grouppull Import of group memberships from an external application Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) * Traversable: ✅ ## General Information The traversable Okta\_GroupPull edges represent the group synchronization relationships from applications to Okta: ```mermaid theme={null} graph LR g1("Okta_Group HR") app1("Okta_Application contoso.com") app1 -- Okta_GroupPull --> g1 ``` # Okta_GroupPush Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_grouppush Provisioning of group memberships to an external application Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) * Destination: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Traversable: ❌ ## General Information The non-traversable Okta\_GroupPush edges represent the group push assignments to applications. This indicates group provisioning and membership synchronization from Okta to external applications. ```mermaid theme={null} graph LR g1("Okta_Group Engineering") app1("Okta_Application contoso.com") g1 -. Okta_GroupPush .-> app1 ``` # Okta_HasRole Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_hasrole Assignment of a built-in or custom role to a principal Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_Role](/opengraph/extensions/okta/nodes/okta_role), [Okta\_CustomRole](/opengraph/extensions/okta/nodes/okta_customrole) * Traversable: ❌ ## General Information The non-traversable Okta\_HasRole edges represent the role assignments for users in Okta: ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") u2("Okta_User steve\@contoso.com") g1("Okta_Group IT") a1("Okta_Application Python Script") r1("Okta_Role Group Administrator") r2("Okta_Role Application Administrator") u1 -. Okta_HasRole .-> r1 g1 -. Okta_HasRole .-> r1 g1 -. Okta_HasRole .-> r2 a1 -. Okta_HasRole .-> r2 u2 -- Okta_MemberOf --> g1 ``` # Okta_HasRoleAssignment Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_hasroleassignment Relationship between a principal and a role assignment Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) * Traversable: ❌ ## General Information The Okta\_HasRoleAssignment edges connect users, groups, and applications to their respective [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) nodes. The [Okta\_ScopedTo](/opengraph/extensions/okta/edges/okta_scopedto) edges connect the [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) nodes to the resources they are scoped to, such as the organization or specific groups or applications. ```mermaid theme={null} graph TB ra1("Okta_RoleAssignment Help Desk Administrator") ra2("Okta_RoleAssignment Super Administrator") r1("Okta_Role Help Desk Administrator") r2("Okta_Role Super Administrator") u1("Okta_User john\@contoso.com") u2("Okta_User steve\@contoso.com") u3("Okta_User alice\@contoso.com") g1("Okta_Group Seattle Help Desk") g2("Okta_Group Seattle Office") org("Okta_Organization contoso.okta.com") u1 -- Okta_MemberOf --> g1 g1 -. Okta_HasRoleAssignment .-> ra1 g1 -. Okta_HasRole .-> r1 g1 -- Okta_HelpDeskAdmin --> u3 u3 -- Okta_MemberOf --> g2 ra1 -. Okta_ScopedTo .-> g2 u2 -. Okta_HasRoleAssignment .-> ra2 ra2 -. Okta_ScopedTo .-> org u2 -- Okta_SuperAdmin --> org u2 -. Okta_HasRole .-> r2 ``` # Okta_HelpDeskAdmin Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_helpdeskadmin Help desk administrator role assignment Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Traversable: ✅ ## General Information The traversable Okta\_HelpDeskAdmin edges represent Help Desk Administrator role assignments. Help Desk Administrators can perform password resets, unlock accounts, and reset MFA factors for users within their assigned scope. ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") g1("Okta_Group Help Desk") u2("Okta_User alice\@contoso.com") u3("Okta_User bob\@contoso.com") u1 -- Okta_HelpDeskAdmin --> u2 g1 -- Okta_HelpDeskAdmin --> u3 ``` # Okta_HostsAgent Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_hostsagent Relationship between an AD server and the Okta agent running on that host Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Computer](/resources/nodes/computer) * Destination: [Okta\_Agent](/opengraph/extensions/okta/nodes/okta_agent) * Traversable: ✅ ## General Information Hybrid Okta\_HostsAgent edges connect an AD Computer node to the [Okta\_Agent](/opengraph/extensions/okta/nodes/okta_agent) running on that host. ```mermaid theme={null} graph LR subgraph ad["Active Directory"] d1("Domain contoso.com") c1("Computer LON-SRV1$") c2("Computer NY-SRV2$") d1 -- Contains --> c1 d1 -- Contains --> c2 end subgraph okta["Okta"] ap1("Okta_AgentPool contoso.com") a1("Okta_Agent LON-SRV1") a2("Okta_Agent NY-SRV2") a1 -- Okta_AgentMemberOf --> ap1 a2 -- Okta_AgentMemberOf --> ap1 end c1 -- Okta_HostsAgent --> a1 c2 -- Okta_HostsAgent --> a2 ``` # Okta_IdentityProviderFor Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_identityproviderfor Trust relationship between an identity provider and Okta users Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_IdentityProvider](/opengraph/extensions/okta/nodes/okta_identityprovider) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Traversable: ✅ ## General Information The traversable Okta\_IdentityProviderFor edges represent the relationships between identity providers and the users who authenticate through them: ```mermaid theme={null} graph LR idp1("Okta_IdentityProvider Google") idp2("Okta_IdentityProvider Contoso SAML") u1("Okta_User john\@contoso.com") u2("Okta_User alice\@gmail.com") u3("Okta_User bob\@contoso.com") idp1 -- Okta_IdentityProviderFor --> u2 idp2 -- Okta_IdentityProviderFor --> u1 idp2 -- Okta_IdentityProviderFor --> u3 ``` # Okta_IdpGroupAssignment Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_idpgroupassignment Identity provider group assignment to an Okta group Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_IdentityProvider](/opengraph/extensions/okta/nodes/okta_identityprovider) * Destination: [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) * Traversable: ❌ ## General Information The non-traversable Okta\_IdpGroupAssignment edges represent groups automatically assigned to users based on identity provider attributes or user claims: ```mermaid theme={null} graph LR idp1("Okta_IdentityProvider Microsoft Login") g1("Okta_Group Contractors") g2("Okta_Group Employees") g3("Okta_Group Entra ID Users") idp1 -. Okta_IdpGroupAssignment .-> g1 idp1 -. Okta_IdpGroupAssignment .-> g2 idp1 -. Okta_IdpGroupAssignment .-> g3 ``` # Okta_InboundOrgSSO Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_inboundorgsso Single sign-on from an external organization into Okta Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [AZTenant](/resources/nodes/az-tenant) * Destination: [Okta\_IdentityProvider](/opengraph/extensions/okta/nodes/okta_identityprovider) * Traversable: ✅ ## General Information The Okta\_InboundOrgSSO and [Okta\_InboundSSO](/opengraph/extensions/okta/edges/okta_inboundsso) hybrid edges connect external tenants and users to Okta entities: ```mermaid theme={null} graph LR t1("AZTenant Contoso") idp1("Okta_IdentityProvider Microsoft Login") u1("AZUser alice\@contoso.com") ou1("Okta_User alice\@contoso.com") t1 -- Okta_InboundOrgSSO --> idp1 u1 -- Okta_InboundSSO --> ou1 ``` # Okta_InboundSSO Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_inboundsso Single sign-on from an external identity provider into Okta Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [AZUser](/resources/nodes/az-user) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Traversable: ✅ ## General Information The [Okta\_InboundOrgSSO](/opengraph/extensions/okta/edges/okta_inboundorgsso) and Okta\_InboundSSO hybrid edges connect external tenants and users to Okta entities: ```mermaid theme={null} graph LR t1("AZTenant Contoso") idp1("Okta_IdentityProvider Microsoft Login") u1("AZUser alice\@contoso.com") ou1("Okta_User alice\@contoso.com") t1 -- Okta_InboundOrgSSO --> idp1 u1 -- Okta_InboundSSO --> ou1 ``` # Okta_KerberosSSO Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_kerberossso Agentless desktop SSO relationship from on-prem AD user account to Okta AD application Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [User](/resources/nodes/user) * Destination: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Traversable: ✅ ## General Information Hybrid traversable Okta\_KerberosSSO edges represent [agentless desktop SSO](https://help.okta.com/en-us/content/topics/directory/ad-dsso-about-workflow.htm) trust from an on-prem AD User account to an AD-backed [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application). ```mermaid theme={null} graph LR subgraph ad["Active Directory"] d1("Domain contoso.com") u1("User SPN:HTTP/contoso.kerberos.okta.com") u2("User jane.doe\@contoso.com") d1 -- "Contains" --> u1 d1 -- "Contains" --> u2 end subgraph okta["Okta"] app1("Okta_Application contoso.com") u3("Okta_User jane.doe\@contoso.com") app1 -. Okta_UserPull .-> u3 end u1 -- Okta_KerberosSSO --> app1 u2 -. Okta_UserSync .-> u3 ``` # Okta_KeyOf Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_keyof JSON Web Key associated with an Okta application Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_JWK](/opengraph/extensions/okta/nodes/okta_jwk) * Destination: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Traversable: ✅ ## General Information The traversable Okta\_KeyOf edges represent the relationships between applications [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) and their JWKs: ```mermaid theme={null} graph LR app1("Okta_Application OktaHound Collector") app2("Okta_Application Security Scanner") key1("Okta_JWK ABC123") key2("Okta_JWK DEF456") key3("Okta_JWK GHI789") key1 -- Okta_KeyOf --> app1 key2 -- Okta_KeyOf --> app2 key3 -- Okta_KeyOf --> app2 ``` Possession of the private key corresponding to a JWK allows an attacker to authenticate as the application. The Okta\_KeyOf edge can be used in BloodHound to understand which applications use JWK-based authentication and trace potential attack paths involving compromised private keys. # Okta_ManageApp Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_manageapp Ability to manage scoped Okta applications Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Traversable: ✅ ## General Information The traversable Okta\_ManageApp edges correspond to the `okta.apps.manage` custom role permissions that allow a principal (user, group, or application) to fully manage Okta applications and their members. ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") g1("Okta_Group App Operators") app1("Okta_Application GitHub") app2("Okta_Application Salesforce") u1 -- Okta_ManageApp --> app1 g1 -- Okta_ManageApp --> app2 ``` # Okta_ManagerOf Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_managerof Manager relationship between Okta users Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Traversable: ❌ ## General Information Okta uses the `Manager` and `ManagerId` user profile attributes to represent managerial relationships. Unfortunately, these attributes can have any arbitrary value and their referential integrity is not enforced by Okta. They are not even synchronized from external directories by default. Our recommendation is to map the `ManagerId` attribute to the login of the manager in Okta. When synchronizing users from Active Directory, the `getManagerUser("active_directory").login` mapping expression can be used to achieve this. Such values are automatically recognized by the OpenHound Okta collector. The **non-traversable** Okta\_ManagerOf edges represent the organizational structure in BloodHound: ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") u2("Okta_User steve\@contoso.com") u3("Okta_User mary\@contoso.com") u4("Okta_User bob\@contoso.com") u5("Okta_User alice\@contoso.com") u1 -. Okta_ManagerOf .-> u2 u1 -. Okta_ManagerOf .-> u3 u3 -. Okta_ManagerOf .-> u4 u3 -. Okta_ManagerOf .-> u5 ``` # Okta_MemberOf Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_memberof Membership of a user in an Okta group Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Destination: [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) * Traversable: ✅ ## General Information The traversable Okta\_MemberOf edges represent the membership relationships between users and groups in Okta: ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") u2("Okta_User steve\@contoso.com") u3("Okta_User mary\@contoso.com") g1("Okta_Group Marketing") g2("Okta_Group Sales") u1 -- Okta_MemberOf --> g1 u2 -- Okta_MemberOf --> g1 u2 -- Okta_MemberOf --> g2 u3 -- Okta_MemberOf --> g2 ``` # Okta_MembershipSync Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_membershipsync Bidirectional synchronization between Okta groups and external groups Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Group](/resources/nodes/group), [AZGroup](/resources/nodes/az-group) * Destination: [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Group](/resources/nodes/group), [AZGroup](/resources/nodes/az-group) * Traversable: ✅ ## General Information The traversable hybrid Okta\_MembershipSync edges represent the synchronization relationships between groups in external directories and their corresponding groups in Okta: ```mermaid theme={null} graph TB subgraph ad["Active Directory"] adg1("Group IT") adg2("Group HR") end subgraph okta["Okta Org A"] g1("Okta_Group IT") g2("Okta_Group HR") adg1 -- Okta_MembershipSync --> g1 g2 -- Okta_MembershipSync --> adg2 end subgraph okta2["Okta Org B"] g3("Okta_Group IT") g1 -- Okta_MembershipSync --> g3 end ``` ```mermaid theme={null} graph LR subgraph source_org["Okta Org Contoso"] u1("Okta_User alice\@contoso.com") g1("Okta_Group IT") app1("Okta_Application Adatum Org2Org App") end subgraph target_org["Okta Org Adatum"] u2("Okta_User alice\@adatum.com") g2("Okta_Group IT") app2("Okta_Application Contoso Sync API Service") end u1 -->|Okta_MemberOf| g1 u1 .->|Okta_UserSync| u2 u1 .->|Okta_UserPush| app1 u2 -->|Okta_MemberOf| g2 g1 .->|Okta_GroupPush| app1 g1 -->|Okta_MembershipSync| g2 ``` # Okta_MobileAdmin Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_mobileadmin Mobile administrator role assignment Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device) * Traversable: ✅ ## General Information The traversable Okta\_MobileAdmin edges represent Mobile Administrator role assignments. Mobile Administrators can manage mobile device settings and configurations within their assigned scope. ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") d1("Okta_Device Alice's iPhone") d2("Okta_Device Bob's MacBook") u1 -- Okta_MobileAdmin --> d1 u1 -- Okta_MobileAdmin --> d2 ``` # Okta_OrgAdmin Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_orgadmin Organization administrator role assignment Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device) * Traversable: ✅ ## General Information The traversable Okta\_OrgAdmin edges represent Organization Administrator role assignments. Organization Administrators can manage most organizational settings except for administrative role assignments and some security settings. ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") u2("Okta_User alice\@contoso.com") g1("Okta_Group IT") d1("Okta_Device John's MacBook") u1 -- Okta_OrgAdmin --> u2 u1 -- Okta_OrgAdmin --> g1 u1 -- Okta_OrgAdmin --> d1 ``` # Okta_OrgSWA Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_orgswa Secure Web Authentication from an Okta application to an external organization Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration), [OP\_Account](https://github.com/SpecterOps/1PassHound), [SNOW\_Account](https://github.com/SpecterOps/SnowHound) * Traversable: ❌ ## General Information The non-traversable Okta\_OrgSWA edges represent the Secure Web Authentication (SWA) relationships between Okta applications and supported external organizations or tenants. SWA stores user credentials in Okta and automatically fills them in when users access the application, which is less secure than federated SSO protocols. ```mermaid theme={null} graph LR subgraph okta["OktaHound"] direction TB o("Okta_Organization contoso.okta.com") app1("Okta_Application Jamf Pro SWA") o -- Okta_Contains --> app1 end subgraph "Jamf" direction TB jamf("jamf_SSOIntegration contoso.jamfcloud.com-SSO") app1 -. Okta_OrgSWA .-> jamf end ``` The respective BloodHound collectors, e.g., OpenHound Github for GitHub organizations and OpenHound Jamf for Jamf Pro tenants, must be used to gather the external node information. # Okta_OutboundOrgSSO Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_outboundorgsso Single sign-on from an Okta application to an external organization Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [AZTenant](/resources/nodes/az-tenant), [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration), [SNOW\_Account](https://github.com/SpecterOps/SnowHound), [Okta\_IdentityProvider](/opengraph/extensions/okta/nodes/okta_identityprovider) * Traversable: ✅ ## General Information The traversable Okta\_OutboundOrgSSO edges represent the Single Sign-On (SSO) relationships between Okta applications and supported external organizations or tenants, such as GitHub Enterprise or Jamf Pro, using SAML 2.0 or OIDC protocols. ```mermaid theme={null} graph LR subgraph okta["OktaHound"] direction TB o("Okta_Organization contoso.okta.com") app1("Okta_Application GitHub Enterprise Cloud") app2("Okta_Application Jamf Pro SAML") o -- Okta_Contains --> app1 o -- Okta_Contains --> app2 end subgraph "GitHub" direction TB ghorg("GH_Organization Contoso") app1 -- Okta_OutboundOrgSSO --> ghorg end subgraph "Jamf" direction TB jamf("jamf_SSOIntegration contoso.jamfcloud.com-SSO") app2 -- Okta_OutboundOrgSSO --> jamf end ``` The respective BloodHound collectors, e.g., OpenHound Github for GitHub organizations and OpenHound Jamf for Jamf Pro tenants, must be used to gather the external node information. # Okta_OutboundSSO Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_outboundsso Single sign-on from Okta to an external identity provider Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Destination: [AZUser](/resources/nodes/az-user), [GH\_User](/opengraph/extensions/github/nodes/gh_user), [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [SNOW\_User](https://github.com/SpecterOps/SnowHound), [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Traversable: ✅ ## General Information The traversable hybrid Okta\_OutboundSSO edges represent Single Sign-On relationships between Okta users and their linked accounts in external applications using federated authentication (SAML 2.0 or OIDC). ```mermaid theme={null} graph LR subgraph okta["Okta"] u1("Okta_User john\@contoso.com") u2("Okta_User alice\@contoso.com") end subgraph github["GitHub"] ghu1("GH_User john\@contoso.com") ghu2("GH_User alice\@contoso.com") end subgraph jamf["Jamf"] jamfu1("jamf_Account john\@contoso.com") end subgraph snowflake["Snowflake"] snu1("SNOW_User john\@contoso.com") end u1 -- Okta_OutboundSSO --> ghu1 u1 -- Okta_OutboundSSO --> jamfu1 u2 -- Okta_OutboundSSO --> ghu2 u1 -- Okta_OutboundSSO --> snu1 ``` # Okta_PasswordSync Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_passwordsync Password synchronization between user accounts via AD integration, Org2Org, or SCIM Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [User](/resources/nodes/user), [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [User](/resources/nodes/user) * Traversable: ✅ ## General Information The traversable Okta\_PasswordSync edge represents password synchronization between user accounts. This indicates that credentials are synchronized from a source user to a target user. In **Active Directory** hybrid setups, this edge is created between User (AD) and [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) when delegated authentication or password push is enabled. In **Org2Org** setups, this edge is created between [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) nodes across organizations when password synchronization is configured. The Okta API does not indicate if the actual password or a randomly generated value is pushed to the other organization. ### Active Directory Hybrid ```mermaid theme={null} graph LR subgraph ad["Active Directory"] adu1("User john\@contoso.com") end subgraph okta["Okta"] u1("Okta_User john\@contoso.com") adu1 -->|Okta_PasswordSync| u1 adu1 .->|Okta_UserSync| u1 end ``` ### Org2Org ```mermaid theme={null} graph LR subgraph source_org["Okta Org Contoso"] u1("Okta_User alice\@contoso.com") app1("Okta_Application Adatum Org2Org App") end subgraph target_org["Okta Org Adatum"] u2("Okta_User alice\@adatum.com") idp2("Okta_IdentityProvider Contoso Org2Org OIDC") app2("Okta_Application Contoso Sync API Service") end u1 -->|Okta_PasswordSync| u2 u1 -->|Okta_OutboundSSO| u2 u1 .->|Okta_UserSync| u2 u1 .->|Okta_UserPush| app1 u1 .->|Okta_AppAssignment| app1 app1 -->|Okta_ReadPasswordUpdates| u1 app1 -->|Okta_OutboundOrgSSO| idp2 idp2 -->|Okta_IdentityProviderFor| u2 ``` # Okta_PolicyMapping Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_policymapping Association of a policy with an Okta application Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_Policy](/opengraph/extensions/okta/nodes/okta_policy) * Destination: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Traversable: ❌ ## General Information The non-traversable Okta\_PolicyMapping edges represent the association between a policy and the resources to which it is applied. Only application targets are supported in the current version of the Okta BloodHound extension. ```mermaid theme={null} graph LR o["Okta_Organization contoso.okta.com"] p1["Okta_Policy Idp Discovery Policy {Type: 'IDP_DISCOVERY'}"] p2["Okta_Policy Active Directory Policy {Type: 'PASSWORD'}"] p3["Okta_Policy Okta Admin Console {Type: 'ACCESS_POLICY'}"] p4["Okta_Policy Any two factors {Type: 'ACCESS_POLICY'}"] p5["Okta_Policy Default Policy {Type: 'PROFILE_ENROLLMENT'}"] a1["Okta_Application Okta Admin Console"] a2["Okta_Application Salesforce"] a3["Okta_Application Intranet Portal"] o -->|Okta_Contains| p1 o -->|Okta_Contains| p2 o -->|Okta_Contains| p3 p3 -->|Okta_PolicyMapping| a1 o -->|Okta_Contains| p4 p4 -->|Okta_PolicyMapping| a2 p4 -->|Okta_PolicyMapping| a3 o -->|Okta_Contains| p5 p5 -->|Okta_PolicyMapping| a1 p5 -->|Okta_PolicyMapping| a2 p5 -->|Okta_PolicyMapping| a3 ``` # Okta_ReadClientSecret Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_readclientsecret Ability to read client secrets for scoped Okta applications Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_ClientSecret](/opengraph/extensions/okta/nodes/okta_clientsecret) * Traversable: ✅ ## General Information The traversable Okta\_ReadClientSecret edges represent permissions that allow a principal (user, group, or application) to read OAuth client secrets for scoped Okta applications. These edges are created for the **Application Administrator**, **API Access Management Administrator**, and **Read-only Administrator** built-in roles and for custom roles with the `okta.apps.clientCredentials.read` permission. ```mermaid theme={null} graph TD org("Okta_Organization contoso.okta.com") u1("Okta_User john\@contoso.com") g1("Okta_Group Auditors") app1("Okta_Application HR Sync") secret1("Okta_ClientSecret abcdefgh") r1("Okta_Role Read-only Administrator") u1 -- Okta_MemberOf --> g1 g1 -- Okta_ReadClientSecret --> secret1 secret1 -- Okta_SecretOf --> app1 app1 -- Okta_SuperAdmin --> org g1 -. Okta_HasRole .-> r1 ``` ## Potential Attack Scenarios An attacker with the ability to read client secrets for an application assigned the Super Administrator role could potentially use the client secret to authenticate as that application and perform privileged actions in Okta. ## Potential Attack Scenarios An attacker with the ability to read client secrets for an application assigned the Super Administrator role could potentially use the client secret to authenticate as that application and perform privileged actions in Okta. # Okta_ReadPasswordUpdates Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_readpasswordupdates Application can read password updates over the SCIM protocol Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Traversable: ✅ ## General Information The traversable Okta\_ReadPasswordUpdates edges represent applications that can read password updates over SCIM. ```mermaid theme={null} graph LR org("Okta_Organization contoso.okta.com") app("Okta_Application SCIM App") user("Okta_User john\@contoso.com") user2("Okta_User steve\@contoso.com") app -- Okta_ReadPasswordUpdates --> user user -- Okta_SuperAdmin --> org user2 -- Okta_AppAdmin --> app ``` # Okta_RealmContains Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_realmcontains Contains relationship between an Okta realm and its users Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_Realm](/opengraph/extensions/okta/nodes/okta_realm) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Traversable: ✅ ## General Information The traversable Okta\_RealmContains edges represent containment relationships between realms and the users assigned to those realms. ```mermaid theme={null} graph LR r1("Okta_Realm EU") r2("Okta_Realm US") u1("Okta_User john\@contoso.com") u2("Okta_User alice\@contoso.com") u3("Okta_User bob\@contoso.com") r1 -- Okta_RealmContains --> u1 r1 -- Okta_RealmContains --> u2 r2 -- Okta_RealmContains --> u3 ``` Okta Realms are currently not supported by BloodHound due to licensing restrictions. # Okta_ResetFactors Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_resetfactors Ability to reset MFA factors for scoped Okta users Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Traversable: ✅ ## General Information The traversable Okta\_ResetFactors edges represent custom role permissions that allow a principal to reset MFA authenticators for scoped Okta users. These edges are created when a custom role includes the `okta.users.credentials.resetFactors` or `okta.users.credentials.manage` permissions. ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") u2("Okta_User alice\@contoso.com") g1("Okta_Group Tier 1 Support") g1 -- Okta_ResetFactors --> u1 u2 -- Okta_ResetFactors --> u1 ``` # Okta_ResetPassword Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_resetpassword Ability to reset passwords or temporary credentials for scoped Okta users Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Traversable: ✅ ## General Information The traversable Okta\_ResetPassword edges represent custom role permissions that allow a principal (user, group, or application) to reset passwords or temporary credentials for scoped Okta users. These edges are created when a custom role includes password management permissions such as `okta.users.credentials.resetPassword`, `okta.users.credentials.manage`, `okta.users.credentials.manageTemporaryAccessCode`, or `okta.users.manage`. ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") u2("Okta_User alice\@contoso.com") g1("Okta_Group Help Desk") app1("Okta_Application Automation") g1 -- Okta_ResetPassword --> u2 g1 -- Okta_ResetFactors --> u2 app1 -- Okta_ResetPassword --> u1 ``` The edge is calculated based on custom role scoping. ```mermaid theme={null} graph TD u1("Okta_User john\@contoso.com") u2("Okta_User alice\@contoso.com") g1("Okta_Group Help Desk") rs("Okta_ResourceSet Frontline Workers") a("Okta_RoleAssignment Authentication Admins") r("Okta_CustomRole Authentication Admins") g1 -. Okta_HasRole .-> r a -. Okta_ScopedTo .-> rs g1 -. Okta_HasRoleAssignment .-> a rs -- Okta_ResourceSetContains --> u2 u1 -- Okta_MemberOf --> g1 g1 -- Okta_ResetPassword --> u2 g1 -- Okta_ResetFactors --> u2 ``` # Okta_ResourceSetContains Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_resourcesetcontains Membership of objects within an Okta resource set Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration), [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device), [Okta\_AuthorizationServer](/opengraph/extensions/okta/nodes/okta_authorizationserver), [Okta\_IdentityProvider](/opengraph/extensions/okta/nodes/okta_identityprovider), [Okta\_Policy](/opengraph/extensions/okta/nodes/okta_policy) * Traversable: ✅ ## General Information The traversable Okta\_ResourceSetContains edges represent the membership relationships between resource sets and their member entities in Okta: ```mermaid theme={null} graph LR rs1("Okta_ResourceSet Sales Department Resources") u1("Okta_User john\@contoso.com") u2("Okta_User alice\@contoso.com") g1("Okta_Group Sales Team") a1("Okta_Application GitHub") d1("Okta_Device John's MacBook") rs1 -- Okta_ResourceSetContains --> u1 rs1 -- Okta_ResourceSetContains --> g1 rs1 -- Okta_ResourceSetContains --> a1 rs1 -- Okta_ResourceSetContains --> d1 u2 -- Okta_MemberOf --> g1 rs1 -- Okta_ResourceSetContains --> u2 ``` Note that users can also be members of resource sets indirectly through group memberships. The intermediate group will not appear in the graph, but the user membership will be resolved by the collector. # Okta_ScopedTo Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_scopedto Scope relationship between a role assignment and its target Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) * Destination: [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization), [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration), [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device), [Okta\_AuthorizationServer](/opengraph/extensions/okta/nodes/okta_authorizationserver) * Traversable: ❌ ## General Information The [Okta\_HasRoleAssignment](/opengraph/extensions/okta/edges/okta_hasroleassignment) edges connect users, groups, and applications to their respective [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) nodes. The Okta\_ScopedTo edges connect the [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) nodes to the resources they are scoped to, such as the organization or specific groups or applications. ```mermaid theme={null} graph TB ra1("Okta_RoleAssignment Help Desk Administrator") ra2("Okta_RoleAssignment Super Administrator") r1("Okta_Role Help Desk Administrator") r2("Okta_Role Super Administrator") u1("Okta_User john\@contoso.com") u2("Okta_User steve\@contoso.com") u3("Okta_User alice\@contoso.com") g1("Okta_Group Seattle Help Desk") g2("Okta_Group Seattle Office") org("Okta_Organization contoso.okta.com") u1 -- Okta_MemberOf --> g1 g1 -. Okta_HasRoleAssignment .-> ra1 g1 -. Okta_HasRole .-> r1 g1 -- Okta_HelpDeskAdmin --> u3 u3 -- Okta_MemberOf --> g2 ra1 -. Okta_ScopedTo .-> g2 u2 -. Okta_HasRoleAssignment .-> ra2 ra2 -. Okta_ScopedTo .-> org u2 -- Okta_SuperAdmin --> org u2 -. Okta_HasRole .-> r2 ``` # Okta_SecretOf Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_secretof Client secret associated with an application or service integration Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_ClientSecret](/opengraph/extensions/okta/nodes/okta_clientsecret) * Destination: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) * Traversable: ✅ ## General Information The traversable Okta\_SecretOf edges represent the relationship between service applications or API service integrations and their associated client secrets, represented by the [Okta\_ClientSecret](/opengraph/extensions/okta/nodes/okta_clientsecret) nodes. ```mermaid theme={null} graph LR is1("Okta_APIServiceIntegration Elastic Agent") is2("Okta_APIServiceIntegration Falcon Shield") cs1("Okta_ClientSecret pdWB5I2I1LJ_cUAzD9fB1w") cs2("Okta_ClientSecret lLRrn0i2tIa5YowaQuTdtQ") cs3("Okta_ClientSecret EpGPhXPYLxqY2JEWRjTSAQ") cs1 -- Okta_SecretOf --> is1 cs2 -- Okta_SecretOf --> is2 cs3 -- Okta_SecretOf --> is2 ``` # Okta_SuperAdmin Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_superadmin Super administrator role assignment Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) * Traversable: ✅ ## General Information The traversable Okta\_SuperAdmin edges represent Super Administrator role assignments to the Okta organization. Super Administrators have full access to all features and settings in the Okta organization. ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") app1("Okta_Application Service Account") org("Okta_Organization contoso.okta.com") u1 -- Okta_SuperAdmin --> org app1 -- Okta_SuperAdmin --> org ``` # Okta_SWA Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_swa Secure Web Authentication from Okta to an external application Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Destination: [GH\_User](/opengraph/extensions/github/nodes/gh_user), [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [OP\_User](https://github.com/SpecterOps/1PassHound), [SNOW\_User](https://github.com/SpecterOps/SnowHound) * Traversable: ❌ ## General Information The non-traversable hybrid Okta\_SWA edges represent Secure Web Authentication relationships between Okta users and their linked accounts in external applications. SWA stores user credentials in Okta and automatically fills them in, which is less secure than federated SSO. ```mermaid theme={null} graph LR subgraph okta["Okta"] u1("Okta_User john\@contoso.com") u2("Okta_User alice\@contoso.com") end subgraph op["1Password Business"] opu1("OP_User john\@contoso.com") opu2("OP_User alice\@contoso.com") end u1 -. Okta_SWA .-> opu1 u2 -. Okta_SWA .-> opu2 ``` # Okta_UserPull Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_userpull Import of users from an external application Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Traversable: ❌ ## General Information The Okta\_UserPull edges represent user import relationships from external applications to Okta. ```mermaid theme={null} graph LR app1("Okta_Application Workday") u1("Okta_User john\@contoso.com") u2("Okta_User alice\@contoso.com") app1 -. Okta_UserPull .-> u1 app1 -. Okta_UserPull .-> u2 ``` # Okta_UserPush Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_userpush Provisioning of users to an external application Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) * Destination: [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) * Traversable: ❌ ## General Information The non-traversable Okta\_UserPush edges represent user provisioning relationships from Okta to external applications. When configured, Okta can automatically create, update, or deactivate user accounts in integrated applications using protocols like SCIM or LDAP. ```mermaid theme={null} graph LR u1("Okta_User john\@contoso.com") u2("Okta_User alice\@contoso.com") app1("Okta_Application GitHub Enterprise Cloud") app2("Okta_Application Salesforce") u1 -. Okta_UserPush .-> app1 u2 -. Okta_UserPush .-> app1 u2 -. Okta_UserPush .-> app2 ``` # Okta_UserSync Source: https://bloodhound.specterops.io/opengraph/extensions/okta/edges/okta_usersync Bidirectional synchronization between Okta users and external identities Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [User](/resources/nodes/user), [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [SNOW\_User](https://github.com/SpecterOps/SnowHound) * Destination: [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [User](/resources/nodes/user), [AZUser](/resources/nodes/az-user), [OP\_User](https://github.com/SpecterOps/1PassHound), [SNOW\_User](https://github.com/SpecterOps/SnowHound) * Traversable: ❌ ## General Information The non-traversable hybrid Okta\_UserSync edges represent bidirectional user synchronization relationships between Okta and external directories or applications. These edges indicate that user accounts are linked and synchronized between systems. ```mermaid theme={null} graph LR subgraph ad["Active Directory"] adu1("User john\@contoso.com") end subgraph okta["Okta"] u1("Okta_User john\@contoso.com") adu1 -. Okta_UserSync .-> u1 end subgraph snowflake["Snowflake"] snu1("SNOW_User john\@contoso.com") u1 -. Okta_UserSync .-> snu1 end ``` # Getting Started Source: https://bloodhound.specterops.io/opengraph/extensions/okta/getting-started Learn how to get started with the Okta OpenGraph extension in BloodHound. Applies to BloodHound Enterprise and CE ## Prerequisites Full OpenGraph support requires a PostgreSQL graph database and one of the following editions: * BloodHound Enterprise (uses PostgreSQL by default) * BloodHound Community v8.0.0+ (requires changing to a [PostgreSQL database](/get-started/custom-installation#postgresql)) While many OpenGraph features may work on a Neo4j database, there are functional and performance limitations (see the [OpenGraph FAQ](/opengraph/faq#why-is-it-taking-so-long-to-ingest-opengraph-data)). For full support, migrate to a PostgreSQL database. The OpenGraph Extension Management feature must be enabled before you can manage extensions. Enable this feature on the **Administration** > **Early Access Features** page. ## Install the Extension ### Optional Schemas If your uses SCIM, upload the [bh-scim-extension.json](https://github.com/SpecterOps/bloodhound-scim-extension/blob/main/bh-scim-extension.json) schema as well. This schema provides a shared model for provisioned users and groups across cloud identity providers and applications. If is connected to other BloodHound-supported data sources in your environment, such as , make sure the corresponding schema is installed too. In BloodHound Enterprise v9.3.0 and later, some extensions (such as GitHub, Jamf, and Okta) are pre-installed. Upload any companion schemas that are not already installed. Doing so ensures those cross-platform relationships are modeled correctly in BloodHound. ## Import Cypher Queries ## Collect and Upload Data ## Configure Privilege Zones # Okta_Agent Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_agent A synchronization or authentication agent in Okta Applies to BloodHound Enterprise and CE ## Overview The Okta\_Agent node represents an Okta Agent, which is a component used in Okta's integration with on-premises systems. Okta Agents facilitate communication between the Okta cloud and on-premises applications or directories, enabling features such as single sign-on (SSO) and user provisioning. One or more agents are grouped into Agent Pools, represented by the [Okta\_AgentPool](/opengraph/extensions/okta/nodes/okta_agentpool) nodes, to provide redundancy and load balancing. Active Directory Agent in BloodHound ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------- | ------------------------------------- | ----------- | | [Okta\_HostsAgent](/opengraph/extensions/okta/edges/okta_hostsagent) | [Computer](/resources/nodes/computer) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | -------------------------------------------------------------------------- | ------------------------------------------------------------------ | ----------- | | [Okta\_AgentMemberOf](/opengraph/extensions/okta/edges/okta_agentmemberof) | [Okta\_AgentPool](/opengraph/extensions/okta/nodes/okta_agentpool) | ✅ | ## Properties | Name | Source | Type | Description | | -------------------- | ------------------------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `agent.id` | `string` | Unique agent identifier. | | `name` | `agent.name` | `string` | Agent name shown in Okta Admin Console. | | `display_name` | `agent.name` | `string` | Display label used in BloodHound. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the agent exists. | | `pool_name` | `agentPool.name` | `string` | Name of the parent [Okta\_AgentPool](/opengraph/extensions/okta/nodes/okta_agentpool). For AD pools this typically corresponds to the synced AD domain. | | `operational_status` | `agent.operationalStatus` | `string` | Runtime health/operational state reported by Okta. | | `update_status` | `agent.updateStatus` | `string` | Agent software update state. | | `type` | `agent.type` | `string` | Agent type (for example AD, LDAP, IWA, or RADIUS). | | `version` | `agent.version` | `string` | Agent software version. | | `pool_id` | `agent.poolId` | `string` | Identifier of the parent Okta agent pool. | | `last_connection` | `FromUnixTime(agent.lastConnection)` | `datetime` | Timestamp of the last successful agent connection to Okta. | ## Sample Property Values ```yaml theme={null} id: a53xfufl4rqWcHhQo697 name: LON-SRV01 display_name: LON-SRV01 pool_id: 0oaxg9rhdd7ncGCXv697 okta_domain: contoso.okta.com pool_name: contoso.local operational_status: DISRUPTED update_status: Cancelled type: AD version: 3.22.0 last_connection: 2026-01-15T02:29:40+00:00 ``` # Okta_AgentPool Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_agentpool A pool of synchronization or authentication agents in Okta Applies to BloodHound Enterprise and CE ## Overview The Okta\_AgentPool nodes represent Okta Agent Pools, which are collections of Okta Agents (represented as [Okta\_Agent](/opengraph/extensions/okta/nodes/okta_agent) nodes) that work together to provide high availability and load balancing for on-premises integrations. The following agent pool types are supported by Okta: | Agent Pool Type | Description | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | AD | [Active Directory](https://help.okta.com/en-us/content/topics/directory/ad-agent-integration-implementation-options.htm) | | IWA | [Integrated Windows Authentication (Kerberos/NTLM)](https://help.okta.com/en-us/content/topics/directory/ad-iwa-learn.htm) | | LDAP | [Lightweight Directory Access Protocol](https://help.okta.com/en-us/content/topics/directory/ldap-agent-supported-directories.htm) | | RADIUS | [RADIUS authentication proxy](https://help.okta.com/en-us/content/topics/integrations/radius-best-pract-flow.htm) | | MFA | | | OPP | | | RUM | | The most common agent pool type is the Active Directory (AD) Agent Pool, which consists of one or more AD Agents that facilitate bi-directional object synchronization between Okta and on-premises Active Directory environments. Okta AD Agent Pools displayed in BloodHound ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ----------- | | [Okta\_AgentMemberOf](/opengraph/extensions/okta/edges/okta_agentmemberof) | [Okta\_Agent](/opengraph/extensions/okta/nodes/okta_agent) | ✅ | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ------------------------------------------------------------------------ | ---------------------------------------------------------------------- | ----------- | | [Okta\_AgentPoolFor](/opengraph/extensions/okta/edges/okta_agentpoolfor) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | ## Properties | Name | Source | Type | Description | | -------------------- | ----------------------------- | -------- | ----------------------------------------------------- | | `id` | `agentPool.id + "_pool"` | `string` | Unique agent pool identifier. | | `name` | `agentPool.name` | `string` | Name of the Okta agent pool. | | `display_name` | `agentPool.name` | `string` | Display label used in BloodHound. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the agent pool exists. | | `operational_status` | `agentPool.operationalStatus` | `string` | Current health/operational state of the agent pool. | | `type` | `agentPool.type` | `string` | Agent pool type (for example AD, LDAP, IWA, RADIUS). | Active Directory (AD) agent pool identifiers have the same values as the identifiers of the corresponding application objects. The `_pool` suffix is therefore added to the `id` property of `Okta_AgentPool` nodes to ensure uniqueness of node identifiers in BloodHound. ## Sample Property Values ```yaml theme={null} id: 0oaxg9rhdd7ncGCXv697_pool name: contoso.local display_name: contoso.local okta_domain: contoso.okta.com operational_status: DISRUPTED type: AD ``` # Okta_ApiServiceIntegration Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_apiserviceintegration An API service integration Applies to BloodHound Enterprise and CE ## Overview API service integrations in Okta represent OAuth 2.0 service (daemon) applications that can be granted machine-to-machine access to Okta APIs. There are some important differences between API service integrations and [regular OIDC service applications in Okta](/opengraph/extensions/okta/nodes/okta_application): | Feature | Service Applications | API Service Integrations | | -------------------------------------------- | -------------------- | ------------------------ | | Can be created manually: | ✅ | ❌ | | Can be added from the OIN Catalog: | ✅ | ✅ | | Require role assignments: | ✅ | ❌ | | Support authentication using client secrets: | ✅ | ✅ | | Support authentication using private keys: | ✅ | ❌ | | Admins can read cleartext client secrets: | ✅ | ❌ | API service integrations are represented as Okta\_ApiServiceIntegration nodes in BloodHound. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_AppAdmin](/opengraph/extensions/okta/edges/okta_appadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_CreatorOf](/opengraph/extensions/okta/edges/okta_creatorof) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) | ❌ | | [Okta\_ResourceSetContains](/opengraph/extensions/okta/edges/okta_resourcesetcontains) | [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset) | ✅ | | [Okta\_ScopedTo](/opengraph/extensions/okta/edges/okta_scopedto) | [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) | ❌ | | [Okta\_SecretOf](/opengraph/extensions/okta/edges/okta_secretof) | [Okta\_ClientSecret](/opengraph/extensions/okta/nodes/okta_clientsecret) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | ----------- | | [Okta\_CreatorOf](/opengraph/extensions/okta/edges/okta_creatorof) | [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) | ❌ | ## Properties | Name | Source | Type | Description | | -------------- | --------------------------- | ---------- | ------------------------------------------------------ | | `id` | `service.id` | `string` | Unique API service integration identifier. | | `name` | `service.name` | `string` | Name of the API service integration in Okta. | | `display_name` | `service.name` | `string` | Display label used in BloodHound. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the integration exists. | | `app_type` | `service.type` | `string` | Integration/application type identifier. | | `oauth_scopes` | `service.grantedScopes` | `string[]` | OAuth 2.0 scopes granted to the integration. | | `created_at` | `service.createdAt` | `datetime` | Timestamp when the integration was created. | ## Sample Property Values ```yaml theme={null} id: 0oaz7jy5f2oXnvtmN697 name: Falcon Shield display_name: Falcon Shield okta_domain: contoso.okta.com app_type: falconshieldapiservice oauth_scopes: - okta.users.read - okta.oauthIntegrations.read - okta.threatInsights.read - okta.devices.read - okta.apiTokens.read - okta.roles.read - okta.logs.read - okta.groups.read - okta.apps.read - okta.domains.read - okta.factors.read - okta.authenticators.read - okta.policies.read - okta.networkZones.read - okta.features.read created_at: 2026-01-15T12:25:42.000Z ``` ## Integration OAuth 2.0 Scopes Each API service integration comes with a pre-defined set of OAuth 2.0 scopes to access Okta APIs: Okta API service integration scopes in BloodHound # Okta_ApiToken Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_apitoken A secret used by users to authenticate to the Okta API Applies to BloodHound Enterprise and CE ## Overview API tokens (also known as SSWS tokens) in Okta are used to authenticate and authorize access to the Okta API. They are typically used by applications and scripts that need to interact with Okta programmatically. These tokens are always associated with a specific user in Okta, and the permissions of the token are determined by the role assignments of that user. For example, if a user has the Super Administrator role, any API token generated by that user will have full access to all API endpoints. Moreover, the long-lived API tokens are typically stored in plaintext in application configuration files or environment variables, making them a high-value target for attackers. The use of API tokens is generally discouraged in favor of OAuth 2.0 access tokens, as they provide better security and flexibility. However, API tokens are still widely used by Okta customers. Okta API tokens are represented as Okta\_ApiToken nodes in BloodHound. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges No inbound edges are defined by the Okta extension for this node. ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------------- | -------------------------------------------------------- | ----------- | | [Okta\_ApiTokenFor](/opengraph/extensions/okta/edges/okta_apitokenfor) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | ## Properties | Name | Source | Type | Description | | -------------------- | ---------------------------------- | ---------- | ------------------------------------------------------- | | `id` | `apiToken.id` | `string` | Unique API token identifier. | | `name` | `apiToken.name` | `string` | Friendly name of the API token. | | `display_name` | `apiToken.name` | `string` | Display label used in BloodHound. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the token exists. | | `user_id` | `apiToken.userId` | `string` | ID of the Okta user that owns the token. | | `client_name` | `apiToken.clientName` | `string` | Client/application name associated with the token. | | `created` | `apiToken.created` | `datetime` | Token creation timestamp. | | `last_updated` | `apiToken.lastUpdated` | `datetime` | Last update timestamp of token metadata. | | `expires_at` | `apiToken.expiresAt` | `datetime` | Token expiration timestamp. | | `network_connection` | `apiToken.network.connection` | `string` | Network connection restriction for token usage. | | `token_window` | `ToTimeSpan(apiToken.tokenWindow)` | `duration` | Inactivity window converted to `TimeSpan` when present. | ## Sample Property Values ```yaml theme={null} id: 00T36fk75smeJybKx697 name: Postman display_name: Postman okta_domain: contoso.okta.com user_id: 00uw0o8iizq37KgKP697 client_name: Okta API created: 2025-10-03T10:08:09+00:00 last_updated: 2026-01-31T20:22:42+00:00 expires_at: 2026-03-02T20:22:42+00:00 network_connection: ANYWHERE token_window: 30.00:00:00 ``` # Okta_Application Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_application An application registered in Okta, such as a SAML app or an OIDC app Applies to BloodHound Enterprise and CE ## Overview Applications in Okta represent the various software applications and services that users can access through the Okta organization. Applications can be configured to use different authentication methods, such as SAML, OIDC, or SWA. These protocols can either be configured manually by administrators or automatically by adding an application from Okta's App Integration Catalog, which provides a wide range of pre-configured cloud and on-premises application templates. With the exception of API Service applications, Okta users and groups can be assigned to applications. Users can also be synchronized TO and FROM applications in Okta, typically using the SCIM protocol. For example, when integrating with GitHub Enterprise Cloud, Okta can be configured to automatically create user accounts in GitHub when users are assigned to the GitHub application in Okta. Applications are represented as Okta\_Application nodes in BloodHound. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_AgentPoolFor](/opengraph/extensions/okta/edges/okta_agentpoolfor) | [Okta\_AgentPool](/opengraph/extensions/okta/nodes/okta_agentpool) | ✅ | | [Okta\_AppAdmin](/opengraph/extensions/okta/edges/okta_appadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_AppAssignment](/opengraph/extensions/okta/edges/okta_appassignment) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ❌ | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_GroupPush](/opengraph/extensions/okta/edges/okta_grouppush) | [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ❌ | | [Okta\_KerberosSSO](/opengraph/extensions/okta/edges/okta_kerberossso) | [User](/resources/nodes/user) | ✅ | | [Okta\_KeyOf](/opengraph/extensions/okta/edges/okta_keyof) | [Okta\_JWK](/opengraph/extensions/okta/nodes/okta_jwk) | ✅ | | [Okta\_ManageApp](/opengraph/extensions/okta/edges/okta_manageapp) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_PolicyMapping](/opengraph/extensions/okta/edges/okta_policymapping) | [Okta\_Policy](/opengraph/extensions/okta/nodes/okta_policy) | ❌ | | [Okta\_ResourceSetContains](/opengraph/extensions/okta/edges/okta_resourcesetcontains) | [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset) | ✅ | | [Okta\_ScopedTo](/opengraph/extensions/okta/edges/okta_scopedto) | [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) | ❌ | | [Okta\_SecretOf](/opengraph/extensions/okta/edges/okta_secretof) | [Okta\_ClientSecret](/opengraph/extensions/okta/nodes/okta_clientsecret) | ✅ | | [Okta\_UserPush](/opengraph/extensions/okta/edges/okta_userpush) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_AddMember](/opengraph/extensions/okta/edges/okta_addmember) | [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ✅ | | [Okta\_AppAdmin](/opengraph/extensions/okta/edges/okta_appadmin) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) | ✅ | | [Okta\_CreatorOf](/opengraph/extensions/okta/edges/okta_creatorof) | [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) | ❌ | | [Okta\_GroupAdmin](/opengraph/extensions/okta/edges/okta_groupadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ✅ | | [Okta\_GroupMembershipAdmin](/opengraph/extensions/okta/edges/okta_groupmembershipadmin) | [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ✅ | | [Okta\_GroupPull](/opengraph/extensions/okta/edges/okta_grouppull) | [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ✅ | | [Okta\_HasRole](/opengraph/extensions/okta/edges/okta_hasrole) | [Okta\_Role](/opengraph/extensions/okta/nodes/okta_role), [Okta\_CustomRole](/opengraph/extensions/okta/nodes/okta_customrole) | ❌ | | [Okta\_HasRoleAssignment](/opengraph/extensions/okta/edges/okta_hasroleassignment) | [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) | ❌ | | [Okta\_HelpDeskAdmin](/opengraph/extensions/okta/edges/okta_helpdeskadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_ManageApp](/opengraph/extensions/okta/edges/okta_manageapp) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_MobileAdmin](/opengraph/extensions/okta/edges/okta_mobileadmin) | [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device) | ✅ | | [Okta\_OrgAdmin](/opengraph/extensions/okta/edges/okta_orgadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device) | ✅ | | [Okta\_OrgSWA](/opengraph/extensions/okta/edges/okta_orgswa) | [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration), [OP\_Account](https://github.com/SpecterOps/1PassHound), [SNOW\_Account](https://github.com/SpecterOps/SnowHound) | ❌ | | [Okta\_OutboundOrgSSO](/opengraph/extensions/okta/edges/okta_outboundorgsso) | [AZTenant](/resources/nodes/az-tenant), [GH\_Organization](/opengraph/extensions/github/nodes/gh_organization), [jamf\_SSOIntegration](/opengraph/extensions/jamf/nodes/jamf_ssointegration), [SNOW\_Account](https://github.com/SpecterOps/SnowHound), [Okta\_IdentityProvider](/opengraph/extensions/okta/nodes/okta_identityprovider) | ✅ | | [Okta\_ReadClientSecret](/opengraph/extensions/okta/edges/okta_readclientsecret) | [Okta\_ClientSecret](/opengraph/extensions/okta/nodes/okta_clientsecret) | ✅ | | [Okta\_ReadPasswordUpdates](/opengraph/extensions/okta/edges/okta_readpasswordupdates) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_ResetFactors](/opengraph/extensions/okta/edges/okta_resetfactors) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_ResetPassword](/opengraph/extensions/okta/edges/okta_resetpassword) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_SuperAdmin](/opengraph/extensions/okta/edges/okta_superadmin) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_UserPull](/opengraph/extensions/okta/edges/okta_userpull) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ❌ | ## Properties ### Common Application Properties | Name | Source | Type | Description | | ---------------------- | --------------------------------------------------- | ---------- | ------------------------------------------------------------------------------- | | `id` | `application.id` | `string` | Unique application identifier. | | `name` | `application.name` | `string` | App type identifier (for example `office365`, `snowflake`, `githubcloud`). | | `display_name` | `application.label` | `string` | Display label used in BloodHound. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the application exists. | | `has_role_assignments` | Calculated | `bool` | Indicates whether the application is assigned any administrative roles. | | `created` | `application.created` | `datetime` | Application creation timestamp. | | `last_updated` | `application.lastUpdated` | `datetime` | Last update timestamp of the app definition. | | `status` | `application.status` | `string` | Current lifecycle status of the application instance. | | `sign_on_mode` | `application.signOnMode` | `string` | Sign-on protocol mode (for example `OPENID_CONNECT`, `SAML_2_0`, `AUTO_LOGIN`). | | `features` | `application.features` | `string[]` | Enabled app capabilities such as SCIM provisioning and password push. | | `user_name_mapping` | `application.credentials.userNameTemplate.template` | `string` | Username mapping template used for provisioning/federation. | Individual application types may have additional properties specific to the integration or protocol: ### GitHub Cloud | Name | Source | Type | Description | | ------------ | ------------------------------------ | -------- | ---------------------------------------------- | | `github_org` | `application.settings.app.githubOrg` | `string` | GitHub organization mapped to the integration. | ### Google Workspace | Name | Source | Type | Description | | ---------- | ---------------------------------- | -------- | -------------------------------------------------------------- | | `domain` | `application.settings.app.domain` | `string` | Google Workspace domain associated with the integration. | | `afw_only` | `application.settings.app.afwOnly` | `bool` | App-specific flag indicating constrained integration behavior. | ### Jamf Pro SAML | Name | Source | Type | Description | | -------- | --------------------------------- | -------- | ------------------------------------------------------- | | `domain` | `application.settings.app.domain` | `string` | Jamf Pro tenant domain associated with the integration. | ### Active Directory Integration | Name | Source | Type | Description | | --------------------------- | ------------------------------------------------------------------------- | -------- | -------------------------------------------------------- | | `naming_context` | `application.settings.app.namingContext` | `string` | Naming context configured for AD-backed app integration. | | `filter_groups_by_ou` | `application.settings.app.filterGroupsByOU` | `bool` | Whether group filtering by OU is enabled. | | `domain_sid` | Derived from synced AD user/group SID values (not directly in app object) | `string` | Domain SID associated with AD-backed integration. | | `windows_transport_enabled` | `application.settings.app.windowsTransportEnabled` | `bool` | Indicates if Windows transport is enabled. | ### Generic SAML Application | Name | Source | Type | Description | | ----------------------- | --------------------------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------- | | `url` | `application.settings.signOn.ssoAcsUrl` (SAML 2.0) / `application.settings.signOn.ssoAcsUrlOverride` (SAML 1.1) | `string` | Primary sign-on URL exposed for SAML applications. | | `entity_id` | `application.settings.signOn.destination` / `application.settings.signOn.audience` | `string` | SAML Entity ID for SAML integrations. | | `acs_url` | `application.settings.signOn.ssoAcsUrl` | `string` | Assertion Consumer Service (ACS) URL for SAML integrations. | | `ws_fed_configure_type` | `application.settings.app.wsFedConfigureType` | `string` | WS-Federation configuration mode. | ### Generic OIDC Service Application | Name | Source | Type | Description | | -------------------- | ----------------------------------------------------------------------------------------------------- | ---------- | --------------------------------------------------------------------- | | `client_type` | `application.settings.oauthClient.applicationType` | `string` | OIDC client type (for example `web`, `native`, `browser`, `service`). | | `grant_types` | `application.settings.oauthClient.grantTypes[]` | `string[]` | OAuth 2.0 grant types allowed for OIDC apps. | | `redirect_uri` | `application.settings.oauthClient.redirectUris[]` | `string` | OIDC redirect URI configured for the integration. | | `initiate_login_uri` | `application.settings.oauthClient.initiateLoginUri` | `string` | Okta-initiated login URI for supported OIDC apps. | | `url` | Derived from OIDC sign-in URL preference (`initiate_login_uri` first, otherwise first `redirect_uri`) | `string` | Primary sign-in URL for OIDC applications. | | `oauth_scopes` | Derived from app grants in `PopulateOAuthScopes` / grant collection logic | `string[]` | OAuth scopes granted to the application in Okta. | | `domain` | `application.settings.app.domain` | `string` | Directory or service domain associated with the app integration. | | `domains` | `application.settings.app.domains` | `string[]` | Domain list associated with the app integration when provided. | | `service_domain` | `application.settings.app.serviceDomain` | `string` | Service/API domain used by workflow or API-connected apps. | | `sub_domain` | `application.settings.app.subDomain` | `string` | Subdomain value used by app-specific integrations. | | `region_type` | `application.settings.app.regionType` | `string` | Region suffix/type used by the app integration. | ### Microsoft Entra ID External Authentication | Name | Source | Type | Description | | ------------------------------ | ----------------------------------------------------- | -------- | ---------------------------------------------------------------- | | `microsoft_discovery_endpoint` | `application.settings.app.microsoftDiscoveryEndpoint` | `string` | OIDC discovery endpoint used by Microsoft integrations. | | `microsoft_app_id` | `application.settings.app.microsoftAppId` | `string` | Microsoft application/client ID configured in the integration. | | `microsoft_tenant_id` | `application.settings.app.microsoftTenantId` | `string` | Microsoft Entra tenant GUID associated with the app integration. | | `require_admin_consent` | `application.settings.app.requireAdminConsent` | `bool` | Indicates if Microsoft admin consent is required. | ### Microsoft Office 365 | Name | Source | Type | Description | | --------------------- | ------------------------------------- | -------- | ---------------------------------------------------------------------------- | | `msft_tenant` | `application.settings.app.msftTenant` | `string` | Microsoft tenant short name/domain used by the Office 365 integration. | | `microsoft_tenant_id` | Calculated from `msft_tenant` | `string` | Microsoft Entra tenant GUID resolved from the Office 365 onmicrosoft tenant. | ### Generic SWA / Browser Plugin Application | Name | Source | Type | Description | | ---------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------ | | `login_url` | `application.settings.app.loginUrl` | `string` | App login URL used by SWA/browser plugin configurations. | | `url` | `application.settings.signOn.loginUrl` (AutoLogin) / `application.settings.app.url` (BrowserPlugin/BasicAuth/Bookmark/SPS) | `string` | Primary login URL exposed for SWA and related app types. | | `app_filter` | `application.settings.app.appFilter` | `string` | App-side filter expression value. | | `group_filter` | `application.settings.app.groupFilter` | `string` | Group filter pattern used for provisioning/mapping. | | `use_group_mapping` | `application.settings.app.useGroupMapping` | `bool` | Whether group mapping is enabled for integration. | | `join_all_roles` | `application.settings.app.joinAllRoles` | `bool` | Whether all discovered roles are joined/collected. | | `role_value_pattern` | `application.settings.app.roleValuePattern` | `string` | Role mapping pattern template for AWS role federation. | | `aws_environment_type` | `application.settings.app.awsEnvironmentType` | `string` | AWS environment identifier for AWS app integrations. | | `session_duration` | `application.settings.app.sessionDuration` | `integer` | Session duration setting (seconds) for supported app integrations. | ## Sample Property Values ### Github Cloud ```yaml theme={null} id: 0oawyp12cjglrkfId697 name: githubcloud display_name: Github Contoso features: [] github_org: Contoso has_role_assignments: false okta_domain: contoso.okta.com sign_on_mode: SAML_2_0 status: ACTIVE user_name_mapping: ${source.login} created: 2025-10-31T06:08:00+00:00 last_updated: 2025-10-31T06:08:01+00:00 ``` ### Google Workspace ```yaml theme={null} id: 0oax4r57x0V5NHL2W697 name: google afw_only: false display_name: Google Workspace domain: contoso.com features: [] has_role_assignments: false okta_domain: contoso.okta.com sign_on_mode: SAML_2_0 status: ACTIVE user_name_mapping: ${source.login} created: 2025-11-05T09:06:48+00:00 last_updated: 2025-11-05T09:07:21+00:00 ``` ### Jamf Pro SAML ```yaml theme={null} id: 0oax4r3ud0J2WjlNh697 name: jamfsoftwareserver display_name: Jamf Pro SAML domain: contoso.jamfcloud.com features: [] has_role_assignments: false name: Jamf Pro SAML okta_domain: contoso.okta.com sign_on_mode: SAML_2_0 status: ACTIVE user_name_mapping: ${source.login} created: 2025-11-05T09:10:52+00:00 last_updated: 2026-01-19T14:33:39+00:00 ``` ### OktaHound ```yaml theme={null} id: 0oaw0pujq5WtBiMYD697 name: oidc_client client_type: service display_name: OktaHound features: [] grant_types: - client_credentials has_role_assignments: true oauth_scopes: - okta.trustedOrigins.read - okta.policies.read - okta.linkedObjects.read - okta.authModes.read - okta.templates.read - okta.apiTokens.read - okta.factors.read - okta.brands.read - okta.authenticators.read - okta.uischemas.read - okta.logs.read - okta.groups.read - okta.identitySources.read - okta.users.read - okta.orgs.read - okta.threatInsights.read - okta.pushProviders.read - okta.apps.read - ssf.read - okta.roles.read - okta.networkZones.read - okta.emailDomains.read - okta.manifests.read - okta.oauthIntegrations.read - okta.domains.read - okta.deviceAssurance.read - okta.reports.read - okta.authorizationServers.read - okta.enduser.read - okta.schemas.read - okta.idps.read - okta.agentPools.read - okta.appGrants.read - okta.inlineHooks.read - okta.certificateAuthorities.read - okta.devices.read - okta.behaviors.read - okta.profileMappings.read - okta.captchas.read - okta.clients.read - okta.features.read - okta.sessions.read - okta.userTypes.read okta_domain: integrator-5415459.okta.com sign_on_mode: OPENID_CONNECT status: ACTIVE user_name_mapping: ${source.login} created: 2025-10-02T10:11:20+00:00 last_updated: 2025-10-02T10:26:27+00:00 ``` ### Active Directory Integration ```yaml theme={null} id: 0oaxg9rhdd7ncGCXv697 name: active_directory display_name: contoso.local domain_sid: S-1-5-21-71365889-924527929-2677699343 features: - IMPORT_PROFILE_UPDATES - PROFILE_MASTERING - OUTBOUND_DEL_AUTH - IMPORT_USER_SCHEMA - IMPORT_NEW_USERS filter_groups_by_ou: false has_role_assignments: false naming_context: contoso.local okta_domain: contoso.okta.com status: ACTIVE created: 2025-11-14T12:50:42+00:00 last_updated: 2026-01-31T15:12:24+00:00 ``` ## User Name Mapping User name mapping from Okta to SAML 2.0, OpenID Connect (OIDC), and Secure Web Authentication (SWA) applications is configurable in the Okta Admin Console, with the default setting being the Okta username pass-through, i.e., `${source.login}`. | Application username format | Mapping template | | ----------------------------- | ----------------------------------------------------------- | | Okta username | `${source.login}` | | Email | `${source.email}` | | Okta username prefix | `${fn:substringBefore(source.login, "@")}` | | Email prefix | `${fn:substringBefore(source.email, "@")}` | | AD Employee ID | `${source.employeeID}` | | AD SAM account name | `${source.samAccountName}` | | AD SAM account name + domain | `${source.samAccountName}@${source.instance.namingContext}` | | AD user principal name | `${source.userName}` | | AD user principal name prefix | `${fn:substringBefore(source.userName, "@")}` | | (None) | `NONE` | | Custom | ? | ## API Service Applications This application type is the most interesting one from the security perspective, as it represents OAuth 2.0 service (daemon) applications that can be granted machine-to-machine access to Okta APIs, without any user interaction. These applications can be assigned administrative roles, e.g., Super Admin, and OAuth 2.0 scope grants, e.g., `okta.users.manage`. Any API operation must be allowed by both the assigned roles and the granted scopes. Okta Application scopes and roles in BloodHound ## Hybrid Edges For supported systems like Active Directory, GitHub Enterprise Cloud, or Jamf Pro, hybrid edges in BloodHound to represent the relationships between these external systems and Okta. ```mermaid theme={null} graph TB subgraph ad["Active Directory"] direction LR domain("Domain contoso.com") adu1("User john\@contoso.com") adu2("User steve\@contoso.com") adg1("Group IT") domain -- Contains --> adu1 domain -- Contains --> adu2 domain -- Contains --> adg1 adu1 -- MemberOf --> adg1 end subgraph okta["Okta"] direction LR org("Okta_Organization contoso.okta.com") u1("Okta_User john\@contoso.com") u2("Okta_User steve\@contoso.com") g1("Okta_Group IT") gha("Okta_Application GitHub Enterprise Cloud") jmfa("Okta_Application Jamf Pro SAML") org -- Okta_Contains --> u1 org -- Okta_Contains --> u2 org -- Okta_Contains --> g1 u1 -- Okta_MemberOf --> g1 u2 -- Okta_AppAdmin --> gha g1 -. Okta_AppAssignment .-> gha u1 -. Okta_AppAssignment .-> jmfa end subgraph gh["GitHub Enterprise Cloud"] direction LR ghorg("GH_Organization Contoso") ghu1("GH_User john\@contoso.com") ghorg -- GH_Contains --> ghu1 end subgraph jamf["Jamf Pro Cloud"] direction LR jamft("jamf_SSOIntegration contoso.jamfcloud.com-SSO") jmfu1("jamf_Account john\@contoso.com") end adu1 -. Okta_UserSync .-> u1 adu2 -. Okta_UserSync .-> u2 adg1 -- Okta_MembershipSync --> g1 gha -- Okta_OutboundOrgSSO --> ghorg jmfa -- Okta_OutboundOrgSSO --> jamft u1 -- Okta_OutboundSSO --> ghu1 u1 -- Okta_OutboundSSO --> jmfu1 ``` ### Active Directory Synchronization When Okta's Active Directory (AD) integration is configured for user and group synchronization, the connected AD domain is represented as an `Okta_Application` node in BloodHound. This allows you to visualize the AD-backed application alongside other applications in your Okta environment and understand its relationships with users, groups, and roles. The synchronization is performed by domain-joined servers with the Okta AD Agent installed. This agent typically has Domain Admin privileges in the connected AD domain to perform user and group enumeration and synchronization, making it a high-value target for attackers. Okta AD agent settings Authentication can be delegated from Okta to AD in multiple ways: * [Agentless Desktop SSO](https://help.okta.com/oie/en-us/content/topics/directory/ad-dsso-about-workflow.htm) * [Password Synchronization](https://help.okta.com/oie/en-us/content/topics/directory/installing_configuring_active_directory_password_sync_agent.htm) * Active Directory Federation Services (ADFS) integration with Okta as a SAML IdP There is no documented API available to determine the authentication delegation method(s) configured for an AD-backed Okta application. The collector therefore performs some heuristics that might not be 100% accurate in all cases. ### GitHub Enterprise Cloud Organizations When integrating Okta with GitHub Enterprise Cloud, each GitHub organization connected to Okta is represented as a separate `Okta_Application` node in BloodHound. Properties of the GitHub Application node ### Jamf Pro When integrating Okta with Jamf Pro using SAML 2.0, each Jamf Pro instance connected to Okta is represented as a separate `Okta_Application` node in BloodHound. The differentiator is the `domain_fqdn` property: Jamf Pro SAML application in BloodHound It is also possible to integrate Jamf Pro with Okta using Secure Web Authentication (SWA), but this option is less secure. Jamf Pro SWA settings ## Google Workspace Similarly to the Jamf Pro SAML applications, each Google Workspace (formerly G Suite) instance connected to Okta using SAML 2.0 is represented as a separate `Okta_Application` node in BloodHound and is identified by the `domain_fqdn` property: Google Workspace SAML application in BloodHound The SAML 2.0 protocol should always be preferred to SWA when integrating Okta with Google Workspace: Google Workspace sign-in protocol settings ## Generic SAML 2.0 Applications The assertion consumer service (ACS) URLs of generic (non-Catalog) Okta SAML 2.0 applications are exposed via the `url` attribute in BloodHound. Okta SAML application in BloodHound ## Generic Secure Web Authentication (SWA) Applications Secure Web Authentication (SWA) is an Okta technology that provides Single Sign-On (SSO) functionality to external web applications that don't support federated protocols. SWA applications store user credentials in Okta and automatically fill them in when users access the application through the Okta dashboard. The app's login page URL is exposed via the `url` attribute in BloodHound. Okta SWA application in BloodHound ## Generic OpenID Connect (OIDC) Applications Okta supports three types of OIDC applications: * Web Application * Single-Page Application (SPA) * Native Application The default redirect URI of generic (non-Catalog) Okta OIDC single-page applications (SPAs) starts with `http://localhost:8080/`, making it hard to identify the actual application address. The optional Okta-initiated sign-in flow URL is therefore exposed in the `url` attribute in BloodHound instead, if configured. OIDC applications can be granted OAuth 2.0 scopes to access Okta APIs on behalf of users: Okta application OIDC grants ## SCIM-Enabled Applications The `features` attribute of `Okta_Application` nodes may contain the following SCIM-related values, indicating if SCIM is enabled and which protocol capabilities are supported: | Feature | Description | | ------------------------------- | -------------------------------------------------------------------------------------------------------- | | PUSH\_NEW\_USERS | Supports pushing new users from Okta to the application | | PUSH\_PASSWORD\_UPDATES | Supports pushing password updates from Okta to the application | | PUSH\_PENDING\_USERS | Supports pushing users from Okta to the application in pending state | | PUSH\_PROFILE\_UPDATES | Supports pushing profile updates from Okta to the application | | PUSH\_USER\_DEACTIVATION | Supports pushing user deactivation from Okta to the application | | REACTIVATE\_USERS | Supports reactivating users in the application from Okta | | IMPORT\_NEW\_USERS | Supports importing new users into Okta from the application | | OPP\_SCIM\_INCREMENTAL\_IMPORTS | Supports incremental imports of users from the application into Okta | | IMPORT\_PROFILE\_UPDATES | Updates a linked user's app profile in Okta during manual or scheduled imports | | GROUP\_PUSH | Supports pushing groups and group memberships from Okta to the application | | PROFILE\_MASTERING | Supports profile mastering in Okta, allowing the application to be the source of truth for user profiles | # Okta_AuthorizationServer Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_authorizationserver An authorization server in Okta Applies to BloodHound Enterprise and CE ## Overview Authorization servers in Okta are used to issue OAuth 2.0 access tokens for API access. They define the scopes, claims, and access policies that control how tokens are issued and what permissions they grant. Each Okta organization has a default authorization server, and administrators can create additional custom authorization servers for specific use cases. Authorization servers are represented as Okta\_AuthorizationServer nodes in BloodHound. The relationships between authorization servers and applications are currently not evaluated in BloodHound. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ----------- | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_ResourceSetContains](/opengraph/extensions/okta/edges/okta_resourcesetcontains) | [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset) | ✅ | | [Okta\_ScopedTo](/opengraph/extensions/okta/edges/okta_scopedto) | [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) | ❌ | ### Outbound Edges No outbound edges are defined by the Okta extension for this node. ## Properties | Name | Source | Type | Description | | -------------- | --------------------------- | ---------- | --------------------------------------------------------------- | | `id` | `server.id` | `string` | Unique authorization server identifier. | | `name` | `server.name` | `string` | Authorization server name. | | `display_name` | `server.name` | `string` | Display label used in BloodHound. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the authorization server exists. | | `description` | `server.description` | `string` | Human-readable server description. | | `status` | `server.status` | `string` | Current lifecycle status. | | `issuer` | `server.issuer` | `string` | Token issuer URL. | | `issuer_mode` | `server.issuerMode` | `string` | Issuer mode selected in Okta. | | `audiences` | `server.audiences` | `string[]` | Allowed audience values for issued tokens. | | `created` | `server.created` | `datetime` | Authorization server creation timestamp. | | `last_updated` | `server.lastUpdated` | `datetime` | Last update timestamp for the server configuration. | ## Sample Property Values ```yaml theme={null} id: ausz6ipkn4u0hDzyf697 name: app creation display_name: app creation okta_domain: contoso.okta.com status: INACTIVE issuer: https://contoso.okta.com/oauth2/ausz6ipkn4u0hDzyf697 issuer_mode: DYNAMIC audiences: - test created: 2026-01-14T15:41:28+00:00 last_updated: 2026-01-14T16:09:30+00:00 ``` # Okta_ClientSecret Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_clientsecret A secret used by applications to authenticate to the Okta API Applies to BloodHound Enterprise and CE ## Overview Client secrets are used by API service integrations and OIDC applications to authenticate with Okta and obtain access tokens. Okta client secret creation An application can have up to two client secrets configured, to allow for secret rotation. Okta client secret rotation Client secrets are represented as Okta\_ClientSecret nodes in BloodHound. For security reasons, the OpenHound and OktaHound collectors do not collect client secrets, only their hashed identifiers. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_ReadClientSecret](/opengraph/extensions/okta/edges/okta_readclientsecret) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [Okta\_SecretOf](/opengraph/extensions/okta/edges/okta_secretof) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) | ✅ | ## Properties | Name | Source | Type | Description | | -------------- | --------------------------- | ---------- | -------------------------------------------------------- | | `id` | `secret.id` | `string` | Unique client secret identifier. | | `name` | `secret.secretHash` | `string` | Hash of the secret value used as name/display label. | | `display_name` | `secret.secretHash` | `string` | Display label used in BloodHound. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the client secret exists. | | `status` | `secret.status` | `string` | Current lifecycle status of the secret. | | `created` | `secret.created` | `datetime` | Secret creation timestamp. | | `last_updated` | `secret.lastUpdated` | `datetime` | Last update timestamp for the secret metadata. | ## Sample Property Values ```yaml theme={null} id: ocsxqwizfyqsf0aVG697 name: T1e6fl4jGqvPkgd94NKx5g display_name: T1e6fl4jGqvPkgd94NKx5g okta_domain: contoso.okta.com status: ACTIVE created: 2025-11-24T12:24:08.000Z last_updated: 2025-11-24T12:24:08.000Z ``` For security reasons, the OktaHound collector does not write cleartext client secrets to the OpenGraph JSON, only their hashed identifiers. # Okta_CustomRole Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_customrole A custom role in Okta created by an administrator Applies to BloodHound Enterprise and CE ## Overview Custom roles can be created with specific [permissions](https://developer.okta.com/docs/api/openapi/okta-management/guides/permissions/) and then assigned to [users](/opengraph/extensions/okta/nodes/okta_user), [groups](/opengraph/extensions/okta/nodes/okta_group), and [applications](/opengraph/extensions/okta/nodes/okta_application) over [resource sets](/opengraph/extensions/okta/nodes/okta_resourceset). [Complex conditions](https://help.okta.com/oie/en-us/content/topics/security/custom-admin-role/permission-conditions.htm) can be used if the custom admin role has one of the following permissions: * okta.users.read * okta.users.manage * okta.users.create Custom roles are represented as Okta\_CustomRole and [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) nodes, similar to built-in roles. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_HasRole](/opengraph/extensions/okta/edges/okta_hasrole) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ❌ | ### Outbound Edges No outbound edges are defined by the Okta extension for this node. ## Properties | Name | Source | Type | Description | | -------------- | --------------------------- | ---------- | ------------------------------------------------------------ | | `id` | `role.id` | `string` | Unique custom role identifier. | | `name` | `role.label` | `string` | Name of the custom role. | | `display_name` | `role.label` | `string` | Display label used in BloodHound. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the custom role exists. | | `permissions` | `role.permissions` | `string[]` | Effective permission labels associated with the custom role. | | `created` | `role.created` | `datetime` | Custom role creation timestamp. | | `last_updated` | `role.lastUpdated` | `datetime` | Last update timestamp of the role definition. | ## Sample Property Values ```yaml theme={null} id: cr0wwdjuk0w96MpFr697 name: IAM Readers display_name: IAM Readers okta_domain: contoso.okta.com created: 2025-10-29T12:45:55+00:00 last_updated: 2025-10-30T13:35:36+00:00 permissions: - okta.iam.read ``` ## Abusable Permissions of Custom Roles in Okta The following Okta permissions are particularly interesting from an offensive security perspective, as they can be abused to escalate privileges in hybrid scenarios: * okta.users.manage * okta.users.credentials.manage * okta.users.credentials.resetFactors * okta.users.credentials.resetPassword * okta.users.credentials.expirePassword * okta.users.credentials.manageTemporaryAccessCode * okta.groups.manage * okta.groups.members.manage * okta.apps.manage * okta.apps.clientCredentials.read The research on abusable Okta permissions is still ongoing. # Okta_Device Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_device A device registered in Okta, such as a mobile phone or a computer Applies to BloodHound Enterprise and CE ## Overview Devices in Okta represent the physical or virtual devices that users use to authenticate and access the Okta organization. Devices can optionally be managed by 3rd party MDM solutions, which allow administrators to enforce security compliance policies. Devices are represented as Okta\_Device nodes in BloodHound. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_MobileAdmin](/opengraph/extensions/okta/edges/okta_mobileadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_OrgAdmin](/opengraph/extensions/okta/edges/okta_orgadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_ResourceSetContains](/opengraph/extensions/okta/edges/okta_resourcesetcontains) | [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset) | ✅ | | [Okta\_ScopedTo](/opengraph/extensions/okta/edges/okta_scopedto) | [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------- | -------------------------------------------------------- | ----------- | | [Okta\_DeviceOf](/opengraph/extensions/okta/edges/okta_deviceof) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ❌ | ## Properties | Name | Source | Type | Description | | ------------------------- | ------------------------------------------------ | ---------- | ------------------------------------------------------------------- | | `id` | `device.uuid + "@" + okta_domain` or `device.id` | `string` | Unique device identifier (derived from hardware ID + domain). | | `name` | `device.resourceDisplayName` | `string` | Device display name from Okta. | | `display_name` | `device.resourceDisplayName` | `string` | Display label used in BloodHound. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the device exists. | | `okta_id` | `device.id` | `string` | Original Okta device identifier (stored for reference). | | `created` | `device.created` | `datetime` | Device record creation timestamp. | | `last_updated` | `device.lastUpdated` | `datetime` | Last update timestamp. | | `status` | `device.status` | `string` | Device lifecycle/status value. | | `resource_type` | `device.resourceType` | `string` | Okta device resource type. | | `platform` | `device.profile.platform` | `string` | Device platform/OS family. | | `manufacturer` | `device.profile.manufacturer` | `string` | Hardware vendor/manufacturer. | | `model` | `device.profile.model` | `string` | Device model name. | | `os_version` | `device.profile.osVersion` | `string` | Operating system version. | | `registered` | `device.profile.registered` | `bool` | Whether the device is registered in Okta. | | `secure_hardware_present` | `device.profile.secureHardwarePresent` | `bool` | Indicates secure hardware support (for example Secure Enclave/TPM). | | `jail_break` | `device.profile.integrityJailbreak` | `bool` | Device jailbreak/root integrity signal. | | `udid` | `device.profile.udid` | `string` | Apple UDID for iOS devices. | | `object_sid` | `device.profile.sid` | `string` | SID attribute for Windows/AD-linked devices. | | `serial_number` | `device.profile.serialNumber` | `string` | Device serial number, when provided and non-empty. | ## Sample Property Values Windows device: ```yaml theme={null} id: 4C4C4544-0057-4C10-8057-C8C04F573934@contoso.okta.com name: PC01 display_name: PC01 okta_domain: contoso.okta.com okta_id: guoxrzqh8jBxYxEeJ697 created: 2025-11-25T11:01:53+00:00 last_updated: 2026-02-17T08:55:45+00:00 status: ACTIVE resource_type: UDDevice platform: WINDOWS manufacturer: Dell Inc. model: XPS 14 9440 os_version: 10.0.26200.7623 registered: true secure_hardware_present: true jail_break: false udid: 4C4C4544-0057-4C10-8057-C8C04F573934 object_sid: S-1-5-21-1084505731-826279434-3585917670 serial_number: HWLWW94 ``` iOS device: ```yaml theme={null} id: guowq18eyhZaDlkkA697 name: John's iPhone display_name: John's iPhone okta_domain: contoso.okta.com okta_id: guowq18eyhZaDlkkA697 status: ACTIVE resource_type: UDDevice platform: IOS manufacturer: APPLE model: iPhone17,1 os_version: 18.6.2 registered: true secure_hardware_present: true jail_break: false created: 2025-10-23T17:16:46+00:00 last_updated: 2025-10-23T17:16:47+00:00 ``` # Okta_Group Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_group An Okta user group Applies to BloodHound Enterprise and CE ## Overview Groups in Okta are collections of users that can be used to manage access to applications and resources. Groups can be created manually or synchronized from external directories such as Active Directory. The built-in **Everyone** group always contains all users in the Okta organization. Only users can be members of groups and groups cannot be nested. Groups are represented as Okta\_Group nodes in BloodHound. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_AddMember](/opengraph/extensions/okta/edges/okta_addmember) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_GroupAdmin](/opengraph/extensions/okta/edges/okta_groupadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_GroupMembershipAdmin](/opengraph/extensions/okta/edges/okta_groupmembershipadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_GroupPull](/opengraph/extensions/okta/edges/okta_grouppull) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_IdpGroupAssignment](/opengraph/extensions/okta/edges/okta_idpgroupassignment) | [Okta\_IdentityProvider](/opengraph/extensions/okta/nodes/okta_identityprovider) | ❌ | | [Okta\_MemberOf](/opengraph/extensions/okta/edges/okta_memberof) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_MembershipSync](/opengraph/extensions/okta/edges/okta_membershipsync) | [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Group](/resources/nodes/group), [AZGroup](/resources/nodes/az-group) | ✅ | | [Okta\_OrgAdmin](/opengraph/extensions/okta/edges/okta_orgadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_ResourceSetContains](/opengraph/extensions/okta/edges/okta_resourcesetcontains) | [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset) | ✅ | | [Okta\_ScopedTo](/opengraph/extensions/okta/edges/okta_scopedto) | [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_AddMember](/opengraph/extensions/okta/edges/okta_addmember) | [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ✅ | | [Okta\_AppAdmin](/opengraph/extensions/okta/edges/okta_appadmin) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) | ✅ | | [Okta\_AppAssignment](/opengraph/extensions/okta/edges/okta_appassignment) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ❌ | | [Okta\_GroupAdmin](/opengraph/extensions/okta/edges/okta_groupadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ✅ | | [Okta\_GroupMembershipAdmin](/opengraph/extensions/okta/edges/okta_groupmembershipadmin) | [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ✅ | | [Okta\_GroupPush](/opengraph/extensions/okta/edges/okta_grouppush) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ❌ | | [Okta\_HasRole](/opengraph/extensions/okta/edges/okta_hasrole) | [Okta\_Role](/opengraph/extensions/okta/nodes/okta_role), [Okta\_CustomRole](/opengraph/extensions/okta/nodes/okta_customrole) | ❌ | | [Okta\_HasRoleAssignment](/opengraph/extensions/okta/edges/okta_hasroleassignment) | [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) | ❌ | | [Okta\_HelpDeskAdmin](/opengraph/extensions/okta/edges/okta_helpdeskadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_ManageApp](/opengraph/extensions/okta/edges/okta_manageapp) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_MembershipSync](/opengraph/extensions/okta/edges/okta_membershipsync) | [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Group](/resources/nodes/group), [AZGroup](/resources/nodes/az-group) | ✅ | | [Okta\_MobileAdmin](/opengraph/extensions/okta/edges/okta_mobileadmin) | [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device) | ✅ | | [Okta\_OrgAdmin](/opengraph/extensions/okta/edges/okta_orgadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device) | ✅ | | [Okta\_ReadClientSecret](/opengraph/extensions/okta/edges/okta_readclientsecret) | [Okta\_ClientSecret](/opengraph/extensions/okta/nodes/okta_clientsecret) | ✅ | | [Okta\_ResetFactors](/opengraph/extensions/okta/edges/okta_resetfactors) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_ResetPassword](/opengraph/extensions/okta/edges/okta_resetpassword) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_SuperAdmin](/opengraph/extensions/okta/edges/okta_superadmin) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | ## Properties Standard Okta group properties: | Name | Source | Type | Description | | ------------------------- | ----------------------------- | ---------- | ----------------------------------------------------------------- | | `id` | `group.id` | `string` | Unique group identifier. | | `name` | `group.profile.name` | `string` | Group name in Okta (or synchronized source). | | `display_name` | `group.profile.name` | `string` | Display label used in BloodHound. | | `description` | `group.profile.description` | `string` | Group description text. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the group exists. | | `has_role_assignments` | Calculated | `bool` | Indicates whether the group is assigned any administrative roles. | | `okta_group_type` | `group.type` | `string` | Group type (for example `OKTA_GROUP`, `APP_GROUP`, `BUILT_IN`). | | `object_class` | `group.objectClass[0]` | `string` | Source object class (for example AD security principal). | | `created` | `group.created` | `datetime` | Group creation timestamp. | | `last_updated` | `group.lastUpdated` | `datetime` | Last update timestamp. | | `last_membership_updated` | `group.lastMembershipUpdated` | `datetime` | Last membership change timestamp. | Additional properties of groups synchronized from Active Directory: | Name | Source | Type | Description | | ----------------------- | ------------------------------------------ | -------- | ------------------------------------------------------------ | | `object_sid` | `group.profile.objectSid` | `string` | Security Identifier (SID) for the AD group. | | `distinguished_name` | `group.profile.dn` | `string` | Active Directory distinguished name. | | `sam_account_name` | `group.profile.samAccountName` | `string` | Security Account Manager (SAM) account name. | | `domain_qualified_name` | `group.profile.windowsDomainQualifiedName` | `string` | Domain-qualified name of the AD group. | | `group_scope` | `group.profile.groupScope` | `string` | AD group scope (for example global, domainLocal, universal). | | `group_type` | `group.profile.groupType` | `string` | AD group type, i.e., security or distribution. | | `object_guid` | `Base64ToGuid(group.profile.externalId)` | `string` | AD object GUID. | ## Sample Property Values Example of a group created directly in Okta: ```yaml theme={null} id: 00gxg12p4kFOkyXLb697 name: Engineering display_name: Engineering description: Engineering department group okta_domain: contoso.okta.com has_role_assignments: false okta_group_type: OKTA_GROUP object_class: okta:user_group created: 2025-11-14T08:00:25+00:00 last_updated: 2025-11-14T08:00:25+00:00 last_membership_updated: 2025-11-14T08:00:25+00:00 ``` Example of a group synchronized from Active Directory: ```yaml theme={null} id: 00gxga7s3yDJ71OzW697 name: Sales display_name: Sales description: Sales department group okta_domain: contoso.okta.com has_role_assignments: false okta_group_type: APP_GROUP object_class: okta:windows_security_principal object_sid: S-1-5-21-71365889-924527929-2677699343-2536 distinguished_name: CN=Sales,CN=Groups,DC=contoso,DC=local sam_account_name: Sales domain_qualified_name: CONTOSO\Sales group_scope: Global group_type: Security object_guid: 4ab65ef0-ab82-4017-b5ee-1c20facd4d6a created: 2025-11-14T12:58:13+00:00 last_updated: 2025-11-14T13:05:44+00:00 last_membership_updated: 2025-11-14T12:58:13+00:00 ``` ## Synchronization with External Directories Similarly to users, groups can also be synchronized from external directories. The Okta API exposes the original Active Directory attributes: Group synchronized from AD Nested (transitive) group memberships in Active Directory are always flattened (resolved) when synchronized to Okta, as illustrated below: ```mermaid theme={null} graph TB subgraph ad["Active Directory"] ag1("Group A") ag2("Group B") u1("User 1") u2("User 2") u1 -- MemberOf --> ag1 u2 -- MemberOf --> ag2 ag2 -- MemberOf --> ag1 end subgraph Okta og1("Okta_Group A") og2("Okta_Group B") u1o("Okta_User 1") u2o("Okta_User 2") u1o -- Okta_MemberOf --> og1 u2o -- Okta_MemberOf --> og1 u2o -- Okta_MemberOf --> og2 end ad == Sync ==> Okta ``` # Okta_IdentityProvider Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_identityprovider An identity provider trusted by Okta for authentication Applies to BloodHound Enterprise and CE ## Overview Identity Providers (IdPs) in Okta represent external authentication sources that can be used to authenticate users. These can include social identity providers (such as Google, Facebook, or Microsoft), enterprise identity providers using SAML or OIDC, or other Okta organizations in an Org2Org configuration. When users authenticate through an external identity provider, Okta can optionally create or link user accounts, enabling federated authentication across multiple systems. Identity providers are represented as Okta\_IdentityProvider nodes in BloodHound. The inbound identity provider routing rules and JIT (Just-In-Time) provisioning settings are currently not evaluated. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ----------- | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_InboundOrgSSO](/opengraph/extensions/okta/edges/okta_inboundorgsso) | [AZTenant](/resources/nodes/az-tenant) | ✅ | | [Okta\_OutboundOrgSSO](/opengraph/extensions/okta/edges/okta_outboundorgsso) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_ResourceSetContains](/opengraph/extensions/okta/edges/okta_resourcesetcontains) | [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | -------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ----------- | | [Okta\_IdentityProviderFor](/opengraph/extensions/okta/edges/okta_identityproviderfor) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_IdpGroupAssignment](/opengraph/extensions/okta/edges/okta_idpgroupassignment) | [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ❌ | ## Properties These properties are common for all identity provider types: | Name | Source | Type | Description | | ------------------------ | ------------------------------------------ | ---------- | ------------------------------------------------------------ | | `id` | `idp.id` | `string` | Unique identity provider identifier. | | `name` | `idp.name` | `string` | Identity provider name. | | `display_name` | `idp.name` | `string` | Display label used in BloodHound. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the identity provider exists. | | `issuer_mode` | `idp.issuerMode` | `string` | Issuer mode for the identity provider. | | `type` | `idp.type` | `string` | Identity provider category/type. | | `enabled` | `idp.status == "ACTIVE"` | `bool` | Whether the IdP is active/enabled. | | `auto_user_provisioning` | `idp.policy.provisioning.action == "AUTO"` | `bool` | Whether automatic user provisioning is enabled. | | `governed_group_ids` | `idp.policy.provisioning.groups` | `string[]` | Group IDs governed by this IdP provisioning policy. | | `protocol_type` | `idp.protocol.*.type[0]` | `string` | Protocol configured for authentication through this IdP. | | `url` | `idp.protocol.*.endpoints.*.url[0]` | `string` | Primary authorization/SSO endpoint URL for the IdP. | | `created` | `idp.created` | `datetime` | IdP creation timestamp. | Additional properties are provider-specific: | Name | Source | Type | Description | | ----------------- | ------------------------------- | -------- | --------------------------------------------- | | `entra_tenant_id` | `TenantIdFromSamlEndpoint(url)` | `string` | Associated Entra tenant ID when identifiable. | ## Sample Property Values ```yaml theme={null} id: 0oazpi53t1cRNcPL4697 name: Microsoft Entra ID display_name: Microsoft Entra ID okta_domain: contoso.okta.com created: 2026-01-31T15:21:37+00:00 issuer_mode: DYNAMIC type: MICROSOFT enabled: false auto_user_provisioning: true governed_group_ids: [] protocol_type: OIDC url: https://login.microsoftonline.com/common/oauth2/v2.0/authorize ``` # Okta_JWK Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_jwk An Okta JSON Web Key Applies to BloodHound Enterprise and CE ## Overview JSON Web Keys (JWKs) are used by OAuth 2.0 client applications to authenticate with Okta using the `private_key_jwt` client authentication method. This is an asymmetric authentication mechanism where the application possesses a private key and Okta stores the corresponding public key. A service application can have multiple JWKs configured for key rotation purposes. JWKs are represented as Okta\_JWK nodes in BloodHound. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges No inbound edges are defined by the Okta extension for this node. ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------- | ---------------------------------------------------------------------- | ----------- | | [Okta\_KeyOf](/opengraph/extensions/okta/edges/okta_keyof) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | ## Properties | Name | Source | Type | Description | | -------------- | ----------------------------- | ---------- | ---------------------------------------------- | | `id` | `jwk.id` | `string` | Unique JSON Web Key identifier. | | `name` | `jwk.kid` (fallback `jwk.id`) | `string` | Key identifier used as node name. | | `display_name` | `jwk.kid` (fallback `jwk.id`) | `string` | Display label used in BloodHound. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the key exists. | | `status` | `jwk.status` | `string` | Current lifecycle status of the key. | | `kid` | `jwk.kid` | `string` | JSON Web Key identifier (`kid`). | | `kty` | `jwk.kty` | `string` | Key type (`RSA`, `EC`, ...). | | `use` | `jwk.use` | `string` | Intended key usage (`sig`, `enc`). | | `created` | `jwk.created` | `datetime` | Key creation timestamp. | | `last_updated` | `jwk.lastUpdated` | `datetime` | Last update timestamp. | ## Sample Property Values ```yaml theme={null} id: pksw0py294dQ80EdI697 name: ncxmNARybDrxlemwkrvyphCYQ2VwMG9cxV95jgVziZ4 display_name: ncxmNARybDrxlemwkrvyphCYQ2VwMG9cxV95jgVziZ4 okta_domain: contoso.okta.com status: ACTIVE kid: ncxmNARybDrxlemwkrvyphCYQ2VwMG9cxV95jgVziZ4 kty: RSA use: sig created: 2025-10-02T10:14:44Z last_updated: 2025-10-02T10:26:27Z ``` # Okta_Organization Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_organization An Okta organization Applies to BloodHound Enterprise and CE ## Overview The Organization entity represents the Okta tenant itself. It contains general information about the organization, such as its name, domain, and settings. The organization is represented as a single Okta\_Organization node in BloodHound. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_ScopedTo](/opengraph/extensions/okta/edges/okta_scopedto) | [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) | ❌ | | [Okta\_SuperAdmin](/opengraph/extensions/okta/edges/okta_superadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration), [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset), [Okta\_Role](/opengraph/extensions/okta/nodes/okta_role), [Okta\_CustomRole](/opengraph/extensions/okta/nodes/okta_customrole), [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment), [Okta\_Realm](/opengraph/extensions/okta/nodes/okta_realm), [Okta\_AgentPool](/opengraph/extensions/okta/nodes/okta_agentpool), [Okta\_IdentityProvider](/opengraph/extensions/okta/nodes/okta_identityprovider), [Okta\_AuthorizationServer](/opengraph/extensions/okta/nodes/okta_authorizationserver), [Okta\_Policy](/opengraph/extensions/okta/nodes/okta_policy) | ✅ | ## Properties | Name | Source | Type | Description | | -------------- | --------------------------- | ---------- | -------------------------------------------- | | `id` | `settings.id` | `string` | Unique organization identifier. | | `name` | `okta_domain` | `string` | Okta organization domain name. | | `display_name` | `settings.companyName` | `string` | Organization/company display name. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain name. | | `subdomain` | `settings.subdomain` | `string` | Okta subdomain value. | | `status` | `settings.status` | `string` | Organization lifecycle status. | | `created` | `settings.created` | `datetime` | Organization creation timestamp. | | `last_updated` | `settings.lastUpdated` | `datetime` | Last organization metadata update timestamp. | ## Sample Property Values ```yaml theme={null} id: 00ow0o8if0CNwsKmk697 name: contoso.okta.com display_name: Contoso okta_domain: contoso.okta.com subdomain: contoso status: ACTIVE created: 2025-10-02T09:21:31+00:00 last_updated: 2025-12-09T23:04:15+00:00 ``` # Okta_Policy Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_policy A policy defining rules for authentication, password, or other features in Okta Applies to BloodHound Enterprise and CE ## Overview Policies in Okta define the rules and conditions that govern authentication, authorization, and security behaviors within an organization. They control aspects such as password requirements, MFA enrollment, session management, and application access. Policies are represented as Okta\_Policy nodes in BloodHound. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ----------- | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_ResourceSetContains](/opengraph/extensions/okta/edges/okta_resourcesetcontains) | [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | -------------------------------------------------------------------------- | ---------------------------------------------------------------------- | ----------- | | [Okta\_PolicyMapping](/opengraph/extensions/okta/edges/okta_policymapping) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ❌ | ## Properties | Name | Source | Type | Description | | -------------- | --------------------------- | ---------- | ------------------------------------------------------------------------------------------- | | `id` | `policy.id` | `string` | Unique policy identifier. | | `name` | `policy.name` | `string` | Policy name. | | `display_name` | `policy.name` | `string` | Display-friendly policy name. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the policy exists. | | `description` | `policy.description` | `string` | Policy description text. | | `type` | `policy.type` | `string` | Policy type identifier (for example `OKTA_SIGN_ON`, `ACCESS_POLICY`, `PROFILE_ENROLLMENT`). | | `priority` | `policy.priority` | `integer` | Policy evaluation order priority. | | `system` | `policy.system` | `bool` | Indicates whether the policy is system-managed. | | `created` | `policy.created` | `datetime` | Policy creation timestamp. | ## Sample Property Values ```yaml theme={null} id: rstw0o8il8ktUxo3t697 name: Okta Account Management Policy display_name: Okta Account Management Policy okta_domain: contoso.okta.com description: This policy defines how users must authenticate for authenticator enrollment, password reset, or unlock account. Password policy rules control whether to enforce this policy for password reset and unlock account. type: ACCESS_POLICY priority: 1 system: false created: 2025-10-02T09:21:37+00:00 ``` ## Policy Types The following [policy types](https://developer.okta.com/docs/api/openapi/okta-management/management/tag/Policy/) are supported by Okta: | Policy Type ID | Description | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | OKTA\_SIGN\_ON | [Global session policies](https://help.okta.com/oie/en-us/content/topics/identity-engine/policies/about-okta-sign-on-policies.htm) | | PASSWORD | [Password policies](https://help.okta.com/en-us/content/topics/security/policies/about-password-policies.htm) | | MFA\_ENROLL | [Authenticator enrollment policies](https://help.okta.com/en-us/content/topics/security/policies/configure-mfa-policies.htm) | | IDP\_DISCOVERY | [Identity Provider routing rules](https://help.okta.com/en-us/content/topics/security/identity_provider_discovery.htm) | | ACCESS\_POLICY | [App sign-in policies](https://help.okta.com/oie/en-us/content/topics/identity-engine/policies/about-app-sign-on-policies.htm) | | DEVICE\_SIGNAL\_COLLECTION | [Device signal collection policies](https://help.okta.com/oie/en-us/content/topics/identity-engine/policies/create-device-signal-collection-ruleset.htm) | | PROFILE\_ENROLLMENT | [User profile policies](https://help.okta.com/oie/en-us/content/topics/identity-engine/policies/create-profile-enrollment-policy.htm) | | POST\_AUTH\_SESSION | [Identity Threat Protection policies](https://help.okta.com/oie/en-us/content/topics/itp/overview.htm) | | ENTITY\_RISK | [Entity risk policies](https://help.okta.com/oie/en-us/content/topics/itp/entity-risk-policy.htm) | The collector specifically reads the `IDP_DISCOVERY` policies to check if the [Agentless Desktop SSO](https://help.okta.com/en-us/content/topics/directory/configuring_agentless_sso.htm) feature is enabled in the organization through at least one such policy. # Okta_Realm Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_realm An Okta realm Applies to BloodHound Enterprise and CE ## Overview Okta Realms are used to define authentication boundaries within an Okta organization. They allow administrators to segment users and applications based on different criteria, such as geographic location, business unit, or security requirements. Okta Realms are represented as Okta\_Realm nodes in BloodHound. Okta Realms are currently not supported due to licensing restrictions. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------- | ------------------------------------------------------------------------ | ----------- | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | -------------------------------------------------------------------------- | -------------------------------------------------------- | ----------- | | [Okta\_RealmContains](/opengraph/extensions/okta/edges/okta_realmcontains) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | ## Properties | Name | Source | Type | Description | | -------------- | --------------------------- | ---------- | ---------------------------------------------------------- | | `id` | `realm.id` | `string` | Unique realm identifier. | | `name` | `realm.profile.name` | `string` | Realm name. | | `display_name` | `realm.profile.name` | `string` | Display-friendly realm name. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the realm exists. | | `type` | `realm.profile.realmType` | `string` | Realm type classification, such as `PARTNER` or `DEFAULT`. | | `is_default` | `realm.isDefault` | `bool` | Whether this is the default realm. | | `domains` | `realm.profile.domains` | `string[]` | List of domains allowed in the realm. | | `created` | `realm.created` | `datetime` | Realm creation timestamp. | | `last_updated` | `realm.lastUpdated` | `datetime` | Last realm update timestamp. | ## Sample Property Values ```yaml theme={null} id: guor3k19x7pVQ6Abc0g7 name: Car Co display_name: Car Co okta_domain: contoso.okta.com type: PARTNER is_default: false domains: - atko.com - user.com created: 2025-06-01T08:00:00.0000000+00:00 last_updated: 2026-02-20T07:45:12.0000000+00:00 ``` # Okta_ResourceSet Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_resourceset A resource set containing users, groups, applications, and other Okta objects Applies to BloodHound Enterprise and CE ## Overview Resource sets are collections of entities that can be used to scope custom role assignments in Okta. A resource set can contain the following object types: * [x] [Users](/opengraph/extensions/okta/nodes/okta_user) * [x] [Groups](/opengraph/extensions/okta/nodes/okta_group) * [x] [Applications](/opengraph/extensions/okta/nodes/okta_application) * [x] [API Service Integrations](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) * [x] [Devices](/opengraph/extensions/okta/nodes/okta_device) * [x] [Authorization servers](/opengraph/extensions/okta/nodes/okta_authorizationserver) * [x] [Identity Providers](/opengraph/extensions/okta/nodes/okta_identityprovider) * [x] [Policies](/opengraph/extensions/okta/nodes/okta_policy) * [x] Entity risk policy * [x] Session protection policy * [x] Authentication policy * [x] Global session policy * [x] End user account management policy * [ ] Shared Signals Framework (SSF) Receivers * [ ] ~~Workflows~~ (Gaps in the Okta API) * [ ] ~~Customizations~~ (Gaps in the Okta API) * [ ] ~~Support cases~~ (Gaps in the Okta API) * [ ] ~~Identity and Access Management Resources~~ (Gaps in the Okta API) Only the marked resource types are currently supported as resource set members. Some resource types, such as Workflows, are not accessible via the Okta API at all. Okta Resource Set displayed in BloodHound Resource sets are represented as Okta\_ResourceSet nodes in BloodHound. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------- | ---------------------------------------------------------------------------- | ----------- | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_ScopedTo](/opengraph/extensions/okta/edges/okta_scopedto) | [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_ResourceSetContains](/opengraph/extensions/okta/edges/okta_resourcesetcontains) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration), [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device), [Okta\_AuthorizationServer](/opengraph/extensions/okta/nodes/okta_authorizationserver), [Okta\_IdentityProvider](/opengraph/extensions/okta/nodes/okta_identityprovider), [Okta\_Policy](/opengraph/extensions/okta/nodes/okta_policy) | ✅ | ## Properties | Name | Source | Type | Description | | -------------- | -------------------------------------------------------- | ---------- | ------------------------------------------------------- | | `id` | `resourceSet.id + "@" + okta_domain` or `resourceSet.id` | `string` | Unique resource set identifier (domain-qualified). | | `name` | `resourceSet.label` | `string` | Resource set name. | | `display_name` | `resourceSet.label` | `string` | Display-friendly resource set name. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the resource set exists. | | `description` | `resourceSet.description` | `string` | Resource set description text. | | `created` | `resourceSet.created` | `datetime` | Resource set creation timestamp. | | `last_updated` | `resourceSet.lastUpdated` | `datetime` | Last resource set update timestamp. | The built-in resource set `Workflows Resource Set` has the `WORKFLOWS_IAM_POLICY` identifier in all Okta organizations. To make it unique, the collector adds the organization domain name as a suffix to the resource set's ID, e.g., `WORKFLOWS_IAM_POLICY@contoso.okta.com`. ## Sample Property Values ```yaml theme={null} id: WORKFLOWS_IAM_POLICY@contoso.okta.com name: Workflows Resource Set display_name: Workflows Resource Set okta_domain: contoso.okta.com description: A resource set managed by Workflows Administrator created: 2025-10-22T13:29:26+00:00 last_updated: 2025-10-22T13:29:26+00:00 ``` # Okta_Role Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_role A built-in role in Okta, such as Super Admin or Group Admin Applies to BloodHound Enterprise and CE ## Overview Okta provides a handful of [built-in administrative roles](https://help.okta.com/en-us/content/topics/security/administrators-admin-comparison.htm) that can be assigned to users, groups, and applications to delegate administrative tasks. These roles have predefined permissions and cannot be modified. The following roles are organization-wide: * Super Administrator * Organization Administrator * API Access Management Administrator * Mobile Administrator * Workflows Administrator * Report Administrator * Read-only Administrator The most powerful role is the **Super Administrator**, which has full access to all features and settings in the Okta organization. The following roles can either be scoped to specific resources or assigned organization-wide: * Group Administrator (AKA User Administrator) * Group Membership Administrator * Help Desk Administrator * Application Administrator Although the Workflows Administrator role is a built-in role, the Okta API treats it as a custom role that is scoped to the built-in `Workflows Resource Set`. Built-in roles are represented as Okta\_Role nodes in BloodHound. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_HasRole](/opengraph/extensions/okta/edges/okta_hasrole) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ❌ | ### Outbound Edges No outbound edges are defined by the Okta extension for this node. ## Properties | Name | Source | Type | Description | | -------------- | ----------------------------- | ---------- | ----------------------------------------------------- | | `id` | `role.id + "@" + okta_domain` | `string` | Unique role identifier (domain-qualified). | | `name` | `role.label` | `string` | Role name. | | `display_name` | `role.label` | `string` | Display-friendly role name. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the role exists. | | `description` | `role.description` | `string` | Role description text when available. | | `permissions` | Hardcoded mapping | `string[]` | Effective permission labels associated with the role. | ## Sample Property Values ```yaml theme={null} id: APP_ADMIN@contoso.okta.com name: Application Administrator display_name: Application Administrator okta_domain: contoso.okta.com permissions: - okta.apps.manage - okta.apps.read - okta.apps.assignment.manage - okta.apps.clientCredentials.read - okta.users.appAssignment.manage - okta.groups.appAssignment.manage - okta.policies.manage - okta.policies.read - okta.users.read - okta.groups.read - okta.users.userprofile.manage - okta.users.userprofile.read - okta.profilesources.import.run - okta.agents.register - okta.realms.read ``` ## Built-In Role Identifiers When working with roles using the Okta API, the built-in roles are referenced by the following identifiers: | Role Identifier | Role Name | | ------------------------------ | ----------------------------------- | | SUPER\_ADMIN | Super Administrator | | ORG\_ADMIN | Organization Administrator | | USER\_ADMIN | Group Administrator | | GROUP\_MEMBERSHIP\_ADMIN | Group Membership Administrator | | APP\_ADMIN | Application Administrator | | API\_ACCESS\_MANAGEMENT\_ADMIN | API Access Management Administrator | | ~~API\_ADMIN~~ | API Administrator (Deprecated?) | | HELP\_DESK\_ADMIN | Help Desk Administrator | | MOBILE\_ADMIN | Mobile Administrator | | WORKFLOWS\_ADMIN | Workflows Administrator | | REPORT\_ADMIN | Report Administrator | | READ\_ONLY\_ADMIN | Read-Only Administrator | To make the role identifiers unique, the collector adds the organization domain name as a suffix to each role's ID, e.g., `SUPER_ADMIN@contoso.okta.com`. ## Built-In Role Permissions Unlike custom roles, built-in roles have fixed permissions that cannot be changed. However, the exact OAuth 2.0 scopes granted to each built-in role are not publicly documented by Okta and cannot even be retrieved via the API. We therefore did the mapping by ourselves based on the role descriptions in the Okta documentation. Hence, the resulting permissions ingested to BloodHound are best-effort approximations and may not be 100% accurate. # Okta_RoleAssignment Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_roleassignment A set of permissions assigned to a user, group, or an application in Okta Applies to BloodHound Enterprise and CE ## Overview To help visualize role assignments in BloodHound, Okta\_RoleAssignment nodes are created for each role assignment in Okta. These nodes represent the relationship between a [user](/opengraph/extensions/okta/nodes/okta_user), [group](/opengraph/extensions/okta/nodes/okta_group), or [application](/opengraph/extensions/okta/nodes/okta_application) and a role ([built-in](/opengraph/extensions/okta/nodes/okta_role) or [custom](/opengraph/extensions/okta/nodes/okta_customrole)). ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_HasRoleAssignment](/opengraph/extensions/okta/edges/okta_hasroleassignment) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [Okta\_ScopedTo](/opengraph/extensions/okta/edges/okta_scopedto) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization), [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration), [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device), [Okta\_AuthorizationServer](/opengraph/extensions/okta/nodes/okta_authorizationserver) | ❌ | ## Properties | Name | Source | Type | Description | | ----------------- | --------------------------------------- | ---------- | -------------------------------------------------------------------------------- | | `id` | `roleAssignment.id + "_" + assignee.id` | `string` | Unique role-assignment identifier derived from role assignment and assignee IDs. | | `name` | `roleAssignment.label` | `string` | Role name associated with this assignment. | | `display_name` | `roleAssignment.label` | `string` | Display label used in BloodHound. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the role assignment exists. | | `assignment_type` | `roleAssignment.assignmentType` | `string` | Assignment scope/type (for example user or group assignment). | | `type` | `roleAssignment.type` | `string` | Assigned role identifier (for example `WORKFLOWS_ADMIN`, `APP_ADMIN`). | | `status` | `roleAssignment.status` | `string` | Role assignment lifecycle status. | | `created` | `roleAssignment.created` | `datetime` | Role assignment creation timestamp. | | `last_updated` | `roleAssignment.lastUpdated` | `datetime` | Last role assignment update timestamp. | ## Sample Property Values ```yaml theme={null} id: irbwnwe8vjjXl4FbX697_00uw2sodowQc75SUm697 name: Workflows Administrator display_name: Workflows Administrator okta_domain: contoso.okta.com assignment_type: USER type: WORKFLOWS_ADMIN status: ACTIVE created: 2025-10-22T13:29:26+00:00 last_updated: 2025-10-22T13:29:26+00:00 ``` # Okta_User Source: https://bloodhound.specterops.io/opengraph/extensions/okta/nodes/okta_user An Okta user account Applies to BloodHound Enterprise and CE ## Overview User objects (AKA People) represent individuals who have access to the Okta organization. Each user has a unique identifier, username in the email address format, and various attributes such as email, first name, last name, and status. Users are represented as Okta\_User nodes in BloodHound. ## Edges The tables below list edges defined by the Okta extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_ApiTokenFor](/opengraph/extensions/okta/edges/okta_apitokenfor) | [Okta\_ApiToken](/opengraph/extensions/okta/nodes/okta_apitoken) | ✅ | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_DeviceOf](/opengraph/extensions/okta/edges/okta_deviceof) | [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device) | ❌ | | [Okta\_GroupAdmin](/opengraph/extensions/okta/edges/okta_groupadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_HelpDeskAdmin](/opengraph/extensions/okta/edges/okta_helpdeskadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_IdentityProviderFor](/opengraph/extensions/okta/edges/okta_identityproviderfor) | [Okta\_IdentityProvider](/opengraph/extensions/okta/nodes/okta_identityprovider) | ✅ | | [Okta\_InboundSSO](/opengraph/extensions/okta/edges/okta_inboundsso) | [AZUser](/resources/nodes/az-user) | ✅ | | [Okta\_ManagerOf](/opengraph/extensions/okta/edges/okta_managerof) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ❌ | | [Okta\_OrgAdmin](/opengraph/extensions/okta/edges/okta_orgadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_OutboundSSO](/opengraph/extensions/okta/edges/okta_outboundsso) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_PasswordSync](/opengraph/extensions/okta/edges/okta_passwordsync) | [User](/resources/nodes/user), [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_ReadPasswordUpdates](/opengraph/extensions/okta/edges/okta_readpasswordupdates) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_RealmContains](/opengraph/extensions/okta/edges/okta_realmcontains) | [Okta\_Realm](/opengraph/extensions/okta/nodes/okta_realm) | ✅ | | [Okta\_ResetFactors](/opengraph/extensions/okta/edges/okta_resetfactors) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_ResetPassword](/opengraph/extensions/okta/edges/okta_resetpassword) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_ResourceSetContains](/opengraph/extensions/okta/edges/okta_resourcesetcontains) | [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset) | ✅ | | [Okta\_ScopedTo](/opengraph/extensions/okta/edges/okta_scopedto) | [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) | ❌ | | [Okta\_UserPull](/opengraph/extensions/okta/edges/okta_userpull) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ❌ | | [Okta\_UserSync](/opengraph/extensions/okta/edges/okta_usersync) | [User](/resources/nodes/user), [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [SNOW\_User](https://github.com/SpecterOps/SnowHound) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [Okta\_AddMember](/opengraph/extensions/okta/edges/okta_addmember) | [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ✅ | | [Okta\_AppAdmin](/opengraph/extensions/okta/edges/okta_appadmin) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application), [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) | ✅ | | [Okta\_AppAssignment](/opengraph/extensions/okta/edges/okta_appassignment) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ❌ | | [Okta\_CreatorOf](/opengraph/extensions/okta/edges/okta_creatorof) | [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) | ❌ | | [Okta\_GroupAdmin](/opengraph/extensions/okta/edges/okta_groupadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ✅ | | [Okta\_GroupMembershipAdmin](/opengraph/extensions/okta/edges/okta_groupmembershipadmin) | [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ✅ | | [Okta\_HasRole](/opengraph/extensions/okta/edges/okta_hasrole) | [Okta\_Role](/opengraph/extensions/okta/nodes/okta_role), [Okta\_CustomRole](/opengraph/extensions/okta/nodes/okta_customrole) | ❌ | | [Okta\_HasRoleAssignment](/opengraph/extensions/okta/edges/okta_hasroleassignment) | [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) | ❌ | | [Okta\_HelpDeskAdmin](/opengraph/extensions/okta/edges/okta_helpdeskadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_ManageApp](/opengraph/extensions/okta/edges/okta_manageapp) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ✅ | | [Okta\_ManagerOf](/opengraph/extensions/okta/edges/okta_managerof) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ❌ | | [Okta\_MemberOf](/opengraph/extensions/okta/edges/okta_memberof) | [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | ✅ | | [Okta\_MobileAdmin](/opengraph/extensions/okta/edges/okta_mobileadmin) | [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device) | ✅ | | [Okta\_OrgAdmin](/opengraph/extensions/okta/edges/okta_orgadmin) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group), [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device) | ✅ | | [Okta\_OutboundSSO](/opengraph/extensions/okta/edges/okta_outboundsso) | [AZUser](/resources/nodes/az-user), [GH\_User](/opengraph/extensions/github/nodes/gh_user), [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [SNOW\_User](https://github.com/SpecterOps/SnowHound), [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_PasswordSync](/opengraph/extensions/okta/edges/okta_passwordsync) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [User](/resources/nodes/user) | ✅ | | [Okta\_ReadClientSecret](/opengraph/extensions/okta/edges/okta_readclientsecret) | [Okta\_ClientSecret](/opengraph/extensions/okta/nodes/okta_clientsecret) | ✅ | | [Okta\_ResetFactors](/opengraph/extensions/okta/edges/okta_resetfactors) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_ResetPassword](/opengraph/extensions/okta/edges/okta_resetpassword) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | ✅ | | [Okta\_SuperAdmin](/opengraph/extensions/okta/edges/okta_superadmin) | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | ✅ | | [Okta\_SWA](/opengraph/extensions/okta/edges/okta_swa) | [GH\_User](/opengraph/extensions/github/nodes/gh_user), [jamf\_Account](/opengraph/extensions/jamf/nodes/jamf_account), [OP\_User](https://github.com/SpecterOps/1PassHound), [SNOW\_User](https://github.com/SpecterOps/SnowHound) | ❌ | | [Okta\_UserPush](/opengraph/extensions/okta/edges/okta_userpush) | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | ❌ | | [Okta\_UserSync](/opengraph/extensions/okta/edges/okta_usersync) | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user), [User](/resources/nodes/user), [AZUser](/resources/nodes/az-user), [OP\_User](https://github.com/SpecterOps/1PassHound), [SNOW\_User](https://github.com/SpecterOps/SnowHound) | ❌ | ## Properties | Name | Source | Type | Description | | -------------------------- | -------------------------------- | ---------- | ---------------------------------------------------------------- | | `id` | `user.id` | `string` | Unique user identifier. | | `name` | `user.profile.login` | `string` | Okta username/login. | | `display_name` | `user.profile.displayName` | `string` | User display name. | | `okta_domain` | Collector context (non-API) | `string` | Okta organization domain where the user exists. | | `login` | `user.profile.login` | `string` | User login/UPN value. | | `email` | `user.profile.email` | `string` | Primary email address. | | `first_name` | `user.profile.firstName` | `string` | User first/given name. | | `last_name` | `user.profile.lastName` | `string` | User last/family name. | | `title` | `user.profile.title` | `string` | Job title from user profile when present. | | `department` | `user.profile.department` | `string` | Department value from user profile when present. | | `city` | `user.profile.city` | `string` | City/location value from user profile when present. | | `state` | `user.profile.state` | `string` | State/region value from user profile when present. | | `country_code` | `user.profile.countryCode` | `string` | ISO-like country code from user profile when present. | | `status` | `user.status` | `string` | User lifecycle status. | | `enabled` | `IsEnabled(user.status)` | `bool` | Boolean status projection used by BloodHound. | | `has_role_assignments` | Calculated | `bool` | Indicates whether the user is assigned any administrative roles. | | `credential_provider_name` | `user.credentials.provider.name` | `string` | Authentication provider name for this user. | | `credential_provider_type` | `user.credentials.provider.type` | `string` | Authentication provider type for this user. | | `manager_id` | `user.profile.managerId` | `string` | Manager identifier from user profile synchronization. | | `activated` | `user.activated` | `datetime` | Timestamp when the user account was activated. | | `created` | `user.created` | `datetime` | User creation timestamp. | | `password_changed` | `user.passwordChanged` | `datetime` | Timestamp when the password was last changed. | | `last_login` | `user.lastLogin` | `datetime` | Timestamp of the most recent successful login. | | `last_updated` | `user.lastUpdated` | `datetime` | Last profile/update timestamp. | ## Sample Property Values ```yaml theme={null} id: 00uw2sodn4ZPJJQyx697 name: john.doe@contoso.com display_name: John Doe okta_domain: contoso.okta.com login: john.doe@contoso.com email: john.doe@contoso.com first_name: John last_name: Doe title: Senior Identity Engineer department: Security Engineering city: Seattle state: WA country_code: US status: ACTIVE enabled: true has_role_assignments: false credential_provider_name: OKTA credential_provider_type: OKTA manager_id: joe.smith@contoso.com created: 2025-10-03T18:45:57+00:00 activated: 2025-10-03T19:02:11+00:00 password_changed: 2026-01-12T14:27:03+00:00 last_login: 2026-02-20T09:41:55+00:00 last_updated: 2025-10-29T11:09:47+00:00 ``` ## User Status User status can have [multiple values](https://developer.okta.com/docs/api/openapi/okta-management/management/tag/User), as illustrated below: ![Okta user status](https://developer.okta.com/docs/api/images/users/okta-user-status.png) To simplify analysis in BloodHound, the collector maps the **Status** attribute to the virtual boolean **Enabled** attribute as follows: | Okta User Status | Enabled | Explanation | | ----------------- | ------- | -------------------------------------------------------------- | | ACTIVE | ✅ | User can authenticate. | | PASSWORD\_EXPIRED | ✅ | User's password has expired but can still authenticate. | | LOCKED\_OUT | ✅ | User is locked out but can still authenticate after unlocking. | | PROVISIONED | ✅ | User is provisioned but cannot authenticate yet. | | RECOVERY | ✅ | User is in recovery mode and cannot authenticate. | | SUSPENDED | ❌ | User is suspended and cannot authenticate. | | STAGED | ❌ | User is staged and cannot authenticate yet. | | DEPROVISIONED | ❌ | User is deprovisioned and cannot authenticate. | This mapping is a simplification and may not cover all edge cases. Always refer to the actual **Status** attribute for precise user state information. ## Synchronization with External Directories Users can be synchronized from external directories such as Active Directory (AD) or LDAP. When synchronized, certain attributes may be mapped from the external directory to the Okta user profile. Additional Active Directory attributes # Overview Source: https://bloodhound.specterops.io/opengraph/extensions/okta/overview Learn about the Okta OpenGraph extension for BloodHound. Applies to BloodHound Enterprise and CE The Okta extension is an OpenGraph extension for [Okta Platform](https://www.okta.com/products/workforce-identity/) environments that enables BloodHound to model Okta users, groups, applications, roles, policies, and related relationships as graph data. It adds Okta-specific [nodes](/opengraph/extensions/okta/schema#nodes), [edges](/opengraph/extensions/okta/schema#edges), [Cypher queries](/opengraph/extensions/okta/queries), and [Privilege Zone rules](/opengraph/extensions/okta/privilege-zone-rules) to help security professionals visualize and analyze Okta configurations in BloodHound. In BloodHound Enterprise v9.3.0 and later, Okta is supported as a pre-installed extension. Use [OpenGraph Extension Management](/opengraph/extensions/manage) to verify the installed version or upload a newer supported schema manually. The other main product in Okta's portfolio is [Auth0](https://auth0.com/) (previously known as Customer Identity Cloud). The Okta extension does not currently support Auth0. ## Okta Attack Paths Okta is an interesting target for attackers because it is widely used by organizations to manage access to cloud and on-premises applications. Compromising an Okta organization can provide attackers with access to a wide range of resources and data. Okta organizations are often secure by default, with MFA enforced for users and re-authentication required for sensitive administrative tasks. Okta also uses role-based access control (RBAC) to mitigate privilege escalation paths. As a result, many meaningful attack paths stem from misconfigurations, including excessive role assignments, weak authentication policies, insecure application integrations, and exposure of sensitive credentials. You should also account for users who are non-privileged in Okta but hold administrative access in connected applications, such as GitHub Enterprise Cloud or Amazon Web Services (AWS). Hybrid attack paths between on-premises Active Directory and Okta are also possible. Okta role assignments displayed in BloodHound Our research on Okta attack paths is still ongoing. Interesting mappings to MITRE ATT\&CK are [available from Elastic](https://github.com/elastic/detection-rules/tree/main/rules/integrations/okta). ## Available Collectors The Okta extension supports two collector paths: * [OpenHound Okta collector](/openhound/collectors/okta/overview): The SpecterOps-supported Okta collector. This is the primary documented path for collecting Okta data for BloodHound. * [OktaHound collector](https://github.com/SpecterOps/OktaHound): An alternative Okta collector that also targets the Okta extension schema. ## Okta Free Trial Okta provides a [free trial](https://developer.okta.com/signup/) plan that you can use to test the majority of OktaHound features. ## References The following blog posts provide insights into Okta attack vectors and techniques: * [Michael Grafnetter (SpecterOps): Discovering Unexpected Okta Attack Paths with BloodHound](https://specterops.io/blog/2026/03/23/discovering-unexpected-okta-attack-paths-with-bloodhound/) * [Adam Chester (SpecterOps): Okta for Red Teamers](https://blog.xpnsec.com/okta-for-redteamers/) * [Adam Chester (SpecterOps): Identity Providers for RedTeamers](https://blog.xpnsec.com/identity-providers-redteamers/) * [Eli Guy (XM Cyber): Attack Techniques in Okta - Part 1 - A (Really) Deep Dive into Okta Key Terms](https://xmcyber.com/blog/attack-techniques-in-okta/) * [Eli Guy (XM Cyber): Attack Techniques in Okta - Part 2 - Okta RBAC Attacks](https://xmcyber.com/blog/okta-rbac-attacks/) * [Eli Guy (XM Cyber): Attack Techniques in Okta - Part 3 - From Okta to AWS Environments](https://xmcyber.com/blog/okta-attack-techniques-part-3-from-okta-environments-to-aws/) * [AppOmni: Okta PassBleed Risks - A Technical Overview](https://appomni.com/ao-labs/okta-passbleed-risks/) * [Luke Jennings (PushSecurity): Abusing Okta's SWA authentication](https://pushsecurity.com/blog/okta-swa/) * [David French (Elastic): Testing your Okta visibility and detection with Dorothy and Elastic Security](https://www.elastic.co/security-labs/testing-okta-visibility-and-detection-dorothy) ## Research Tools Here are some interesting GitHub repositories related to Okta security research: * [Okta Post-Exploitation Toolkit](https://github.com/xpn/OktaPostExToolkit) * [Okta Terrify](https://github.com/CCob/okta-terrify) * [Dorothy](https://github.com/elastic/dorothy) * [SaaS Attacks](https://github.com/pushsecurity/saas-attacks/) * [Okta SCIM Attack Tool](https://github.com/authomize/okta_scim_attack_tool) ## Community Please join us in the `#okta` channel of the [BloodHound Community Slack](https://slack.specterops.io/) workspace if you want to chat about attack paths in Okta. You are also welcome to open an issue or pull request on [GitHub](https://github.com/SpecterOps/openhound-okta). ## Related Pages * [Getting started](/opengraph/extensions/okta/getting-started) * [Schema reference](/opengraph/extensions/okta/schema) * [Cypher queries](/opengraph/extensions/okta/queries) * [Privilege Zone rules](/opengraph/extensions/okta/privilege-zone-rules) * [OpenHound Okta collector overview](/openhound/collectors/okta/overview) # Privilege Zone Rules Source: https://bloodhound.specterops.io/opengraph/extensions/okta/privilege-zone-rules Okta extension Privilege Zone rules Applies to BloodHound Enterprise and CE The following Privilege Zone rules can be imported into BloodHound to group nodes for Cypher query analysis and BloodHound Enterprise finding generation. This file is automatically generated from the [JSON Privilege Zone rule files](https://github.com/SpecterOps/openhound-okta/tree/main/extension/privilege_zone_rules). ## Organization Organization nodes in Okta. Zone: Tier Zero ```cypher theme={null} MATCH (n:Okta_Organization) RETURN n ``` This rule is defined in the [organization.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/privilege_zone_rules/organization.json) file. ## Tier Zero Devices Devices associated with principals who have SUPER\_ADMIN or ORG\_ADMIN role assignments. Zone: Tier Zero ```cypher theme={null} MATCH (n:Okta_Device)-[:Okta_DeviceOf]->(:Okta)-[:Okta_HasRoleAssignment|Okta_MemberOf*1..2]->(r:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta_Organization) WHERE r.type = "SUPER_ADMIN" OR r.type = "ORG_ADMIN" RETURN n ``` This rule is defined in the [tier0-devices.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/privilege_zone_rules/tier0-devices.json) file. ## Tier Zero Principals Principals with SUPER\_ADMIN or ORG\_ADMIN role assignments. Zone: Tier Zero ```cypher theme={null} MATCH (n:Okta)-[:Okta_HasRoleAssignment|Okta_MemberOf*1..2]->(r:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta_Organization) WHERE r.type = "SUPER_ADMIN" OR r.type = "ORG_ADMIN" RETURN n ``` This rule is defined in the [tier0-principals.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/privilege_zone_rules/tier0-principals.json) file. # Cypher Queries Source: https://bloodhound.specterops.io/opengraph/extensions/okta/queries Okta extension Cypher queries Applies to BloodHound Enterprise and CE The following custom Cypher queries can be imported into BloodHound to enhance visibility. This file is automatically generated from the [JSON query files](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches). ## Agents, Agent Pools, and Host Servers Lists Okta agents, their associated agent pools, and the AD servers hosting each agent. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(:Okta_AgentPool)<-[:Okta_AgentMemberOf|Okta_HostsAgent*1..2]-(agent) WHERE agent:Okta_Agent OR agent:Computer RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [ad-agents.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/ad-agents.json) file. ## Principals with Admin Console Access Identifies principals with access to the Okta Admin Console. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(:Okta)-[:Okta_AppAssignment]->(console:Okta_Application) WHERE console.name = "saasure" RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [admin-console-access.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/admin-console-access.json) file. ## Application Assignments List all application assignments. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(:Okta)-[:Okta_AppAssignment]->(:Okta_Application) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [app-assignments.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/app-assignments.json) file. ## Application Credentials Lists all service application secrets and JWTs. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(:Okta_Application)<-[:Okta_SecretOf|Okta_KeyOf]->(credential) WHERE credential:Okta_ClientSecret OR credential:Okta_JWK RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [app-credentials.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/app-credentials.json) file. ## Devices List all devices, their owners, and any mobile admins. ```cypher theme={null} MATCH path = (:Okta_Device)-[:Okta_DeviceOf]->(:Okta_User) OPTIONAL MATCH adminPath = (admin)-[:Okta_MobileAdmin]->(:Okta_Device) WHERE admin:Okta_User OR admin:Okta_Group OR admin:Okta_Application RETURN path,adminPath LIMIT 1000 ``` This query can be imported into BloodHound from the [devices.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/devices.json) file. ## Group Membership Retrieves all group membership relationships. ```cypher theme={null} MATCH path = (:Okta_User)-[:Okta_MemberOf]->(:Okta_Group) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [group-members.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/group-members.json) file. ## Hybrid Relationships Inbound Retrieves all hybrid relationships from external systems to Okta. ```cypher theme={null} MATCH path = (source)-[]->(:Okta) WHERE NOT source:Okta RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [hybrid-inbound.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/hybrid-inbound.json) file. ## Hybrid Relationships Outbound Retrieves all hybrid relationships from Okta to external systems. ```cypher theme={null} MATCH path = (:Okta)-[]->(target) WHERE NOT target:Okta RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [hybrid-outbound.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/hybrid-outbound.json) file. ## Security Principal Synchronization Retrieves all users and groups that are synchronized TO or FROM Okta. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(:Okta)-[:Okta_UserPull|Okta_UserPush|Okta_GroupPull|Okta_GroupPush]->(:Okta) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [hybrid-sync.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/hybrid-sync.json) file. ## Identity Provider Assignments - Direct Privileged Access Identity providers associated with users or groups that hold direct privileged role assignments in Okta. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(:Okta_IdentityProvider)-[:Okta_IdentityProviderFor|Okta_IdpGroupAssignment]->(assignee)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) WHERE assignee:Okta_User OR assignee:Okta_Group RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [identity-providers-direct-privileged.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/identity-providers-direct-privileged.json) file. ## Identity Provider Assignments - Indirect Privileged Access Identity providers associated with users who hold privileged role assignments through group membership in Okta. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(:Okta_IdentityProvider)-[:Okta_IdentityProviderFor]->(:Okta_User)-[:Okta_MemberOf]->(:Okta_Group)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [identity-providers-indirect-privileged.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/identity-providers-indirect-privileged.json) file. ## Identity Provider Assignments Lists all identity providers and the users and groups they are associated with, including per-user trust relationships and automatic group assignments. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(:Okta_IdentityProvider)-[:Okta_IdentityProviderFor|Okta_IdpGroupAssignment]->(assignee) WHERE assignee:Okta_User OR assignee:Okta_Group RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [identity-providers.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/identity-providers.json) file. ## Organizational Structure Retrieves all manager relationships. ```cypher theme={null} MATCH path = (:Okta_User)-[:Okta_ManagerOf]->(:Okta_User) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [org-chart.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/org-chart.json) file. ## Org Trust Relationships Lists all org-to-org trust relationships including inbound and outbound SSO federation, Secure Web Authentication (SWA), and Kerberos SSO relationships between Okta applications and supported external organizations or tenants. ```cypher theme={null} MATCH path = (source)-[:Okta_InboundOrgSSO|Okta_OutboundOrgSSO|Okta_OrgSWA|Okta_KerberosSSO]-() WHERE source:Okta_Application OR source:Okta_IdentityProvider RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [org-trust-relationships.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/org-trust-relationships.json) file. ## Password and MFA Permissions Lists permissions to reset passwords and MFA factors. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(actor)-[:Okta_ResetPassword|Okta_ResetFactors|Okta_HelpDeskAdmin|Okta_OrgAdmin|Okta_GroupAdmin]->(:Okta_User) WHERE actor:Okta_User OR actor:Okta_Group OR actor:Okta_Application RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [password-and-mfa-permissions.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/password-and-mfa-permissions.json) file. ## Policy Mappings Retrieves all policy mappings. ```cypher theme={null} MATCH policies = (:Okta_Organization)-[:Okta_Contains]->(:Okta_Policy) MATCH mappings = (:Okta_Policy)-[:Okta_PolicyMapping]->(:Okta) RETURN policies,mappings LIMIT 1000 ``` This query can be imported into BloodHound from the [policy-mappings.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/policy-mappings.json) file. ## Unrotated Active Access Keys on Privileged Apps Finds active JWKs or client secrets older than 365 days on applications that have role assignments. ```cypher theme={null} MATCH path = (credential)-[:Okta_KeyOf|Okta_SecretOf]->(:Okta_Application)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) WHERE (credential:Okta_JWK OR credential:Okta_ClientSecret) AND credential.status = "ACTIVE" AND datetime(credential.created) <= datetime() - duration("P365D") RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [privileged-app-unrotated-access-keys.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/privileged-app-unrotated-access-keys.json) file. ## Applications with Role Assignments Applications that have roles assigned. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(:Okta_Application)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [privileged-apps.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/privileged-apps.json) file. ## Synced Principals with Privileged Access (Direct) - Hybrid Edges Users, groups, and applications with inbound hybrid relationships (sync, SSO, or AD agent) that hold privileged role assignments in Okta. ```cypher theme={null} MATCH path = ()-[:Okta_UserSync|Okta_MembershipSync|Okta_InboundSSO|Okta_HostsAgent]->(principal)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) WHERE principal:Okta_User OR principal:Okta_Group OR principal:Okta_Application RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [privileged-hybrid-inbound-direct.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/privileged-hybrid-inbound-direct.json) file. ## Synced Principals with Privileged Access (Indirect) - Hybrid Edges Users and applications with inbound hybrid relationships (sync, SSO, or AD agent) that hold privileged role assignments through group membership in Okta. ```cypher theme={null} MATCH path = ()-[:Okta_UserSync|Okta_InboundSSO|Okta_HostsAgent]->(principal)-[:Okta_MemberOf]->(:Okta_Group)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) WHERE principal:Okta_User OR principal:Okta_Application RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [privileged-hybrid-inbound-indirect.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/privileged-hybrid-inbound-indirect.json) file. ## Synced Principals with Privileged Access (Direct) - Okta Edges Users and groups synchronized from external sources that have privileged role assignments. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(provider)-[:Okta_UserPull|Okta_GroupPull|Okta_IdentityProviderFor|Okta_IdpGroupAssignment]->(:Okta)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) WHERE provider:Okta_Application OR provider:Okta_IdentityProvider RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [privileged-principals-hybrid-direct.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/privileged-principals-hybrid-direct.json) file. ## Synced Principals with Privileged Access (Indirect) - Okta Edges Users synchronized from external sources that hold privileged role assignments through group membership in Okta. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(provider)-[:Okta_UserPull|Okta_IdentityProviderFor]->(:Okta_User)-[:Okta_MemberOf]->(:Okta_Group)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) WHERE provider:Okta_Application OR provider:Okta_IdentityProvider RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [privileged-principals-hybrid-indirect.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/privileged-principals-hybrid-indirect.json) file. ## Privileged Users with Old Passwords (Direct) Finds users whose last password change was more than a year ago and directly hold privileged role assignments. ```cypher theme={null} MATCH path = (user:Okta_User)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) WHERE user.password_changed IS NOT NULL AND datetime(user.password_changed) <= datetime() - duration("P365D") RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [privileged-users-old-passwords-direct.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/privileged-users-old-passwords-direct.json) file. ## Privileged Users with Old Passwords (Indirect) Finds users whose last password change was more than a year ago and hold privileged role assignments through group membership. ```cypher theme={null} MATCH path = (user:Okta_User)-[:Okta_MemberOf]->(:Okta_Group)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) WHERE user.password_changed IS NOT NULL AND datetime(user.password_changed) <= datetime() - duration("P365D") RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [privileged-users-old-passwords-indirect.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/privileged-users-old-passwords-indirect.json) file. ## Privileged Users with Non-Active Status (Direct) Finds users whose status is not ACTIVE and directly hold privileged role assignments, including deactivated, suspended, or provisioning-incomplete accounts. ```cypher theme={null} MATCH path = (user:Okta_User)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) WHERE user.status <> "ACTIVE" RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [privileged-users-unexpected-status-direct.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/privileged-users-unexpected-status-direct.json) file. ## Privileged Users with Non-Active Status (Indirect) Finds users whose status is not ACTIVE and hold privileged role assignments through group membership, including deactivated, suspended, or provisioning-incomplete accounts. ```cypher theme={null} MATCH path = (user:Okta_User)-[:Okta_MemberOf]->(:Okta_Group)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) WHERE user.status <> "ACTIVE" RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [privileged-users-unexpected-status-indirect.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/privileged-users-unexpected-status-indirect.json) file. ## Read Client Secrets of Privileged Applications Searches for client secrets associated with privileged applications that are readable to non-Super Admins. ```cypher theme={null} MATCH path = (:Okta)-[:Okta_ReadClientSecret|Okta_MemberOf*1..2]->(:Okta_ClientSecret)-[:Okta_SecretOf]->(:Okta_Application)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [read-client-secrets.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/read-client-secrets.json) file. ## Realm Membership Lists all Okta realms and the users assigned to them. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(:Okta_Realm)-[:Okta_RealmContains]->(:Okta_User) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [realm-membership.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/realm-membership.json) file. ## Resource Set Membership Lists all resource sets and their associated members. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(:Okta_ResourceSet)-[:Okta_ResourceSetContains]->(:Okta) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [resource-set-membership.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/resource-set-membership.json) file. ## Application Administrators and Managers List all Application Administrators and Managers. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(admin)-[:Okta_AppAdmin|Okta_ManageApp]->(app) WHERE (admin:Okta_User OR admin:Okta_Group OR admin:Okta_Application) AND (app:Okta_Application OR app:Okta_ApiServiceIntegration) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [role-app-admins.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/role-app-admins.json) file. ## Role Assignments - Role Assignments and Scope Lists all role assignments and scope, including transitive group membership. ```cypher theme={null} MATCH path = (:Okta)-[:Okta_HasRoleAssignment|Okta_MemberOf*1..2]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [role-assignments.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/role-assignments.json) file. ## Role Assignments - All Custom Roles Lists all role assignments, linking principals to their assigned custom roles. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(assignee)-[:Okta_HasRole]->(:Okta_CustomRole) WHERE assignee:Okta_User OR assignee:Okta_Group OR assignee:Okta_Application RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [role-custom-assignments.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/role-custom-assignments.json) file. ## Role Assignments - All Built-in Roles Lists all role assignments, linking principals to their assigned built-in roles. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(assignee)-[:Okta_HasRole]->(:Okta_Role) WHERE assignee:Okta_User OR assignee:Okta_Group OR assignee:Okta_Application RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [role-direct-assignments.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/role-direct-assignments.json) file. ## Role Assignments - Group Administrators List all Group Administrators and Group Membership Administrators. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(admin)-[:Okta_GroupAdmin|Okta_GroupMembershipAdmin|Okta_OrgAdmin]->(:Okta_Group) WHERE admin:Okta_User OR admin:Okta_Group OR admin:Okta_Application RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [role-group-admins.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/role-group-admins.json) file. ## SCIM Apps Receiving Password Updates Lists application-to-user assignments where the app receives password updates. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(:Okta_Application)-[:Okta_ReadPasswordUpdates]->(:Okta_User) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [scim-read-passwords.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/scim-read-passwords.json) file. ## API Service Integration Creators Lists all API service integrations and their creators. ```cypher theme={null} MATCH path = (:Okta_Organization)-[:Okta_Contains]->(:Okta)-[:Okta_CreatorOf]->(:Okta_ApiServiceIntegration) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [service-integration-creators.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/service-integration-creators.json) file. ## Stale Privileged Users (Direct) Finds user accounts that have not logged in for at least 180 days and directly hold privileged role assignments. ```cypher theme={null} MATCH path = (user:Okta_User)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) WHERE user.last_login IS NULL OR datetime(user.last_login) <= datetime() - duration("P180D") RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [stale-privileged-accounts-direct.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/stale-privileged-accounts-direct.json) file. ## Stale Privileged Users (Indirect) Finds user accounts that have not logged in for at least 180 days and hold privileged role assignments through group membership. ```cypher theme={null} MATCH path = (user:Okta_User)-[:Okta_MemberOf]->(:Okta_Group)-[:Okta_HasRoleAssignment]->(:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta) WHERE user.last_login IS NULL OR datetime(user.last_login) <= datetime() - duration("P180D") RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [stale-privileged-accounts-indirect.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/stale-privileged-accounts-indirect.json) file. ## Secure Web Authentication Applications Secure Web Authentication (SWA) relationships between Okta users and their linked accounts in external applications. ```cypher theme={null} MATCH path = (:Okta_User)-[:Okta_SWA]->(target) WHERE NOT target:Okta RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [swa-applications.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/swa-applications.json) file. ## Inbound User and Group Synchronization Lists all inbound user and group synchronization relationships to Okta, including password synchronization across Org2Org setups. ```cypher theme={null} MATCH path = (source)-[:Okta_UserSync|Okta_MembershipSync|Okta_PasswordSync]->(target) WHERE target:Okta_User OR target:Okta_Group RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [sync-relationships-inbound.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/sync-relationships-inbound.json) file. ## Outbound User and Group Synchronization Lists all outbound user and group synchronization relationships from Okta, including password synchronization across Org2Org setups. ```cypher theme={null} MATCH path = (source)-[:Okta_UserSync|Okta_MembershipSync|Okta_PasswordSync]->(target) WHERE source:Okta_User OR source:Okta_Group RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [sync-relationships-outbound.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/sync-relationships-outbound.json) file. ## Tier Zero Principals and Devices Principals with SUPER\_ADMIN or ORG\_ADMIN role assignments and their associated devices. ```cypher theme={null} MATCH path = (:Okta)-[:Okta_HasRoleAssignment|Okta_MemberOf|Okta_DeviceOf*1..3]->(role:Okta_RoleAssignment)-[:Okta_ScopedTo]->(:Okta_Organization) WHERE role.type = "SUPER_ADMIN" OR role.type = "ORG_ADMIN" RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [tier0.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/tier0.json) file. ## Users with API Tokens Retrieves all (privileged) users who have been assigned API tokens. ```cypher theme={null} MATCH path = (:Okta_ApiToken)-[:Okta_ApiTokenFor]->(:Okta_User)<-[:Okta_Contains]-(:Okta_Organization) RETURN path LIMIT 1000 ``` This query can be imported into BloodHound from the [users-api-tokens.json](https://github.com/SpecterOps/openhound-okta/tree/main/extension/saved_searches/users-api-tokens.json) file. # Schema Source: https://bloodhound.specterops.io/opengraph/extensions/okta/schema Okta extension schema definition Applies to BloodHound Enterprise and CE ## Metadata **Name:** SOOkta
**Display Name:** Okta Extension (by SpecterOps)
**Version:** v2.8.1
**Namespace:** Okta
**Environment Kind:** Okta\_Organization
**Source Kind:** Okta This file is automatically generated from the [extension schema definition file](https://github.com/SpecterOps/openhound-okta/blob/main/extension/schema.json). ## Nodes | Icon | Node Kind | Display Name | | ---------------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------- | | Okta_Agent | [Okta\_Agent](/opengraph/extensions/okta/nodes/okta_agent) | Okta Agent | | Okta_AgentPool | [Okta\_AgentPool](/opengraph/extensions/okta/nodes/okta_agentpool) | Okta Agent Pool | | Okta_ApiServiceIntegration | [Okta\_ApiServiceIntegration](/opengraph/extensions/okta/nodes/okta_apiserviceintegration) | Okta API Service Integration | | Okta_ApiToken | [Okta\_ApiToken](/opengraph/extensions/okta/nodes/okta_apitoken) | Okta API Token | | Okta_Application | [Okta\_Application](/opengraph/extensions/okta/nodes/okta_application) | Okta Application | | Okta_AuthorizationServer | [Okta\_AuthorizationServer](/opengraph/extensions/okta/nodes/okta_authorizationserver) | Okta Authorization Server | | Okta_ClientSecret | [Okta\_ClientSecret](/opengraph/extensions/okta/nodes/okta_clientsecret) | Okta Client Secret | | Okta_CustomRole | [Okta\_CustomRole](/opengraph/extensions/okta/nodes/okta_customrole) | Okta Custom Role | | Okta_Device | [Okta\_Device](/opengraph/extensions/okta/nodes/okta_device) | Okta Device | | Okta_Group | [Okta\_Group](/opengraph/extensions/okta/nodes/okta_group) | Okta Group | | Okta_IdentityProvider | [Okta\_IdentityProvider](/opengraph/extensions/okta/nodes/okta_identityprovider) | Okta Identity Provider | | Okta_JWK | [Okta\_JWK](/opengraph/extensions/okta/nodes/okta_jwk) | Okta JWK | | Okta_Organization | [Okta\_Organization](/opengraph/extensions/okta/nodes/okta_organization) | Okta Organization | | Okta_Policy | [Okta\_Policy](/opengraph/extensions/okta/nodes/okta_policy) | Okta Policy | | Okta_Realm | [Okta\_Realm](/opengraph/extensions/okta/nodes/okta_realm) | Okta Realm | | Okta_ResourceSet | [Okta\_ResourceSet](/opengraph/extensions/okta/nodes/okta_resourceset) | Okta Resource Set | | Okta_Role | [Okta\_Role](/opengraph/extensions/okta/nodes/okta_role) | Okta Role | | Okta_RoleAssignment | [Okta\_RoleAssignment](/opengraph/extensions/okta/nodes/okta_roleassignment) | Okta Role Assignment | | Okta_User | [Okta\_User](/opengraph/extensions/okta/nodes/okta_user) | Okta User | ## Edges | Relationship Kind | Traversable | Description | | ---------------------------------------------------------------------------------------- | :---------: | -------------------------------------------------------------------------------------- | | [Okta\_AddMember](/opengraph/extensions/okta/edges/okta_addmember) | ✅ | Ability to add or remove members in scoped Okta groups | | [Okta\_AgentMemberOf](/opengraph/extensions/okta/edges/okta_agentmemberof) | ✅ | Membership of an Okta agent in an agent pool | | [Okta\_AgentPoolFor](/opengraph/extensions/okta/edges/okta_agentpoolfor) | ✅ | Relationship between an AD agent pool and its backing AD application | | [Okta\_ApiTokenFor](/opengraph/extensions/okta/edges/okta_apitokenfor) | ✅ | User ownership of an Okta API token | | [Okta\_AppAdmin](/opengraph/extensions/okta/edges/okta_appadmin) | ✅ | Application administrator role assignment | | [Okta\_AppAssignment](/opengraph/extensions/okta/edges/okta_appassignment) | ❌ | Assignment of users or groups to an Okta application | | [Okta\_Contains](/opengraph/extensions/okta/edges/okta_contains) | ✅ | Contains relationship between the Okta organization and its objects | | [Okta\_CreatorOf](/opengraph/extensions/okta/edges/okta_creatorof) | ❌ | Creator relationship for API service integrations | | [Okta\_DeviceOf](/opengraph/extensions/okta/edges/okta_deviceof) | ❌ | Ownership relationship between a device and its assigned user | | [Okta\_GroupAdmin](/opengraph/extensions/okta/edges/okta_groupadmin) | ✅ | Group administrator role assignment | | [Okta\_GroupMembershipAdmin](/opengraph/extensions/okta/edges/okta_groupmembershipadmin) | ✅ | Group membership administrator role assignment | | [Okta\_GroupPull](/opengraph/extensions/okta/edges/okta_grouppull) | ✅ | Import of group memberships from an external application | | [Okta\_GroupPush](/opengraph/extensions/okta/edges/okta_grouppush) | ❌ | Provisioning of group memberships to an external application | | [Okta\_HasRole](/opengraph/extensions/okta/edges/okta_hasrole) | ❌ | Assignment of a built-in or custom role to a principal | | [Okta\_HasRoleAssignment](/opengraph/extensions/okta/edges/okta_hasroleassignment) | ❌ | Relationship between a principal and a role assignment | | [Okta\_HelpDeskAdmin](/opengraph/extensions/okta/edges/okta_helpdeskadmin) | ✅ | Help desk administrator role assignment | | [Okta\_HostsAgent](/opengraph/extensions/okta/edges/okta_hostsagent) | ✅ | Relationship between an AD server and the Okta agent running on that host | | [Okta\_IdentityProviderFor](/opengraph/extensions/okta/edges/okta_identityproviderfor) | ✅ | Trust relationship between an identity provider and Okta users | | [Okta\_IdpGroupAssignment](/opengraph/extensions/okta/edges/okta_idpgroupassignment) | ❌ | Identity provider group assignment to an Okta group | | [Okta\_InboundOrgSSO](/opengraph/extensions/okta/edges/okta_inboundorgsso) | ✅ | Single sign-on from an external organization into Okta | | [Okta\_InboundSSO](/opengraph/extensions/okta/edges/okta_inboundsso) | ✅ | Single sign-on from an external identity provider into Okta | | [Okta\_KerberosSSO](/opengraph/extensions/okta/edges/okta_kerberossso) | ✅ | Agentless desktop SSO relationship from on-prem AD user account to Okta AD application | | [Okta\_KeyOf](/opengraph/extensions/okta/edges/okta_keyof) | ✅ | JSON Web Key associated with an Okta application | | [Okta\_ManageApp](/opengraph/extensions/okta/edges/okta_manageapp) | ✅ | Ability to manage scoped Okta applications | | [Okta\_ManagerOf](/opengraph/extensions/okta/edges/okta_managerof) | ❌ | Manager relationship between Okta users | | [Okta\_MemberOf](/opengraph/extensions/okta/edges/okta_memberof) | ✅ | Membership of a user in an Okta group | | [Okta\_MembershipSync](/opengraph/extensions/okta/edges/okta_membershipsync) | ✅ | Bidirectional synchronization between Okta groups and external groups | | [Okta\_MobileAdmin](/opengraph/extensions/okta/edges/okta_mobileadmin) | ✅ | Mobile administrator role assignment | | [Okta\_OrgAdmin](/opengraph/extensions/okta/edges/okta_orgadmin) | ✅ | Organization administrator role assignment | | [Okta\_OrgSWA](/opengraph/extensions/okta/edges/okta_orgswa) | ❌ | Secure Web Authentication from an Okta application to an external organization | | [Okta\_OutboundOrgSSO](/opengraph/extensions/okta/edges/okta_outboundorgsso) | ✅ | Single sign-on from an Okta application to an external organization | | [Okta\_OutboundSSO](/opengraph/extensions/okta/edges/okta_outboundsso) | ✅ | Single sign-on from Okta to an external identity provider | | [Okta\_PasswordSync](/opengraph/extensions/okta/edges/okta_passwordsync) | ✅ | Password synchronization between user accounts via AD integration, Org2Org, or SCIM | | [Okta\_PolicyMapping](/opengraph/extensions/okta/edges/okta_policymapping) | ❌ | Association of a policy with an Okta application | | [Okta\_ReadClientSecret](/opengraph/extensions/okta/edges/okta_readclientsecret) | ✅ | Ability to read client secrets for scoped Okta applications | | [Okta\_ReadPasswordUpdates](/opengraph/extensions/okta/edges/okta_readpasswordupdates) | ✅ | Application can read password updates over the SCIM protocol | | [Okta\_RealmContains](/opengraph/extensions/okta/edges/okta_realmcontains) | ✅ | Contains relationship between an Okta realm and its users | | [Okta\_ResetFactors](/opengraph/extensions/okta/edges/okta_resetfactors) | ✅ | Ability to reset MFA factors for scoped Okta users | | [Okta\_ResetPassword](/opengraph/extensions/okta/edges/okta_resetpassword) | ✅ | Ability to reset passwords or temporary credentials for scoped Okta users | | [Okta\_ResourceSetContains](/opengraph/extensions/okta/edges/okta_resourcesetcontains) | ✅ | Membership of objects within an Okta resource set | | [Okta\_ScopedTo](/opengraph/extensions/okta/edges/okta_scopedto) | ❌ | Scope relationship between a role assignment and its target | | [Okta\_SecretOf](/opengraph/extensions/okta/edges/okta_secretof) | ✅ | Client secret associated with an application or service integration | | [Okta\_SuperAdmin](/opengraph/extensions/okta/edges/okta_superadmin) | ✅ | Super administrator role assignment | | [Okta\_SWA](/opengraph/extensions/okta/edges/okta_swa) | ❌ | Secure Web Authentication from Okta to an external application | | [Okta\_UserPull](/opengraph/extensions/okta/edges/okta_userpull) | ❌ | Import of users from an external application | | [Okta\_UserPush](/opengraph/extensions/okta/edges/okta_userpush) | ❌ | Provisioning of users to an external application | | [Okta\_UserSync](/opengraph/extensions/okta/edges/okta_usersync) | ❌ | Bidirectional synchronization between Okta users and external identities | # SCIM_Contains Source: https://bloodhound.specterops.io/opengraph/extensions/scim/edges/scim_contains Organization contains a SCIM resource Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [SCIM\_Organization](/opengraph/extensions/scim/nodes/scim_organization) * Destination: [SCIM\_User](/opengraph/extensions/scim/nodes/scim_user), [SCIM\_Group](/opengraph/extensions/scim/nodes/scim_group), [SCIM\_Role](/opengraph/extensions/scim/nodes/scim_role) * Traversable: ✅ ## General Information The SCIM\_Contains edge represents the containment relationship between an organization and its SCIM resources. Each SCIM user, group, and role belongs to exactly one organization, establishing a clear ownership boundary. This edge is significant for scoping identity governance — all resources contained by an organization are managed by that organization's identity provider. ```mermaid theme={null} graph LR node1("SCIM_Organization Contoso") node2("SCIM_User dschrute") node3("SCIM_Group Sales Team") node4("SCIM_Role Sales") node1 -- SCIM_Contains --> node2 node1 -- SCIM_Contains --> node3 node1 -- SCIM_Contains --> node4 ``` # SCIM_HasRole Source: https://bloodhound.specterops.io/opengraph/extensions/scim/edges/scim_hasrole User is assigned to a role Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [SCIM\_User](/opengraph/extensions/scim/nodes/scim_user) * Destination: [SCIM\_Role](/opengraph/extensions/scim/nodes/scim_role) * Traversable: ✅ ## General Information The SCIM\_HasRole edge represents the relationship between users and their assigned roles, as defined by the `roles` attribute in the SCIM user schema. Roles are extracted from user attributes and represented as separate nodes to enable graph-based analysis of role assignments across the organization. This edge allows identifying all users who share a particular role. ```mermaid theme={null} graph LR node1("SCIM_User dschrute") node2("SCIM_Role Sales") node1 -- SCIM_HasRole --> node2 ``` # SCIM_ManagerOf Source: https://bloodhound.specterops.io/opengraph/extensions/scim/edges/scim_managerof User is a manager of another user Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [SCIM\_User](/opengraph/extensions/scim/nodes/scim_user) * Destination: [SCIM\_User](/opengraph/extensions/scim/nodes/scim_user) * Traversable: ❌ ## General Information The SCIM\_ManagerOf edge represents the managerial relationship between users, as defined by the `manager` attribute in the SCIM Enterprise User schema extension. This edge captures the organizational hierarchy, connecting a manager to their direct reports. Manager relationships can be significant for understanding organizational structure and potential privilege escalation paths through social engineering or delegated approval workflows. ```mermaid theme={null} graph LR node1("SCIM_User mscott") node2("SCIM_User dschrute") node1 -. SCIM_ManagerOf .-> node2 ``` # SCIM_MemberOf Source: https://bloodhound.specterops.io/opengraph/extensions/scim/edges/scim_memberof User or group is a member of a group Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [SCIM\_User](/opengraph/extensions/scim/nodes/scim_user), [SCIM\_Group](/opengraph/extensions/scim/nodes/scim_group) * Destination: [SCIM\_Group](/opengraph/extensions/scim/nodes/scim_group) * Traversable: ✅ ## General Information The SCIM\_MemberOf edge represents group membership relationships, as defined by the `members` attribute of groups and the `groups` attribute of users in the SCIM schema. Users can be members of groups, and groups can be nested within other groups. Group membership propagated through SCIM is a primary mechanism for granting application access, making these edges critical for understanding transitive access paths. ```mermaid theme={null} graph LR node1("SCIM_User dschrute") node2("SCIM_Group Sales Team") node3("SCIM_Group All Employees") node1 -- SCIM_MemberOf --> node2 node2 -- SCIM_MemberOf --> node3 ``` # SCIM_Provisioned Source: https://bloodhound.specterops.io/opengraph/extensions/scim/edges/scim_provisioned SCIM resource is provisioned to a target system Applies to BloodHound Enterprise and CE ## Edge Schema * Source: [SCIM\_User](/opengraph/extensions/scim/nodes/scim_user), [SCIM\_Group](/opengraph/extensions/scim/nodes/scim_group) * Destination: [GH\_ExternalIdentity](/opengraph/extensions/github/nodes/gh_externalidentity), [GH\_EnterpriseTeam](/opengraph/extensions/github/nodes/gh_enterpriseteam) * Traversable: ✅ ## General Information The SCIM\_Provisioned edge represents the hybrid relationship between SCIM resources and their provisioned counterparts in downstream applications, such as GitHub. When an identity provider provisions a user or group via SCIM, this edge connects the SCIM source identity to the resulting application-specific identity. These edges are critical for tracing cross-domain access paths from cloud IdP identities to application-level permissions. ```mermaid theme={null} graph LR node1("SCIM_User dschrute") node2("GH_ExternalIdentity dschrute") node3("SCIM_Group Sales Team") node4("GH_EnterpriseTeam Sales Team") node1 -- SCIM_Provisioned --> node2 node3 -- SCIM_Provisioned --> node4 ``` # SCIM_Group Source: https://bloodhound.specterops.io/opengraph/extensions/scim/nodes/scim_group A group provisioned via SCIM Applies to BloodHound Enterprise and CE Represents a group resource provisioned via the [System for Cross-domain Identity Management (SCIM)](https://scim.cloud/) protocol. SCIM groups are used by identity providers to organize users and manage access to downstream applications. Group membership changes propagated through SCIM can grant or revoke application access, making groups a key control point for identity governance. ## Edges The tables below list edges defined by the SCIM extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ----------- | | [SCIM\_Contains](/opengraph/extensions/scim/edges/scim_contains) | [SCIM\_Organization](/opengraph/extensions/scim/nodes/scim_organization) | ✅ | | [SCIM\_MemberOf](/opengraph/extensions/scim/edges/scim_memberof) | [SCIM\_User](/opengraph/extensions/scim/nodes/scim_user), [SCIM\_Group](/opengraph/extensions/scim/nodes/scim_group) | ✅ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [SCIM\_MemberOf](/opengraph/extensions/scim/edges/scim_memberof) | [SCIM\_Group](/opengraph/extensions/scim/nodes/scim_group) | ✅ | | [SCIM\_Provisioned](/opengraph/extensions/scim/edges/scim_provisioned) | [GH\_ExternalIdentity](/opengraph/extensions/github/nodes/gh_externalidentity), [GH\_EnterpriseTeam](/opengraph/extensions/github/nodes/gh_enterpriseteam) | ✅ | ## Properties | Property | SCIM Property | Type | Description | Sample Value | | -------------- | ------------------- | ---------- | ---------------------------------------------- | -------------------------------------- | | `id` | `id` | `string` | The unique identifier of the group. | `2819c223-7f76-453a-919d-413861904646` | | `displayName` | `displayName` | `string` | The display name of the group. | `Sales Team` | | `created` | `meta.created` | `datetime` | The date and time the group was created. | `2010-01-23T04:56:22Z` | | `lastModified` | `meta.lastModified` | `datetime` | The date and time the group was last modified. | `2011-05-13T04:42:34Z` | ## Diagram ```mermaid theme={null} flowchart TD SCIM_Organization[fa:fa-building SCIM_Organization] SCIM_User[fa:fa-user SCIM_User] SCIM_Group[fa:fa-users SCIM_Group] SCIM_Organization -->|SCIM_Contains| SCIM_Group SCIM_User -->|SCIM_MemberOf| SCIM_Group SCIM_Group -->|SCIM_MemberOf| SCIM_Group ``` # SCIM_Organization Source: https://bloodhound.specterops.io/opengraph/extensions/scim/nodes/scim_organization An organization or tenant in the IdP Applies to BloodHound Enterprise and CE Represents a synchronized organization or tenant in the identity provider (IdP). An application may synchronize users and groups from multiple organizations or tenants via SCIM. The `SCIM_Organization` node serves as the root container for all SCIM resources belonging to a given tenant, providing a clear boundary for identity governance and access control. ## Edges The tables below list edges defined by the SCIM extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges No inbound edges are defined by the SCIM extension for this node. ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------- | | [SCIM\_Contains](/opengraph/extensions/scim/edges/scim_contains) | [SCIM\_User](/opengraph/extensions/scim/nodes/scim_user), [SCIM\_Group](/opengraph/extensions/scim/nodes/scim_group), [SCIM\_Role](/opengraph/extensions/scim/nodes/scim_role) | ✅ | ## Properties | Property | Type | Description | Sample Value | | ------------- | -------------- | ---------------------------------------------------- | -------------------------- | | `id` | `string` | The unique identifier of the organization or tenant. | `contoso.com` | | `displayName` | `string` | The display name of the organization or tenant. | `Contoso` | | `url` | `string (uri)` | The URL of the organization or tenant in the IdP. | `https://contoso.com/scim` | ## Diagram ```mermaid theme={null} flowchart TD SCIM_Organization[fa:fa-building SCIM_Organization] SCIM_User[fa:fa-user SCIM_User] SCIM_Group[fa:fa-users SCIM_Group] SCIM_Role[fa:fa-id-badge SCIM_Role] SCIM_Organization -->|SCIM_Contains| SCIM_User SCIM_Organization -->|SCIM_Contains| SCIM_Group SCIM_Organization -->|SCIM_Contains| SCIM_Role ``` # SCIM_Role Source: https://bloodhound.specterops.io/opengraph/extensions/scim/nodes/scim_role A role assigned to users Applies to BloodHound Enterprise and CE Represents a role derived from the `roles` attribute of SCIM users. In SCIM, roles are typically multi-valued string attributes on user resources rather than standalone objects. To enable graph-based analysis, we create `SCIM_Role` nodes to represent each unique role, allowing visibility into which users share the same role assignments across the organization. ## Edges The tables below list edges defined by the SCIM extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ---------------------------------------------------------------- | ------------------------------------------------------------------------ | ----------- | | [SCIM\_Contains](/opengraph/extensions/scim/edges/scim_contains) | [SCIM\_Organization](/opengraph/extensions/scim/nodes/scim_organization) | ✅ | | [SCIM\_HasRole](/opengraph/extensions/scim/edges/scim_hasrole) | [SCIM\_User](/opengraph/extensions/scim/nodes/scim_user) | ✅ | ### Outbound Edges No outbound edges are defined by the SCIM extension for this node. ## Properties | Property | SCIM Property | Type | Description | Sample Value | | -------- | ------------- | -------- | ---------------------------------- | ------------------------------------------------------------------ | | `id` | `User.roles` | `string` | The unique identifier of the role. | `8de4e0ea7370e4e60a521379c9edf3253afcba7660e647f3aa788e49e8993d1a` | | `name` | `User.roles` | `string` | The name of the role. | `Sales` | ## Diagram ```mermaid theme={null} flowchart TD SCIM_Organization[fa:fa-building SCIM_Organization] SCIM_User[fa:fa-user SCIM_User] SCIM_Role[fa:fa-id-badge SCIM_Role] SCIM_Organization -->|SCIM_Contains| SCIM_Role SCIM_User -->|SCIM_HasRole| SCIM_Role ``` # SCIM_User Source: https://bloodhound.specterops.io/opengraph/extensions/scim/nodes/scim_user A user account provisioned via SCIM Applies to BloodHound Enterprise and CE Represents a user account provisioned via the [System for Cross-domain Identity Management (SCIM)](https://scim.cloud/) protocol. SCIM users are created and managed by cloud identity providers (IdPs) such as Okta or Entra ID, which synchronize user identities to downstream applications. A compromised SCIM user account may grant access to any application the user is provisioned to, and the `externalId` links back to the user's identity in the source IdP. ## Edges The tables below list edges defined by the SCIM extension only. Additional edges to or from this node may be created by other extensions. ### Inbound Edges | Edge Type | Source Node Types | Traversable | | ------------------------------------------------------------------ | ------------------------------------------------------------------------ | ----------- | | [SCIM\_Contains](/opengraph/extensions/scim/edges/scim_contains) | [SCIM\_Organization](/opengraph/extensions/scim/nodes/scim_organization) | ✅ | | [SCIM\_ManagerOf](/opengraph/extensions/scim/edges/scim_managerof) | [SCIM\_User](/opengraph/extensions/scim/nodes/scim_user) | ❌ | ### Outbound Edges | Edge Type | Destination Node Types | Traversable | | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | [SCIM\_HasRole](/opengraph/extensions/scim/edges/scim_hasrole) | [SCIM\_Role](/opengraph/extensions/scim/nodes/scim_role) | ✅ | | [SCIM\_ManagerOf](/opengraph/extensions/scim/edges/scim_managerof) | [SCIM\_User](/opengraph/extensions/scim/nodes/scim_user) | ❌ | | [SCIM\_MemberOf](/opengraph/extensions/scim/edges/scim_memberof) | [SCIM\_Group](/opengraph/extensions/scim/nodes/scim_group) | ✅ | | [SCIM\_Provisioned](/opengraph/extensions/scim/edges/scim_provisioned) | [GH\_ExternalIdentity](/opengraph/extensions/github/nodes/gh_externalidentity), [GH\_EnterpriseTeam](/opengraph/extensions/github/nodes/gh_enterpriseteam) | ✅ | ## Properties | Property | SCIM Property | Type | Description | Sample Value | | ----------------- | -------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------- | | `id` | `id` | `string` | Unique identifier for the SCIM resource as defined by the Service Provider; stable and non-reassignable. | `2819c223-7f76-453a-919d-413861904646` | | `externalId` | `externalId` | `string` | Identifier defined by the SCIM client for cross-system correlation. | `dschrute` | | `userName` | `userName` | `string` | Unique user identifier used for authentication or display; required. | `dschrute` | | `enabled` | `active` | `boolean` | Whether the user account is active. | `true` | | `displayName` | `displayName` / `name.formatted` | `string` | Display name for the user. | `Dwight Schrute` | | `givenName` | `name.givenName` | `string` | Given (first) name. | `Dwight` | | `familyName` | `name.familyName` | `string` | Family (last) name. | `Schrute` | | `middleName` | `name.middleName` | `string` | Middle name(s). | `Kurt` | | `honorificPrefix` | `name.honorificPrefix` | `string` | Honorific prefix (title). | `Mr.` | | `honorificSuffix` | `name.honorificSuffix` | `string` | Honorific suffix. | `Jr.` | | `title` | `title` | `string` | Job title. | `Assistant to the Regional Manager` | | `userType` | `userType` | `string` | Organization-to-user relationship type. | `Employee` | | `profileUrl` | `profileUrl` | `string (uri)` | URL to the user's profile page. | `https://example.com/dschrute` | | `mail` | `emails.primary` | `string` | Primary email address from `emails` where `primary=true`. | `dschrute@example.com` | | `otherMails` | `emails` | `string[]` | Secondary email addresses from other `emails` entries. | `["dschrute@contoso.com"]` | | `role` | `roles` | `string[]` | Role names from the user's `roles` attribute. | `["Sales", "Management"]` | | `employeeNumber` | `employeeNumber` | `string` | Enterprise user employee number. | `12345` | | `organization` | `organization` | `string` | Enterprise user organization name. | `Contoso` | | `department` | `department` | `string` | Enterprise user department name. | `Sales` | | `managerId` | `manager.managerId` | `string` | Identifier of the user's manager. | `2819c223-7f76-453a-919d-413861904646` | | `created` | `meta.created` | `datetime` | Resource creation timestamp. | `2010-01-23T04:56:22Z` | | `lastModified` | `meta.lastModified` | `datetime` | Resource last modified timestamp. | `2011-05-13T04:42:34Z` | Most attributes use 1:1 mapping, but some, such as `mail` and `otherMails`, are transformed from multi-valued SCIM attributes like `emails`. ## Diagram ```mermaid theme={null} flowchart TD SCIM_Organization[fa:fa-building SCIM_Organization] SCIM_User[fa:fa-user SCIM_User] SCIM_Group[fa:fa-users SCIM_Group] SCIM_Role[fa:fa-id-badge SCIM_Role] SCIM_Organization -->|SCIM_Contains| SCIM_User SCIM_User -->|SCIM_MemberOf| SCIM_Group SCIM_User -->|SCIM_HasRole| SCIM_Role SCIM_User -->|SCIM_ManagerOf| SCIM_User ``` # Overview Source: https://bloodhound.specterops.io/opengraph/extensions/scim/overview Learn about the SCIM extension schema for BloodHound, representing SCIM-provisioned users, groups, and roles in the graph. Applies to BloodHound Enterprise and CE The SCIM (System for Cross-domain Identity Management) protocol is used by various cloud identity providers (IdPs), such as Okta or Entra ID, to provision user accounts and groups to and from applications. This OpenGraph extension schema allows BloodHound to represent SCIM-provisioned users and groups as nodes in the graph. By modeling SCIM as a shared, technology-neutral layer, BloodHound avoids the need to introduce technology-specific edges for each integration (such as Okta+GitHub, Entra+GitHub, or Entra+SalesForce). SCIM_Users of a SCIM_Group combined to a GH_EnterpriseTeam The SCIM extension is a **schema-only** extension — it does not include a collector. SCIM nodes and edges are produced by other collectors such as the [OpenHound Okta and GitHub collectors](/openhound/overview#collectors). Even in BloodHound Enterprise tenants where GitHub and Okta are supported as built-in extensions, you must still upload the SCIM extension schema separately. ## Graph Model The SCIM extension defines a small, focused model with four node types and five edge types. See the [extension schema](/opengraph/extensions/scim/schema) for the full details. An **SCIM\_Organization** represents a tenant in the identity provider and acts as the top-level container. It **contains** the three other node types: **SCIM\_User** (a user account provisioned via SCIM), **SCIM\_Group** (a group provisioned via SCIM), and **SCIM\_Role** (a role that can be assigned to users). Users and groups can be **members of** groups, and users can be **assigned to** roles. A user can also be marked as the **manager of** another user. The key edge that ties SCIM to other extensions is **SCIM\_Provisioned**, which connects a SCIM resource to a node in another extension's graph — for example, linking an Okta user (via SCIM) to the corresponding GitHub user. ## Getting Started 1. Download the SCIM extension schema from the [bloodhound-scim-extension](https://github.com/SpecterOps/bloodhound-scim-extension) repository. 2. Upload the SCIM schema to your BloodHound instance alongside the extension schemas for the collectors you are using (for example, Okta or GitHub). In BloodHound Enterprise v9.3.0 and later, some extensions (such as GitHub, Jamf, and Okta) are pre-installed. Verify that these are installed before you upload the SCIM companion schema. 3. Run the relevant collectors — they will produce SCIM nodes and edges automatically. ## References * [SCIM Extension Schema (GitHub)](https://github.com/SpecterOps/bloodhound-scim-extension) * [Okta Extension](/opengraph/extensions/okta/overview) * [GitHub Extension](/opengraph/extensions/github/overview) * [SCIM Schema Reference](/opengraph/extensions/scim/schema) # Schema Source: https://bloodhound.specterops.io/opengraph/extensions/scim/schema SCIM extension definition schema Applies to BloodHound Enterprise and CE ## Metadata **Name:** SOSCIM
**Display Name:** SCIM Extension (by SpecterOps)
**Version:** v1.2.1
**Namespace:** SCIM
**Environment Kind:** SCIM\_Organization
**Source Kind:** SCIM This file is automatically generated from the [extension definition schema file](https://github.com/SpecterOps/bloodhound-scim-extension/blob/main/bh-scim-extension.json). ## Nodes | Icon | Node Kind | Display Name | | ------------------------------- | ------------------------------------------------------------------------ | ----------------- | | SCIM_Group | [SCIM\_Group](/opengraph/extensions/scim/nodes/scim_group) | SCIM Group | | SCIM_Organization | [SCIM\_Organization](/opengraph/extensions/scim/nodes/scim_organization) | SCIM Organization | | SCIM_Role | [SCIM\_Role](/opengraph/extensions/scim/nodes/scim_role) | SCIM Role | | SCIM_User | [SCIM\_User](/opengraph/extensions/scim/nodes/scim_user) | SCIM User | ## Edges | Relationship Kind | Traversable | Description | | ---------------------------------------------------------------------- | :---------: | ----------------------------------------------- | | [SCIM\_Contains](/opengraph/extensions/scim/edges/scim_contains) | ✅ | Organization contains a SCIM resource | | [SCIM\_HasRole](/opengraph/extensions/scim/edges/scim_hasrole) | ✅ | User is assigned to a role | | [SCIM\_ManagerOf](/opengraph/extensions/scim/edges/scim_managerof) | ❌ | User is a manager of another user | | [SCIM\_MemberOf](/opengraph/extensions/scim/edges/scim_memberof) | ✅ | User or group is a member of a group | | [SCIM\_Provisioned](/opengraph/extensions/scim/edges/scim_provisioned) | ✅ | SCIM resource is provisioned to a target system | # OpenGraph FAQ Source: https://bloodhound.specterops.io/opengraph/faq The following are common questions about OpenGraph Applies to BloodHound Enterprise and CE Yes you can! You can find all the details [here](/opengraph/developer/custom-icons) Yes you can! You can remove generic data by using one of the following three options: 1. Cypher commands in the Explore UI Requires `enable_cypher_mutations: true` in config. For more info [click here](/manage-bloodhound/bh-config#enable-cypher-mutations) 2. Admin UI → Database Management page The following checkboxes will be displayed: * All Data * Active Directory Data * HasSession edges Use the **HasSession edges** option when you need to refresh time-sensitive session data without deleting the rest of the collected graph. Manual **HasSession** edge deletion is generally only necessary in BloodHound Community. In BloodHound Enterprise, **HasSession** edges reconcile automatically by [retention](/collect-data/enterprise-collection/data-retention#data-retention) and only need manual deletion when you want an immediate session data refresh. * Azure Data * … X Data There will be 0 or more checkboxes here that allow you to delete any data that had been ingested with a `source_kind` provided. * Sourceless Data Checking this box will delete all entities that do not have a kind that can be found in the `source_kinds` table. You can observe the `source_kinds` your BloodHound instance is currently aware of by calling `GET /api/v2/graphs/source_kinds`. 3. API: [`/api/v2/clear-database`](/reference/database/delete-your-bloodhound-data) Yes! BloodHound supports **Search** and **Pathfinding** for [*structured*](/opengraph/extensions/manage#structured-graphs) graphs. For [*generic*](/opengraph/extensions/manage#generic-graphs) graphs, only **Search** is supported. Pathfinding, findings, and analysis require traversable relationship kinds defined in an extension definition schema. Generic graph data does not define traversability and is treated as non-traversable. All forms of OpenGraph data support Cypher querying in general. This usually happens when a node's object ID contains a colon (`:`). Colons are not currently supported in object IDs in BloodHound. * You cannot enter an object ID that contains a colon in the **Search** bar. * You can still select a node by name, even when its object ID contains a colon. * If you select a node by name, **Search** or **Pathfinding** can complete, but the node disappears from the search bar afterward. To avoid this issue, use OpenGraph node IDs that do not contain colons. Have you built a cool project using OpenGraph and want it featured here? Already got your project in the list and need to update something? Open a ["Library Change" issue](https://github.com/SpecterOps/bloodhound-docs/issues) on the BloodHound Docs repo and we'll get it added for you! Submissions from the community will have a icon next to them while those by SpecterOps employees will have a SpecterOps icon. Slow ingestion speeds are commonly seen when using a Neo4j graph database and are typically resolved by switching to a PostgreSQL backend. For full, officially supported OpenGraph functionality, use a PostgreSQL backend (see [Requirements](/opengraph/best-practices#requirements)). To switch to a PostgreSQL graph database, set `"graph_driver": "pg"` in your `bloodhound.config.json` file and restart the BloodHound services/containers. You will need to re-ingest any data into the new database. The initial OpenGraph implementation introduced the `source_kind` metadata field to let you create a source and quickly apply it to all nodes in the payload. This preliminary process applies the `source_kind` to all nodes in the payload—whether you define or reference them—which can create unintended side-effects when building hybrid paths that connect OpenGraph nodes to Active Directory (AD) or Azure (AZ) nodes. If you reference AD/AZ nodes when defining an edge in an OpenGraph payload that uses this metadata field, the system inadvertently applies the `source_kind` to those non-OpenGraph nodes. If you delete this source and its OpenGraph data, then those AD/AZ nodes are also deleted. A more comprehensive solution is currently being designed to prevent this issue. The process described below is a workaround until the new design is implemented. **What this means:** When you delete OpenGraph data that references AD/AZ nodes with an OpenGraph `source_kind`, those AD/AZ nodes may also be deleted even though they are part of the core AD/AZ graph. **Recommended workaround:** Use a two-step upload process when building hybrid paths. * **First payload (isolated subgraph)**: Upload only your OpenGraph nodes and edges and set `source_kind` in `metadata`. Ensure that every node has at least one edge within this payload (for example, connect nodes to each other or to a root node and add a containment edge, such as `(:OGRoot)-[:OGContains]->(:YourNode)`). Do not link to AD/AZ objects in this payload. * **Second payload (linking AD/AZ nodes)**: Link the isolated subgraph's nodes to the AD/AZ nodes that connect your subgraph to existing AD/AZ entities. This avoids cross-source side effects when `source_kind` is present. Do **not** define `source_kind` in the second payload. # BloodHound Community Extensions Source: https://bloodhound.specterops.io/opengraph/library Explore extensions created by the community and SpecterOps that extend the coverage of BloodHound with OpenGraph. Applies to BloodHound Enterprise and CE Have you built a cool extension using OpenGraph and want to feature it on this page? Is your extension already in the list and you need to update something? Open a ["Library Change" issue](https://github.com/SpecterOps/bloodhound-docs/issues) on the BloodHound Docs repo and someone from the team will review it and get back to you! * icons represent community extensions that leverage OpenGraph to extend BloodHound's coverage and capabilities. * icons represent SpecterOps extensions that can be used as-is or serve as examples and inspiration for your own OpenGraph extensions. All code linked via this library is provided “as is,” without review, approval, or endorsement by SpecterOps, regardless of authorship. It has not been audited for accuracy, security, or fitness for any purpose. Use at your own risk. You are solely responsible for testing, validating, and ensuring the code meets your requirements before use in any environment. SpecterOps is not responsible for any damages, losses, or security issues arising from the use of any linked code. ## OpenGraph Library }> #### 1PassHound **Description** The 1Password for Business OpenGraph extension lets you bring your [1Password](https://1password.com/) ACL data into BloodHound's graph-analysis framework. Whether you're auditing permissions, responding to incidents, or simply exploring your 1Password configuration, this extension brings clarity, control and rich visualization to your vaults and items. **Authors/Maintainers** * [Jared Atkinson](https://x.com/jaredcatkinson) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SpecterOps/1PassHound](https://github.com/SpecterOps/1PassHound) #### AnsibleHound **Description** AnsibleHound is a BloodHound OpenGraph collector for [Ansible AWX](https://github.com/ansible/awx) and [Ansible Tower](https://docs.ansible.com/ansible-tower/). The collector is designed to map the structure and permissions of your organization into a navigable attack-path graph. **Authors/Maintainers** * [Ramoreik](https://github.com/Ramoreik) * [s-lck](https://github.com/s-lck) **Repo** * [https://github.com/TheSleekBoyCompany/AnsibleHound](https://github.com/TheSleekBoyCompany/AnsibleHound) }> #### ADAttributeHound **Description** ADAttributeHound is an [OpenGraph](/opengraph/overview) extension for BloodHound that exports Active Directory custom attributes as node properties. BloodHound ingestion will make the properties merge with existing nodes otherwise it creates new nodes. ADAttributeHound enables targeted attribute collection for SharpHound Enterprise and SharpHound CE, which has the flag `-CollectAllProperties`. ![](https://github.com/martinsohn/ADAttributeHound/blob/main/adattributehound.png) **Authors/Maintainers** * [Martin Sohn Christensen](https://x.com/martinsohndk) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/martinsohn/ADAttributeHound](https://github.com/martinsohn/ADAttributeHound) #### ManagerOfHound **Description** ManagerOfHound is an OpenGraph extension for BloodHound that collects manager-subordinate relationships from Active Directory and exports them as custom "ManagerOf" edges for BloodHound ingestion. Some organizations implement self-service portals where managers can control the user accounts of their subordinates (e.g. password resets). This can create implicit privilege escalation paths not captured by the default BloodHound edges. ManagerOfHound makes these hidden relationships visible through OpenGraph, enabling security teams to identify and assess novel attack paths in their environment. Demonstration available in the [@SpecterOps #BloodHoundBasics post on X](https://x.com/SpecterOps/status/1969104194012406144) **Authors/Maintainers** * [Martin Sohn Christensen](https://x.com/martinsohndk) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/martinsohn/ManagerOfHound](https://github.com/martinsohn/ManagerOfHound) #### GhostHound **Description** GhostHound is a BloodHound OpenGraph extension for Active Directory tombstone reanimation. SharpHound and other AD collectors skip `CN=Deleted Objects` entirely, so deleted objects (tombstones) are invisible to standard attack-path analysis, even though the AD Recycle Bin and tombstone reanimation mechanisms let a sufficiently privileged principal restore one and take over whatever identity it represents. GhostHound enumerates tombstones over LDAP with the `SHOW_DELETED`/`SHOW_DEACTIVATED_LINK` controls, determines who holds the Reanimate-Tombstones right, and checks whether a tombstone's group memberships (e.g. Domain Admins) are still recoverable. It emits an OpenGraph payload plus the `model.json` extension definition BloodHound needs to render it. Implemented as a Rust workspace: LDAP collection plus a from-scratch `nTSecurityDescriptor`/DACL/ACE parser, no `unsafe` code, fuzzed with cargo-fuzz. **Authors/Maintainers** * [João Victor Botelho Gonçalves](https://www.linkedin.com/in/joao-victor-botelho/) **Repo** * [https://github.com/JVBotelho/ghosthound](https://github.com/JVBotelho/ghosthound) #### ProfileHound **Description** ProfileHound is a post-escalation tool that helps find and achieve red-teaming objectives by locating user profiles on machines. It builds a new edge called `HasUserProfile`, which determines if a user profile exists on a computer. This edge allows operators to make informed decisions about which computers to target for looting secrets. ProfileHound collects information from the `\\\C$\Users\` directory on domain machines and checks the SIDs for domain users. It keeps track of the created and last modified timestamps to add to the edge's properties. **Authors/Maintainers** * [Chris Haller](https://www.linkedin.com/in/christopher-haller/) @[Omada Technologies](https://omadatechnologies.com/) **Repo** * [https://github.com/m4lwhere/profilehound](https://github.com/m4lwhere/profilehound) #### WinSSHound **Description** WinSSHound maps lateral movement paths through misconfigured native and third-party SSH servers in Active Directory environments. **Authors/Maintainers** * [Robin Unglaub](https://www.linkedin.com/in/robin-unglaub/) @[ProSec GmbH](https://www.prosec-networks.com/) **Repo** * [https://github.com/1r0BIT/WinSSHound](https://github.com/1r0BIT/WinSSHound) #### IAMhounddog **Description** A tool to help pentesters quickly identify privileged principals and second-order privilege escalation opportunities in unfamiliar AWS environments. Creates OpenGraph-compatible IAM to resource models that can be ingested and used in BloodHound CE along with pre-written queries to identify common misconfigurations. **Authors/Maintainers** * [Nathan Tucker](https://github.com/vntucker) @[Virtue Security](https://www.virtuesecurity.com/) **Repo** * [https://github.com/VirtueSecurity/IAMhounddog](https://github.com/VirtueSecurity/IAMhounddog) }> #### AtlassianHound **Description** This extension collects foundational Jira and Confluence access data and exports it in the BloodHound OpenGraph format using the [bhopengraph](https://github.com/p0dalirius/bhopengraph/tree/main) Python library (gr33ts @[p0dalirius](https://x.com/podalirius_)). This extension should support both Atlassian Cloud and on-premises deployments, however it has been tested on Atlassian Cloud deployments only; contributions for on-premises deployments are welcome. **Authors/Maintainers** * [Craig Wright](https://x.com/werdhaihai) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/werdhaihai/AtlassianHound](https://github.com/werdhaihai/AtlassianHound) }> #### DuoHound **Description** DuoHound is a Python-based collector that extracts data from Duo Security's Admin API and converts it to BloodHound's OpenGraph format. This data can be used to visualize MFA relationships, identify enrollment gaps, and analyze application access paths. **Authors/Maintainers** * [Jacob Julian](https://www.linkedin.com/in/jacobjulian/) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/julian1j/DuoHound/](https://github.com/julian1j/DuoHound/) }> #### AIHound **Description** AIHound is an AI credential and secrets scanner that can export results as OpenGraph JSON for BloodHound to visualize attack paths across AI tools, services, and datastores. **Authors/Maintainers** * [dfirdeferred](https://x.com/dfirdeferred) @[Netwrix](https://www.netwrix.com/en/) **Repo** * [https://github.com/netwrix/AIHound](https://github.com/netwrix/AIHound) #### SecretHound **Description** SecretHound converts secret scanning results from various sources into a BloodHound OpenGraph format. You can read the associated blog [here](https://specterops.io/blog/2025/11/13/taming-the-attack-graph-a-many-subgraphs-approach-to-attack-path-analysis/). It leverages @p0dalirius's [bhopengraph](https://github.com/p0dalirius/bhopengraph) library. This extension's primary goal is to expand the graph quickly using a single edge: `ContainsCredentialsFor`. Currently, SecretHound will map secrets to 141 technology subgraphs (using the default `taxonomy/taxonomy.json` system). This translates to potentially 141 possible hybrid attack paths. It includes existing subgraphs accessible through the `kind` array values of: `AZBase` for Azure/Entra ID, `GHBase` for GitHub, and `GCPBase` for Google Cloud Platform. Generic secrets get mapped to an abstract `kind` (i.e., `StargateNetwork`) that is a catchall. Most of the testing was around git repositories. **Supported Scanners:** * GitHub Secret Scanning * NoseyParker * TruffleHog * Nemesis **Authors/Maintainers** * [JD Crandell](https://x.com/c0kernel) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/C0KERNEL/SecretHound](https://github.com/C0KERNEL/SecretHound) #### CyberArkHound **Description** Export CyberArk PVWA data (users, groups, safes, accounts and permissions) into a BloodHound-compatible OpenGraph JSON file for security analysis and attack path visualization. The refactored implementation separates concerns into modules for client access, graph construction, export serialization, and a clean CLI entrypoint. **Authors/Maintainers** * Javier Azofra @Siemens Healthineers * Julian Garcia @Siemens Healthineers **Repo** * [https://github.com/jazofra/CyberArkHound/tree/main](https://github.com/jazofra/CyberArkHound/tree/main) #### Dop2Mop **Description** Dop2Mop is a proof-of-concept OpenGraph collector for BloodHound that maps attack paths from DevOps to MLOps infrastructure. It collects data across GitHub, Azure DevOps, Azure ML, and AWS SageMaker, then outputs BloodHound-compatible OpenGraph JSON for visualization and analysis. **Authors/Maintainers** * [Brett Hawkins](https://x.com/h4wkst3r) @[Armadin](https://armadin.com/) **Repo** * [https://github.com/h4wkst3r/Dop2Mop](https://github.com/h4wkst3r/Dop2Mop) }> #### EntraAuthPolicyHound **Description** This PoC community extension provides a sample `PowerShell` script that collects Microsoft Entra ID permissions related to [Temporary Access Passes (TAPs)](https://learn.microsoft.com/en-us/entra/identity/authentication/howto-authentication-temporary-access-pass) and [Passkeys (FIDO2 security keys or mobile devices)](https://learn.microsoft.com/en-us/entra/identity/authentication/how-to-enable-passkey-fido2) and exports the data in [BloodHound OpenGraph](https://specterops.io/opengraph/) format. **Authors/Maintainers** * [Michael Grafnetter](https://x.com/mgrafnetter) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/MichaelGrafnetter/EntraAuthPolicyHound](https://github.com/MichaelGrafnetter/EntraAuthPolicyHound) #### EntraSSSOHound **Description** Entra ID Seamless Single Sign-On (Seamless SSO) is a feature that non-interactively signs users into cloud applications whenever they are connected to Active Directory. EntraSSSOHound extends BloodHound CE with OpenGraph-format coverage, modeling how Active Directory computers can compromise synced Entra ID users through the trust established between the trusted on-premises computer and Entra ID. **Authors/Maintainers** * [Daniel Heinsen](https://x.com/hotnops) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SpecterOps/EntraSSSOHound](https://github.com/SpecterOps/EntraSSSOHound) #### IDMHound **Description** IDMHound is a collector for [FreeIPA](https://www.freeipa.org/) and Red Hat Identity Management environments. It gathers users, groups, domains, and computers, then maps their relationships based on host-based access controls (HBAC), sudoer rights, and group memberships. **Authors/Maintainers** * [Samuel Bovy](https://github.com/lvruibr) **Repo** * [https://github.com/lvruibr/idmhound](https://github.com/lvruibr/idmhound) }> #### GitHound **Description** GitHound is a BloodHound OpenGraph collector for [GitHub](https://github.com), designed to map your organization's structure and permissions into a navigable attack-path graph. With GitHound, you get a clear, interactive graph of your GitHub permissions landscape—perfect for security reviews, compliance audits, and rapid incident investigations. The collector is compatible with SpecterOps's [OpenGraph extension for GitHub](/opengraph/extensions/github/overview). **Authors/Maintainers** * [Jared Atkinson](https://x.com/jaredcatkinson) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SpecterOps/GitHound](https://github.com/SpecterOps/GitHound) #### openhound-github **Description** An [OpenHound](/opengraph/library#openhound) collector that collects resources from GitHub organizations and transforms them into usable nodes and edges for BloodHound. The collector is compatible with SpecterOps's [OpenGraph extension for GitHub](/opengraph/extensions/github/overview). **Authors/Maintainers** * [Joey Dreijer](https://github.com/d3vzer0) @[SpecterOps](https://specterops.io) * [Jonas Bülow Knudsen](https://github.com/JonasBK) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SpecterOps/openhound-github](https://github.com/SpecterOps/openhound-github) #### GitHoundPy **Description** A Python implementation of the GitHound collector for BloodHound OpenGraph. This extension aims to stay in sync with the main PowerShell version. Credit and tons of props to the SpecterOps team for the main implementation, for a detailed breakdown on the features check the main [repo](https://github.com/SpecterOps/GitHound) **Authors/Maintainers** * [Derrick Polakoff](https://www.linkedin.com/in/derrick-polakoff-54a34a237) @[CorvraLabs](https://github.com/CorvraLabs) **Repo** * [https://github.com/CorvraLabs/GitHoundPy](https://github.com/CorvraLabs/GitHoundPy) #### GitLabHound **Description** GitLabHound is a BloodHound OpenGraph collector for GitLab to generate a navigable attack-path graph composed of: * Core GitLab resources: users, groups, roles, projects, repositories, branches, CI/CD pipelines and jobs * Hybrid identities using SSO from Active Directory and Entra ID * Cross-cloud paths through OIDC trust relationships * Domain-joined Windows runners with shell executors * Secrets leaked through public resources * Renovate dependency bot abuse **Authors/Maintainers** * [Marc André Tanner](https://x.com/marcandretanner) @[Compass Security](https://compass-security.com/) **Repo** * [https://github.com/CompassSecurity/GitLabHound](https://github.com/CompassSecurity/GitLabHound) #### GCP-Hound **Description** GCP-Hound is an open-source security enumeration and privilege escalation discovery tool designed specifically for Google Cloud Platform environments. Built to integrate seamlessly with BloodHound's OpenGraph framework, it transforms complex GCP IAM relationships into interactive attack graphs. **Authors/Maintainers** * [Faiz Karim](https://in.linkedin.com/in/faiz-karim-8421bb195) **Repo** * [https://github.com/F41zK4r1m/GCP-Hound](https://github.com/F41zK4r1m/GCP-Hound) #### GCPwn **Description** GCPwn (gee-see-pwn) is a Google Cloud offensive security assessment framework built for workspace-driven credential handling, service enumeration, artifact collection, and graph-based attack-path analysis. It converts collected data into OpenGraph output for BloodHound-style analysis and can be expanded with verbose output, inheritance evaluation, and multi-permission edge logic. **Authors/Maintainers** * [WebbinRoot](https://www.linkedin.com/in/webbinroot/) @[NetSPI](https://www.netspi.com/) **Repo** * [https://github.com/NetSPI/gcpwn](https://github.com/NetSPI/gcpwn) }> #### JamfHound **Description** JamfHound is a Python 3 extension designed to collect and identify attack-paths in [Jamf Pro](https://www.jamf.com/products/jamf-pro/) tenants for privilege escalation and lateral movement based on existing object permissions. The collector saves data as JSON for ingestion into BloodHound to easily visualize and evaluate the risks of compromise within the Jamf Pro tenant. The collector is compatible with SpecterOps's [OpenGraph extension for Jamf](/opengraph/extensions/jamf/overview). **Authors/Maintainers** * [Lance Cain](https://www.linkedin.com/in/lance-cain-3ab262184) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SpecterOps/jamfhound](https://github.com/SpecterOps/jamfhound) #### openhound-jamf **Description** An [OpenHound](/opengraph/library#openhound) collector that collects Jamf Pro resources and transforms them into usable nodes and edges for BloodHound. The collector is compatible with SpecterOps's [OpenGraph extension for Jamf](/opengraph/extensions/jamf/overview). **Authors/Maintainers** * [Joey Dreijer](https://github.com/d3vzer0) @[SpecterOps](https://specterops.io) * [Jonas Bülow Knudsen](https://github.com/JonasBK) @[SpecterOps](https://specterops.io) * [Lance Cain](https://www.linkedin.com/in/lance-cain-3ab262184) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SpecterOps/openhound-jamf](https://github.com/SpecterOps/openhound-jamf) #### Bloodhound-Kube **Description** Bloodhound-Kube is a BloodHound OpenGraph collector for Kubernetes and OpenShift designed for fast collections from large-scale clusters, helping to visualize multi-step attack paths through a variety of Kubernetes objects and potential vectors. This tool also supports multiple commonly found custom resources: * CNI Specific network policies (Cilium & Calico) * External Secrets * Cert-Manager With more coming soon! **Authors/Maintainers** * [Don Cowan](https://www.linkedin.com/in/don-cowan/) @[IBM X-Force Red](https://www.ibm.com/x-force) **Repo** * [https://github.com/HackinAhab/bloodhound-kube](https://github.com/HackinAhab/bloodhound-kube) #### ClusterHound **Description** ClusterHound brings Kubernetes into BloodHound CE. It collects a cluster's topology and RBAC configuration with kubectl and outputs an OpenGraph JSON file for ingestion, turning multi-hop Kubernetes attack paths (service-account assumption, privilege escalation, host escape, and secret access) into traversable edges you can pathfind across and visualize. **Authors/Maintainers** * [Nathan Dove](https://www.linkedin.com/in/nathan-dove/) @[KPMG UK](https://kpmg.com/uk/en.html) * [Josh Hickling](https://www.linkedin.com/in/joshua-hickling/) @[KPMG UK](https://kpmg.com/uk/en.html) **Repo** * [https://github.com/dovesec/ClusterHound](https://github.com/dovesec/ClusterHound) #### GoLinHound **Description** GoLinHound is a BloodHound collector written in Go that discovers Linux and SSH attack paths. GoLinHound models local privilege escalation, SSH key and certificate authentication, agent forwarding, and paths connecting Linux hosts to Entra ID and Active Directory identities. **Authors/Maintainers** * [Lukas Klein](https://www.linkedin.com/in/klein-lukas/) **Repo** * [https://github.com/RantaSec/golinhound](https://github.com/RantaSec/golinhound) #### ExchangeHound **Description** ExchangeHound is a BloodHound OpenGraph extension for Microsoft Exchange on-premises environments. It models Exchange-specific objects and relationships, including mailbox delegation such as `FullAccess`, `SendAs`, `SendOnBehalf`, folder and public folder access, transport rules, and Exchange RBAC assignments so you can analyze graph-based abuse paths alongside existing Active Directory relationships. **Authors/Maintainers** * [Filip Wozniak](https://github.com/FilipPwn) **Repo** * [https://github.com/FilipPwn/exchangehound](https://github.com/FilipPwn/exchangehound) }> #### MSSQLHound **Description** Collects BloodHound OpenGraph compatible data from one or more [MSSQL](https://www.microsoft.com/en-us/sql-server) servers into individual temporary files, then zips them in the current directory. **Authors/Maintainers** * [Chris Thompson](https://x.com/_Mayyhem) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SpecterOps/MSSQLHound](https://github.com/SpecterOps/MSSQLHound) #### NetworkHound **Description** NetworkHound connects to Active Directory Domain Controllers, discovers computer objects, resolves hostnames to IP addresses using multiple DNS methods, performs comprehensive network scanning (port scanning, HTTP/HTTPS validation), and discovers shadow-IT devices. It then builds a detailed network topology graph in OpenGraph JSON format compatible with BloodHound. **Authors/Maintainers** * [Mor David](https://x.com/m0rd4vid) @[mordavid.com](https://www.mordavid.com/) **Repo** * [https://github.com/mordavid/NetworkHound](https://github.com/mordavid/NetworkHound) }> #### OktaHound **Description** OktaHound is an OpenGraph data collector for Okta Platform (also known as Okta Workforce Identity Cloud) environments that helps security professionals visualize and analyze their Okta configurations in BloodHound. It collects data about users, groups, applications, roles, and other entities within an Okta organization and represents them as nodes and edges in BloodHound's graph database. The collector is compatible with SpecterOps's [OpenGraph extension for Okta](/opengraph/extensions/okta/overview). **Authors/Maintainers** * [Michael Grafnetter](https://www.linkedin.com/in/grafnetter/) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SpecterOps/OktaHound](https://github.com/SpecterOps/OktaHound) #### openhound-okta **Description** An [OpenHound](/opengraph/library#openhound) collector that collects Okta resources and transforms them into usable nodes and edges for BloodHound. The collector is compatible with SpecterOps's [OpenGraph extension for Okta](/opengraph/extensions/okta/overview). **Authors/Maintainers** * [Joey Dreijer](https://github.com/d3vzer0) @[SpecterOps](https://specterops.io) * [Jonas Bülow Knudsen](https://github.com/JonasBK) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SpecterOps/openhound-okta](https://github.com/SpecterOps/openhound-okta) #### OCInferno **Description** OCInferno (O-C-Inferno) is an OCI offensive security assessment framework for workspace-driven credential handling, service enumeration, artifact download, and graph-based attack-path analysis. It includes a module to generate a custom OpenGraph data payload that you can upload to BloodHound to map privilege-escalation paths. **Authors/Maintainers** * [WebbinRoot](https://www.linkedin.com/in/webbinroot/) @[NetSPI](https://www.netspi.com/) **Repo** * [https://github.com/NetSPI/ocinferno](https://github.com/NetSPI/ocinferno) }> #### PingOneHound **Description** PingOne is an identity provider (IDP) product from the Ping Identity Corporation. PingOneHound collects the data necessary to: * Identify, analyze, and execute PingOne attack paths * Easily audit object-level permissions **Authors/Maintainers** * [Andy Robbins](https://www.linkedin.com/in/robbinsandy/) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/andyrobbins/PingOneHound](https://github.com/andyrobbins/PingOneHound) #### RacfHound **Description** RacfHound is a BloodHound ingestor for the RACF database in z/OS mainframes. The collector works through SSH and does not require an IRRDBU00 dump of the RACF DB. Current supported classes are USER, GROUP, SURROGAT, UNIXPRIV, FACILITY, DATASET, GCICSTRN and TCICSTRN. **Authors/Maintainers** * [Alexander Henriksson](https://linkedin.com/in/alexhenriksson) **Repo** * [https://github.com/4-L3X/racfhound](https://github.com/4-L3X/racfhound) #### runZeroHound **Description** Bring runZero Exposure Management into BloodHound via OpenGraph. Read their [initial blog post](https://www.runzero.com/blog/introducing-runzerohound/). **Authors/Maintainers** * [HD Moore](https://infosec.exchange/@hdm) @[runZero](https://www.runzero.com/) **Repo** * [https://github.com/runZeroInc/runZeroHound/](https://github.com/runZeroInc/runZeroHound/) #### ForceHound **Description** ForceHound maps Salesforce identity, permission, and access-control structures into an attack-path graph compatible with BloodHound Community Edition. It outputs OpenGraph v1 JSON that can be ingested by BloodHound CE to discover privilege-escalation paths, over-permissioned accounts, and hidden lateral-movement opportunities inside a Salesforce organization. **Authors/Maintainers** * [Weylon Solis](https://www.linkedin.com/in/weylon-solis) @[NetSPI](https://www.netspi.com/) **Repo** * [https://github.com/NetSPI/ForceHound](https://github.com/NetSPI/ForceHound) #### SFHound **Description** SFHound is a Salesforce identity and access management collector for BloodHound that enumerates users, profiles, permission sets, roles, groups, queues, connected apps, and object/field-level permissions, modeling them as a traversable attack graph to surface privilege escalation paths across a Salesforce organization. **Authors/Maintainers** * [Kaden 'kaib3r' Butt](https://www.linkedin.com/in/kaden-b-a428b9219/) **Repo** * [https://github.com/Khadinxc/sfhound](https://github.com/Khadinxc/sfhound) }> #### SnowHound **Description** An OpenGraph extension for [Snowflake](https://www.snowflake.com/) tenants that enables organizations to visualize their Snowflake environment by mapping key elements such as Users, Databases, Roles, Warehouses, and Integrations, along with the permissions that connect them. This provides a comprehensive view of access and potential attack paths within the Snowflake tenant, empowering security teams to identify vulnerabilities and better manage their environment's security posture. **Authors/Maintainers** * [Jared Atkinson](https://x.com/jaredcatkinson) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SpecterOps/SnowHound](https://github.com/SpecterOps/SnowHound) }> #### ConfigManBearPig **Description** A PowerShell collector for adding SCCM attack paths to [BloodHound](https://github.com/SpecterOps/BloodHound) with OpenGraph by Chris Thompson at [SpecterOps](https://x.com/SpecterOps) **Authors/Maintainers** * [Chris Thompson](https://x.com/_Mayyhem) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SpecterOps/ConfigManBearPig](https://github.com/SpecterOps/ConfigManBearPig) #### SCCM\_SQL\_Collector **Description** PoC script to collect SCCM attack paths from a SCCM site DB. Credits to [@sanjivkawa](https://x.com/sanjivkawa) for SQLRecon, which is where most of the scaffolding code to allow for connecting to SQL came from (thanks Sanj!) **Authors/Maintainers** * [Dave Cossa](https://x.com/G0ldenGunSec) **Repo** * [https://github.com/G0ldenGunSec/SCCM\_SQL\_Collector](https://github.com/G0ldenGunSec/SCCM_SQL_Collector) #### SCOMHound **Description** A BloodHound OpenGraph proof of concept for enumerating System Center Operations Manager (SCOM) infrastructure from Active Directory. image **Authors/Maintainers** * [Garrett Foster](https://x.com/unsigned_sh0rt) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SpecterOps/SCOMHound](https://github.com/SpecterOps/SCOMHound) }> #### TailscaleHound **Description** TailscaleHound is a BloodHound OpenGraph collector for Tailscale. It collects tailnet users, devices, groups, tags, ACLs, grants, SSH rules, routes, app connectors, services, invites, webhooks, and related control-plane metadata, then emits a BloodHound-compatible OpenGraph JSON file. **Authors/Maintainers** * [Andrew Gomez](https://github.com/KingOfTheNOPs) @[SpecterOps](https://specterops.io) * [Andrew Luke](https://github.com/Sw4mpf0x) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/KingOfTheNOPs/TailscaleHound](https://github.com/KingOfTheNOPs/TailscaleHound) #### vCenterHound **Description** vCenterHound connects to one or more [vCenters](https://www.vmware.com/products/cloud-infrastructure/vcenter/future-overview), collects infrastructure entities (Datacenter/Cluster/Host/VM/Network/Datastore, etc.) and permissions (Roles/Users/Groups/Assignments), then builds a BloodHound-compatible JSON graph with Custom Nodes/Edges. The model.json file provides icons and styles for these custom kinds. **Authors/Maintainers** * [Mor David](https://x.com/m0rd4vid) @[mordavid.com](https://www.mordavid.com/) **Repo** * [https://github.com/MorDavid/vCenterHound](https://github.com/MorDavid/vCenterHound) #### PrivHound **Description** PrivHound is a BloodHound OpenGraph collector that models Windows local privilege escalation as interconnected attack paths. Traditional privesc tools (WinPEAS, PowerUp, Seatbelt) report findings in isolation, but PrivHound connects them into exploitable multi-hop chains. It enumerates 29 categories of escalation vectors including weak service permissions, unquoted service paths, DLL hijacking, COM hijacking, credential discovery, cross-user profile access, WebClient relay, named pipe impersonation, WMI subscriptions, and service recovery commands. When credentials are discovered (via GPP passwords, PowerShell history, AutoLogon, unattend files, etc.), PrivHound validates them and analyzes what those users can actually access, building full cross-user escalation chains. The result is multi-hop privilege escalation paths that are visible, queryable with Cypher, and can be overlayed on top of existing SharpHound Active Directory attack paths. Includes 30 custom node kinds, 40+ edge kinds, MITRE ATT\&CK mapping, custom node icons, multi-endpoint support, and 50+ prebuilt Cypher queries. PowerShell-based and runs as standard user. **Authors/Maintainers** * [Arun Nair](https://x.com/dazzyddos) **Repo** * [https://github.com/dazzyddos/PrivHound](https://github.com/dazzyddos/PrivHound) #### ShareHound **Description** ShareHound is an OpenGraph collector that maps network shares, permissions, and paths at scale to help identify attack paths to network shares. **Authors/Maintainers** * [Remi Gascou](https://x.com/podalirius_) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/p0dalirius/sharehound](https://github.com/p0dalirius/sharehound) #### TaskHound **Description** Windows Privileged Scheduled Task Discovery Tool for fun and profit. TaskHound hunts for Windows scheduled tasks that run with privileged accounts and stored credentials. It enumerates tasks over SMB, parses XMLs, and identifies high-value attack opportunities through BloodHound export support. **Authors/Maintainers** * [Robin 'r0BIT' Unglaub](https://www.linkedin.com/in/robin-unglaub/) @[ProSec GmbH](https://www.prosec-networks.com) **Repo** * [https://github.com/1r0BIT/TaskHound](https://github.com/1r0BIT/TaskHound) ## Non-Attack Paths }> #### BloodSOCer **Description** BloodSOCer is a Python automation tool that aggregates threat intelligence data from multiple sources (Mitre ATT\&CK, Sigma rules, Atomic Red Team) and produces JSON files to ingest in BloodHound in OpenGraph format. BloodSOCer can also upload the files to BloodHound and set the icons for the custom objects if it has API Tokens defined in the configuration. Security analysts can then visualize the data from any angle, and a few Cypher queries are provided to help you get started. **Authors/Maintainers** * [Mat Saulnier](https://x.com/ScoubiMtl) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/Scoubi/BloodSOCer](https://github.com/Scoubi/BloodSOCer) ## OpenGraph Tools #### OpenHound **Description** OpenHound is a standardized framework for building OpenGraph collectors and converters. Built on [DLT](https://dlthub.com/docs/intro) (Data Load Tool), it provides a consistent workflow for collecting, processing, and converting data from any source into BloodHound-compatible graphs. OpenHound enforces a collect-first, convert-later pipeline: raw data collected from a source is always stored before transformation, ensuring reproducibility. Custom decorators simplify collector development with minimal boilerplate, while CLI commands and graph documentation are automatically generated for every source. Extend OpenHound with pre-built extensions for other services: * [openhound-github](https://github.com/SpecterOps/openhound-github) * [openhound-jamf](https://github.com/SpecterOps/openhound-jamf) * [openhound-okta](https://github.com/SpecterOps/openhound-okta) **Authors/Maintainers** * [Joey Dreijer](https://github.com/d3vzer0) @[SpecterOps](https://specterops.io) * [Emmanuel Robles](https://github.com/emmanuelrobles) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SpecterOps/OpenHound](https://github.com/SpecterOps/OpenHound) #### bhopengraph **Description** This module provides Python classes for creating and managing graph structures that are compatible with BloodHound OpenGraph. The classes follow the BloodHound OpenGraph schema and best practices. If you don't know about BloodHound OpenGraph yet, a great introduction can be found here: [https://bloodhound.specterops.io/opengraph/best-practices](https://bloodhound.specterops.io/opengraph/best-practices) The complete documentation of this library can be found here: [https://bhopengraph.readthedocs.io/en/latest/](https://bhopengraph.readthedocs.io/en/latest/) **Authors/Maintainers** * [Remi Gascou](https://x.com/podalirius_) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/p0dalirius/bhopengraph](https://github.com/p0dalirius/bhopengraph) #### BloodHoundOperator **Description** PowerShell client for BloodHound Community Edition and BloodHound Enterprise Learn more: * Release blog post: BloodHound Operator — Dog Whispering Reloaded * Presentation at PowerShell Conference Europe: The Dog Ate My Homework - A new chapter in my BloodHound adventures with PowerShell **Authors/Maintainers** * [SadProcessor](https://x.com/sadprocessor) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/SadProcessor/BloodHoundOperator](https://github.com/SadProcessor/BloodHoundOperator) #### BloodHound OpenGraph Helper Library **Description** A Python library for creating BloodHound OpenGraph JSON data that conforms to the BloodHound OpenGraph [data payload schema](/opengraph/developer/graph-data). **Authors/Maintainers** * [Luke Roberts](https://x.com/rookuu_) **Repo** * [https://github.com/rookuu/bloodhound-opengraph](https://github.com/rookuu/bloodhound-opengraph) #### gopengraph **Description** This module provides Go types and helpers for creating and managing graph structures that are compatible with BloodHound OpenGraph. If you don't know about BloodHound OpenGraph yet, a great introduction can be found here: [https://bloodhound.specterops.io/opengraph/best-practices](https://bloodhound.specterops.io/opengraph/best-practices) **Authors/Maintainers** * [Remi Gascou](https://x.com/podalirius_) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/TheManticoreProject/gopengraph/](https://github.com/TheManticoreProject/gopengraph/) #### flashingestor **Description** The main goals of this extension are: 1. Be a **full data ingestor** compatible with BloodHound CE 2. Be faster, less noisy, and more customizable than other collectors 3. Be a friendly TUI (terminal user interface) with progress tracking **Authors/Maintainers** * [Artur Marzano](https://github.com/Macmod) **Repo** * [https://github.com/Macmod/flashingestor](https://github.com/Macmod/flashingestor) #### HoundTrainer **Description** HoundTrainer is a Python-based utility designed to streamline the management of custom node types and Cypher queries within BloodHound. By automating JSON schema handling and API interactions, it provides an intuitive interface for maintaining your environment. Instead of wrestling with schemas or API endpoints, you can use HoundTrainer to keep your BloodHound instance organized and up-to-date effortlessly. * Create OG Custom Icon definitions from a CSV format * Supports CRUD operations for Custom Icons and Cypher queries * Export functionality to help with portability * Deleteall operation to 'reset' an environment For more information, check out the extension on GitHub or the blog post on [Medium](https://medium.com/@toneillcodes/managing-bloodhound-custom-icons-and-cypher-queries-with-houndtrainer-297834dad414). **Authors/Maintainers** * [Tom O'Neill](https://github.com/toneillcodes) **Repo** * [https://github.com/toneillcodes/HoundTrainer](https://github.com/toneillcodes/HoundTrainer) #### ScrappyDoo **Description** Opengraph-Compatible JSON Generator for BloodHound ScrappyDoo is a very simple self-hosted web app that can be used to generate BloodHound Opengraph-compatible JSON that can be mapped using BloodHound CE. **Authors/Maintainers** * [Hunter Orrantia](https://www.linkedin.com/in/horrantia) @[SpecterOps](https://specterops.io) **Repo** * [https://github.com/c0rdyc3ps/ScrappyDoo](https://github.com/c0rdyc3ps/ScrappyDoo) # OpenGraph Overview Source: https://bloodhound.specterops.io/opengraph/overview Learn how graph structure affects your OpenGraph experience and how to choose the right approach. Applies to BloodHound Enterprise and CE OpenGraph extends BloodHound beyond Active Directory and Entra ID by letting you collect and ingest data from other identity providers, developer platforms, device management systems, and custom data sources. OpenGraph is built on a flexible graph data model that supports custom nodes, edges, and properties. You can use it to model any system or environment as a graph to explore and analyze relationships and Attack Paths in BloodHound. ## Graph structure Graph structure affects what you can explore and analyze in BloodHound. To choose the right approach for your use case, it's important to understand how **generic** and **structured** graphs differ and how each works with OpenGraph projects and extensions. ### Generic graphs When OpenGraph was introduced in BloodHound v8.0.0, it required data payloads to conform to the basic node, edge, and metadata format only. It produced **generic graphs** to support basic exploration through Cypher queries (and later, node search). This enabled the BloodHound community to rapidly iterate and experiment with OpenGraph extensions to generate and ingest data payloads only. However, it also meant that OpenGraph data was not integrated with other BloodHound features and capabilities. ### Structured graphs In BloodHound v9.0.0, SpecterOps expanded the capabilities of OpenGraph extensions by adding support for an extension definition schema. After installing an extension definition schema *and* uploading a data payload that conforms to it, BloodHound produces a **structured graph**. Structured graphs enable enhanced features and a more integrated experience in BloodHound. When an extension provides a structured graph with an extension definition schema, saved Cypher queries can run even when some expected data types are absent. For example, in an Okta environment where application credentials are stored only as `ClientSecrets` (not JWKs), queries that reference `Okta_JWK` nodes would normally fail if those nodes are missing. With a structured graph, the [Application Credentials](/opengraph/extensions/okta/queries#application-credentials) saved query can reference both `Okta_JWK` and `Okta_ClientSecret` and still return expected results. See the table below for a comparison of features available in structured and generic graphs: | Feature | Structured | Generic | | --------------------------------------- | :--------------------------: | :--------------------------: | | Node search | | | | Cypher search | | | | Bulk data removal | | | | Pathfinding | | | | Relationship-based findings1 | | | | Remediation guidance1 | | | | Risk metrics1 | | | 1 Findings, remediation guidance, and risk metrics are available in Enterprise only. ## Next steps To get started with OpenGraph, choose your next step based on your goals: Install extension definition schemas and manage OpenGraph extensions in BloodHound. Start by defining your extension's schema, then format data payloads that conform to it. # Configure the Collector Source: https://bloodhound.specterops.io/openhound/collectors/github/collect-data Configure the GitHub collector to gather data from your GitHub organization or enterprise. Applies to BloodHound Enterprise and CE This page covers configuring the OpenHound GitHub collector for your GitHub organization or enterprise account. Use this page to choose an authentication method and configure the collector settings in the `secrets.toml` file or environment variables. ## Prerequisites Before you configure the GitHub collector, ensure that the following prerequisites are met: * OpenHound installed with the GitHub collector included. * For BloodHound Community Edition, install the [OpenHound CLI](/openhound/community). * For BloodHound Enterprise, [deploy](/openhound/enterprise) OpenHound as a container. * One of the following authentication setups configured: * [Enterprise GitHub App installation](/openhound/collectors/github/configure-enterprise-app) (recommended) * [Organization GitHub App installation](/openhound/collectors/github/configure-app) * [Fine-grained Personal Access Token (PAT)](/openhound/collectors/github/configure-pat) ## Configure OpenHound The GitHub collector needs different settings based on the authentication method you choose. You can set those values in one of two places: | Method | Set the values here | | --------------------- | --------------------------------------------- | | `secrets.toml` file | `[sources.source.github.credentials]` section | | Environment variables | `SOURCES__SOURCE__GITHUB__CREDENTIALS` | Click the tab that matches your authentication setup for details and example configurations. Use this option when you need enterprise-scoped collection. | Setting | Description | Environment Variable | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------- | | `client_id` | The GitHub App Client ID used to authenticate to the GitHub API. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__CLIENT_ID` | | `app_id` | The GitHub App ID used to authenticate to the GitHub API. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__APP_ID` | | `key_path` | The path to the GitHub App private key file. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__KEY_PATH` | | `enterprise_name` | The slug of the GitHub enterprise to collect data from. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__ENTERPRISE_NAME` | | `install_id` | The GitHub App Installation ID for the enterprise installation. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__INSTALL_ID` | | `api_uri` | The GitHub API base URI. For GitHub.com, use `https://api.github.com`. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__API_URI` | | `pat_token` | Optional classic PAT from an Enterprise Owner with the `read:enterprise` scope. OpenHound uses this token only to collect enterprise SAML SSO and SCIM data. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__PAT_TOKEN` | **`secrets.toml`** ```toml title="~/.dlt/secrets_github.toml" theme={null} [sources.source.github.credentials] client_id = "your-client-id" app_id = "your-app-id" key_path = "/path/to/private/key.pem" enterprise_name = "your-enterprise-slug" install_id = "12345678" api_uri = "https://api.github.com" pat_token = "ghp_xxxxxxxxxxxxxxxxxxxx" ``` **Environment variables** ```text theme={null} SOURCES__SOURCE__GITHUB__CREDENTIALS__CLIENT_ID=your-client-id SOURCES__SOURCE__GITHUB__CREDENTIALS__APP_ID=your-app-id SOURCES__SOURCE__GITHUB__CREDENTIALS__KEY_PATH=/path/to/private/key.pem SOURCES__SOURCE__GITHUB__CREDENTIALS__ENTERPRISE_NAME=your-enterprise-slug SOURCES__SOURCE__GITHUB__CREDENTIALS__INSTALL_ID=12345678 SOURCES__SOURCE__GITHUB__CREDENTIALS__API_URI=https://api.github.com SOURCES__SOURCE__GITHUB__CREDENTIALS__PAT_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx ``` The `pat_token` field is optional for enterprise collection, but strongly recommended. If you omit it, the collector continues to collect GitHub App-supported enterprise and organization data, but enterprise SAML SSO and SCIM nodes and relationships are not collected. Use this option for organization-scoped collection with higher GitHub API rate limits. | Setting | Description | Environment Variable | | ------------ | ----------------------------------------------------------------- | -------------------------------------------------- | | `org_name` | The name of the GitHub organization to collect data from. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__ORG_NAME` | | `app_id` | The GitHub App ID used to authenticate to the GitHub API. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__APP_ID` | | `client_id` | The GitHub App Client ID used to authenticate to the GitHub API. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__CLIENT_ID` | | `key_path` | The path to the GitHub App private key file. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__KEY_PATH` | | `install_id` | The GitHub App Installation ID for the organization installation. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__INSTALL_ID` | **`secrets.toml`** ```toml title="~/.dlt/secrets_github.toml" theme={null} [sources.source.github.credentials] org_name = "your-github-org" key_path = "/path/to/private/key.pem" app_id = "123456" client_id = "your-client-id" install_id = "12345678" ``` **Environment variables** ```text theme={null} SOURCES__SOURCE__GITHUB__CREDENTIALS__ORG_NAME=your-github-org SOURCES__SOURCE__GITHUB__CREDENTIALS__KEY_PATH=/path/to/private/key.pem SOURCES__SOURCE__GITHUB__CREDENTIALS__APP_ID=123456 SOURCES__SOURCE__GITHUB__CREDENTIALS__CLIENT_ID=your-client-id SOURCES__SOURCE__GITHUB__CREDENTIALS__INSTALL_ID=12345678 ``` Use this option for smaller environments or testing. | Setting | Description | Environment Variable | | ---------- | ------------------------------------------------------------------------------------ | ------------------------------------------------ | | `org_name` | The name of the GitHub organization to collect data from. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__ORG_NAME` | | `token` | The fine-grained Personal Access Token (PAT) used to authenticate to the GitHub API. | `SOURCES__SOURCE__GITHUB__CREDENTIALS__TOKEN` | **`secrets.toml`** ```toml title="~/.dlt/secrets_github.toml" theme={null} [sources.source.github.credentials] org_name = "your-github-org" token = "github_pat_xxxxxxxxxxxxx" ``` **Environment variables** ```text theme={null} SOURCES__SOURCE__GITHUB__CREDENTIALS__ORG_NAME=your-github-org SOURCES__SOURCE__GITHUB__CREDENTIALS__TOKEN=github_pat_xxxxxxxxxxxxx ``` ## Running OpenHound and Collecting Data After you set the required configuration parameters, [run](/openhound/community#collect) OpenHound to start the collector and collect data from your . The collector will generate JSON files in the output directory that can be uploaded to BloodHound for analysis. If your GitHub collection includes SCIM nodes and edges, upload the [SCIM extension schema](/opengraph/extensions/scim/overview) before importing the collected data. Large GitHub organizations or enterprises can trigger GitHub's API rate limits during collection. If you see failed or retried requests, tune the [HTTP request parameters](/openhound/configuration#http-request-parameters) to ride out rate limits instead of failing the run. # Configure an Organization GitHub App Source: https://bloodhound.specterops.io/openhound/collectors/github/configure-app Create a GitHub App in a GitHub organization for OpenHound data collection. Applies to BloodHound Enterprise and CE This page covers creating a GitHub App in a GitHub organization for OpenHound data collection. GitHub App installations provide a higher API rate limit of **15,000 requests per hour** compared to **5,000** for Personal Access Tokens (PATs). If you need enterprise-scoped collection for GitHub Enterprise, use [Configure an Enterprise GitHub App](/openhound/collectors/github/configure-enterprise-app). ## Install a GitHub App Follow these steps to create and configure a GitHub App for a single GitHub organization. Navigate to your **Organization Settings** > **Developer settings** > **GitHub Apps**. Click the **New GitHub App** button. 1. Configure the app with the following settings: * **GitHub App name**: Choose a unique name (e.g., `YourOrg-OpenHound`) * **Homepage URL**: We recommend pointing to the [OpenHound GitHub repository](https://github.com/SpecterOps/openhound-github) * **Webhook**: Clear **Active** because OpenHound does not require a webhook for collection * **Permissions**: Set the following permissions to **Read-only**: **Repository permissions** * Actions * Administration * Contents * Environments * Metadata * Secret scanning alerts * Secrets * Variables **Organization permissions** * Administration * Custom organization roles * Custom repository roles * Members * Personal access tokens * Personal access token requests * Secrets * Self-hosted runners * Variables 2. Under **Where can this GitHub App be installed?**, select **Only on this account**. Click **Create GitHub App** to create the app with the specified settings and permissions. Scroll down and click **Generate a private key**. Save the downloaded `.pem` file securely and record the **App ID** and **Client ID** shown on the app settings page. Click **Install App** in the left sidebar to install the app to your organization. ## Get the Installation Details 1. Navigate to your **Organization Settings** > **Third-party Access** > **GitHub Apps**. 2. Find the app you just created and click **Configure**. 3. The numeric value in the URL is the **App Installation ID**. 4. Click **App settings** to access the **Client ID** and **App ID**. ## Security Considerations * Store the `.pem` private key securely and never commit it to version control. * Rotate keys periodically and revoke old ones. * Only install the app on organizations that require collection. * Periodically verify the app has only the minimum required permissions. ## Next Steps After installing and configuring the GitHub App, proceed to [configure the collector](/openhound/collectors/github/collect-data) to start collection. # Configure an Enterprise GitHub App Source: https://bloodhound.specterops.io/openhound/collectors/github/configure-enterprise-app Create a GitHub App in a GitHub Enterprise account for OpenHound data collection. Applies to BloodHound Enterprise and CE This page covers creating a GitHub App in a GitHub Enterprise account so OpenHound can collect enterprise-scoped GitHub data. Use this flow when you need to install the app at the enterprise level and then reuse that same app across the organizations owned by the enterprise. To collect enterprise SAML Single Sign-On (SSO) and System for Cross-domain Identity Management (SCIM) data, you also create a GitHub Personal Access Token (classic) from an Enterprise Owner account. OpenHound uses this token only for the enterprise SSO and SCIM endpoints; all other GitHub data is collected through the GitHub App installation. For organization-only collection, use [Configure an Organization GitHub App](/openhound/collectors/github/configure-app). ## Before You Begin * Verify that you can create or manage GitHub Apps in the target enterprise account. * Verify that you can install GitHub Apps on the enterprise account and on each organization that OpenHound will collect. * Verify that an Enterprise Owner can create a classic PAT with the `read:enterprise` scope if you need enterprise SSO and SCIM data. * Identify the enterprise slug you will use during configuration, such as `your-enterprise-name` from `https://github.com/enterprises/your-enterprise-name`. ## Create the GitHub App Follow these steps to create a GitHub App that can be installed at the enterprise level. Navigate to your enterprise account homepage at `https://github.com/enterprises/`. From the enterprise homepage, click **Settings** > **GitHub Apps** > **New GitHub App**. 1. Configure the app with these settings: * **GitHub App name**: Choose a unique name, such as `OpenHound-Enterprise` * **Homepage URL**: We recommend pointing to the [OpenHound GitHub repository](https://github.com/SpecterOps/openhound-github) * **Webhook**: Clear **Active** unless you have a separate webhook requirement * **Permissions**: Set the following permissions to **Read-only**: **Repository permissions** * Actions * Administration * Contents * Environments * Metadata * Secret scanning alerts * Secrets * Variables **Organization permissions** * Administration * Custom organization roles * Custom repository roles * Members * Personal access tokens * Personal access token requests * Secrets * Self-hosted runners * Variables **Enterprise permissions** * Custom enterprise roles * Enterprise SCIM * Enterprise organization installation repositories * Enterprise organization installations * Enterprise single sign-on * Enterprise teams 2. Under **Where can this GitHub App be installed?**, select the option for organizations owned by your enterprise. Click **Create GitHub App**. On the app settings page, scroll to **Private keys** and click **Generate a private key**. Save the downloaded `.pem` file securely. On the same page, record the **App ID** and **Client ID**. OpenHound uses the **App ID**, **Client ID**, **key path**, **enterprise name**, **Installation ID**, and **API URI** in the GitHub enterprise app collector configuration. ## Install the GitHub App Install the same GitHub App in the enterprise account and in each organization you plan to collect. Open the GitHub App settings page, click **Install App**, select the enterprise account, and complete the installation. Record the enterprise **Installation ID** for the collector configuration. From the same GitHub App, install the app on every organization owned by the enterprise that you want OpenHound to collect. If GitHub prompts you to choose a repository scope, select **All repositories** unless you intentionally want a partial collection. The GitHub collector enterprise orchestration expects a real enterprise installation and then enumerates related organization installations for follow-on organization collection. ## Create a Classic PAT for SSO and SCIM GitHub Enterprise App installations are currently in preview and do not support all GitHub Enterprise REST APIs. The GitHub App installation token cannot access the enterprise SAML SSO and SCIM endpoints that OpenHound uses to collect enterprise identity configuration. Create a classic PAT when you need OpenHound to collect enterprise SSO and SCIM objects. The following video provides a walkthrough of creating a classic PAT for enterprise SSO and SCIM collection. Text instructions follow the video.