BloodHound Enterprise (BHE) recently saw the addition of a new, game-changing feature: open-ended Cypher searches. For those unfamiliar, Cypher is a declarative query language used for retrieving data from a graph database (in this case, Neo4j). As you’ll soon see, the nature of Cypher is one that helps drive the concept of relationships between nodes like those within BHE. Consequently, the addition of Cypher helps users bring forth the visualization of conceptual models you may hold about the relationship between principals and objects within Active Directory and Entra ID.
As a brief primer, I’ll note that the Cypher language can seem a little confusing at first — or maybe it’s just me. Generally, we don’t tend to visualize our search queries in the way that Cypher queries are constructed, but over time it begins to make sense as you dig into the way these relationships exist, are modeled, and consequently searched. Structurally, Cypher uses a Node
-Relationship
-Node
format as its basis. To demonstrate this, on the Cypher developer page we can find this example visual:
This query identifies a Person
with the name attribute Dan
and looks for another person using the variable whom
with the relationship attribute LOVES
. Then it returns whom
, which would provide us with the name(s) associated with whom
.
What does this mean for BloodHound Enterprise? This means that if you want to find an instance of a User
that has GenericAll
privileges over something like a Container
, you could specify MATCH p=(n:User)-[:GenericAll]->(c:Container) RETURN p
which might return something like:
Here we can visualize the relationship described as, “any instance where a User has GenericAll privileges on a Container.” And while this example may be simple, it should provide a basic understanding of how these queries work, what they can produce, and this will lead into leveraging them for developing better clarity over our Active Directory and Entra ID environments.
In this blog, I will further discuss how we at SpecterOps have seen our BHE customers leverage Cypher for additional value realization in both understanding Attack Paths and contextualizing their own enterprise environments for the purpose of greater security. I will include three specific use cases that we have identified as particularly useful, as well as the necessary queries, and a description of how these are beneficial for our customers. These include:
As a side note, to help simplify the output of some of these searches, I have used diagrams to demonstrate the “so what” rather than sharing a potential domain that is dealing with massive misconfiguration debt where the noise far outweighs the signal as this can obfuscate the desired intent.
Our first use case is identifying Domain Trusts that exist within an environment. For this one, we have pre-built the query into BHE, so it is an easy one for anyone to come back to regardless of your Cypher knowledge. Regarding pre-built queries, if you are a BHE user and are not familiar with this feature, it is found under the Explore
tab. Referencing the below image, select the Folder icon (1) which will open our Pre-built Searches
. Our specific query here, Map Domain Trusts
(2) can be selected which automatically populates the search window with the built-in query (3). Selecting Search
will then return a similar result should your own environment have several instances of domain trusts. Of course, results will vary across environments and if yours looks different, this is to be expected.
While this search is not directly linked to Tier Zero Attack Paths, it still has everything to do with understanding the security posture of an environment. We’ve discovered frequently that customers are surprised to find domains that they were either unaware of or didn’t realize the nature of the trust relationship that existed between domains. In the above figure, you’ll notice that most domains have a TrustedBy
relationship that goes in both directions. This may be normal, but perhaps there are some domains where this shouldn’t be the case. For example, the below figure demonstrates how there are two instances of one-way TrustedBy
relationships between QSCPROD.DOMAIN.CORP and SITRAKA.COM and TITANCORP.LOCAL, respectively. This may be the case in situations where an organization allows users in one domain to access resources from another domain, but not in the other direction. It may also be a misconfiguration where a trust relationship was never severed as an organization grows, and over time it is forgotten under the ever-increasing pile of other priorities. There are certainly many reasons; these are just a few.
BloodHound Enterprise currently includes over 30 of these Pre-built Searches which include categories like Domain Information, Domain Privileges, and Shortest Paths, each of which have context-providing queries that can shed some light on the nature of your own environment.
Of note, I will include the queries for each of these. And though this one is already in BHE, you can replicate it for yourself using the following:
MATCH p=(n:Domain)-[]->(m:Domain)
RETURN p
Our second use case pivots from the expected BloodHound relationship/path analysis and focuses on the improper allocation of administrative rights through naming convention analysis within an organization. Consider an environment where you may use a particular naming convention to separate users. An example might be to use a naming convention where a user is assigned a name like “j.smith” for their regular user account, and “j.smith.helpdesk” for an account where additional permissions are required to enable their ability to provide some form of support. In such a situation, it is reasonable to assume that “j.smith.helpdesk” and (for that matter) any other user with the “.helpdesk” suffix may be assigned certain privileges, such as “AdminTo” a set of various workstations. For the purpose of managing users, this certainly seems like it would provide some benefit. And ideally, this means that only those users with the proper naming convention would be granted the appropriate permissions. That is, “j.smith” should never be added to the group where they would have “AdminTo” privileges over a computer.
This assumes, however, that misconfigurations don’t take place. And if you work in the realm of IT security, you’re likely familiar with the range of misconfigurations that can occur. Even more so if you’re familiar with Active Directory and such environments where decades of different managers trying their best to keep AD afloat despite various business challenges pushing for faster changes, ad-hoc adjustments, and the resulting misconfiguration debt has resulted in some level of chaos.
One of those misconfigurations could easily occur where “j.smith” is accidentally added to the group where they now have “AdminTo” privileges, and despite the honest intention to add the “j.smith.helpdesk” user, “j.smith” has been added and is now over-privileged, presenting a potential attack path for an attacker. Fear not, however, for we can identify where these situations exist, and consequently squash these misconfigurations with the valid justification of, “They never should have had it in the first place.”
The query for this, as described above, is:
MATCH p=(m:User)-[r:AdminTo]->(n:Computer)
WHERE NOT m.name CONTAINS “.helpdesk”
RETURN m
For your own use case, you can replace the ".helpdesk"
title with anything else that makes sense for your environment (e.g., “admin”, “poweruser”, “bloodhound”). To keep this simple, I have included a graphic where three users with the -king
suffix have AdminTo
privileges over the Rohan computer. You may notice, however, that one of these users is not a -king
but an -advisor
and they were wrongly added to the group of users who have AdminTo
privileges over the Rohan computer.
Using our above query, changing the ".helpdesk”
to "-king”
(change the second line to: WHERE NOT g.name CONTAINS “-king”
), we would return a graph that shows only the following, which ignores those users that are supposed to have AdminTo
privileges and returns only the imposter:
In this case, or any with a similar naming convention setting, the justification for removing the misconfiguration is easy, straightforward, and likely self-justifying.
Our final use case returns us to a Tier Zero focus and an analysis of more complex paths that we can visualize using BHE. In this case, the requirements we have are:
Together, this allows us to see instances where bi-directional trust dramatically increases Tier Zero and expands the potential for undesirable Attack Paths within a forest. This further demonstrates value in that we can identify those areas where differences in domain management across a forest may be problematic within the larger enterprise environment.
It is problematic to begin with when non-Tier Zero users can reach Tier Zero, but our attack surface is dramatically increased when non-Tier Zero users in Domain B can reach Tier Zero in Domain A because of improper nesting of non-Tier Zero principals within Tier Zero groups. For real world examples, consider a company merger where two domains are joined and the parent company group, Domain Admins
adds the child company’s group Domain Admins
to the parent Domain Admins
group. Only, consider the situation where the child group has also included something like Workstation Admins
to their Domain Admins
group. While not necessarily common, the ability to identify where such nesting has occurred provides significant value in being able to spot where misconfigurations abound.
The query for this search is:
MATCH p=((n:Group)-[:MemberOf*..]->(t:Group))
WHERE n.domainsid <> t.domainsid AND coalesce(n.system_tags,"") CONTAINS ('tier_0') AND coalesce(t.system_tags,"") CONTAINS ('tier_0')
AND NOT n.objectid ENDS WITH "-512"
AND NOT n.objectid ENDS WITH "-519"
RETURN p
To provide some clarity on this query, we are looking for a relationship, p
, where:
n
is a member of group t
, with no constraint on the number of links between groups (*..
)n
and group t
are in separate domains (represented by the NOT EQUALS operator, <>
)n
and t
are both part of Tier Zeron
object IDs do not include either –512 (Domain Admin) or –519 (Enterprise Admin)Returning relationship, p
, will then show us those relationships that include nesting from external domains.
As an example of what this might look like, consider the MiddleEarth domain where the parent domain, MiddleEarth, has added both domain admins for Shire.MiddleEarth and Mordor.MiddleEarth to its group. This might have been fine within the original intent of nesting administrators under a certain group, but Mordor in its domain administration excellence has added its workstation administrators under its domain administrators group. And so now there is a nesting where workstation administrators within Mordor have been added to the Administrators group for MiddleEarth, which is great for Sauron and his desire to reclaim Tier 0 from Frodo, but not so great for the rest of the MiddleEarth domain.
The use of Cypher queries within BHE is quite extensive, and certainly not limited to these examples. As mentioned, we do include the “Pre-built Searches” which are an “easy button” for you, but with the ability to craft your own, the options for identifying relationships are virtually endless. For more information, the resources below will provide a useful starting point. For those already using BHE, please reach out to your Technical Account Manager should you need help solving any unique problems within your own BHE tenant.
1. BloodHound Enterprise, “Searching with Cypher”: https://support.bloodhoundenterprise.io/hc/en-us/articles/16721164740251-Searching-with-Cypher
2. Cypher 5 Manual: https://neo4j.com/docs/cypher-manual/current/introduction/
3. Cypher Cheat Sheet (Neo4j): https://neo4j.com/docs/cypher-cheat-sheet/5/neo4j-enterprise