Adding MSSQL to BloodHound with OpenGraph

Aug 4 2025
Share
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.

What is OpenGraph?

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, the ALTER and CONTROL 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:

  1. reflects the MSSQL permission model more closely and
  2. [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 the ALTER permission to the target user-defined database role and user objects:

  • traversable MSSQL_AddMember edges are drawn from the grantee of the ALTER 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 the ALTER DATABASE permission to the database object:

  • traversable MSSQL_ChangePassword edges are drawn from the grantee of the ALTER DATABASE permission to every application role object in the database:

  • traversable MSSQL_AddMember edges are drawn from the grantee of the ALTER 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 a GRANT 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 with securityadmin, IMPERSONATE ANY LOGIN, or CONTROL 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 well
  • CoerceAndRelayToMSSQL edges were being drawn to the MSSQL_Server node instead of to the MSSQL_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 granted CONNECT SQL by default, each new individual login is
  • I didn’t consider cases when the remote login had CONTROL SERVER or IMPERSONATE ANY LOGIN for creation of the MSSQL_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!