diff --git a/analyzers/Crowdsec/requirements.txt b/analyzers/Crowdsec/requirements.txt index 8ad52a568..4a21dbf63 100644 --- a/analyzers/Crowdsec/requirements.txt +++ b/analyzers/Crowdsec/requirements.txt @@ -1 +1,2 @@ cortexutils +requests \ No newline at end of file diff --git a/analyzers/Onyphe/ONYPHE_ASM.json b/analyzers/Onyphe/ONYPHE_ASM.json new file mode 100644 index 000000000..e72edfa28 --- /dev/null +++ b/analyzers/Onyphe/ONYPHE_ASM.json @@ -0,0 +1,65 @@ +{ + "name": "ONYPHE_ASM", + "version": "1.0", + "author": "Pierre Baudry, Adrien Barchapt, Andrea Garavaglia, Davide Arcuri, James Atack", + "license": "AGPL-V3", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "description": "Retrieve results from ONYPHE Search API for a given ip, domain or fqdn from specified category", + "dataTypeList": ["ip", "domain", "fqdn", "hash"], + "command": "Onyphe/onyphe_analyzer.py", + "baseConfig": "Onyphe", + "config": { + "service": "asm" + }, + "configurationItems": [ + { + "name": "key", + "description": "Define the API key to use to connect the service", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "time_filter", + "description": "Specify ONYPHE time filter to be used for searches (see https://www.onyphe.io/docs/onyphe-query-language)", + "type": "string", + "multi": false, + "required": false, + "defaultValue": "-since:1M" + }, + { + "name": "fields_filter", + "description": "[!!Advanced!!] Modify ONYPHE fields to return in raw data (see https://www.onyphe.io/docs/onyphe-query-language)", + "type": "string", + "multi": false, + "required": false, + "defaultValue": "ip,port,protocol,tag,tls,cpe,cve,hostname,domain,alternativeip,forward,url,organization,transport,organization,device.class,device.product,device.productvendor,device.productversion,product,productvendor,productversion" + }, + { + "name": "auto_import", + "description": "Automatically import artifacts as observables (risks, cves, assets, ...)", + "type": "boolean", + "multi": false, + "required": true, + "defaultValue": true + } + ], + "registration_required": true, + "subscription_required": true, + "free_subscription": true, + "service_homepage": "https://www.onyphe.io", + "service_logo": { + "path": "assets/onyphe_logo.png", + "caption": "logo" + }, + "screenshots": [ + { + "path": "assets/ONYPHE_ASM_long.png", + "caption": "ONYPHE ASM report sample (IPs obscured) with click to expand accordion." + }, + { + "path": "assets/ONYPHE_ASM_short.png", + "caption": "ONYPHE ASM mini report showing no. of risks" + } + ] +} diff --git a/analyzers/Onyphe/ONYPHE_Search.json b/analyzers/Onyphe/ONYPHE_Search.json new file mode 100644 index 000000000..d0915db9b --- /dev/null +++ b/analyzers/Onyphe/ONYPHE_Search.json @@ -0,0 +1,65 @@ +{ + "name": "ONYPHE_Search", + "version": "1.0", + "author": "Pierre Baudry, Adrien Barchapt, Andrea Garavaglia, Davide Arcuri, James Atack", + "license": "AGPL-V3", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "description": "Retrieve results from ONYPHE Search API for a given ip, domain, fqdn or hash (sha256 TLS fingerprint) from specified category", + "dataTypeList": ["ip", "domain", "fqdn", "hash"], + "command": "Onyphe/onyphe_analyzer.py", + "baseConfig": "Onyphe", + "config": { + "service": "search" + }, + "configurationItems": [ + { + "name": "key", + "description": "Define the API key to use to connect the service", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "category", + "description": "Specify ONYPHE category to be used for search API (default datascan)", + "type": "string", + "multi": false, + "required": true, + "defaultValue": "datascan" + }, + { + "name": "time_filter", + "description": "Specify ONYPHE time filter to be used for searches (see https://www.onyphe.io/docs/onyphe-query-language)", + "type": "string", + "multi": false, + "required": true, + "defaultValue": "-since:1M" + }, + { + "name": "auto_import", + "description": "Automatically import artifacts as observables (risks, cves, assets, ...)", + "type": "boolean", + "multi": false, + "required": true, + "defaultValue": false + } + ], + "registration_required": true, + "subscription_required": true, + "free_subscription": true, + "service_homepage": "https://www.onyphe.io", + "service_logo": { + "path": "assets/onyphe_logo.png", + "caption": "logo" + }, + "screenshots": [ + { + "path": "assets/ONYPHE_Search_long.png", + "caption": "ONYPHE Search report sample (IPs obscured)" + }, + { + "path": "assets/ONYPHE_Search_short.png", + "caption": "ONYPHE Search mini report showing no. of open ports" + } + ] +} diff --git a/analyzers/Onyphe/ONYPHE_Summary_API.json b/analyzers/Onyphe/ONYPHE_Summary_API.json new file mode 100644 index 000000000..b0b75a727 --- /dev/null +++ b/analyzers/Onyphe/ONYPHE_Summary_API.json @@ -0,0 +1,49 @@ +{ + "name": "ONYPHE_Summary_API", + "version": "1.1", + "author": "Pierre Baudry, Adrien Barchapt, Andrea Garavaglia, Davide Arcuri, James Atack", + "license": "AGPL-V3", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "description": "Retrieve summary information Onyphe has for given ip, domain, or fqdn.", + "dataTypeList": ["ip", "domain", "fqdn"], + "command": "Onyphe/onyphe_analyzer.py", + "baseConfig": "Onyphe", + "config": { + "service": "summary" + }, + "configurationItems": [ + { + "name": "key", + "description": "Define the API key to use to connect the service", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "verbose_taxonomies", + "description": "Set true if you want detailed taxonomies for port, subnet, geoloc, domain", + "type": "boolean", + "multi": false, + "required": true, + "defaultValue": false + } + ], + "registration_required": true, + "subscription_required": true, + "free_subscription": true, + "service_homepage": "https://www.onyphe.io", + "service_logo": { + "path": "assets/onyphe_logo.png", + "caption": "logo" + }, + "screenshots": [ + { + "path": "assets/Onyphe_Summary_long.png", + "caption": "Onyphe_Summary long report sample" + }, + { + "path": "assets/Onyphe_Summary_short.png", + "caption": "Onyphe_Summary mini report sample" + } + ] +} diff --git a/analyzers/Onyphe/ONYPHE_Vulnscan.json b/analyzers/Onyphe/ONYPHE_Vulnscan.json new file mode 100644 index 000000000..8d2768dad --- /dev/null +++ b/analyzers/Onyphe/ONYPHE_Vulnscan.json @@ -0,0 +1,65 @@ +{ + "name": "ONYPHE_Vulnscan", + "version": "1.0", + "author": "Pierre Baudry, Adrien Barchapt, Andrea Garavaglia, Davide Arcuri, James Atack", + "license": "AGPL-V3", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "description": "Retrieve vulnerability data from ONYPHE vulnscan category for a given ip, domain, fqdn or hash (sha256 TLS fingerprint)", + "dataTypeList": ["ip", "domain", "fqdn", "hash"], + "command": "Onyphe/onyphe_analyzer.py", + "baseConfig": "Onyphe", + "config": { + "service": "vulnscan" + }, + "configurationItems": [ + { + "name": "key", + "description": "Define the API key to use to connect the service", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "time_filter", + "description": "Specify ONYPHE time filter to be used for searches (see https://www.onyphe.io/docs/onyphe-query-language)", + "type": "string", + "multi": false, + "required": true, + "defaultValue": "-since:1M" + }, + { + "name": "only_vulnerable", + "description": "Only return results where a CVE exists (-exists:cve)", + "type": "boolean", + "multi": false, + "required": true, + "defaultValue": true + }, + { + "name": "auto_import", + "description": "Automatically import artifacts as observables (risks, cves, assets, ...)", + "type": "boolean", + "multi": false, + "required": true, + "defaultValue": false + } + ], + "registration_required": true, + "subscription_required": true, + "free_subscription": true, + "service_homepage": "https://www.onyphe.io", + "service_logo": { + "path": "assets/onyphe_logo.png", + "caption": "logo" + }, + "screenshots": [ + { + "path": "assets/ONYPHE_Vulnscan_long.png", + "caption": "ONYPHE Vulnscan report sample (IPs obscured)" + }, + { + "path": "assets/ONYPHE_Vulnscan_short.png", + "caption": "ONYPHE Vulnscan mini report showing no. of CVEs" + } + ] +} diff --git a/analyzers/Onyphe/assets/ONYPHE_ASM_long.png b/analyzers/Onyphe/assets/ONYPHE_ASM_long.png new file mode 100644 index 000000000..7e96fd41c Binary files /dev/null and b/analyzers/Onyphe/assets/ONYPHE_ASM_long.png differ diff --git a/analyzers/Onyphe/assets/ONYPHE_ASM_short.png b/analyzers/Onyphe/assets/ONYPHE_ASM_short.png new file mode 100644 index 000000000..1a2e47c7f Binary files /dev/null and b/analyzers/Onyphe/assets/ONYPHE_ASM_short.png differ diff --git a/analyzers/Onyphe/assets/ONYPHE_Search_long.png b/analyzers/Onyphe/assets/ONYPHE_Search_long.png new file mode 100644 index 000000000..20b15630c Binary files /dev/null and b/analyzers/Onyphe/assets/ONYPHE_Search_long.png differ diff --git a/analyzers/Onyphe/assets/ONYPHE_Search_short.png b/analyzers/Onyphe/assets/ONYPHE_Search_short.png new file mode 100644 index 000000000..1a2e47c7f Binary files /dev/null and b/analyzers/Onyphe/assets/ONYPHE_Search_short.png differ diff --git a/analyzers/Onyphe/assets/ONYPHE_Vulnscan_long.png b/analyzers/Onyphe/assets/ONYPHE_Vulnscan_long.png new file mode 100644 index 000000000..70f0b337e Binary files /dev/null and b/analyzers/Onyphe/assets/ONYPHE_Vulnscan_long.png differ diff --git a/analyzers/Onyphe/assets/ONYPHE_Vulnscan_short.png b/analyzers/Onyphe/assets/ONYPHE_Vulnscan_short.png new file mode 100644 index 000000000..1a2e47c7f Binary files /dev/null and b/analyzers/Onyphe/assets/ONYPHE_Vulnscan_short.png differ diff --git a/analyzers/Onyphe/assets/onyphe_logo.png b/analyzers/Onyphe/assets/onyphe_logo.png index 27bbc183f..dfa143cb6 100644 Binary files a/analyzers/Onyphe/assets/onyphe_logo.png and b/analyzers/Onyphe/assets/onyphe_logo.png differ diff --git a/analyzers/Onyphe/onyphe_analyzer.py b/analyzers/Onyphe/onyphe_analyzer.py index 89e84e854..74ef47311 100755 --- a/analyzers/Onyphe/onyphe_analyzer.py +++ b/analyzers/Onyphe/onyphe_analyzer.py @@ -3,7 +3,7 @@ from cortexutils.analyzer import Analyzer from onyphe_api import Onyphe from datetime import datetime - +#from tld import get_fld # TODO FLD/subdomains check class OnypheAnalyzer(Analyzer): def __init__(self): @@ -13,14 +13,123 @@ def __init__(self): ) self.onyphe_key = self.get_param("config.key", None, "Missing Onyphe API key") self.onyphe_client = None - self.verbose_taxonomies = self.get_param("config.verbose_taxonomies", False) + self.onyphe_category = self.get_param("config.category", "datascan") #only used for Search service + self.time_filter = self.get_param("config.time_filter", "-since:1M") + self.auto_import = self.get_param("config.auto_import", False) + self.verbose_taxonomies = self.get_param("config.verbose_taxonomies", False) #Only used for Summary service self.polling_interval = self.get_param("config.polling_interval", 60) def summary(self, raw): taxonomies = [] - namespace = "Onyphe" + namespace = "ONYPHE" + + if (self.service == "search" and self.onyphe_category == "vulnscan") or self.service == "vulnscan": + #report number of CVEs + + reportlist = [] + + for odoc in raw["results"]: + if "cve" in odoc: + for cve in odoc["cve"]: + if cve not in reportlist: + reportlist.append(cve) + + if len(reportlist) > 0: + taxonomies.append( + self.build_taxonomy( + "malicious", + namespace, + "CVE", + "{} CVE found".format(len(reportlist)), + ) + ) + else: + taxonomies.append( + self.build_taxonomy("info", namespace, "CVE", "No CVE found",) + ) + + elif self.service == "search" and self.onyphe_category == "riskscan": + #report number of unique risks/ports/services + + reportlist = [] + + for odoc in raw["results"]: + if "forward" in odoc: + assetport = str(odoc["ip"]) + ":" + str(odoc["port"]) + ":" + str(odoc["forward"]) + else: + assetport = str(odoc["ip"]) + ":" + str(odoc["port"]) + if not assetport in reportlist: + reportlist.append(assetport) - if not self.verbose_taxonomies: + if len(reportlist) > 0: + taxonomies.append( + self.build_taxonomy( + "suspicious", + namespace, + "Risk", + "{} risks found".format(len(reportlist)), + ) + ) + else: + taxonomies.append( + self.build_taxonomy("info", namespace, "Risk", "No risks found",) + ) + + elif self.service == "search" and self.onyphe_category == "datascan": + #report number of unique ports/services + + reportlist = [] + + for odoc in raw["results"]: + if "forward" in odoc: + assetport = str(odoc["ip"]) + ":" + str(odoc["port"]) + ":" + str(odoc["forward"]) + else: + assetport = str(odoc["ip"]) + ":" + str(odoc["port"]) + if not assetport in reportlist: + reportlist.append(assetport) + + if len(reportlist) > 0: + taxonomies.append( + self.build_taxonomy( + "info", + namespace, + "Services", + "{} services found".format(len(reportlist)), + ) + ) + else: + taxonomies.append( + self.build_taxonomy("info", namespace, "Services", "No services found",) + ) + + elif self.service == "asm": + #report number of unique risks/ports/services + + reportlist = [] + + for odoc in raw["results"]: + if "forward" in odoc: + assetport = str(odoc["ip"]) + ":" + str(odoc["port"]) + ":" + str(odoc["forward"]) + else: + assetport = str(odoc["ip"]) + ":" + str(odoc["port"]) + if not assetport in reportlist: + reportlist.append(assetport) + + if len(reportlist) > 0: + taxonomies.append( + self.build_taxonomy( + "suspicious", + namespace, + "Risk", + "{} risks found".format(len(reportlist)), + ) + ) + else: + taxonomies.append( + self.build_taxonomy("info", namespace, "Risk", "No risks found",) + ) + + elif (self.service == "summary" and not self.verbose_taxonomies) or self.service == "threatlist": threatlist = list( set( @@ -45,7 +154,8 @@ def summary(self, raw): taxonomies.append( self.build_taxonomy("info", namespace, "Threat", "No threat found",) ) - else: + + elif self.service == "summary" and self.verbose_taxonomies: output_data = { "threatlist": {}, @@ -166,27 +276,262 @@ def summary(self, raw): return {"taxonomies": taxonomies} + def artifacts(self, raw): + artifacts = [] + dedup = {} + + if self.service != "summary": + for odoc in raw["results"]: + if ("forward" in odoc and "port" in odoc): + dedup_key = str(odoc["ip"]) + ":" + str(odoc["port"]) + ":" + str(odoc["forward"]) + elif "port" in odoc: + dedup_key = str(odoc["ip"]) + ":" + str(odoc["port"]) + elif "threatlist" in odoc: + dedup_key = str(odoc["ip"]) + ":" + str(odoc["threatlist"]) #dedup key for threatlist, as no port in that category + else: + dedup_key = str(odoc["ip"]) + + newasset = True + if dedup_key in dedup: + newdate = datetime.strptime(odoc["seen_date"], "%Y-%m-%d") + olddate = datetime.strptime(dedup[dedup_key], "%Y-%m-%d") + if olddate > newdate: + newasset = False + + if newasset: + if odoc["@category"] == "riskscan": #category riskscan, so artifacts are risks + otags=["onyphe:risk"] + if self.auto_import: + otags.append("autoImport:true") + for ta in odoc["tag"]: + otags.append(str(ta)) + if "cve" in odoc: + for cve in odoc["cve"]: + otags.append(str(cve)) + otags.append(str(odoc["protocol"])) + otags.append(str(odoc["transport"]) + "/" + str(odoc["port"])) + if self.data_type == "ip": #datatype is IP, so create fqdn artifacts + if "hostname" in odoc: + for fqdns in odoc["hostname"]: + artifacts.append( + self.build_artifact( + "fqdn", str(fqdns), tags=otags + ) + ) + elif "reverse" in odoc: #no hostnames so use reverse if possible + artifacts.append( + self.build_artifact( + "fqdn", odoc["reverse"], tags=otags + ) + ) + else: #no hostnames or reverse so use ip, but user can't import as observable exists :( + artifacts.append( + self.build_artifact( + "ip", str(odoc["ip"]), tags=otags + ) + ) + else: + artifacts.append( + self.build_artifact( + "ip", str(odoc["ip"]), tags=otags + ) + ) + elif odoc["@category"] == "vulnscan": #category vulnscan, so artifacts are cves + if "cve" in odoc: + otags=["onyphe:cve"] + if self.auto_import: + otags.append("autoImport:true") + for cve in odoc["cve"]: + otags.append(str(cve)) + if "tag" in odoc: + for ta in odoc["tag"]: + otags.append(str(ta)) + otags.append(str(odoc["protocol"])) + otags.append(str(odoc["transport"]) + "/" + str(odoc["port"])) + if self.data_type == "ip": #datatype is IP, so create fqdn artifacts + if "hostname" in odoc: + for fqdns in odoc["hostname"]: + artifacts.append( + self.build_artifact( + "fqdn", str(fqdns), tags=otags + ) + ) + elif "reverse" in odoc: #no hostnames so use reverse if possible + artifacts.append( + self.build_artifact( + "fqdn", odoc["reverse"], tags=otags + ) + ) + else: #no hostnames or reverse so use ip, but user can't import as observable exists :( + artifacts.append( + self.build_artifact( + "ip", str(odoc["ip"]), tags=otags + ) + ) + else: + artifacts.append( + self.build_artifact( + "ip", str(odoc["ip"]), tags=otags + ) + ) + elif odoc["@category"] == "datascan" or odoc["@category"] == "onionscan": #category datascan, so artifacts is all results + otags=["onyphe:asset"] + if self.auto_import: + otags.append("autoImport:true") + otags.append(str(odoc["protocol"])) + otags.append(str(odoc["transport"]) + "/" + str(odoc["port"])) + if "tag" in odoc: + for ta in odoc["tag"]: + otags.append(str(ta)) + if self.data_type == "ip": #datatype is IP, so create fqdn artifacts + if "hostname" in odoc: + for fqdns in odoc["hostname"]: + artifacts.append( + self.build_artifact( + "fqdn", str(fqdns), tags=otags + ) + ) + elif "reverse" in odoc: #no hostnames so use reverse if possible + artifacts.append( + self.build_artifact( + "fqdn", odoc["reverse"], tags=otags + ) + ) + else: #no hostnames or reverse so use ip, but user can't import as observable exists :( + artifacts.append( + self.build_artifact( + "ip", str(odoc["ip"]), tags=otags + ) + ) + else: + artifacts.append( + self.build_artifact( + "ip", str(odoc["ip"]), tags=otags + ) + ) + elif odoc["@category"] == "threatlist": + otags=["onyphe:threat"] + if self.auto_import: + otags.append("autoImport:true") + if "tag" in odoc: + for ta in odoc["tag"]: + otags.append(str(ta)) + if self.data_type == "ip": #datatype is IP, so create fqdn artifacts + if "hostname" in odoc: + for fqdns in odoc["hostname"]: + artifacts.append( + self.build_artifact( + "fqdn", str(fqdns), tags=otags + ) + ) + elif "reverse" in odoc: #no hostnames so use reverse if possible + artifacts.append( + self.build_artifact( + "fqdn", odoc["reverse"], tags=otags + ) + ) + else: #no hostnames or reverse so use ip, but user can't import as observable exists :( + artifacts.append( + self.build_artifact( + "ip", str(odoc["ip"]), tags=otags + ) + ) + else: + artifacts.append( + self.build_artifact( + "ip", str(odoc["ip"]), tags=otags + ) + ) + else: #category other, so assuming resolver / hostname enumeration + otags=["onyphe:" + self.onyphe_category] + if self.auto_import: #YOLO + otags.append("autoImport:true") + if "tag" in odoc: + for ta in odoc["tag"]: + otags.append(str(ta)) + if self.data_type == "ip": #datatype is IP, so create fqdn artifacts + if "hostname" in odoc: + for fqdns in odoc["hostname"]: + artifacts.append( + self.build_artifact( + "fqdn", str(fqdns), tags=otags + ) + ) + elif "reverse" in odoc: #no hostnames so use reverse if possible + artifacts.append( + self.build_artifact( + "fqdn", odoc["reverse"], tags=otags + ) + ) + else: #no hostnames or reverse so use ip, but user can't import as observable exists :( + artifacts.append( + self.build_artifact( + "ip", str(odoc["ip"]), tags=otags + ) + ) + else: + artifacts.append( + self.build_artifact( + "ip", str(odoc["ip"]), tags=otags + ) + ) + dedup[dedup_key] = str(odoc["seen_date"]) + return artifacts + def run(self): Analyzer.run(self) try: self.onyphe_client = Onyphe(self.onyphe_key) data = self.get_param("data", None, "Data is missing") - results = self.onyphe_client.summary(data, self.data_type) - results["totals_category"] = { - k: len( - [x for x in results["results"] if x["@category"] == k] - ) - for k in [ - "threatlist", - "threats", - "geoloc", - "inetnum", - "ports", - "reverse", - "datascan", - "forward", - ] - } + + if self.service == "search": + results = self.onyphe_client.search(data, self.data_type,self.onyphe_category,self.time_filter) + results["category"] = self.onyphe_category + results["total_category"] = len(results["results"]) + + elif self.service == "asm": + self.onyphe_category = "riskscan" #ASM service so force category to riskscan + self.fields_filter = self.get_param("config.fields_filter", "ip,port,protocol,tag,tls,cpe,cve,hostname,domain,alternativeip,forward,url,organization,transport,organization,device.class,device.product,device.productvendor,device.productversion,product,productvendor,productversion") + asmfilter = self.time_filter + "+-fields:" + self.fields_filter #Fields filter is faster and saves space in The Hive database. + results = self.onyphe_client.search(data, self.data_type,self.onyphe_category,asmfilter.replace(",","%2C")) + #results = self.onyphe_client.search(data, self.data_type,self.onyphe_category,self.time_filter) + results["category"] = self.onyphe_category + results["total_category"] = len(results["results"]) + + elif self.service == "vulnscan": + self.onyphe_category = "vulnscan" + + vulnfilter = self.time_filter + if self.get_param("config.only_vulnerable", True): + vulnfilter += "+-exists:cve" + results = self.onyphe_client.search(data, self.data_type,self.onyphe_category,vulnfilter) + results["category"] = self.onyphe_category + results["total_category"] = len(results["results"]) + + elif self.service == "threatlist": + self.onyphe_category = "threatlist" + + results = self.onyphe_client.search(data, self.data_type,self.onyphe_category,self.time_filter) + results["category"] = self.onyphe_category + results["total_category"] = len(results["results"]) + + elif self.service == "summary": + results = self.onyphe_client.summary(data, self.data_type) + results["totals_category"] = { + k: len( + [x for x in results["results"] if x["@category"] == k] + ) + for k in [ + "threatlist", + "threats", + "geoloc", + "inetnum", + "ports", + "reverse", + "datascan", + "forward", + ] + } self.report(results) diff --git a/analyzers/Onyphe/onyphe_api.py b/analyzers/Onyphe/onyphe_api.py index eef046601..e417ae863 100755 --- a/analyzers/Onyphe/onyphe_api.py +++ b/analyzers/Onyphe/onyphe_api.py @@ -49,10 +49,23 @@ def summary(self, data: str, datatype: str): url_path = "/api/v2/summary/ip/{ip}".format(ip=data) elif datatype == 'domain': url_path = "/api/v2/summary/domain/{domain}".format(domain=data) - elif datatype == 'hostname': - url_path = "/api/v2/summary/hostname/{hostname}".format(hostname=data) + elif datatype == 'fqdn': + url_path = "/api/v2/summary/hostname/{hostname}".format(hostname=data) return self._request(path=url_path) + + def search(self, data: str, datatype: str, category: str, filter: str): + """Return data from specified category using Search API and the provided data as the OQL filter. + """ + if datatype == 'ip': + url_path = "/api/v2/search/?q=category:{category}+ip:{ip}+{filter}".format(category=category,ip=data,filter=filter) + elif datatype == 'domain': + url_path = "/api/v2/search/?q=category:{category}+domain:{domain}+{filter}".format(category=category,domain=data,filter=filter) + elif datatype == 'fqdn': + url_path = "/api/v2/search/?q=category:{category}+hostname:{hostname}+{filter}".format(category=category,hostname=data,filter=filter) + elif datatype == 'hash': + url_path = "/api/v2/search/?q=category:{category}+fingerprint.sha256:{hash}+{filter}".format(category=category,hash=data,filter=filter) + return self._request(path=url_path) class APIError(Exception): """This exception gets raised when the returned error code is non-zero positive""" diff --git a/analyzers/StamusNetworks/hostid_analyzer.py b/analyzers/StamusNetworks/hostid_analyzer.py index d7c19dc08..a240464b3 100755 --- a/analyzers/StamusNetworks/hostid_analyzer.py +++ b/analyzers/StamusNetworks/hostid_analyzer.py @@ -37,7 +37,7 @@ def artifacts(self, raw): host['host'], tags=tags)) net_info = raw['host_id'].get('net_info', []) - if len(net_info) > -1: + if len(net_info) > 0: net_info = sorted(net_info, key=lambda k: k['last_seen'], reverse=True)[0]['agg'] tags=["network-info"] artifacts.append( diff --git a/thehive-templates/ONYPHE_ASM_1_0/long.html b/thehive-templates/ONYPHE_ASM_1_0/long.html new file mode 100644 index 000000000..7230dd872 --- /dev/null +++ b/thehive-templates/ONYPHE_ASM_1_0/long.html @@ -0,0 +1,88 @@ +
+
+ ONYPHE ASM search - {{(artifact.data || artifact.attachment.name) | fang}} - {{content.total_category}} results +
+
+ +
+
+ ONYPHE ASM search - {{(artifact.data || artifact.attachment.name) | fang}} - Displaying first 100 results (search returned {{content.total}}) +
+
+ +
+ +
+  Open in ONYPHE  + +  {{ bista }}  +  {{ bista }}  +  {{ bista }}  +  {{ bista }}  +
+
+
Timestamp:
+
{{r['@timestamp']}}
+
+
+
Asset:
+
{{r.ip}}:{{r.port}} | ({{r.transport}}/{{r.protocol}}/tls)
+
+
+
CVE(s):
+
{{ cv }}
+
+
+
Hostname(s):
+
{{ h }}
+
+
+
Domain:
+
{{ d }}
+
+
+
Alternative IP(s):
+
{{ ip }}
+
+
+
Device:
+
{{r.device.productvendor}} {{r.device.product}}
+
{{r.device.class}}
+
+
+
URL:
+
https://{{r.forward}}{{r.url}}
+
https://{{r.forward}}:{{r.port}}{{r.url}}
+
http://{{r.forward}}{{r.url}}
+
http://{{r.forward}}:{{r.port}}{{r.url}}
+
https://{{r.ip}}{{r.url}}
+
https://{{r.ip}}:{{r.port}}{{r.url}}
+
http://{{r.ip}}{{r.url}}
+
http://{{r.ip}}:{{r.port}}{{r.url}}
+
+
+
Organization:
+
{{r.organization}}
+
+
+
CPE(s):
+
{{ cp }}
+
+ + +
+ + +
+
+ {{artifact.data | fang}} +
+
+ {{content.errorMessage}} +
+
diff --git a/thehive-templates/ONYPHE_ASM_1_0/short.html b/thehive-templates/ONYPHE_ASM_1_0/short.html new file mode 100644 index 000000000..9fd48f9fa --- /dev/null +++ b/thehive-templates/ONYPHE_ASM_1_0/short.html @@ -0,0 +1,3 @@ + + {{t.namespace}}:{{t.predicate}}="{{t.value}}" + \ No newline at end of file diff --git a/thehive-templates/ONYPHE_Search_1_0/long.html b/thehive-templates/ONYPHE_Search_1_0/long.html new file mode 100644 index 000000000..4afb3cf7c --- /dev/null +++ b/thehive-templates/ONYPHE_Search_1_0/long.html @@ -0,0 +1,200 @@ +
+
+ ONYPHE {{content.category}} search - {{(artifact.data || artifact.attachment.name) | fang}} - {{content.total_category}} results +
+
+ +
+
+ ONYPHE {{content.category}} search - {{(artifact.data || artifact.attachment.name) | fang}} - Displaying first 100 results (search returned {{content.total}}) +
+
+ + +
+
+ {{r.ip}}:{{r.port}} | ({{r.transport}}/{{r.protocol}}/tls) last seen on {{r.seen_date}} | country {{r.country}}   + {{r.ip}} | ({{r.threatlist}}) last seen on {{r.seen_date}} | country {{r.country}}   +  Open in ONYPHE  +
+
+ +  {{ ta }}  +  {{ ta }}  +  {{ ta }}  +  {{ ta }}  +  {{ ta }}  + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CVE(s){{ cve }}
Threat List{{ r.threatlist }}
IP{{r.ip}}
Alternative IP(s){{ ip }}
Network{{r.subnet}}
Domain(s){{ d }}
Device{{r.device.productvendor}} {{r.device.product}}{{r.device.class}}
OS{{r.os}}
URLhttps://{{r.forward}}{{r.url}}https://{{r.forward}}:{{r.port}}{{r.url}}http://{{r.forward}}{{r.url}}http://{{r.forward}}:{{r.port}}{{r.url}}https://{{r.ip}}{{r.url}}https://{{r.ip}}:{{r.port}}{{r.url}}http://{{r.ip}}{{r.url}}http://{{r.ip}}:{{r.port}}{{r.url}}
HTTP Title{{r.app.http.title}}
HTTP Description{{r.app.http.description}}
Reverse{{ rev }}
ASN{{r.asn}}
Organization{{r.organization}}
Protocol{{r.protocol}}
Source{{r.source}}
+
+ + + + + + + + + + + + + + + + + +
Product{{r.productvendor}} {{r.product}} {{r.productversion}}{{r.productvendor}} {{r.product}}
HTTP Component(s){{ ponent.productvendor }} {{ ponent.product }} {{ponent.productversion}}
CPE(s){{ cp }}
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Issuer Common Name{{r.issuer.commonname}}
Issuer Organization{{r.issuer.commonname}}
Subject Organization{{r.subject.organization}}
Subject Common Name{{r.subject.commonname}}
Subject Alt Name{{ altnam }}
SHA256 Fingerprint{{r.fingerprint.sha256}}
Validity Not Before{{r.validity.notbefore}}
Validity Not After{{r.validity.notafter}}
+
+ + + + + + + + + + + + + + + + +
Data MD5{{r.datamd5}}
HTTP Header MD5{{r.app.http.headermd5}}
HTTP Body MD5{{r.app.http.bodymd5}}
+
+ + + + + + +
+
+ + +
+
+ {{artifact.data | fang}} +
+
+ {{content.errorMessage}} +
+
\ No newline at end of file diff --git a/thehive-templates/ONYPHE_Search_1_0/short.html b/thehive-templates/ONYPHE_Search_1_0/short.html new file mode 100644 index 000000000..9fd48f9fa --- /dev/null +++ b/thehive-templates/ONYPHE_Search_1_0/short.html @@ -0,0 +1,3 @@ + + {{t.namespace}}:{{t.predicate}}="{{t.value}}" + \ No newline at end of file diff --git a/thehive-templates/ONYPHE_Summary_API_1_1/long.html b/thehive-templates/ONYPHE_Summary_API_1_1/long.html new file mode 100644 index 000000000..c301559e6 --- /dev/null +++ b/thehive-templates/ONYPHE_Summary_API_1_1/long.html @@ -0,0 +1,247 @@ +
+
+ Onyphe Summary - {{(artifact.data || artifact.attachment.name) | fang}} +
+
+ + + + + + + + + + + + + + + +
IPv6SubnetThreat listSeen date
{{r.ipv6}}{{r.subnet}}{{r.threatlist}}{{r.seen_date}}
+
+
+ +
+
+ Threats +
+
+ + + + + + + + + + + + + + + +
IPv6SubnetThreat listSeen date
{{r.ipv6}}{{r.subnet}}{{r.threatlist}}{{r.seen_date}}
+
+
+ +
+
+ Geolocate +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
ASNCityCountryLatitudeLongitudeOrganizationIPv4IPv6Subnet
{{r.asn}}{{r.city}}{{r.country_name}}/{{r.country}}{{r.latitude}}{{r.longitude}}{{r.organisation}}{{r.ip}}{{r.ipv6}}{{r.subnet}}
+
+
+ +
+
+ inetnum +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
ASNCountrySubnetOrganisationLocationNetnameSeen DateSource
{{r.asn}}{{r.country}}{{r.subnet}}{{r.organization}}{{r.location}}{{r.netname}}{{r.seen_date}}{{r.source}}
+
+
+ +
+
+ Ports +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
ASNCountryIPv4OrganisationLocationOSPortSeen date
{{r.asn}}{{r.country}}{{r.ip}}{{r.organization}}{{r.location}}{{r.os}}{{r.port}}{{r.seen_date}}
+
+
+ +
+
+ Reverse +
+
+ + + + + + + + + + + + + + + + + +
DomainIPv4IPv6ReverseSeen date
{{r.domain}}{{r.ip}}{{r.ipv6}}{{r.reverse}}{{r.seen_date}}
+
+
+ +
+
+ DataScan +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ASNCountryIPv4OrganisationLocationPortProtocolOSProductProduct VersionSeen DateData MD5
{{r.asn}}{{r.country}}{{r.ip}}{{r.organization}}{{r.location}}{{r.port}}{{r.protocol}}{{r.os}}{{r.product}}{{r.productversion}}{{r.seen_date}}{{r.datamd5}}
+
+
+ + +
+
+ Forward +
+
+ + + + + + + + + + + + + + + +
DomainIPv4IPv6Seen date
{{r.domain}}{{r.ip}}{{r.ipv6}}{{r.seen_date}}
+
+
+ + +
+
+ {{artifact.data | fang}} +
+
+ {{content.errorMessage}} +
+
\ No newline at end of file diff --git a/thehive-templates/ONYPHE_Vulnscan_1_0/long.html b/thehive-templates/ONYPHE_Vulnscan_1_0/long.html new file mode 100644 index 000000000..0104e81c0 --- /dev/null +++ b/thehive-templates/ONYPHE_Vulnscan_1_0/long.html @@ -0,0 +1,203 @@ +
+
+ ONYPHE {{content.category}} search - {{(artifact.data || artifact.attachment.name) | fang}} - {{content.total_category}} results +
+
+ +
+
+ ONYPHE {{content.category}} search - {{(artifact.data || artifact.attachment.name) | fang}} - Displaying first 100 results (search returned {{content.total}}) +
+
+ + +
+
+ {{r.ip}}:{{r.port}} | ({{r.transport}}/{{r.protocol}}/tls) last seen on {{r.seen_date}} | country {{r.country}}   +  Open in ONYPHE  +
+
+ +  {{ ta }}  +  {{ ta }}  +  {{ ta }}  +  {{ ta }}  + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CVE(s){{ cve }}
Threat List{{ r.threatlist }}
IP{{r.ip}}
Alternative IP(s){{ ip }}
Network{{r.subnet}}
Domain(s){{ d }}
Device{{r.device.productvendor}} {{r.device.product}}{{r.device.class}}
Organization{{r.organization}}
OS{{r.os}}
URLhttps://{{r.forward}}{{r.url}}https://{{r.forward}}:{{r.port}}{{r.url}}http://{{r.forward}}{{r.url}}http://{{r.forward}}:{{r.port}}{{r.url}}https://{{r.ip}}{{r.url}}https://{{r.ip}}:{{r.port}}{{r.url}}http://{{r.ip}}{{r.url}}http://{{r.ip}}:{{r.port}}{{r.url}}
HTTP Title{{r.app.http.title}}
HTTP Description{{r.app.http.description}}
Reverse{{ rev }}
ASN{{r.asn}}
Organization{{r.organization}}
Protocol{{r.protocol}}
Source{{r.source}}
+
+ + + + + + + + + + + + + + + + + +
Product{{r.productvendor}} {{r.product}} {{r.productversion}}{{r.productvendor}} {{r.product}}
HTTP Component(s){{ ponent.productvendor }} {{ ponent.product }} {{ponent.productversion}}
CPE(s){{ cp }}
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Issuer Common Name{{r.issuer.commonname}}
Issuer Organization{{r.issuer.commonname}}
Subject Organization{{r.subject.organization}}
Subject Common Name{{r.subject.commonname}}
Subject Alt Name{{ altnam }}
SHA256 Fingerprint{{r.fingerprint.sha256}}
Validity Not Before{{r.validity.notbefore}}
Validity Not After{{r.validity.notafter}}
+
+ + + + + + + + + + + + + + + + +
Data MD5{{r.datamd5}}
HTTP Header MD5{{r.app.http.headermd5}}
HTTP Body MD5{{r.app.http.bodymd5}}
+
+ + + + + + +
+
+ + +
+
+ {{artifact.data | fang}} +
+
+ {{content.errorMessage}} +
+
\ No newline at end of file diff --git a/thehive-templates/ONYPHE_Vulnscan_1_0/short.html b/thehive-templates/ONYPHE_Vulnscan_1_0/short.html new file mode 100644 index 000000000..9fd48f9fa --- /dev/null +++ b/thehive-templates/ONYPHE_Vulnscan_1_0/short.html @@ -0,0 +1,3 @@ + + {{t.namespace}}:{{t.predicate}}="{{t.value}}" + \ No newline at end of file