class Automation(): def __init__(self, webhook, cfg): logger.info('Initiating MISPautomation') self.TheHiveConnector = TheHiveConnector(cfg) if self.cfg.getboolean('Cortex', 'enabled'): self.CortexConnector = CortexConnector(cfg) self.webhook = webhook self.report_action = report_action self.qr_config = {} for key, value in cfg.items('QRadar'): self.qr_config[key] = value def parse_hooks(self): """ Check for new MISP Alert containing supported IOC to search automatically """ if self.webhook.isNewMispAlert(): logger.info( 'Alert {} has been tagged as MISP and is just created'.format( self.webhook.data['rootId'])) #Check alert for supported ioc types supported_iocs = False for artifact in self.webhook.data['object']['artifacts']: if artifact['dataType'] in self.qr_config[ 'supported_datatypes']: supported_iocs = True #Promote alert to case if there are support ioc types if supported_iocs: alert_id = self.webhook.data['rootId'] casetemplate = "MISP Event" logger.info('Alert {} contains IOCs that are supported'.format( alert_id)) response = self.TheHiveConnector.createCaseFromAlert( alert_id, casetemplate) self.report_action = 'createCase' """ Add timestamps to keep track of the search activity per case (we do not want to keep searching forever) """ #Perform automated Analyzer runs for supported observables in a case that has been created from a MISP alert if self.webhook.isNewMispCase(): logger.info( 'Case {} has been tagged as MISP and is just created'.format( self.webhook.data['rootId'])) #Retrieve caseid caseid = self.webhook.data['object']['id'] #Add customFields firstSearched and lastSearched #Create a Case object? Or whatever it is case = Case() #Add the case id to the object case.id = caseid #Debug output logger.info('Updating case %s' % case.id) #Define which fields need to get updated fields = ['customFields'] #Retrieve all required attributes from the alert and add them as custom fields to the case current_time = int(round(time.time() * 1000)) customFields = CustomFieldHelper()\ .add_date('firstSearched', current_time)\ .add_date('lastSearched', current_time)\ .build() #Add custom fields to the case object case.customFields = customFields #Update the case self.TheHiveConnector.updateCase(case, fields) self.report_action = 'updateCase' """ Start the analyzers automatically for MISP observables that are supported and update the case with a new timestamp """ #Automatically run Analyzers for newly created MISP cases where supported IOC's are present if self.webhook.isNewMispArtifact(): logger.info( 'Case artifact is tagged with "MISP-extern". Checking if observable is of a supported type' ) #Retrieve caseid caseid = self.webhook.data['rootId'] #Retrieve case data case_data = self.TheHiveConnector.getCase(caseid) #List all supported ioc's for the case observable = self.webhook.data['object'] #When supported, start a cortex analyzer for it if observable['dataType'] in self.qr_config['supported_datatypes']: supported_observable = observable['_id'] #Trigger a search for the supported ioc logger.info('Launching analyzers for observable: {}'.format( observable['_id'])) response = self.CortexConnector.runAnalyzer( "Cortex-intern", supported_observable, "IBMQRadar_Search_Manual_0_1") #Add customFields firstSearched and lastSearched #Create a Case object case = Case() #Add the case id to the object case.id = caseid #Debug output logger.info('Updating case %s' % case.id) #Define which fields need to get updated fields = ['customFields'] #Retrieve all required attributes from the alert and add them as custom fields to the case current_time = int(round(time.time() * 1000)) customFields = CustomFieldHelper()\ .add_date('firstSearched', case_data['customFields']['firstSearched']['date'])\ .add_date('lastSearched', current_time)\ .build() #Add custom fields to the case object case.customFields = customFields #Update the case self.TheHiveConnector.updateCase(case, fields) self.report_action = 'updateCase' """ Automatically create a task for a found IOC """ #If the Job result contains a successful search with minimum of 1 hit, create a task to investigate the results if self.webhook.isCaseArtifactJob() and self.webhook.isSuccess( ) and self.webhook.isMisp(): #Case ID caseid = self.webhook.data['rootId'] #Load Case information case_data = self.TheHiveConnector.getCase(caseid) logger.info( 'Job {} is part of a case that has been tagged as MISP case and has just finished' .format(self.webhook.data['object']['cortexJobId'])) #Check if the result count higher than 0 if int( float(self.webhook.data['object']['report']['summary'] ['taxonomies'][0]['value'])) > 0: logger.info( 'Job {} contains hits, checking if a task is already present for this observable' .format(self.webhook.data['object']['cortexJobId'])) #Retrieve case task information response = self.TheHiveConnector.getCaseTasks(caseid) case_tasks = response.json() #Load CaseTask template casetask = CaseTask() #Observable + Link observable = self.webhook.data['object']['artifactId'] observable_link = TheHive.get( 'url' ) + "/index.html#!/case/" + caseid + "/observables/" + self.webhook.data[ 'object']['artifactId'] #Task name casetask.title = "Investigate found IOC with id: {}".format( observable) #Date date_found = time.strftime("%d-%m-%Y %H:%M") case_task_found = False for case_task in case_tasks: #Check if task is present for investigating the new results if casetask.title == case_task['title']: case_task_found = True if not case_task_found: logger.info( 'No task found, creating task for observable found in job {}' .format(self.webhook.data['object']['cortexJobId'])) #Add description casetask.description = "The following ioc is hit in the environment. Investigate the results and act accordingly:\n\n" casetask.description = casetask.description + "{} is seen on {}\n".format( observable_link, date_found) #Check if case is closed if case_data['status'] == "Resolved": #Create a Case object? Or whatever it is case = Case() #Add the case id to the object case.id = caseid logger.info('Updating case %s' % case.id) #Define which fields need to get updated fields = ['status'] #Reopen the case case.status = "Open" #Update the case self.TheHiveConnector.updateCase(case, fields) #Add the case task self.TheHiveConnector.createTask(caseid, casetask) self.report_action = 'createTask' return self.report_action
class Automators(Main): def __init__(self, cfg, use_case_config): self.logger = logging.getLogger(__name__) self.logger.info('Initiating The Hive Automator') self.cfg = cfg self.TheHiveConnector = TheHiveConnector(cfg) if self.cfg.getboolean('Cortex', 'enabled'): self.CortexConnector = CortexConnector(cfg) #Read mail config self.mailsettings = self.cfg.get('TheHive', 'mail') ''' Can be used to check if there is a match between tags and the provided list. Useful for checking if there is a customer tag (having a list of customers) present where only one can match. ''' def MatchValueAgainstTags(self, tags, list): for tag in tags: if tag in list: return tag def craftUcTask(self, title, description): self.logger.debug('%s.craftUcTask starts', __name__) self.uc_task = CaseTask(title=title, description=description) return self.uc_task def createBasicTask(self, action_config, webhook): #Only continue if the right webhook is triggered if webhook.isImportedAlert(): pass else: return False #Perform actions for the CreateBasicTask action self.case_id = webhook.data['object']['case'] self.title = action_config['title'] self.description = action_config['description'] self.logger.info('Found basic task to create: %s' % self.title) #Create Task self.uc_task = self.craftUcTask(self.title, self.description) self.uc_task_id = self.TheHiveConnector.createTask( self.case_id, self.uc_task) return True def createMailTask(self, action_config, webhook): #Only continue if the right webhook is triggered if webhook.isImportedAlert(): pass else: return False self.tags = webhook.data['object']['tags'] self.case_id = webhook.data['object']['case'] if self.cfg.getboolean('Automation', 'enable_customer_list', fallback=False): self.customer_id = self.MatchValueAgainstTags( self.tags, self.customers) self.logger.info('Found customer %s, retrieving recipient' % self.customer_id) else: self.customer_id = None self.notification_type = "email" self.title = action_config['title'] self.description = self.renderTemplate(action_config['long_template'], self.tags, webhook, self.notification_type, customer_id=self.customer_id, mail_settings=self.mailsettings) self.logger.info('Found mail task to create: %s' % self.title) #Create Task self.ucTask = self.craftUcTask(self.title, self.description) self.ucTaskId = self.TheHiveConnector.createTask( self.case_id, self.ucTask) if 'auto_send_mail' in action_config and action_config[ 'auto_send_mail'] and not self.stopsend: self.logger.info('Sending mail for task with id: %s' % self.ucTaskId) self.TheHiveConnector.runResponder( 'case_task', self.ucTaskId, self.use_case_config['configuration']['mail']['responder_id']) def runAnalyzer(self, action_config, webhook): #Automatically run Analyzers for newly created cases where supported IOC's are present if webhook.isNewArtifact(): self.logger.debug( 'Case artifact found. Checking if observable is of a supported type to automatically fire the analyzer' ) #Retrieve caseid self.caseid = webhook.data['rootId'] #List all supported ioc's for the case self.observable = webhook.data['object'] #When supported, start a cortex analyzer for it if self.observable['dataType'] in action_config['datatypes']: self.supported_observable = self.observable['_id'] #Blacklist IP addresses, make sure the blacklist is present if self.observable[ 'dataType'] == "ip" and 'blacklist' in action_config and 'ip' in action_config[ 'blacklist']: for entry in action_config['blacklist']['ip']: #Initial values match = False observable_ip = ipaddress.ip_address( self.observable['data']) #Match ip with CIDR syntax if entry[-3:] == "/32": bl_entry = ipaddress.ip_address(entry[:-3]) match = observable_ip == bl_entry #Match ip without CIDR syntax elif "/" not in entry: bl_entry = ipaddress.ip_address(entry) match = observable_ip == bl_entry #Capture actual network entries else: bl_entry = ipaddress.ip_network(entry, strict=False) match = observable_ip in bl_entry #If matched add it to new entries to use outside of the loop if match: self.logger.debug( "Observable {} has matched {} of blacklist. Ignoring..." .format(self.observable['data'], entry)) return #Trigger a search for the supported ioc self.logger.debug( 'Launching analyzers for observable: {}'.format( self.observable['_id'])) self.TheHiveConnector.runAnalyzer( action_config['cortex_instance'], self.supported_observable, action_config['analyzer']) def closeCaseForTaxonomyInAnalyzerResults(self, action_config, webhook): #If the Job result contains a successful search with minimum of 1 hit, create a task to investigate the results if webhook.isCaseArtifactJob() and webhook.isSuccess(): #Case ID self.caseid = webhook.data['rootId'] #Load Case information self.case_data = self.TheHiveConnector.getCase(self.caseid) self.logger.debug('Job {} has just finished'.format( webhook.data['object']['cortexJobId'])) #Check if the result count higher than 0 if webhook.data['object']['report']['summary']['taxonomies'][0][ 'level'] in action_config["taxonomy_level"]: self.logger.info( 'Job {} has configured taxonomy level, checking if a task is already present for this observable' .format(webhook.data['object']['cortexJobId'])) #Check if task is present for investigating the new results if self.case_data['status'] != "Resolved": self.logger.info( 'Case is not yet closed, closing case for {} now...'. format(webhook.data['object']['cortexJobId'])) #Close the case self.TheHiveConnector.closeCase(self.caseid) self.report_action = 'closeCase' return self.report_action def createTaskForTaxonomyinAnalyzerResults(self, action_config, webhook): #If the Job result contains a successful search with minimum of 1 hit, create a task to investigate the results if webhook.isCaseArtifactJob() and webhook.isSuccess(): #Case ID self.caseid = webhook.data['rootId'] #Load Case information self.case_data = self.TheHiveConnector.getCase(self.caseid) self.logger.debug('Job {} has just finished'.format( webhook.data['object']['cortexJobId'])) #Check if the result count higher than 0 if webhook.data['object']['report']['summary']['taxonomies'][0][ 'level'] in action_config["taxonomy_level"]: self.logger.info( 'Job {} has configured taxonomy level, checking if a task is already present for this observable' .format(webhook.data['object']['cortexJobId'])) #Retrieve case task information self.response = self.TheHiveConnector.getCaseTasks(self.caseid) self.case_tasks = self.response.json() #Load CaseTask template self.casetask = CaseTask() #Observable + Link self.observable = webhook.data['object']['artifactId'] self.observable_link = self.cfg.get( 'Automation', 'hive_url', fallback="https://localhost" ) + "/index.html#!/case/" + self.caseid + "/observables/" + webhook.data[ 'object']['artifactId'] #Task name self.casetask.title = "{} {}".format(action_config['title'], self.observable) #Date self.date_found = time.strftime("%d-%m-%Y %H:%M") self.case_task_found = False for case_task in self.case_tasks: #Check if task is present for investigating the new results if self.casetask.title == case_task['title']: self.case_task_found = True if not self.case_task_found: self.logger.info( 'No task found, creating task for observable found in job {}' .format(webhook.data['object']['cortexJobId'])) #Add description self.casetask.description = action_config['description'] self.casetask.description = self.casetask.description + "\n\n {} is seen on {}\n".format( self.observable_link, self.date_found) #Check if case is closed if self.case_data['status'] == "Resolved": #Create a Case object case = Case() #Add the case id to the object case.id = self.caseid self.logger.info('Updating case %s' % case.id) #Define which fields need to get updated fields = ['status'] #Reopen the case case.status = "Open" #Update the case self.TheHiveConnector.updateCase(case, fields) #Add the case task self.TheHiveConnector.createTask(self.caseid, self.casetask) self.report_action = 'createTask' return self.report_action