NetExec/nxc/helpers/bloodhound.py

79 lines
3.5 KiB
Python
Raw Normal View History

2021-11-19 21:16:37 +00:00
def add_user_bh(user, domain, logger, config):
"""Adds a user to the BloodHound graph database.
2023-09-24 04:25:08 +00:00
Args:
2023-10-12 19:13:16 +00:00
----
2023-09-24 04:25:08 +00:00
user (str or list): The username of the user or a list of user dictionaries.
domain (str): The domain of the user.
logger (Logger): The logger object for logging messages.
config (ConfigParser): The configuration object for accessing BloodHound settings.
Returns:
2023-10-12 19:13:16 +00:00
-------
2023-09-24 04:25:08 +00:00
None
Raises:
2023-10-12 19:13:16 +00:00
------
2023-09-24 04:25:08 +00:00
AuthError: If the provided Neo4J credentials are not valid.
ServiceUnavailable: If Neo4J is not available on the specified URI.
Exception: If an unexpected error occurs with Neo4J.
"""
2021-11-20 21:37:14 +00:00
users_owned = []
if isinstance(user, str):
2023-05-02 15:17:59 +00:00
users_owned.append({"username": user.upper(), "domain": domain.upper()})
2021-11-20 21:37:14 +00:00
else:
users_owned = user
2023-09-22 14:40:29 +00:00
2023-05-02 15:17:59 +00:00
if config.get("BloodHound", "bh_enabled") != "False":
# we do a conditional import here to avoid loading these if BH isn't enabled
from neo4j import GraphDatabase
from neo4j.exceptions import AuthError, ServiceUnavailable
uri = f"bolt://{config.get('BloodHound', 'bh_uri')}:{config.get('BloodHound', 'bh_port')}"
2021-11-19 21:16:37 +00:00
2023-05-02 15:17:59 +00:00
driver = GraphDatabase.driver(
uri,
auth=(
config.get("BloodHound", "bh_user"),
config.get("BloodHound", "bh_pass"),
),
encrypted=False,
)
2021-11-19 21:16:37 +00:00
try:
with driver.session().begin_transaction() as tx:
for info in users_owned:
distinguished_name = "".join([f"DC={dc}," for dc in info["domain"].split(".")]).rstrip(",")
domain_query = tx.run(f"MATCH (d:Domain) WHERE d.distinguishedname STARTS WITH '{distinguished_name}' RETURN d").data()
if not domain_query:
raise Exception("Domain not found in bloodhound")
else:
domain = domain_query[0]["d"].get("name")
if info["username"][-1] == "$":
user_owned = f"{info['username'][:-1]}.{domain}"
account_type = "Computer"
else:
user_owned = f"{info['username']}@{domain}"
account_type = "User"
2023-10-30 17:38:10 +00:00
result = tx.run(f"MATCH (c:{account_type} {{name:'{user_owned}'}}) RETURN c")
if result.data()[0]["c"].get("owned") in (False, None):
logger.debug(f"MATCH (c:{account_type} {{name:'{user_owned}'}}) SET c.owned=True RETURN c.name AS name")
result = tx.run(f"MATCH (c:{account_type} {{name:'{user_owned}'}}) SET c.owned=True RETURN c.name AS name")
logger.highlight(f"Node {user_owned} successfully set as owned in BloodHound")
2023-09-20 15:59:16 +00:00
except AuthError:
2023-05-10 20:18:07 +00:00
logger.fail(f"Provided Neo4J credentials ({config.get('BloodHound', 'bh_user')}:{config.get('BloodHound', 'bh_pass')}) are not valid.")
2021-11-19 21:16:37 +00:00
return
2023-09-20 15:59:16 +00:00
except ServiceUnavailable:
2023-05-10 20:18:07 +00:00
logger.fail(f"Neo4J does not seem to be available on {uri}.")
2021-11-19 21:16:37 +00:00
return
2023-09-22 14:40:29 +00:00
except Exception as e:
if "Domain not found in bloodhound" in str(e):
logger.fail("Neo4J Error: Domain not found in BloodHound. Please specify the FQDN ex:domain.local.")
else:
logger.fail(f"Unexpected error with Neo4J: {e}")
logger.fail("Account not found on the domain")
2021-11-19 21:16:37 +00:00
return
2023-05-02 15:17:59 +00:00
driver.close()