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-spnsnew 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-infonew 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-spwnew function: add a SharesPasswordWith relationship between objects, helpful to map relationships of shared local administrator for modeling/etc
del-edgenew 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-maxnew-function: not cowsay, but dogsay. No real use, just for fun, its national dog day after all!
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
ObjectPropsto collect SPNs. This pulls the
serviceprincipalnamesproperty 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-membersto pull out all members of a specified group, typically used for targeting and grepping
dasessionsto see where any Domain Administrator sessions are located, in the format
DA username - computer with session
sessionswill 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-groupspull the groups for each owned user, primarily to be used for grepping and analysis
sidhistreturns 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 SID object namerely 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 domainobjects. The
foreign SID object namedoes 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.
unsupportedreturns 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.
nolapsreturns a list of computer objects not configured with Microsoft LAPS
passnotreqreturns 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.
After completion, you’re left with an entanglement of relationships that vaguely resembles a flower or spider web.
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.
Thanks to everyone who suggested new features and helped with testing. Dedicated to my dog Arlo, ‘tis national dog day!