Adding MSSQL to BloodHound with OpenGraph
Aug 4 2025
By: Chris Thompson • 27 min read
TL;DR MSSQLHound is a standalone PowerShell collector that adds 7 new nodes and 37 new MSSQL attack path edges to BloodHound using the new OpenGraph feature that was released in version 8.0. This post details my experience and lessons learned while designing and implementing MSSQL attack paths with OpenGraph and provides practical examples of how MSSQLHound can be used to research and discover new attack paths using BloodHound.
UPDATE EffectivePermissions SET Visibility = ‘1’ WHERE Unknown = ‘TRUE’
I started looking at MSSQL permissions so I could model some of the SCCM hierarchy TAKEOVER techniques that rely on control of the site database (stay tuned!). Using BloodHound’s new OpenGraph feature, which allows you to add your own nodes and edges to the graph from pretty much anything that’s in JSON format, I was able to write MSSQLHound, a standalone PowerShell collector, to visualize 7 new nodes and 37 new edges for MSSQL attack paths without ever having to touch the SharpHound or BloodHound codebases.
Just show me how to use the script
Just show me the new nodes and edges
Just show me the new attack path research and operational capabilities
I can’t express just how excited I am about this. It’s been my dream for years to be able to rapidly add whatever technology I’m researching at the time to BloodHound, and that’s finally a reality with the release of OpenGraph. It opens up a ton of possibilities for new attack path research and discovery in new platforms and technologies.
And the best part:
- You can write your collector or processor in any programming language.
- You no longer need to wait for SpecterOps to code or merge attack paths you want to see into the graph.
The engineering team is expanding OpenGraph’s capabilities rapidly, so look out for more features in the coming months. I’m hyped to see what the community comes up with!
To use OpenGraph, just massage your JSON into this format using any language you’d like:
{
"graph": {
"metadata": {
"source_kind": "<base_node_type>"
},
"nodes": [
{
"kinds": [
"<node_type>"
],
"id": "<node_unique_identifier>",
"properties": {
"<name>": "<value>",
"<list_name>": [
"<value1>",
"<value2>"
]
}
}
],
"edges": [
{
"kind": "<edge_type>",
"start": {
"value": "<source_node_unique_identifier>"
},
"end": {
"value": "<target_node_unique_identifier>"
},
"properties": {
"<name>": "<value>",
"<list_name>": [
"<value1>",
"<value2>"
]
}
}
]
}
}
That’s all there is to it. When you upload this to the File Ingest page or API, BloodHound takes care of the rest, and it’s exponentially faster than ingesting the same individual nodes and edges in Neo4j. Afterward, you can use cypher queries to identify attack paths that consist of your new nodes and edges.
You can read all about the new MSSQL nodes and edges below, but first I’d like to share my experience and lessons learned designing and implementing the MSSQL platform for BloodHound and how I discovered several new attack paths along the way.
MSSQL Refresher
If you’re like me and never learned the difference between server-level and database-level principals in MSSQL, this section is for you. Feel free to skip ahead if you’re familiar with MSSQL and its nuances.
Here is the entire MSSQL permission model as documented by Microsoft to provide some visual context for these concepts as you go. It’s worth giving a click just to see how wildly complex it is.
At a high level, Microsoft Structured Query Language Server (MSSQL Server) is a database management system (DBMS) consisting of:
- Server Level:
- the server instance, encompassing one installation of MSSQL Server
- accessed with logins that are assigned permissions either explicitly or through membership in server roles
- server roles are assigned permissions either explicitly or based on hardcoded values for fixed, built-in roles like
sysadmin
- Database Level
- one or more databases containing tables of information
- accessed with database users that are assigned permissions either explicitly or through membership in database roles
- database roles are assigned permissions either explicitly or based on hardcoded values for fixed, built-in roles like
db_owner
- database users can be mapped to a server-level login or may be created without one
Both server logins and database users can be explicitly defined within the MSSQL instance or map to Windows / Active Directory / Entra principals representing one or more objects (e.g., users, computers, or groups).
In other words:
- SQL logins and users use SQL Server Authentication
- Windows logins and users use Windows Authentication
Various methods of Microsoft Entra authentication are also available.
By default, only Windows logins can connect to the server. When mixed mode authentication is enabled, both SQL and Windows logins can connect to the server.
In either case, with the appropriate permissions, it is still possible to impersonate any login or user (SQL or Windows) and temporarily assume their permissions using the EXECUTE AS
statement.
The permissions in MSSQL Server are a hierarchy, with server-level permissions applied to the entire instance as well as database permissions applied to individual databases and objects within those databases. Permissions can be applied at the table or object level as well, but MSSQLHound stops at the database level so the graph doesn’t get so huge it can’t be queried.
Objects can also have an owner, which provides full control over the object, including the ability to grant permissions, change properties, and in many cases, impersonate or control access to the object.
OpenGraph Design
For a fundamental understanding and deeper dive into node and edge design patterns for attack path graphs, I highly encourage you to check out Andy Robbins’s recent post, Attack Graph Model Design Requirements and Examples.
My first challenge to tackle was designing the graph model for MSSQL, so I started by researching which MSSQL principals and permissions can be abused to escalate privileges, execute code on the host, etc.
I kept notes of every permission I tested in a spreadsheet, including:
- the permission name
- whether it existed on SQL Server 2005 (oldest we care about) and SQL Server 2022 (latest)
- whether it was abusable
- what fixed roles implicitly held the permission
- what edge(s) should be created from that permission
- what node types could be at the start and end of that edge
- any dependencies or other conditions that must be met for the edge to be created
- specific commands to abuse the permission/edge
I wish I took better notes about what I tried that did not work or was not abusable. I ended up repeating myself quite a bit because I couldn’t remember if I tried things like setting TRUSTWORTHY
to On
with only ALTER
permission on the database or adding a member to a fixed role I wasn’t a member of.
Next, I needed to draft what I wanted the graph to look like when pathfinding in BloodHound. Keep in mind that for the initial BloodHound 8.0 release, OpenGraph nodes and edges are not supported in the Pathfinding tab, so the Cypher tab must be used to query the data manually.
If you’re planning on adding nodes/edges to BloodHound, it’s important to start with a pathfinding design in mind, but also to be flexible and leave plenty of room for changes. I tweaked and iterated on the MSSQL design a million times while implementing the collector, edge creation logic, and unit tests. Don’t wait to start testing permission logic and writing collection and processing code until you think you’ve perfected the design. It will change a lot once you start identifying edge cases and attack primitives you didn’t account for.
v0.1 – Avoiding Non-traversable Edges
For a long time, I thought the best approach for the design was to only create non-traversable edges when absolutely necessary to create other edges. This only happens when creation of an edge relies on nodes or edges that are not always available to either the collector or to ingest at the time of creation or is so computationally expensive that the system running the collector may not have sufficient resources to hold all the objects in memory or on disk. For example, the logic to create an edge could require data that could be collected or uploaded at different times, such as GenericAll
permissions on a certain AD object combined with nodes only available in an MSSQL collection that is uploaded three days later.
In this first model, I drew traversable edges labeled with the combination of the abusable permission and the target node class to every abusable node (instead of to the object the permission targets) only when the conditions were met for the permission to be abusable. The edge was not drawn if the permission was present but not abusable.
For example, an MSSQL server principal can be granted ALTER ANY SERVER ROLE
permission on the SERVER
object, in which case I drew edges named MSSQL_AlterAnyServerRole
to every server role that could be altered rather than to the server object that contained them, and only to user-defined roles or to fixed roles the principal was already member of (except sysadmin
).
This seemed like a decent balance of offensive and defensive use cases for the graph. Offensive folks would be able to see the abusable edge from the source principal directly to the target node that could be compromised and view abuse info in the entity panel, while defenders could see the offending permission’s name at a glance.
Ideally, every traversable edge implies full control of the target node’s abusable permissions, so I couldn’t draw a traversable MSSQL_AlterAnyServerRole
edge from principals assigned ALTER ANY SERVER ROLE
to the MSSQL_Server
, because that edge wouldn’t allow complete control of the server. Full control implies not only that you gain some control of the target node, but such a degree of control that you can follow every outbound edge from that node as well. There can be exceptions to this rule, but if you are able to adhere to it in your pathfinding design, it helps future proof the model.
I lived in arrows.app until I eventually arrived at this model. Traversable edges are in green, non-traversable edges are in red (but don’t look too hard, I completely changed it later):
This simplified the design at first because I wouldn’t have to write cypher to display the other objects and permissions that are involved in creating and remediating the traversable edges – almost every edge was based on an atomic permission and target node type. However, pretty soon it started getting unwieldy and confusing to create edges for every single combination of permission and target node, but only when certain conditions were met for the permission to be abusable. The design didn’t reflect the MSSQL permission model very well, making querying difficult and detracting from the user experience.
For example, I had a hard time answering questions like:
- What object do I need to remove this permission from?
- When multiple permissions allow an action, how do I know all of the edge types to include in my query (e.g., in addition to
ALTER ANY SERVER ROLE
, theALTER
andCONTROL
permissions on specific user-defined server roles allow the ability to add a login to a role)? How do I answer:- Who can add a login to this server role?
- Who can add a user to this database role?
- Who can impersonate this login?
- Who can change the password for this SQL login or application role?
- What does the
MSSQL_AlterDB
edge allow an attacker to do? - Why is the
MSSQL_AlterDB
edge drawn to roles and app roles instead of only to the database? - Why is the
MSSQL_AlterDB
permission NOT drawn as an edge sometimes – what are the conditions that result in it being drawn or not drawn?
While this model works just fine and is a decent choice for a design pattern, the answers to these questions are not immediately apparent from this model and users have to dig for answers in the entity panel or by designing cypher queries based on a deeper understanding of the technology. It’s vital to keep the end user in mind when coming up with new graph designs and ask yourself:
- What jobs do the users have to do?
- What are their desired outcomes from using an attack graph?
- How does this model remove friction from their ability to complete their jobs with the desired outcomes?
As is tradition, I realized this well after implementing the collector and processing logic for the model above, with just a few weeks left to complete the project, so I rage-drafted a new model that:
- reflects the MSSQL permission model more closely and
- [hopefully] makes it easier for users to understand attack paths in the environment
v0.2 – Composition Edges
In this revised model, which is what MSSQLHound uses now, non-traversable edges are drawn directly from the grantee of the permission to the securable object, and traversable edges are drawn to the abusable node. The non-traversable edges are named after the permission (e.g., MSSQL_Alter
or MSSQL_Control
) and the traversable edges are named after the attack action (e.g., MSSQL_ChangePassword
).
Many permissions are only abusable sometimes. For example, a server principal with the ALTER ANY SERVER ROLE
permission can only add members to user-defined roles and to fixed roles they are already a member of (except sysadmin), granting the effective permissions assigned to that role to the new member.
Although the target of the ALTER ANY SERVER ROLE
permission is the SERVER
object, the permission allows full control of all server role objects in the scope of that server that meet those criteria. As a result, a non-traversable MSSQL_AlterAnyServerRole
edge is drawn from the server principal with ALTER ANY SERVER ROLE
permission to the SERVER
object, and an MSSQL_AddMember
edge is drawn from the server principal to every server role. The traversable attack edge is composed of the non-traversable permission edge(s) that can be removed to prevent the abuse.
In summary, non-traversable edges are created for permissions that:
- are not always abusable between every combination of node types they could be drawn for. Maybe abuse is only possible for certain target node types or in combination with another permission.
and/or
- allow control of an object that is not the target of the permission. These are often inherited due to ownership of, membership in, or assignment to a parent object.
Example: permission that is not always abusable
A principal may be granted ALTER
permission to any securable object, but while ALTER
permission on a user-defined database role is abusable by adding a member, ALTER
permission on a database user object is not abusable (that I’m aware of), because there is no password to change. In this case, the following edges are drawn:
- non-traversable
MSSQL_Alter
edges are drawn from the grantee of theALTER
permission to the target user-defined database role and user objects:
- traversable
MSSQL_AddMember
edges are drawn from the grantee of theALTER
permission to the abusable user-defined database roles:
Example: permission that allows control of an object that is not the target
The ALTER DATABASE
permission doesn’t allow full control of the database, but effectively grants the principal ALTER ANY APPLICATION ROLE
and ALTER ANY ROLE
, which can be used to compromise application roles and user-defined database roles in that database. In this case, the following edges are drawn:
- a non-traversable
MSSQL_Alter
edge is drawn from the grantee of theALTER DATABASE
permission to the database object:
- traversable
MSSQL_ChangePassword
edges are drawn from the grantee of theALTER DATABASE
permission to every application role object in the database:
- traversable
MSSQL_AddMember
edges are drawn from the grantee of theALTER DATABASE
permission to every user-defined database role object in the database:
Note that even the CONTROL
permission doesn’t always guarantee abuse complete control of the target of the permission. For example, a database principal with CONTROL
of an application role object cannot compromise it as they cannot change the application role’s password or impersonate it.
Here’s the model of the new non-traversable edges collected by MSSQLHound:
On the other hand, traversable edges are created for permissions and abuse actions that:
- are always abusable for every node class it can be drawn to
- allow full control of the target object such that the source principal can also traverse all possible outbound edges from the target object
Let’s revisit the questions we were left with from the first model in the context of the composition model:
What object do I need to remove this permission from?
This question can be answered by executing a cypher query to display the composition of the traversable edge – the traversable edge and the non-traversable permission edges that are conditions for its creation:
Note: I haven’t finished adding Composition sections to the entity panels of every new edge class yet. Dynamic cypher is hard.
When multiple permissions allow an action, how do I know all of the edge types to include in my query?
- Who can add a login to this server role?
- Who can add a user to this database role?
- Who can impersonate this login?
- Who can change the password for this SQL login or application role?
All of these can be answered by querying for the traversable edge named for the attack action.
For example – who can add a login to this server role?
MATCH p = ()-[:MSSQL_AddMember]->(d:MSSQL_ServerRole)
WHERE d.name = 'ALTERTEST_SERVERROLE_TARGETOF_LOGIN_CANALTERSERVERROLE'
RETURN p
The following questions are avoided completely by using the composition model:
- What does the
MSSQL_AlterDB
edge allow an attacker to do? - Why is the
MSSQL_AlterDB
edge drawn to roles and app roles instead of only to the database? - Why is the
MSSQL_AlterDB
permission NOT drawn as an edge sometimes – what are the conditions that result in it being drawn or not drawn?
In summary, both models work just fine, but in my opinion, the composition model is better suited for MSSQL and is a better option for attack path graph designs in general.
Collection and Processing
To collect the information necessary for creating the nodes and edges, Claude, ChatGPT, and I vibe coded up the initial PowerShell collector to gather server and database principals and permissions and spit out JSON in the right format for OpenGraph in just a few hours. The logic needed to calculate complex traversable edges based on multiple conditions was added later.
The collector queries LDAP for MSSQL service principal names (SPNs) and/or connects to specified remote MSSQL server(s), executes SQL statements (and sometimes WMI/Remote Registry as a fallback) to enumerate interesting settings and permissions, then processes them to create edges based on relationships between different principals at the server and database levels.
Luckily for me, MSSQL is relatively self-contained and the processing logic was not dependent on looping through massive amounts of previously collected data from Active Directory, so the collector could do all of the heavy lifting and create every edge type in the design without post-processing, which is processing that must occur after file ingest.
After writing the processing logic for the more complex edges, I wrote a script to create a bunch of test objects in MSSQL and unit tests to ensure that the edge processing logic continued to work as I made changes. This might seem boring and unimportant, but for complex designs with lots of combinations of source/target node types and conditions for an edge to be created, I can’t recommend unit testing enough.
For example, before I wrote unit tests, I hadn’t factored in how the script should react when:
- a
DENY
permission is set (it processed it as if it was aGRANT
and created an edge) - a server login is disabled (it processed it anyway and created a
MSSQL_Connect
edge) - A database is
TRUSTWORTHY
and owned by a server login nested in a role withsecurityadmin
,IMPERSONATE ANY LOGIN
, orCONTROL SERVER
(MSSQL_ExecuteAsOwner
should have been created, but is wasn’t before implementing the test) - the target is running SQL Server 2005 (this required major changes)
Unit testing also revealed that:
MSSQL_HasLogin
edges were only being created for domain computer accounts, not user accounts as wellCoerceAndRelayToMSSQL
edges were being drawn to theMSSQL_Server
node instead of to theMSSQL_Login
node (successful relay of a computer account with a login does not always mean control of the entire server)- the
public
server role isn’t grantedCONNECT SQL
by default, each new individual login is - I didn’t consider cases when the remote login had
CONTROL SERVER
orIMPERSONATE ANY LOGIN
for creation of theMSSQL_LinkedAsAdmin
edge and didn’t look at permissions/roles nested in other roles on the remote server
All in all, unit testing saved me from forgetting to revert countless tweaks used to test the effectiveness of logic. To be honest, I’ve always avoided it, but now that AI assistance can do most of the heavy lifting, consider me a convert. I’m sold.
I also found the following cypher queries very useful after importing my test data into BloodHound to check whether or not I implemented the intended nodes, edges, and their properties correctly:
- Graph all paths between nodes and edges you’ve created:
MATCH p = ()-[]->() RETURN p
- Graph all paths from a Computer node to an MSSQL_Server node:
MATCH p = (:Computer)-[]->(:MSSQL_Server) RETURN p
- Graph all MSSQL_ExecuteAsOwner edges:
MATCH p = ()-[:ExecuteAsOwner]->() RETURN p
- Graph all MSSQL_DatabaseRole nodes:
MATCH (n:MSSQL_DatabaseRole) RETURN n
- Graph nodes with Unknown type
MATCH (n) WHERE n.name IS NULL RETURN n
- This is great for finding nodes that are created based edges with a start and end value where no matching node exists
I ran into some other gotchas along the way. Here are some things to look out for while coding the logic to create your edges.
Membership Nesting
Identifying issues that occur due to long-forgotten nested group/role/team memberships is BloodHound’s bread and butter and we need to account for it. For example, to create the traversable MSSQL_LinkedAsAdmin
edge from one MSSQL server to a server it is linked to, we need to know if the login context on the other side of the connection has any permission or role that allows complete compromise of the remote server, either through direct assignment or assigned through membership in roles that may be nested members of other roles with any level of depth. In other words, we need to calculate the remote principal’s effective permissions recursively.
This means we need to either:
- store every principal’s direct, explicitly defined permissions in memory or on disk so they can be evaluated recursively to identify effective permissions, or
- upload every principal’s direct, explicitly defined permissions to the BloodHound API, then pull down the results of cypher queries used to identify effective permissions
On that note, let’s talk about…
Resource Consumption
Complex logic such as nested memberships and effective permissions can be calculated by the collector when it can hold all the necessary objects in memory, but that can be resource intensive, so it’s not a good idea to collect and store this data for all MSSQL servers and ship it all at once to BloodHound. I needed to chunk the data by SQL instance to either save it to files on disk or send it up to the API so MSSQLHound doesn’t consume too much RAM or disk space on the system running the collector.
So MSSQLHound holds the permissions and roles for each MSSQL server instance in memory while that server is processed, then writes the resulting nodes and edges for that individual server to JSON files on disk, all the while monitoring for excessive memory and disk space consumption. When collection is finished, MSSQLHound zips all of the files and deletes the originals. Since I didn’t need to hold all the permissions and roles across multiple MSSQL servers in memory for the initial design, this worked for all of the new edges.
However, when dealing with really large data sets such as all Active Directory principals and permissions, you may end up needing to use the latter option and calculate effective permissions by uploading the simple nodes and edges to BloodHound, then querying the API to return the results of cypher queries you can use to calculate additional edges.
Attack Path Capabilities
Let’s dive into some attack paths this new platform provides visibility into, how they were discovered, and how they can be executed.
Research
While writing the logic for each new edge type, I had to take into consideration things like what principals are included in or excluded from certain actions, what permissions implicitly grant other permissions, etc. This led to a few interesting discoveries including an upcoming CVE (details coming after the patch) that goes to show, even in well-researched, seemingly ancient technologies like MSSQL, there are still new attack paths to be found that date back all the way to the product’s inception.
One of the most interesting results of this work has been the ability to use MSSQLHound to research attack paths in default MSSQL installations for widespread software like SCCM. For starters, I queried what Andy Robbins taught me is called the “longest shortest path” in my lab’s SCCM site databases.
To visualize the longest shortest path in an environment, he told me to think of a map of all the roads in the United States. Then, think about the shortest path between every pair of cities in the country (New York to Boston, Orlando to Las Vegas, Denver to Las Vegas, etc.). The longest shortest path is the longest of these paths, so likely Key West to Anchorage or something like that.
In BloodHound, the longest shortest path identifies the shortest path between any two nodes that has the most traversable edges between them.
I queried the longest shortest path in a default SCCM database with variations of the following Cypher:
MATCH p = shortestPath((n)-[*1..]->(m))
WHERE NOT n = m
RETURN p
ORDER BY LENGTH(p) DESC
LIMIT 1
I recommend only using this query in a lab environment with a small amount of servers and when BloodHound only contains MSSQL data (not combined with Active Directory data), otherwise it won’t produce similar results and may never finish. For production data sets, you’ll need to be more specific with the node and edge types included in the query.
This revealed a path I hadn’t expected and wouldn’t have found buried in the permissions manually without using OpenGraph. Everything in the graph below is a default in SCCM.
By default, an MSSQL server login (MAYYHEM\PS1-SMS$
) is created for the Active Directory domain computer account for each SMS Provider in an SCCM hierarchy. That server-level login is mapped to a database-level user (MAYYHEM\PS1-SMS$
) in the SCCM site database (CM_PS1
). That database user is a member of the SMSDBROLE_SITEPROVIDER
database role, which is a member of the db_owner
database role, which has the CONTROL
permission on the database, allowing it to execute any action within the database, but not any action within the instance of MSSQL server (e.g., enabling/executing xp_cmdshell
for host-level access).
Here’s where it gets interesting. Apparently by default, SCCM site databases have their TRUSTWORTHY
property to be set to On
. Microsoft’s MSSQL documentation warns against this property, stating “We recommend that you leave the TRUSTWORTHY database property set to OFF… There are ways to elevate a user with the db_owner role to become a sysadmin when setting TRUSTWORTHY to ON. Use caution when using the TRUSTWORTHY property.”
I wasn’t aware it existed before researching abusable permissions for the BloodHound design, but offensive use cases for this property are well-documented and tools like PowerUpSQL and Metasploit have modules to abuse it (all by Scott Sutherland, whose excellent work provided the foundation for many of the new edges). Essentially, setting TRUSTWORTHY
to On
allows the database to access server-level resources. Further, the EXECUTE AS OWNER
SQL statement will execute with the permissions of the database owner, which is the sa
account in SCCM by default.
As a result, you can coerce authentication from an SMS Provider and relay to the MSSQL server, run EXECUTE AS OWNER
, then enable xp_cmdshell
and execute actions on the host operating system (or use any other MSSQL tradecraft) to compromise the underlying computer. If the MSSQL instance is running in the context of a domain service account, that account will have a session on the system and tools like mimikatz can be used to dump the cleartext password from LSA secrets.
In the context of SCCM, this means we should always be able to compromise the host of the site database if we can coerce and relay an SMS Provider to the MSSQL instance. Before finding this, I thought that you could only use an SMS Provider’s database access to escalate privileges via TAKEOVER-1.2, but apparently you can execute arbitrary commands on the operating system as well.
I’m positive there are many more opportunities to research and identify complex attack paths using this model and by modeling other technologies with OpenGraph. I look forward to seeing what you can find!
Operations
When attacking/defending MSSQL, here are some interesting questions that the MSSQL graph can answer that are difficult or impossible to extract using SQL Server Management Studio or other tools.
What computer accounts have MSSQL logins that can be coerced and relayed to gain access to MSSQL?
MATCH p = ()-[:CoerceAndRelayToMSSQL]->() RETURN p
What Active Directory user principals have an MSSQL login?
MATCH p = ()-[:MSSQL_HasLogin]->() RETURN p
What Active Directory principals have paths into MSSQL?
MATCH p = ()-[:CoerceAndRelayToMSSQL|MSSQL_GetTGS|MSSQL_GetAdminTGS|MSSQL_HasLogin|MSSQL_HostFor|MSSQL_ServiceAccountFor]->() RETURN p
What MSSQL servers are linked?
MATCH p = ()-[:MSSQL_LinkedAsAdmin|:MSSQL_LinkedTo]->() RETURN p
Who is a hop away from ALTER permission on database objects?
MATCH p0 = (db:MSSQL_Database)<-[:MSSQL_Alter]-(n)<-[*0..1]-(n2)
RETURN p0
What objects does this principal own?
MATCH p = (:MSSQL_Login {name:"SA"})-[:MSSQL_Owns]->() RETURN p
What is the shortest path from an Active Directory principal to take control of an MSSQL instance?
MATCH p = shortestPath((n:Base)-[*1..]->(m:MSSQL_Server))
WHERE NOT n = m
RETURN p
ORDER BY LENGTH(p) DESC
LIMIT 1
What MSSQL principals have a path back into Active Directory (e.g., by executing code on the host, abusing stored credentials, etc.)?
MATCH p = ()-[:MSSQL_ExecuteOnHost|MSSQL_HasDBScopedCred|MSSQL_HasMappedCred|MSSQL_HasProxyCred]->() RETURN p
What principals have a path to compromising an MSSQL server (including other servers it’s linked to) after coercing and relaying authentication to MSSQL?
MATCH p1 = ()-[:CoerceAndRelayToMSSQL]->(login)
MATCH p2 = shortestPath((login)-[*1..]->(d:MSSQL_Server))
WHERE ALL(r IN relationships(p2) WHERE r.traversable = true)
RETURN p1,p2
I hope MSSQLHound helps you discover some interesting attack paths – please share them if it does! If you have any questions or could use some help, please reach out to me on the BloodHound Slack, Twitter, or open an issue.
And if you’d like help or feedback while designing your own OpenGraph collector/processor for a new technology not currently supported in BloodHound (or that extends a supported technology), please join us in the #opengraph channel in the BloodHound Slack!