Max Updates and Primitives

6 minute read



In a previous post, Max was released to aid in BloodHound operations in a bash-based pentesting cycle. The idea was to combine the Neo4j database with standard output and bash tools to make data extraction and manipulation during a pentest smooth and painless. See the previous post here: post.

Now I’ve added a few new sections & features:

  • add-spns new function: A new potential attack primitive, creates a HasSPNConfigured relationship between objects. This is based on clear text credentials being stored in LSA secrets for running service accounts, and Service Principal Names giving a good indication of where to find them which allows a new pivot path. Note this is not guaranteed, but merely a good indicator of such.

  • get-info new additions: Functions to find DA sessions, extract specific group members, extract the groups of owned objects (for grepping), return all computers without LAPS, return all users with PasswordNotRequired set, get all computers with a session for a specific user

  • add-spw new function: add a SharesPasswordWith relationship between objects, helpful to map relationships of shared local administrator for modeling/etc

  • del-edge new function: delete an unused or “bad” edge, helps when there are things that you’re “not allowed” to do like change a service account password, enc

  • pet-max new-function: not cowsay, but dogsay. No real use, just for fun, its national dog day after all!

Full Post

A month or two ago, I released Max (post). I thought it was an great little tool for BloodHound, but like any good tool, there’s always room for improvement. Based off some comments & suggestions from co-workers, plus some prior research on my own, I’ve added a number of other functions and options to the tools.

One of the big things I wanted to highlight is the introduction of a new possible attack primitive; see the following “add-spns” section for full details.

As always, if you have any features or functions that you’d like added, feel free to reach out @knavesec on Twitter & the BloodHoundHQ slack channel.

add-spns & a new primitive

This function will create a new relationship HasSPNConfigured pointing from computer to user indicating that there is a possible method of compromise if you have access to the specific computer.


This function is also an introduction of a new attack primitive that I’ve been looking into. The concept is that a user account running a service on a machine stores their cleartext password within LSA Secrets, so if you have admin right on that system you can secretsdump the machine and extract the credentials. Service Principal Names (SPNs) are good indicators that the user would be running a service on that specific machine, so it’s also then a good indicator that their credentials would be stored in the registry. I will note that this is an INDICATOR and is not 100% guaranteed, though in my experience on clients the correlation is true roughly 2/3s of the time. It just varies.


There are 3 ways to upload this information:

  • Upload the output of Impackets GetUserSPNs. It will iterate through each of the configured SPNs and create a relationship for each entry if possible.


  • Use the information already stored within BloodHound, assuming you’ve ingested information with a collection method of All or ObjectProps to collect SPNs. This pulls the serviceprincipalnames property from users and assigns relationships based on them.


  • Import a file with object pairs of Computer, User, which will simply create the relationships manually specified.


As you can see in the screenshots, sometimes the relationships can’t be created. Typically this is because a computer within the SPN doesn’t exist within the bloodhound data, OR the SPN was in a non-standard format and wasn’t parsed properly (the program should warn if that happens).


A few extra features have been added to this, at this point the get-info function is just becoming a hotkey for queries I use frequently or for general analysis (happy to add others on suggestion/adding your own is pretty simple). All of the features listed below are new to the project.

  • group-members to pull out all members of a specified group, typically used for targeting and grepping

group-members feature

  • dasessions to see where any Domain Administrator sessions are located, in the format DA username - computer with session

dasessions feature

  • sessions will retrieve all the computers a specified user has, for targeting a specific user session. I use this when targeting specific HVTs like PCI or SCADA accounts to try and extract their cleartext/hashed password from memory.


  • owned-groups pull the groups for each owned user, primarily to be used for grepping and analysis

owned-groups feature

  • sidhist returns SID history information for all objects in the database, returned in a format of username - SID - foreign domain - foreign SID object name. If you have SID history information stored in the database, this will extract the important information. That being said the foreign domain and foreign SID object name rely on the Domain information (Domain Trusts) and actual foreign domain BloodHound data to be imported into the database. For example, if you’ve run the ingestor on one domain, you can query for domain trusts which satisfies the foreign domain objects. The foreign SID object name does a lookup by SID, therefore if you do not actually have the object with the respective SID in the database then it will not register. Note the first entry in the screenshot below corresponds to a foreign group, but there is no information for the second remote SID and RID.

sidhist feature

  • unsupported returns a list of computers running unsupported operating systems in the format computer - OS, typically I use this as a direct output for the client to make a note of any outdated systems there are on the network.

unsupported feature

  • nolaps returns a list of computer objects not configured with Microsoft LAPS
  • passnotreq returns a list of all users with the PASSWORD_NOT_REQ flag set, a common misconfiguration

As said before, I’m always open to adding functions upon request.


This was one of the original functionalities of porterhau5’s Bloodhound-Owned tool, so I thought I would include it as well. It’ll take in a list of objects and create a SharesPasswordWith relationship between each object. This is primarily used for repeated local administrators, but in theory could also be used for domain users. Since you have to know in advance who’s passwords are shared by who, its more useful after the fact in determining alternate paths to get places. I personally just mark all the objects as owned when I have repeated passwords (see the mark-owned function), but I know some people who prefer the relationship route so I’ve included it for completeness.

spw create

After completion, you’re left with an entanglement of relationships that vaguely resembles a flower or spider web.

flower power

Flower power.


If you happen to have one relationship that you’d like to remove from the database (like a certain flower-shaped mess), you can delete all edges of a certain type. For example, often times I don’t want to change an account’s password on a real engagement so this allows you to simply remove ForcePasswordChange relationships. Filtering through the GUI is handy, but sometimes deletion is necessary.



Arguably the most important contribution to this project: dogsay. He says various predetermined phrases and spreads happiness.

pet-max feature

Thanks to everyone who suggested new features and helped with testing. Dedicated to my dog Arlo, ‘tis national dog day!

- @knavesec