def submitTheHive(message): ''' Create a new case in TheHive based on the email Return 'TRUE' is successfully processed otherwise 'FALSE' ''' global log # Decode email msg = email.message_from_bytes(message) decode = email.header.decode_header(msg['From'])[0] if decode[1] is not None: fromField = decode[0].decode(decode[1]) else: fromField = str(decode[0]) decode = email.header.decode_header(msg['Subject'])[0] if decode[1] is not None: subjectField = decode[0].decode(decode[1]) else: subjectField = str(decode[0]) log.info("From: %s Subject: %s" % (fromField, subjectField)) attachments = [] observables = [] # Extract SMTP headers and search for observables parser = HeaderParser() headers = parser.parsestr(msg.as_string()) headers_string = '' i = 0 while i < len(headers.keys()): headers_string = headers_string + headers.keys()[i] + ': ' + headers.values()[i] + '\n' i+=1 # Temporary disabled # observables = searchObservables(headers_string, observables) body = '' for part in msg.walk(): if part.get_content_type() == "text/plain": try: body = part.get_payload(decode=True).decode() except UnicodeDecodeError: body = part.get_payload(decode=True).decode('ISO-8859-1') observables.extend(searchObservables(body, observables)) elif part.get_content_type() == "text/html": try: html = part.get_payload(decode=True).decode() except UnicodeDecodeError: html = part.get_payload(decode=True).decode('ISO-8859-1') observables.extend(searchObservables(html, observables)) else: # Extract MIME parts filename = part.get_filename() mimetype = part.get_content_type() if filename and mimetype: if mimetype in config['caseFiles'] or not config['caseFiles']: log.info("Found attachment: %s (%s)" % (filename, mimetype)) # Decode the attachment and save it in a temporary file charset = part.get_content_charset() if charset is None: charset = chardet.detect(bytes(part))['encoding'] # Get filename extension to not break TheHive analysers (see Github #11) fname, fextension = os.path.splitext(filename) fd, path = tempfile.mkstemp(prefix=slugify(fname) + "_", suffix=fextension) try: with os.fdopen(fd, 'w+b') as tmp: tmp.write(part.get_payload(decode=1)) attachments.append(path) except OSerror as e: log.error("Cannot dump attachment to %s: %s" % (path,e.errno)) return False # Cleanup observables (remove duplicates) new_observables = [] for o in observables: if not {'type': o['type'], 'value': o['value'] } in new_observables: # Is the observable whitelisted? if isWhitelisted(o['value']): log.debug('Skipping whitelisted observable: %s' % o['value']) else: new_observables.append({ 'type': o['type'], 'value': o['value'] }) log.debug('Found observable %s: %s' % (o['type'], o['value'])) else: log.info('Ignoring duplicate observable: %s' % o['value']) log.info("Removed duplicate observables: %d -> %d" % (len(observables), len(new_observables))) observables = new_observables api = TheHiveApi(config['thehiveURL'], config['thehiveUser'], config['thehivePassword'], {'http': '', 'https': ''}) # Search for interesting keywords in subjectField: log.debug("Searching for %s in '%s'" % (config['alertKeywords'], subjectField)) if re.match(config['alertKeywords'], subjectField, flags=0): # # Add observables found in the mail body # artifacts = [] if config['thehiveObservables'] and len(observables) > 0: for o in observables: artifacts.append(AlertArtifact(dataType=o['type'], data=o['value'])) # # Prepare tags - add alert keywords found to the list of tags # tags = list(config['alertTags']) match = re.findall(config['alertKeywords'], subjectField) for m in match: tags.append(m) # # Prepare the alert # sourceRef = str(uuid.uuid4())[0:6] alert = Alert(title=subjectField.replace('[ALERT]', ''), tlp = int(config['alertTLP']), tags = tags, description = body, type = 'external', source = fromField, sourceRef = sourceRef, artifacts = artifacts) # Create the Alert id = None response = api.create_alert(alert) if response.status_code == 201: log.info('Created alert %s' % response.json()['sourceRef']) else: log.error('Cannot create alert: %s (%s)' % (response.status_code, response.text)) return False else: # Prepare the sample case tasks = [] for task in config['caseTasks']: tasks.append(CaseTask(title=task)) # Prepare the custom fields customFields = CustomFieldHelper()\ .add_string('from', fromField)\ .add_string('attachment', str(attachments))\ .build() # If a case template is specified, use it instead of the tasks if len(config['caseTemplate']) > 0: case = Case(title=subjectField, tlp = int(config['caseTLP']), flag = False, tags = config['caseTags'], description = body, template = config['caseTemplate'], customFields = customFields) else: case = Case(title = subjectField, tlp = int(config['caseTLP']), flag = False, tags = config['caseTags'], description = body, tasks = tasks, customFields = customFields) # Create the case id = None response = api.create_case(case) if response.status_code == 201: newID = response.json()['id'] log.info('Created case %s' % response.json()['caseId']) if len(attachments) > 0: for path in attachments: observable = CaseObservable(dataType='file', data = [path], tlp = int(config['caseTLP']), ioc = False, tags = config['caseTags'], message = 'Found as email attachment' ) response = api.create_case_observable(newID, observable) if response.status_code == 201: log.info('Added observable %s to case ID %s' % (path, newID)) os.unlink(path) else: log.warning('Cannot add observable: %s - %s (%s)' % (path, response.status_code, response.text)) # # Add observables found in the mail body # if config['thehiveObservables'] and len(observables) > 0: for o in observables: observable = CaseObservable( dataType = o['type'], data = o['value'], tlp = int(config['caseTLP']), ioc = False, tags = config['caseTags'], message = 'Found in the email body' ) response = api.create_case_observable(newID, observable) if response.status_code == 201: log.info('Added observable %s: %s to case ID %s' % (o['type'], o['value'], newID)) else: log.warning('Cannot add observable %s: %s - %s (%s)' % (o['type'], o['value'], response.status_code, response.text)) else: log.error('Cannot create case: %s (%s)' % (response.status_code, response.text)) return False return True
flag=True) ] #Build TheHive Case with custom fields thehive_case = Case(title=offense_desc, tlp=tlp, flag=True, tags=['offense', 'qradar', offense_type_name], description=build_desc, customFields=custom_fields.fields, tasks=tasks) print('-------Posting Case with initial values--------') case_id = None try: resp = hive_api.create_case(thehive_case) if resp.status_code == 201: case_id = resp.json()['id'] case_num = resp.json()['caseId'] qr.post_offense_note(offense_id, offense_note.format(case_num)) sl.insert_record( case_table, 'id, case_id, status', '"{}","{}","{}"'.format(offense_id, case_id, 'Open')) #sl.new_case_record(offense_id, case_id, 'Open') logger.info('Case created. Case Id:{} - Case Num:{}'.format( case_id, case_num)) except Exception as err: logger.error('Error at case creation.:{}'.format(err)) sys.exit(1) if case_id:
class HiveManagement: def __init__( self, config_file='C:\\automation-hunting\\the-hive\\conf\\thehive-provider.yaml' ): self.hive_url = None self.api_key = None self.alert_tags = None self.source = None self.alert_type = None self.case_tags = None self.ioc_tags = None if not self.get_config_data(config_file): raise Exception('Invalid Configuration File') self.api = TheHiveApi(self.hive_url, self.api_key) def get_config_data(self, yaml_file): with open(yaml_file, 'r') as ymlfile: cfg = yaml.load(ymlfile, Loader=yaml.FullLoader) valid = False if self.validate_cfg_yml(cfg): self.hive_url = cfg['hive']['hive_url'] self.api_key = cfg['hive']['api_key'] self.alert_tags = cfg['hive']['alert_tags'] self.source = cfg['hive']['source'] self.alert_type = cfg['hive']['alert_type'] self.case_tags = cfg['hive']['case_tags'] self.ioc_tags = cfg['hive']['ioc_tags'] valid = True return valid @staticmethod def validate_cfg_yml(cfg): if 'hive' not in cfg: print('Not main') return False else: if 'hive_url' not in cfg['hive'] or 'api_key' not in cfg['hive']: return False return True def create_alarm(self, title, source_ref=None, description='N/A', alert_type='external', source='LogRhythm', iocs=None, additional_fields=None, additional_tags=None, tlp=TLP.AMBER, pap=PAP.AMBER, severity=HiveSeverity.MEDIUM): if source_ref is None: source_ref = str(uuid.uuid4())[0:6] alert_tags = self.alert_tags.copy() if additional_tags is not None: for additional_tag in additional_tags: alert_tags.append(additional_tag) custom_fields_helper = CustomFieldHelper() if additional_fields is not None: for field in additional_fields: custom_fields_helper.add_string(field['name'], field['value']) custom_fields = custom_fields_helper.build() artifacts = list() if iocs is not None: for ioc in iocs: artifacts.append( AlertArtifact(dataType=ioc['type'].value, data=ioc['value'])) hive_alert = Alert(title=title, tlp=tlp.value, tags=alert_tags, description=description, type=alert_type, source=source, sourceRef=source_ref, pap=pap.value, artifacts=artifacts, customFields=custom_fields, severity=severity.value) response = self.api.create_alert(hive_alert) if response.status_code == 201: print('Alerta Creada Exitosamente') print(json.dumps(response.json(), indent=4, sort_keys=True)) else: print('Error') print(response.text) return response.json() def create_case(self, title, tasks=None, tlp=TLP.AMBER, pap=PAP.AMBER, severity=HiveSeverity.MEDIUM, additional_fields=None, additional_tags=None, flag=False, description='N/A'): case_tags = self.case_tags.copy() if additional_tags is not None: for additional_tag in additional_tags: case_tags.append(additional_tag) custom_fields_helper = CustomFieldHelper() if additional_fields is not None: for field in additional_fields: custom_fields_helper.add_string(field['name'], field['value']) custom_fields = custom_fields_helper.build() new_tasks = list() if tasks is not None: for task in tasks: new_tasks.append(CaseTask(title=task)) hive_case = Case(title=title, tlp=tlp.value, pap=pap.value, description=description, tags=case_tags, severity=severity.value, flag=flag, customFields=custom_fields, tasks=new_tasks) response = self.api.create_case(hive_case) if response.status_code == 201: print('Caso Creada Exitosamente') print(json.dumps(response.json(), indent=4, sort_keys=True)) else: print('Error') print(response.text) return response.json() def create_case_observable(self, data_type: HiveDataType, value: list, tlp=TLP.AMBER, ioc=True, additional_tags=None, description='LogRhythm IoC'): ioc_tags = self.ioc_tags.copy() if additional_tags is not None: for additional_tag in additional_tags: ioc_tags.append(additional_tag) hive_observable = CaseObservable(data_type=data_type.value, data=value, tlp=tlp.value, ioc=ioc, tags=ioc_tags, message=description) return hive_observable def add_observable_to_case(self, case_id, observable: CaseObservable): response = self.api.create_case_observable(case_id, observable) if response.status_code == 201: print('Observable successfully added to the case') print(json.dumps(response.json(), indent=4, sort_keys=True)) else: print('Error') print(response.text) def search_case(self, title=None, tlp: TLP = None, pap: PAP = None, severity: HiveSeverity = None, or_operator=False): if title is None and tlp is None and pap is None and severity is None: print('Can\'t search without a filter') return None operators = list() if title is not None: operators.append(String('title: ' + urllib.parse.quote(title))) if tlp is not None: operators.append(Gte('tlp', tlp.value)) if pap is not None: operators.append(Gte('pap', pap.value)) if severity is not None: operators.append(Gte('severity', severity.value)) if len(operators) == 1: query = operators[0] else: if or_operator: query = Or(operators) else: query = And(operators) response = self.api.find_cases(query=query, range='all', sort=[]) if response.status_code == 200: print('Busqueda correcta') print(json.dumps(response.json(), indent=4, sort_keys=True)) else: print('Error') print(response.text) return response.json() def promote_alert(self, alert_id): response = self.api.promote_alert_to_case(alert_id) if response.status_code == 201: print('Correct Promotion') print(json.dumps(response.json(), indent=4, sort_keys=True)) else: print('Error') print(response.text) return response.json()