class Block_port(Responder): def __init__(self): Responder.__init__(self) self.hostname_PaloAltoNGFW = self.get_param( 'config.Hostname_PaloAltoNGFW') self.User_PaloAltoNGFW = self.get_param('config.User_PaloAltoNGFW') self.Password_PaloAltoNGFW = self.get_param( 'config.Password_PaloAltoNGFW') self.name_external_Service_Group = self.get_param( 'config.name_external_Service_Group') self.thehive_instance = self.get_param('config.thehive_instance') self.thehive_api_key = self.get_param('config.thehive_api_key', 'YOUR_KEY_HERE') self.api = TheHiveApi(self.thehive_instance, self.thehive_api_key) def run(self): alertId = self.get_param('data.id') response = self.api.get_alert(alertId) data_list = [] data = None for i in response.json().get("artifacts"): if "'port'," in str(i): ioc = i.get("data") data_list.append(i.get("data")) elif "'protocol'," in str(i): ioc = i.get("data") data_list.append(i.get("data")) data = " ".join(data_list) protocol = re.findall(r'[a-z]+', str(data)) protocol = str("".join(protocol)).lower() port = re.findall(r'[0-9]+', str(data)) port = "".join(port) fw = firewall.Firewall(self.hostname_PaloAltoNGFW, api_username=self.User_PaloAltoNGFW, api_password=self.Password_PaloAltoNGFW) panos.objects.ServiceObject.refreshall(fw) if port not in str(fw.find(port, panos.objects.ServiceObject)): new_port_object = panos.objects.ServiceObject( port, protocol, description="Blocked port", destination_port=port) fw.add(new_port_object) new_port_object.create() panos.objects.ServiceGroup.refreshall(fw) block_list = fw.find(self.name_external_Service_Group, panos.objects.ServiceGroup) port_list = block_list.about().get('value') if port not in port_list: port_list.append(port) temp1 = panos.objects.ServiceGroup( self.name_external_Service_Group, value=port_list) fw.add(temp1) temp1.apply() self.report({'message': 'message sent'})
class Block_domain(Responder): def __init__(self): Responder.__init__(self) self.hostname_PaloAltoNGFW = self.get_param( 'config.Hostname_PaloAltoNGFW') self.User_PaloAltoNGFW = self.get_param('config.User_PaloAltoNGFW') self.Password_PaloAltoNGFW = self.get_param( 'config.Password_PaloAltoNGFW') self.name_external_Address_Group_for_domain = self.get_param( 'config.name_external_Address_Group') self.thehive_instance = self.get_param('config.thehive_instance') self.thehive_api_key = self.get_param('config.thehive_api_key', 'YOUR_KEY_HERE') self.api = TheHiveApi(self.thehive_instance, self.thehive_api_key) def run(self): alertId = self.get_param('data.id') response = self.api.get_alert(alertId) ioc = None ioc_clear = [] for i in list(response.json().get("artifacts")): if 'hostname' in str(i): ioc = i.get("data") for i in ioc: if i == "[" or i == "]": continue else: ioc_clear.append(i) ioc = "".join(ioc_clear) fw = firewall.Firewall(self.hostname_PaloAltoNGFW, api_username=self.User_PaloAltoNGFW, api_password=self.Password_PaloAltoNGFW) panos.objects.AddressObject.refreshall(fw) if ioc not in str(fw.find(ioc, panos.objects.AddressObject)): new_ioc_object = panos.objects.AddressObject( ioc, ioc, description="Blocked fqdn", type="fqdn") fw.add(new_ioc_object) new_ioc_object.create() panos.objects.AddressGroup.refreshall(fw) block_list = fw.find(self.name_external_Address_Group_for_domain, panos.objects.AddressGroup) ioc_list = block_list.about().get('static_value') if ioc not in ioc_list: ioc_list.append(ioc) temp1 = panos.objects.AddressGroup( self.name_external_Address_Group_for_domain, static_value=ioc_list) fw.add(temp1) temp1.apply() self.report({'message': 'message sent'})
def prepare_alert(subject, indicatorlevel, emailbody, alerttype, tag): hive_address = ''.join(settings.stored_hive_address[0]) hive_api = ''.join(settings.stored_api_key[0]) #Define the connection to thehive installation (including the generated API key). api = TheHiveApi(hive_address, hive_api, None, {'http': '', 'https': ''}) # Prepare the sample Alert print("Preparing the alert for", alerttype) sourceRef = str(uuid.uuid4())[0:6] alert = Alert(title=subject, tlp=indicatorlevel, tags=[tag], description=emailbody, type=alerttype, source='instance1', sourceRef=sourceRef, artifacts="") # Create the Alert print('Create Alert') print('-----------------------------') id = None response = api.create_alert(alert) if response.status_code == 201: print("Alert created sucessfully!") # print(json.dumps(response.json(), indent=4, sort_keys=True)) # print('') id = response.json()['id'] else: print("Unable to create alert") # print('ko: {}/{}'.format(response.status_code, response.text)) sys.exit(0) # Get all the details of the created alert print('Get created alert {}'.format(id)) print('-----------------------------') response = api.get_alert(id) if response.status_code == requests.codes.ok: # print(json.dumps(response.json(), indent=4, sort_keys=True)) print('') else: print("Error retreiving the alert!") print('ko: {}/{}'.format(response.status_code, response.text))
class Block_url(Responder): def __init__(self): Responder.__init__(self) self.hostname_PaloAltoNGFW = self.get_param( 'config.Hostname_PaloAltoNGFW') self.User_PaloAltoNGFW = self.get_param('config.User_PaloAltoNGFW') self.Password_PaloAltoNGFW = self.get_param( 'config.Password_PaloAltoNGFW') self.name_internal_URL_category = self.get_param( 'config.name_internal_URL_category') self.thehive_instance = self.get_param('config.thehive_instance') self.thehive_api_key = self.get_param('config.thehive_api_key', 'YOUR_KEY_HERE') self.api = TheHiveApi(self.thehive_instance, self.thehive_api_key) def run(self): alertId = self.get_param('data.id') response = self.api.get_alert(alertId) ioc = None ioc_clear = [] for i in list(response.json().get("artifacts")): if 'url' in str(i): ioc = i.get("data") for i in ioc: if i == "[" or i == "]": continue else: ioc_clear.append(i) ioc = "".join(ioc_clear) fw = firewall.Firewall(self.hostname_PaloAltoNGFW, api_username=self.User_PaloAltoNGFW, api_password=self.Password_PaloAltoNGFW) panos.objects.CustomUrlCategory.refreshall(fw) block_list = fw.find(self.name_internal_URL_category, panos.objects.CustomUrlCategory) ioc_list = block_list.about().get('url_value') if ioc not in ioc_list: ioc_list.append(ioc) temp1 = panos.objects.CustomUrlCategory( self.name_internal_URL_category, url_value=ioc_list) fw.add(temp1) temp1.create() self.report({'message': 'message sent'})
HiveApi = False hive_case_url = '#' print('The HIVE not connected') else: HiveApi = TheHiveApi(the_hive_url, the_hive_key, cert=the_hive_verifycert) hive_case_url = the_hive_url + '/index.html#/case/id_here/details' except: print('The HIVE not connected') HiveApi = False hive_case_url = '#' if HiveApi != False: try: HiveApi.get_alert(0) print('The Hive connected') except thehive4py.exceptions.AlertException: HiveApi = False print('The Hive not connected') #### VARIABLES #### baseUrl = config_loader.get_config_str("Flask", "baseurl") baseUrl = baseUrl.replace('/', '') if baseUrl != '': baseUrl = '/' + baseUrl max_preview_char = int( config_loader.get_config_str("Flask", "max_preview_char") ) # Maximum number of character to display in the tooltip max_preview_modal = int(
class Block_ip(Responder): def __init__(self): Responder.__init__(self) self.hostname_PaloAltoNGFW = self.get_param( 'config.Hostname_PaloAltoNGFW') self.User_PaloAltoNGFW = self.get_param('config.User_PaloAltoNGFW') self.Password_PaloAltoNGFW = self.get_param( 'config.Password_PaloAltoNGFW') self.name_security_rule = self.get_param( 'config.Security_rule_for_block_external_IP_address', 'TheHive Block external IP address') self.TheHive_instance = self.get_param('config.TheHive_instance') self.TheHive_API_key = self.get_param('config.TheHive_API_key', 'YOUR_KEY_HERE') self.api = TheHiveApi(self.TheHive_instance, self.TheHive_API_key) def run(self): self.instance_type = self.get_param('data._type') if self.instance_type == 'case_artifact': ioc = self.get_param('data.data') if self.instance_type == 'alert': alertId = self.get_param('data.id') response = self.api.get_alert(alertId) ioc = None ioc_clear = [] for i in list(response.json().get("artifacts")): if 'ip' in str(i): ioc = i.get("data") for i in ioc: if i == "[" or i == "]": continue else: ioc_clear.append(i) ioc = "".join(ioc_clear) if self.instance_type == 'case': import requests case_id = self.get_param('data._id') payload = { "query": { "_parent": { "_type": "case", "_query": { "_id": case_id } } }, "range": "all" } headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(self.TheHive_API_key) } thehive_api_url_case_search = '{}/api/case/artifact/_search'.format( self.TheHive_instance) r = requests.post(thehive_api_url_case_search, data=json.dumps(payload), headers=headers) if r.status_code != requests.codes.ok: self.error(json.dumps(r.text)) a = None data = r.json() for n in data: if n.get('dataType') == 'ip': ioc = n.get('data') fw = firewall.Firewall(self.hostname_PaloAltoNGFW, api_username=self.User_PaloAltoNGFW, api_password=self.Password_PaloAltoNGFW) panos.objects.AddressObject.refreshall(fw) rulebase = panos.policies.Rulebase() fw.add(rulebase) current_security_rules = panos.policies.SecurityRule.refreshall( rulebase) if f"thehive-{ioc}" not in str( fw.find(f"thehive-{ioc}", panos.objects.AddressObject)): new_ioc_object = panos.objects.AddressObject( f"thehive-{ioc}", ioc, description="TheHive Blocked ip address") fw.add(new_ioc_object) new_ioc_object.create() panos.objects.AddressGroup.refreshall(fw) block_list = fw.find("TheHive Block list external IP address", panos.objects.AddressGroup) if block_list != None: ioc_list = block_list.about().get('static_value') if f"thehive-{ioc}" not in ioc_list: ioc_list.append(f"thehive-{ioc}") temp1 = panos.objects.AddressGroup( "TheHive Block list external IP address", static_value=ioc_list) fw.add(temp1) temp1.apply() elif block_list == None: temp1 = panos.objects.AddressGroup( "TheHive Block list external IP address", static_value=f"thehive-{ioc}") fw.add(temp1) temp1.apply() desired_rule_params = None for i in current_security_rules: if self.name_security_rule == i.about().get("name"): rule_atrib = i.about() temp_rule_atrib = rule_atrib.get("destination") if "TheHive Block list external IP address" not in temp_rule_atrib: temp_rule_atrib.append( "TheHive Block list external IP address") if "any" in temp_rule_atrib: temp_rule_atrib.remove("any") rule_atrib.update({"destination": temp_rule_atrib}) desired_rule_params = rule_atrib else: desired_rule_params = rule_atrib new_rule = panos.policies.SecurityRule(**desired_rule_params) rulebase.add(new_rule) new_rule.apply() fw.commit() self.report({ 'message': 'Responder successfully added %s into TheHive Block list external IP address from %s' % (ioc, self.name_security_rule) })
class Block_port(Responder): def __init__(self): Responder.__init__(self) self.hostname_PaloAltoNGFW = self.get_param('config.Hostname_PaloAltoNGFW') self.User_PaloAltoNGFW = self.get_param('config.User_PaloAltoNGFW') self.Password_PaloAltoNGFW = self.get_param('config.Password_PaloAltoNGFW') self.name_security_rule = self.get_param('config.Security_rule_for_block_port_external_communication','TheHive Block port for external communication') self.TheHive_instance = self.get_param('config.TheHive_instance') self.TheHive_API_key = self.get_param('config.TheHive_API_key', 'YOUR_KEY_HERE') self.api = TheHiveApi(self.TheHive_instance, self.TheHive_API_key) def run(self): self.instance_type = self.get_param('data._type') if self.instance_type == 'case_artifact': data = self.get_param('data.data') port=str(data).split('-')[0] protocol=str(data).split('-')[1] protocol=re.findall(r'[a-z]+',str(protocol)); protocol=str("".join(protocol)).lower() port=re.findall(r'[0-9]+',str(port)); port="".join(port) if self.instance_type == 'alert': alertId = self.get_param('data.id') response = self.api.get_alert(alertId) data_list=[] data=None for i in response.json().get("artifacts"): if "'port-protocol'," in str(i): data_list.append(i.get("data")) port=str(data_list).split('-')[0] protocol=str(data_list).split('-')[1] protocol=re.findall(r'[a-z]+',str(protocol)); protocol=str("".join(protocol)).lower() port=re.findall(r'[0-9]+',str(port)); port="".join(port) if self.instance_type == 'case': import requests case_id = self.get_param('data._id') payload = { "query": { "_parent": { "_type": "case", "_query": { "_id": case_id } } }, "range": "all" } headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(self.TheHive_API_key) } thehive_api_url_case_search = '{}/api/case/artifact/_search'.format(self.TheHive_instance) r = requests.post(thehive_api_url_case_search, data=json.dumps(payload), headers=headers) if r.status_code != requests.codes.ok: self.error(json.dumps(r.text)) a=None data = r.json() data_list = [] for n in data: if "'port-protocol'," in str(n): data_list.append(n.get("data")) port=str(data_list).split('-')[0] protocol=str(data_list).split('-')[1] protocol=re.findall(r'[a-z]+',str(protocol)); protocol=str("".join(protocol)).lower() port=re.findall(r'[0-9]+',str(port)); port="".join(port) fw = firewall.Firewall(self.hostname_PaloAltoNGFW, api_username=self.User_PaloAltoNGFW, api_password=self.Password_PaloAltoNGFW) panos.objects.ServiceObject.refreshall(fw) rulebase = panos.policies.Rulebase() fw.add(rulebase) current_security_rules =panos.policies.SecurityRule.refreshall(rulebase) if f"thehive-{port}-{protocol}" not in str(fw.find(f"thehive-{port}-{protocol}", panos.objects.ServiceObject)): new_port_object = panos.objects.ServiceObject(f"thehive-{port}-{protocol}", protocol, description="TheHive Blocked port",destination_port=port) fw.add(new_port_object) new_port_object.create() panos.objects.ServiceGroup.refreshall(fw) block_list = fw.find("TheHive Block list for external port communication", panos.objects.ServiceGroup) if block_list != None: port_list = block_list.about().get('value') if f"thehive-{port}-{protocol}" not in port_list: port_list.append(f"thehive-{port}-{protocol}") temp1 = panos.objects.ServiceGroup("TheHive Block list for external port communication", value=port_list) fw.add(temp1) temp1.apply() elif block_list == None: temp1 = panos.objects.ServiceGroup("TheHive Block list for external port communication", value=f"thehive-{port}-{protocol}") fw.add(temp1) temp1.apply() desired_rule_params = None for i in current_security_rules: if self.name_security_rule == str(i.about().get("name")): rule_atrib = i.about() temp_rule_atrib = rule_atrib.get("service") if "TheHive Block list for external port communication" not in temp_rule_atrib: temp_rule_atrib.append("TheHive Block list for external port communication") if "application-default" in temp_rule_atrib: temp_rule_atrib.remove("application-default") rule_atrib.update({"service": temp_rule_atrib}) desired_rule_params = rule_atrib else: desired_rule_params = rule_atrib new_rule = panos.policies.SecurityRule(**desired_rule_params) rulebase.add(new_rule) new_rule.apply() fw.commit() self.report({'message': 'Responder successfully added %s into TheHive Block list for external port communication to %s' % (port,self.name_security_rule)})
class TheHive(AppBase): """ An example of a Walkoff App. Inherit from the AppBase class to have Redis, logging, and console logging set up behind the scenes. """ __version__ = "1.0.0" app_name = "thehive" def __init__(self, redis, logger, console_logger=None): """ Each app should have this __init__ to set up Redis and logging. :param redis: :param logger: :param console_logger: """ super().__init__(redis, logger, console_logger) # async def run_analyzer(self, apikey, url, title_query): # self.thehive = TheHiveApi(url, apikey, cert=False) # response = self.thehive.find_cases(query=String("title:'%s'" % title_query), range='all', sort=[]) # return response.text async def search_cases(self, apikey, url, title_query): self.thehive = TheHiveApi(url, apikey, cert=False) response = self.thehive.find_cases(query=ContainsString( "title", title_query), range="all", sort=[]) return response.text async def search_query(self, apikey, url, search_for, custom_query): self.thehive = TheHiveApi(url, apikey, cert=False) try: query = json.loads(custom_query) except: raise IOError("Invalid JSON payload received.") if search_for == "alert": response = self.thehive.find_alerts(query=query, range="all", sort=[]) else: response = self.thehive.find_cases(query=query, range="all", sort=[]) if response.status_code == 200: return response.text else: raise IOError(response.text) async def add_observable(self, apikey, url, case_id, data, datatype, tags): self.thehive = TheHiveApi(url, apikey, cert=False) if tags: if ", " in tags: tags = tags.split(", ") elif "," in tags: tags = tags.split(",") else: tags = [tags] else: tags = [] item = thehive4py.models.CaseObservable( dataType=datatype, data=data, tlp=1, ioc=False, sighted=False, tags=["Shuffle"], message="Created by shuffle", ) return self.thehive.create_case_observable(case_id, item).text async def search_alerts(self, apikey, url, title_query, search_range="0-25"): self.thehive = TheHiveApi(url, apikey, cert=False) # Could be "all" too if search_range == "": search_range = "0-25" response = self.thehive.find_alerts(query=ContainsString( "title", title_query), range=search_range, sort=[]) return response.text async def create_case(self, apikey, url, title, description="", tlp=1, severity=1, tags=""): self.thehive = TheHiveApi(url, apikey, cert=False) if tags: if ", " in tags: tags = tags.split(", ") elif "," in tags: tags = tags.split(",") else: tags = [tags] else: tags = [] # Wutface fix if not tlp: tlp = 1 if not severity: severity = 1 if isinstance(tlp, str): if not tlp.isdigit(): return "TLP needs to be a number from 0-2, not %s" % tlp tlp = int(tlp) if isinstance(severity, str): if not severity.isdigit(): return "Severity needs to be a number from 0-2, not %s" % tlp severity = int(severity) if tlp > 3 or tlp < 0: return "TLP needs to be a number from 0-3, not %d" % tlp if severity > 2 or severity < 0: return "Severity needs to be a number from 0-2, not %d" % tlp case = thehive4py.models.Case( title=title, tlp=tlp, severity=severity, tags=tags, description=description, ) try: ret = self.thehive.create_case(case) return ret.text except requests.exceptions.ConnectionError as e: return "ConnectionError: %s" % e async def create_alert( self, apikey, url, type, source, sourceref, title, description="", tlp=1, severity=1, tags="", ): self.thehive = TheHiveApi(url, apikey, cert=False) if tags: if ", " in tags: tags = tags.split(", ") elif "," in tags: tags = tags.split(",") else: tags = [tags] else: tags = [] # Wutface fix if not tlp: tlp = 1 if not severity: severity = 1 if isinstance(tlp, str): if not tlp.isdigit(): return "TLP needs to be a number from 0-3, not %s" % tlp tlp = int(tlp) if isinstance(severity, str): if not severity.isdigit(): return "Severity needs to be a number from 1-3, not %s" % severity severity = int(severity) if tlp > 3 or tlp < 0: return "TLP needs to be a number from 0-3, not %d" % tlp if severity > 3 or severity < 1: return "Severity needs to be a number from 1-3, not %d" % severity alert = thehive4py.models.Alert( title=title, tlp=tlp, severity=severity, tags=tags, description=description, type=type, source=source, sourceRef=sourceref, ) try: ret = self.thehive.create_alert(alert) return ret.text except requests.exceptions.ConnectionError as e: return "ConnectionError: %s" % e async def create_alert_artifact(self, apikey, url, alert_id, dataType, data, message=None, tlp="2", ioc="False", sighted="False", ignoreSimilarity="False", tags=None): self.thehive = TheHiveApi(url, apikey, cert=False, version=4) if tlp: tlp = int(tlp) else: tlp = 2 ioc = ioc.lower().strip() == "true" sighted = sighted.lower().strip() == "true" ignoreSimilarity = ignoreSimilarity.lower().strip() == "true" if tags: tags = [x.strip() for x in tags.split(",")] else: tags = [] alert_artifact = thehive4py.models.AlertArtifact( dataType=dataType, data=data, message=message, tlp=tlp, ioc=ioc, sighted=sighted, ignoreSimilarity=ignoreSimilarity, tags=tags) try: ret = self.thehive.create_alert_artifact(alert_id, alert_artifact) except requests.exceptions.ConnectionError as e: return "ConnectionError: %s" % e if ret.status_code > 299: raise ConnectionError(ret.text) return ret.text # Gets an item based on input. E.g. field_type = Alert async def get_item(self, apikey, url, field_type, cur_id): self.thehive = TheHiveApi(url, apikey, cert=False) newstr = "" ret = "" if field_type.lower() == "alert": ret = self.thehive.get_alert(cur_id + "?similarity=1") elif field_type.lower() == "case": ret = self.thehive.get_case(cur_id) elif field_type.lower() == "case_observables": ret = self.thehive.get_case_observables(cur_id) elif field_type.lower() == "case_task": ret = self.thehive.get_case_task(cur_id) elif field_type.lower() == "case_tasks": ret = self.thehive.get_case_tasks(cur_id) elif field_type.lower() == "case_template": ret = self.thehive.get_case_tasks(cur_id) elif field_type.lower() == "linked_cases": ret = self.thehive.get_linked_cases(cur_id) elif field_type.lower() == "task_log": ret = self.thehive.get_task_log(cur_id) elif field_type.lower() == "task_logs": ret = self.thehive.get_task_logs(cur_id) else: return ( "%s is not implemented. See https://github.com/frikky/shuffle-apps for more info." % field_type) return ret.text async def close_alert(self, apikey, url, alert_id): self.thehive = TheHiveApi(url, apikey, cert=False) return self.thehive.mark_alert_as_read(alert_id).text async def reopen_alert(self, apikey, url, alert_id): self.thehive = TheHiveApi(url, apikey, cert=False) return self.thehive.mark_alert_as_unread(alert_id).text async def create_case_from_alert(self, apikey, url, alert_id, case_template=None): self.thehive = TheHiveApi(url, apikey, cert=False) response = self.thehive.promote_alert_to_case( alert_id=alert_id, case_template=case_template) return response.text async def merge_alert_into_case(self, apikey, url, alert_id, case_id): self.thehive = TheHiveApi(url, apikey, cert=False) req = url + f"/api/alert/{alert_id}/merge/{case_id}" ret = requests.post(req, auth=self.thehive.auth) return ret.text # Not sure what the data should be async def update_field(self, apikey, url, field_type, cur_id, field, data): # This is kinda silly but.. if field_type.lower() == "alert": newdata = {} if data.startswith("%s"): ticket = self.thehive.get_alert(cur_id) if ticket.status_code != 200: pass newdata[field] = "%s%s" % (ticket.json()[field], data[2:]) else: newdata[field] = data # Bleh url = "%s/api/alert/%s" % (url, cur_id) if field == "status": if data == "New" or data == "Updated": url = "%s/markAsUnread" % url elif data == "Ignored": url = "%s/markAsRead" % url ret = requests.post( url, headers={ "Content-Type": "application/json", "Authorization": "Bearer %s" % apikey, }, ) else: ret = requests.patch( url, headers={ "Content-Type": "application/json", "Authorization": "Bearer %s" % apikey, }, json=newdata, ) return str(ret.status_code) else: return ( "%s is not implemented. See https://github.com/frikky/walkoff-integrations for more info." % field_type) # https://github.com/TheHive-Project/TheHiveDocs/tree/master/api/connectors/cortex async def run_analyzer(self, apikey, url, cortex_id, analyzer_id, artifact_id): self.thehive = TheHiveApi(url, apikey, cert=False) return self.thehive.run_analyzer(cortex_id, artifact_id, analyzer_id).text # Creates a task log in TheHive with file async def create_task_log(self, apikey, url, task_id, message, filedata={}): if filedata["success"] == False: return "No file to upload. Skipping message." headers = { "Authorization": "Bearer %s" % apikey, } files = {} if len(filedata["data"]) > 0: files = { "attachment": (filedata["filename"], filedata["data"]), } data = {"_json": """{"message": "%s"}""" % message} response = requests.post( "%s/api/case/task/%s/log" % (url, task_id), headers=headers, files=files, data=data, ) return response.text # Creates an observable as a file in a case async def create_case_file_observable(self, apikey, url, case_id, tags, filedata): if filedata["success"] == False: return "No file to upload. Skipping message." headers = { "Authorization": "Bearer %s" % apikey, } if tags: if ", " in tags: tags = tags.split(", ") elif "," in tags: tags = tags.split(",") else: tags = [tags] files = {} if len(filedata["data"]) > 0: files = { "attachment": (filedata["filename"], filedata["data"]), } outerarray = {"dataType": "file", "tags": tags} data = {"_json": """%s""" % json.dumps(outerarray)} response = requests.post( "%s/api/case/%s/artifact" % (url, case_id), headers=headers, files=files, data=data, ) return response.text
tags=['<add tag here>'], description='<add some description>', type='notification', source='Email Server', sourceRef=sourceRef, artifacts=artifacts) # Create the Alert print('Create Alert') print('-----------------------------') id = None response = api.create_alert(alert) if response.status_code == 201: print(json.dumps(response.json(), indent=4, sort_keys=True)) print('') id = response.json()['id'] else: print('ko: {}/{}'.format(response.status_code, response.text)) sys.exit(0) # Get all the details of the created alert print('Get created alert {}'.format(id)) print('-----------------------------') response = api.get_alert(id) if response.status_code == requests.codes.ok: print(json.dumps(response.json(), indent=4, sort_keys=True)) print('') else: print('ko: {}/{}'.format(response.status_code, response.text))
class Unblock_ip(Responder): def __init__(self): Responder.__init__(self) self.hostname_PaloAltoNGFW = self.get_param('config.Hostname_PaloAltoNGFW') self.User_PaloAltoNGFW = self.get_param('config.User_PaloAltoNGFW') self.Password_PaloAltoNGFW = self.get_param('config.Password_PaloAltoNGFW') self.name_external_Address_Group = self.get_param('config.Address_group_for_unblock_external_IP_address',"TheHive Block list external IP address") self.TheHive_instance = self.get_param('config.TheHive_instance') self.TheHive_API_key = self.get_param('config.TheHive_API_key', 'YOUR_KEY_HERE') self.api = TheHiveApi(self.TheHive_instance, self.TheHive_API_key) def run(self): self.instance_type = self.get_param('data._type') if self.instance_type == 'case_artifact': ioc = self.get_param('data.data') if self.instance_type == 'alert': alertId = self.get_param('data.id') response = self.api.get_alert(alertId) ioc=None ioc_clear=[] for i in list(response.json().get("artifacts")): if 'ip' in str(i): ioc = i.get("data") for i in ioc: if i == "[" or i == "]": continue else: ioc_clear.append(i) ioc="".join(ioc_clear) if self.instance_type == 'case': import requests case_id = self.get_param('data._id') payload = { "query": { "_parent": { "_type": "case", "_query": { "_id": case_id } } }, "range": "all" } headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(self.TheHive_API_key) } thehive_api_url_case_search = '{}/api/case/artifact/_search'.format(self.TheHive_instance) r = requests.post(thehive_api_url_case_search, data=json.dumps(payload), headers=headers) if r.status_code != requests.codes.ok: self.error(json.dumps(r.text)) a=None data = r.json() for n in data: if n.get('dataType') == 'ip': ioc=n.get('data') fw = firewall.Firewall(self.hostname_PaloAltoNGFW, api_username=self.User_PaloAltoNGFW, api_password=self.Password_PaloAltoNGFW) panos.objects.AddressGroup.refreshall(fw) block_list = fw.find(self.name_external_Address_Group, panos.objects.AddressGroup) ioc_list = block_list.about().get('static_value') if f"thehive-{ioc}" in ioc_list: ioc_list.remove(f"thehive-{ioc}") temp1 = panos.objects.AddressGroup(self.name_external_Address_Group, static_value=ioc_list) fw.add(temp1) temp1.apply() panos.objects.AddressObject.refreshall(fw) if f"thehive-{ioc}" in str(fw.find(f"thehive-{ioc}", panos.objects.AddressObject)): try: deleted_ioc = fw.find(f"thehive-{ioc}", panos.objects.AddressObject) deleted_ioc.delete() except: self.report({'message': 'Responder did not comlite. Warning in AddressObject'}) self.report({'message': 'Responder successfully deleted %s from %s' % (f"thehive-{ioc}",self.name_external_Address_Group)}) fw.commit()
from theHiveKEYS import the_hive_url, the_hive_key, the_hive_verifycert if the_hive_url == '': HiveApi = False hive_case_url = '#' print('The HIVE not connected') else: HiveApi = TheHiveApi(the_hive_url, the_hive_key, cert=the_hive_verifycert) hive_case_url = the_hive_url+'/index.html#/case/id_here/details' except: print('The HIVE not connected') HiveApi = False hive_case_url = '#' if HiveApi != False: try: HiveApi.get_alert(0) print('The Hive connected') except thehive4py.exceptions.AlertException: HiveApi = False print('The Hive not connected') # VARIABLES # baseUrl = cfg.get("Flask", "baseurl") baseUrl = baseUrl.replace('/', '') if baseUrl != '': baseUrl = '/'+baseUrl max_preview_char = int(cfg.get("Flask", "max_preview_char")) # Maximum number of character to display in the tooltip max_preview_modal = int(cfg.get("Flask", "max_preview_modal")) # Maximum number of character to display in the modal DiffMaxLineLength = int(cfg.get("Flask", "DiffMaxLineLength"))#Use to display the estimated percentage instead of a raw value
class Unblock_port(Responder): def __init__(self): Responder.__init__(self) self.hostname_PaloAltoNGFW = self.get_param( 'config.Hostname_PaloAltoNGFW') self.User_PaloAltoNGFW = self.get_param('config.User_PaloAltoNGFW') self.Password_PaloAltoNGFW = self.get_param( 'config.Password_PaloAltoNGFW') self.name_internal_Service_Group = self.get_param( 'config.Internal_service_group_for_unblock_internal_port', 'TheHive Block list for internal port communication') self.TheHive_instance = self.get_param('config.TheHive_instance') self.TheHive_API_key = self.get_param('config.TheHive_API_key', 'YOUR_KEY_HERE') self.api = TheHiveApi(self.TheHive_instance, self.TheHive_API_key) def run(self): self.instance_type = self.get_param('data._type') if self.instance_type == 'case_artifact': data = self.get_param('data.data') port = str(data).split('-')[0] protocol = str(data).split('-')[1] protocol = re.findall(r'[a-z]+', str(protocol)) protocol = str("".join(protocol)).lower() port = re.findall(r'[0-9]+', str(port)) port = "".join(port) if self.instance_type == 'alert': alertId = self.get_param('data.id') response = self.api.get_alert(alertId) data_list = [] data = None for i in response.json().get("artifacts"): if "'port-protocol'," in str(i): data_list.append(i.get("data")) port = str(data_list).split('-')[0] protocol = str(data_list).split('-')[1] protocol = re.findall(r'[a-z]+', str(protocol)) protocol = str("".join(protocol)).lower() port = re.findall(r'[0-9]+', str(port)) port = "".join(port) if self.instance_type == 'case': import requests case_id = self.get_param('data._id') payload = { "query": { "_parent": { "_type": "case", "_query": { "_id": case_id } } }, "range": "all" } headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(self.TheHive_API_key) } thehive_api_url_case_search = '{}/api/case/artifact/_search'.format( self.TheHive_instance) r = requests.post(thehive_api_url_case_search, data=json.dumps(payload), headers=headers) if r.status_code != requests.codes.ok: self.error(json.dumps(r.text)) a = None data = r.json() data_list = [] for n in data: if "'port-protocol'," in str(n): data_list.append(n.get("data")) port = str(data_list).split('-')[0] protocol = str(data_list).split('-')[1] protocol = re.findall(r'[a-z]+', str(protocol)) protocol = str("".join(protocol)).lower() port = re.findall(r'[0-9]+', str(port)) port = "".join(port) fw = firewall.Firewall(self.hostname_PaloAltoNGFW, api_username=self.User_PaloAltoNGFW, api_password=self.Password_PaloAltoNGFW) panos.objects.ServiceGroup.refreshall(fw) block_list = fw.find(self.name_internal_Service_Group, panos.objects.ServiceGroup) port_list = block_list.about().get('value') if f"thehive-{port}-{protocol}" in port_list: port_list.remove(f"thehive-{port}-{protocol}") temp1 = panos.objects.ServiceGroup( self.name_internal_Service_Group, value=port_list) fw.add(temp1) temp1.apply() panos.objects.ServiceObject.refreshall(fw) self.report({ 'message': 'Responder successfully deleted %s from %s' % (port, self.name_internal_Service_Group) }) fw.commit()
import ConfigLoader sys.path.append('../../configs/keys') try: from thehive4py.api import TheHiveApi import thehive4py.exceptions from theHiveKEYS import the_hive_url, the_hive_key, the_hive_verifycert if the_hive_url == '': is_hive_connected = False else: is_hive_connected = TheHiveApi(the_hive_url, the_hive_key, cert=the_hive_verifycert) except: is_hive_connected = False if is_hive_connected != False: try: is_hive_connected.get_alert(0) is_hive_connected = True except thehive4py.exceptions.AlertException: is_hive_connected = False ## LOAD CONFIG ## config_loader = ConfigLoader.ConfigLoader() r_serv_cache = config_loader.get_redis_conn("Redis_Cache") r_serv_db = config_loader.get_redis_conn("ARDB_DB") r_serv_metadata = config_loader.get_redis_conn("ARDB_Metadata") config_loader = None ## -- ## def get_ail_uuid(): uuid_ail = r_serv_db.get('ail:uuid') if uuid_ail is None:
class TheHive(AppBase): """ An example of a Walkoff App. Inherit from the AppBase class to have Redis, logging, and console logging set up behind the scenes. """ __version__ = "0.0.3" app_name = "thehive" def __init__(self, redis, logger, console_logger=None): """ Each app should have this __init__ to set up Redis and logging. :param redis: :param logger: :param console_logger: """ self.thehive = TheHiveApi(secret.url, secret.apikey) super().__init__(redis, logger, console_logger) async def show_secret(self): return "url=%s, apikey=%s" % (secret.url, secret.apikey) async def get_case_count(self, title_query): response = self.thehive.find_cases(query=String("title:'%s'" % title_query), range='all', sort=[]) casecnt = len(response.json()) return casecnt async def string_contains(self, field, string_check): if string_check in field.lower(): return True return False async def string_startswith(self, field, string_check): if field.lower().startswith(string_check): return True return False # Gets an item based on input. E.g. field_type = Alert async def get_item(self, field_type, cur_id): newstr = "" ret = "" if field_type.lower() == "alert": ret = self.thehive.get_alert(cur_id) elif field_type.lower() == "case": ret = self.thehive.get_case(cur_id) elif field_type.lower() == "case_observables": ret = self.thehive.get_case_observables(cur_id) elif field_type.lower() == "case_task": ret = self.thehive.get_case_task(cur_id) elif field_type.lower() == "case_tasks": ret = self.thehive.get_case_tasks(cur_id) elif field_type.lower() == "case_template": ret = self.thehive.get_case_tasks(cur_id) elif field_type.lower() == "linked_cases": ret = self.thehive.get_linked_cases(cur_id) elif field_type.lower() == "task_log": ret = self.thehive.get_task_log(cur_id) elif field_type.lower() == "task_logs": ret = self.thehive.get_task_logs(cur_id) else: return "%s is not implemented. See https://github.com/frikky/walkoff-integrations for more info." % field_type newstr = str(ret.json()).replace("\'", "\"") newstr = newstr.replace("True", "true") newstr = newstr.replace("False", "false") return newstr # Not sure what the data should be async def update_field_string(self, field_type, cur_id, field, data): # This is kinda silly but.. if field_type.lower() == "alert": newdata = {} if data.startswith("%s"): ticket = self.thehive.get_alert(cur_id) if ticket.status_code != 200: pass newdata[field] = "%s%s" % (ticket.json()[field], data[2:]) else: newdata[field] = data # Bleh url = "%s/api/alert/%s" % (secret.url, cur_id) if field == "status": if data == "New" or data == "Updated": url = "%s/markAsUnread" % url elif data == "Ignored": url = "%s/markAsRead" % url ret = requests.post(url, headers={ 'Content-Type': 'application/json', 'Authorization': 'Bearer %s' % secret.apikey }) else: ret = requests.patch( url, headers={ 'Content-Type': 'application/json', 'Authorization': 'Bearer %s' % secret.apikey }, json=newdata, ) return ret.status_code else: return 0
class Block_user(Responder): def __init__(self): Responder.__init__(self) self.hostname_PaloAltoNGFW = self.get_param( 'config.Hostname_PaloAltoNGFW') self.User_PaloAltoNGFW = self.get_param('config.User_PaloAltoNGFW') self.Password_PaloAltoNGFW = self.get_param( 'config.Password_PaloAltoNGFW') self.name_security_rule = self.get_param( 'config.Security_rule_for_block_external_user', 'TheHive Block external user') self.TheHive_instance = self.get_param('config.TheHive_instance') self.TheHive_API_key = self.get_param('config.TheHive_API_key', 'YOUR_KEY_HERE') self.api = TheHiveApi(self.TheHive_instance, self.TheHive_API_key) def run(self): self.instance_type = self.get_param('data._type') if self.instance_type == 'case_artifact': user = self.get_param('data.data') if self.instance_type == 'alert': alertId = self.get_param('data.id') response = self.api.get_alert(alertId) user = None user_list_alert = [] for i in list(response.json().get("artifacts")): if 'username' in str(i): ioc = i.get("data") for i in ioc: if i == "[" or i == "]": continue else: user_list_alert.append(i) user = "".join(user_list_alert) if self.instance_type == 'case': import requests case_id = self.get_param('data._id') payload = { "query": { "_parent": { "_type": "case", "_query": { "_id": case_id } } }, "range": "all" } headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(self.TheHive_API_key) } thehive_api_url_case_search = '{}/api/case/artifact/_search'.format( self.TheHive_instance) r = requests.post(thehive_api_url_case_search, data=json.dumps(payload), headers=headers) if r.status_code != requests.codes.ok: self.error(json.dumps(r.text)) a = None data = r.json() for n in data: if n.get('dataType') == 'username': user = n.get('data') fw = firewall.Firewall(self.hostname_PaloAltoNGFW, api_username=self.User_PaloAltoNGFW, api_password=self.Password_PaloAltoNGFW) rulebase = panos.policies.Rulebase() fw.add(rulebase) current_security_rules = panos.policies.SecurityRule.refreshall( rulebase) user_list = [] for i in current_security_rules: if i.about().get('name') == self.name_security_rule: user_list = i.about().get("source_user") if user not in user_list: user_list.append(user) if "any" in user_list: user_list.remove("any") desired_rule_params = None for i in current_security_rules: if self.name_security_rule == i.about().get("name"): rule_atrib = i.about() rule_atrib.update({"source_user": user_list}) desired_rule_params = rule_atrib new_rule = panos.policies.SecurityRule(**desired_rule_params) rulebase.add(new_rule) new_rule.apply() fw.commit() self.report({ 'message': 'Responder successfully added %s to %s' % (user, self.name_security_rule) })
class TheHive(AppBase): """ An example of a Walkoff App. Inherit from the AppBase class to have Redis, logging, and console logging set up behind the scenes. """ __version__ = "1.1.0" app_name = "thehive" def __init__(self, redis, logger, console_logger=None): """ Each app should have this __init__ to set up Redis and logging. :param redis: :param logger: :param console_logger: """ super().__init__(redis, logger, console_logger) # async def run_analyzer(self, apikey, url, title_query): # self.thehive = TheHiveApi(url, apikey, cert=False) # response = self.thehive.find_cases(query=String("title:'%s'" % title_query), range='all', sort=[]) # return response.text def __connect_thehive(self, url, apikey, organisation): if organisation: self.thehive = TheHiveApi(url, apikey, cert=False, organisation=organisation) else: self.thehive = TheHiveApi(url, apikey, cert=False) async def search_case_title(self, apikey, url, organisation, title_query): self.__connect_thehive(url, apikey, organisation) response = self.thehive.find_cases(query=ContainsString( "title", title_query), range="all", sort=[]) return response.text async def custom_search(self, apikey, url, organisation, search_for, custom_query, range="all"): self.__connect_thehive(url, apikey, organisation) try: custom_query = json.loads(custom_query) except: # raise IOError("Invalid JSON payload received.") pass if search_for == "alert": response = self.thehive.find_alerts(query=custom_query, range="all", sort=[]) else: response = self.thehive.find_cases(query=custom_query, range="all", sort=[]) if (response.status_code == 200 or response.status_code == 201 or response.status_code == 202): return response.text else: raise IOError(response.text) async def add_case_artifact( self, apikey, url, organisation, case_id, data, datatype, tags=None, tlp=None, ioc=None, sighted=None, description="", ): self.__connect_thehive(url, apikey, organisation) tlp = int(tlp) if tlp else 2 ioc = True if ioc.lower() == "true" else False sighted = True if sighted.lower() == "true" else False if not description: description = "Created by shuffle" tags = (tags.split(", ") if ", " in tags else tags.split(",") if "," in tags else []) item = thehive4py.models.CaseObservable( dataType=datatype, data=data, tlp=tlp, ioc=ioc, sighted=sighted, tags=tags, message=description, ) return self.thehive.create_case_observable(case_id, item).text async def search_alert_title(self, apikey, url, organisation, title_query, search_range="0-25"): self.__connect_thehive(url, apikey, organisation) # Could be "all" too if search_range == "": search_range = "0-25" response = self.thehive.find_alerts(query=ContainsString( "title", title_query), range=search_range, sort=[]) return response.text async def create_case( self, apikey, url, organisation, template, title, description="", tlp=1, severity=1, tags="", ): self.__connect_thehive(url, apikey, organisation) if tags: if ", " in tags: tags = tags.split(", ") elif "," in tags: tags = tags.split(",") else: tags = [tags] else: tags = [] # Wutface fix if not tlp: tlp = 1 if not severity: severity = 1 if isinstance(tlp, str): if not tlp.isdigit(): return "TLP needs to be a number from 0-2, not %s" % tlp tlp = int(tlp) if isinstance(severity, str): if not severity.isdigit(): return "Severity needs to be a number from 0-2, not %s" % tlp severity = int(severity) if tlp > 3 or tlp < 0: return "TLP needs to be a number from 0-3, not %d" % tlp if severity > 2 or severity < 0: return "Severity needs to be a number from 0-2, not %d" % tlp Casetemplate = template if template else None case = thehive4py.models.Case( title=title, tlp=tlp, severity=severity, tags=tags, description=description, template=Casetemplate, ) try: ret = self.thehive.create_case(case) return ret.text except requests.exceptions.ConnectionError as e: return "ConnectionError: %s" % e async def create_alert( self, apikey, url, organisation, type, source, sourceref, title, description="", tlp=1, severity=1, tags="", artifacts="", ): self.__connect_thehive(url, apikey, organisation) if tags: if ", " in tags: tags = tags.split(", ") elif "," in tags: tags = tags.split(",") else: tags = [tags] else: tags = [] # Wutface fix if not tlp: tlp = 1 if not severity: severity = 1 if isinstance(tlp, str): if not tlp.isdigit(): return "TLP needs to be a number from 0-3, not %s" % tlp tlp = int(tlp) if isinstance(severity, str): if not severity.isdigit(): return "Severity needs to be a number from 1-3, not %s" % severity severity = int(severity) if tlp > 3 or tlp < 0: return "TLP needs to be a number from 0-3, not %d" % tlp if severity > 3 or severity < 1: return "Severity needs to be a number from 1-3, not %d" % severity all_artifacts = [] if artifacts != "": # print("ARTIFACTS: %s" % artifacts) if isinstance(artifacts, str): # print("ITS A STRING!") try: artifacts = json.loads(artifacts) except: print("[ERROR] Error in parsing artifacts!") # print("ART HERE: %s" % artifacts) # print("ART: %s" % type(artifacts)) if isinstance(artifacts, list): print("ITS A LIST!") for item in artifacts: print("ITEM: %s" % item) try: artifact = thehive4py.models.AlertArtifact( dataType=item["data_type"], data=item["data"], ) try: artifact["message"] = item["message"] except: pass if item["data_type"] == "ip": try: if item["is_private_ip"]: message += " IP is private." except: pass all_artifacts.append(artifact) except KeyError as e: print("Error in artifacts: %s" % e) alert = thehive4py.models.Alert( title=title, tlp=tlp, severity=severity, tags=tags, description=description, type=type, source=source, sourceRef=sourceref, artifacts=all_artifacts, ) try: ret = self.thehive.create_alert(alert) return ret.text except requests.exceptions.ConnectionError as e: return "ConnectionError: %s" % e async def create_alert_artifact( self, apikey, url, organisation, alert_id, dataType, data, message=None, tlp="2", ioc="False", sighted="False", ignoreSimilarity="False", tags=None, ): self.__connect_thehive(url, apikey, organisation, version=4) if tlp: tlp = int(tlp) else: tlp = 2 ioc = ioc.lower().strip() == "true" sighted = sighted.lower().strip() == "true" ignoreSimilarity = ignoreSimilarity.lower().strip() == "true" if tags: tags = [x.strip() for x in tags.split(",")] else: tags = [] alert_artifact = thehive4py.models.AlertArtifact( dataType=dataType, data=data, message=message, tlp=tlp, ioc=ioc, sighted=sighted, ignoreSimilarity=ignoreSimilarity, tags=tags, ) try: ret = self.thehive.create_alert_artifact(alert_id, alert_artifact) except requests.exceptions.ConnectionError as e: return "ConnectionError: %s" % e if ret.status_code > 299: raise ConnectionError(ret.text) return ret.text # Gets an item based on input. E.g. field_type = Alert async def get_item(self, apikey, url, organisation, field_type, cur_id): self.__connect_thehive(url, apikey, organisation) newstr = "" ret = "" if field_type.lower() == "alert": ret = self.thehive.get_alert(cur_id + "?similarity=1") elif field_type.lower() == "case": ret = self.thehive.get_case(cur_id) elif field_type.lower() == "case_observables": ret = self.thehive.get_case_observables(cur_id) elif field_type.lower() == "case_task": ret = self.thehive.get_case_task(cur_id) elif field_type.lower() == "case_tasks": ret = self.thehive.get_case_tasks(cur_id) elif field_type.lower() == "case_template": ret = self.thehive.get_case_tasks(cur_id) elif field_type.lower() == "linked_cases": ret = self.thehive.get_linked_cases(cur_id) elif field_type.lower() == "task_log": ret = self.thehive.get_task_log(cur_id) elif field_type.lower() == "task_logs": ret = self.thehive.get_task_logs(cur_id) else: return ( "%s is not implemented. See https://github.com/frikky/shuffle-apps for more info." % field_type) return ret.text async def close_alert(self, apikey, url, organisation, alert_id): self.__connect_thehive(url, apikey, organisation) return self.thehive.mark_alert_as_read(alert_id).text async def reopen_alert(self, apikey, url, organisation, alert_id): self.__connect_thehive(url, apikey, organisation) return self.thehive.mark_alert_as_unread(alert_id).text async def create_case_from_alert(self, apikey, url, organisation, alert_id, case_template=None): self.__connect_thehive(url, apikey, organisation) response = self.thehive.promote_alert_to_case( alert_id=alert_id, case_template=case_template) return response.text async def merge_alert_into_case(self, apikey, url, organisation, alert_id, case_id): self.__connect_thehive(url, apikey, organisation) req = url + f"/api/alert/{alert_id}/merge/{case_id}" ret = requests.post(req, auth=self.thehive.auth) return ret.text # Not sure what the data should be async def update_field(self, apikey, url, organisation, field_type, cur_id, field, data): # This is kinda silly but.. if field_type.lower() == "alert": newdata = {} if data.startswith("%s"): ticket = self.thehive.get_alert(cur_id) if ticket.status_code != 200: pass newdata[field] = "%s%s" % (ticket.json()[field], data[2:]) else: newdata[field] = data # Bleh url = "%s/api/alert/%s" % (url, cur_id) if field == "status": if data == "New" or data == "Updated": url = "%s/markAsUnread" % url elif data == "Ignored": url = "%s/markAsRead" % url ret = requests.post( url, headers={ "Content-Type": "application/json", "Authorization": "Bearer %s" % apikey, }, ) else: ret = requests.patch( url, headers={ "Content-Type": "application/json", "Authorization": "Bearer %s" % apikey, }, json=newdata, ) return str(ret.status_code) else: return ( "%s is not implemented. See https://github.com/frikky/walkoff-integrations for more info." % field_type) # https://github.com/TheHive-Project/TheHiveDocs/tree/master/api/connectors/cortex async def delete_alert_artifact(self, apikey, url, organisation, artifact_id): self.__connect_thehive(url, apikey, organisation, version=4) return self.thehive.delete_alert_artifact(artifact_id).text # https://github.com/TheHive-Project/TheHiveDocs/tree/master/api/connectors/cortex async def run_analyzer(self, apikey, url, organisation, cortex_id, analyzer_id, artifact_id): self.__connect_thehive(url, apikey, organisation) return self.thehive.run_analyzer(cortex_id, artifact_id, analyzer_id).text # Creates a task log in TheHive with file async def create_task_log(self, apikey, url, organisation, task_id, message, filedata={}): if filedata["success"] == False: return "No file to upload. Skipping message." headers = { "Authorization": "Bearer %s" % apikey, } files = {} if len(filedata["data"]) > 0: files = { "attachment": (filedata["filename"], filedata["data"]), } data = {"_json": """{"message": "%s"}""" % message} response = requests.post( "%s/api/case/task/%s/log" % (url, task_id), headers=headers, files=files, data=data, ) return response.text # Creates an observable as a file in a case async def create_case_file_observable(self, apikey, url, organisation, case_id, tags, filedata): if filedata["success"] == False: return "No file to upload. Skipping message." headers = { "Authorization": "Bearer %s" % apikey, } if tags: if ", " in tags: tags = tags.split(", ") elif "," in tags: tags = tags.split(",") else: tags = [tags] files = {} if len(filedata["data"]) > 0: files = { "attachment": (filedata["filename"], filedata["data"]), } outerarray = {"dataType": "file", "tags": tags} data = {"_json": """%s""" % json.dumps(outerarray)} response = requests.post( "%s/api/case/%s/artifact" % (url, case_id), headers=headers, files=files, data=data, verify=False, ) return response.text # Get all artifacts of a given case async def get_case_artifacts( self, apikey, url, organisation, case_id, dataType, ): self.__connect_thehive(url, apikey, organisation) query = And(Eq("dataType", dataType)) if dataType else {} # Call the API response = self.thehive.get_case_observables( case_id, query=query, sort=["-startDate", "+ioc"], range="all") # Display the result if response.status_code == 200: # Get response data list = response.json() # Display response data return (json.dumps(list, indent=4, sort_keys=True) if list else json.dumps( { "status": 200, "message": "No observable results" }, indent=4, sort_keys=True, )) else: return f"Failure: {response.status_code}/{response.text}" async def close_case( self, apikey, url, organisation, id, resolution_status="", impact_status="", summary="", ): self.__connect_thehive(url, apikey, organisation) case = self.thehive.case(id) case.status = "Resolved" case.summary = summary case.resolutionStatus = resolution_status case.impactStatus = impact_status result = self.thehive.update_case( case, fields=[ "status", "summary", "resolutionStatus", "impactStatus", ], ) return json.dumps(result.json(), indent=4, sort_keys=True) # Update TheHive Case async def update_case( self, apikey, url, organisation, id, title="", description="", severity=None, owner="", flag=None, tlp=None, pap=None, tags="", status="", custom_fields=None, custom_json=None, ): self.__connect_thehive(url, apikey, organisation) # Get current case data and update fields if new data exists case = self.thehive.get_case(id).json() print(case) case_title = title if title else case["title"] case_description = description if description else case["description"] case_severity = int(severity) if severity else case["severity"] case_owner = owner if owner else case["owner"] case_flag = ((False if flag.lower() == "false" else True) if flag else case["flag"]) case_tlp = int(tlp) if tlp else case["tlp"] case_pap = int(pap) if pap else case["pap"] case_tags = tags.split(",") if tags else case["tags"] case_tags = tags.split(",") if tags else case["tags"] case_status = status if status else case["status"] case_customFields = case["customFields"] # Prepare the customfields customfields = CustomFieldHelper() if case_customFields: for key, value in case_customFields.items(): if list(value)[0] == "integer": customfields.add_integer(key, list(value.items())[0][1]) elif list(value)[0] == "string": customfields.add_string(key, list(value.items())[0][1]) elif list(value)[0] == "boolean": customfields.add_boolean(key, list(value.items())[0][1]) elif list(value)[0] == "float": customfields.add_float(key, list(value.items())[0][1]) else: print( f'The value type "{value}" of the field {key} is not suported by the function.' ) custom_fields = json.loads(custom_fields) if custom_fields else {} for key, value in custom_fields.items(): if type(value) == int: customfields.add_integer(key, value) elif type(value) == str: customfields.add_string(key, value) elif type(value) == bool: customfields.add_boolean(key, value) elif type(value) == float: customfields.add_float(key, value) else: print( f'The value type "{value}" of the field {key} is not suported by the function.' ) customfields = customfields.build() custom_json = json.loads(custom_json) if custom_json else {} # Prepare the fields to be updated case = Case( id=id, title=case_title, description=case_description, severity=case_severity, owner=case_owner, flag=case_flag, tlp=case_tlp, pap=case_pap, tags=case_tags, status=case_status, customFields=customfields, json=custom_json, ) # resolutionStatus=case_resolutionStatus, result = self.thehive.update_case( case, fields=[ "title", "description", "severity", "owner", "flag", "tlp", "pap", "tags", "customFields", "status", ], ) return json.dumps(result.json(), indent=4, sort_keys=True) # Get TheHive Organisations async def get_organisations( self, apikey, url, organisation, ): headers = { "Authorization": f"Bearer {apikey}", "Content-Type": "application/json", } response = requests.get( f"{url}/api/organisation", headers=headers, verify=False, ) return response.text # Create TheHive Organisation async def create_organisation( self, apikey, url, organisation, name, description, ): headers = { "Authorization": f"Bearer {apikey}", "Content-Type": "application/json", } data = {"name": f"{name}", "description": f"{description}"} response = requests.post( f"{url}/api/organisation", headers=headers, json=data, verify=False, ) return response.text # Create User in TheHive async def create_user( self, apikey, url, organisation, login, name, profile, ): headers = { "Authorization": f"Bearer {apikey}", "Content-Type": "application/json", } data = { "login": f"{login}", "name": f"{name}", "profile": f"{profile}", "organisation": f"{organisation}", } response = requests.post( f"{url}/api/v1/user", headers=headers, json=data, verify=False, ) return response.text
class TheHive(AppBase): """ An example of a Walkoff App. Inherit from the AppBase class to have Redis, logging, and console logging set up behind the scenes. """ __version__ = "1.0.0" app_name = "thehive" def __init__(self, redis, logger, console_logger=None): """ Each app should have this __init__ to set up Redis and logging. :param redis: :param logger: :param console_logger: """ super().__init__(redis, logger, console_logger) #async def run_analyzer(self, apikey, url, title_query): # self.thehive = TheHiveApi(url, apikey) # response = self.thehive.find_cases(query=String("title:'%s'" % title_query), range='all', sort=[]) # return response.text async def search_cases(self, apikey, url, title_query): self.thehive = TheHiveApi(url, apikey) response = self.thehive.find_cases(query=String("title:'%s'" % title_query), range='all', sort=[]) return response.text async def add_observable(self, apikey, url, case_id, data, datatype, tags): self.thehive = TheHiveApi(url, apikey) if tags: if ", " in tags: tags = tags.split(", ") elif "," in tags: tags = tags.split(",") else: tags = [] else: tags = [] item = thehive4py.models.CaseObservable( dataType=datatype, data=data, tlp=1, ioc=False, sighted=False, tags=["Shuffle"], message="Created by shuffle", ) return self.thehive.create_case_observable(case_id, item).text async def search_alerts(self, apikey, url, title_query, search_range="0-25"): self.thehive = TheHiveApi(url, apikey) # Could be "all" too if search_range == "": search_range = "0-25" response = self.thehive.find_alerts(query=String("title:'%s'" % title_query), range=search_range, sort=[]) return response.text async def create_case(self, apikey, url, title, description="", tlp=1, severity=1, tags=""): self.thehive = TheHiveApi(url, apikey) if tags: if ", " in tags: tags = tags.split(", ") elif "," in tags: tags = tags.split(",") else: tags = [] else: tags = [] # Wutface fix if not tlp: tlp = 1 if not severity: severity = 1 if isinstance(tlp, str): if not tlp.isdigit(): return "TLP needs to be a number from 0-2, not %s" % tlp tlp = int(tlp) if isinstance(severity, str): if not severity.isdigit(): return "Severity needs to be a number from 0-2, not %s" % tlp severity = int(severity) if tlp > 3 or tlp < 0: return "TLP needs to be a number from 0-3, not %d" % tlp if severity > 2 or severity < 0: return "Severity needs to be a number from 0-2, not %d" % tlp case = thehive4py.models.Case( title=title, tlp=tlp, severity=severity, tags=tags, description=description, ) try: ret = self.thehive.create_case(case) return ret.text except requests.exceptions.ConnectionError as e: return "ConnectionError: %s" % e async def create_alert(self, apikey, url, type, source, sourceref, title, description="", tlp=1, severity=1, tags=""): self.thehive = TheHiveApi(url, apikey) if tags: if ", " in tags: tags = tags.split(", ") elif "," in tags: tags = tags.split(",") else: tags = [] else: tags = [] # Wutface fix if not tlp: tlp = 1 if not severity: severity = 1 if isinstance(tlp, str): if not tlp.isdigit(): return "TLP needs to be a number from 0-2, not %s" % tlp tlp = int(tlp) if isinstance(severity, str): if not severity.isdigit(): return "Severity needs to be a number from 0-2, not %s" % tlp severity = int(severity) if tlp > 2 or tlp < 0: return "TLP needs to be a number from 0-2, not %d" % tlp if severity > 2 or severity < 0: return "Severity needs to be a number from 0-2, not %d" % tlp alert = thehive4py.models.Alert( title=title, tlp=tlp, severity=severity, tags=tags, description=description, type=type, source=source, sourceRef=sourceref, ) try: ret = self.thehive.create_alert(alert) return ret.text except requests.exceptions.ConnectionError as e: return "ConnectionError: %s" % e # Gets an item based on input. E.g. field_type = Alert async def get_item(self, apikey, url, field_type, cur_id): self.thehive = TheHiveApi(url, apikey) newstr = "" ret = "" if field_type.lower() == "alert": ret = self.thehive.get_alert(cur_id + "?similarity=1") elif field_type.lower() == "case": ret = self.thehive.get_case(cur_id) elif field_type.lower() == "case_observables": ret = self.thehive.get_case_observables(cur_id) elif field_type.lower() == "case_task": ret = self.thehive.get_case_task(cur_id) elif field_type.lower() == "case_tasks": ret = self.thehive.get_case_tasks(cur_id) elif field_type.lower() == "case_template": ret = self.thehive.get_case_tasks(cur_id) elif field_type.lower() == "linked_cases": ret = self.thehive.get_linked_cases(cur_id) elif field_type.lower() == "task_log": ret = self.thehive.get_task_log(cur_id) elif field_type.lower() == "task_logs": ret = self.thehive.get_task_logs(cur_id) else: return "%s is not implemented. See https://github.com/frikky/shuffle-apps for more info." % field_type return ret.text async def close_alert(self, apikey, url, alert_id): self.thehive = TheHiveApi(url, apikey) return self.thehive.mark_alert_as_read(alert_id).text async def reopen_alert(self, apikey, url, alert_id): self.thehive = TheHiveApi(url, apikey) return self.thehive.mark_alert_as_unread(alert_id).text async def create_case_from_alert(self, apikey, url, alert_id, case_template=None): self.thehive = TheHiveApi(url, apikey) response = self.thehive.promote_alert_to_case(alert_id=alert_id, case_template=case_template) return response.text async def merge_alert_into_case(self, apikey, url, alert_id, case_id): self.thehive = TheHiveApi(url, apikey) req = url + f"/api/alert/{alert_id}/merge/{case_id}" ret = requests.post(req, auth=self.thehive.auth) return ret.text # Not sure what the data should be async def update_field(self, apikey, url, field_type, cur_id, field, data): # This is kinda silly but.. if field_type.lower() == "alert": newdata = {} if data.startswith("%s"): ticket = self.thehive.get_alert(cur_id) if ticket.status_code != 200: pass newdata[field] = "%s%s" % (ticket.json()[field], data[2:]) else: newdata[field] = data # Bleh url = "%s/api/alert/%s" % (url, cur_id) if field == "status": if data == "New" or data == "Updated": url = "%s/markAsUnread" % url elif data == "Ignored": url = "%s/markAsRead" % url ret = requests.post( url, headers={ 'Content-Type': 'application/json', 'Authorization': 'Bearer %s' % apikey } ) else: ret = requests.patch( url, headers={ 'Content-Type': 'application/json', 'Authorization': 'Bearer %s' % apikey }, json=newdata, ) return str(ret.status_code) else: return "%s is not implemented. See https://github.com/frikky/walkoff-integrations for more info." % field_type # https://github.com/TheHive-Project/TheHiveDocs/tree/master/api/connectors/cortex async def run_analyzer(self, apikey, url, cortex_id, analyzer_id, artifact_id): self.thehive = TheHiveApi(url, apikey) return self.thehive.run_analyzer(cortex_id, artifact_id, analyzer_id).text