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}} |
+
+
+ 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}} |
+
+
+ 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}}
+
+
+
+
+ IPv6 |
+ Subnet |
+ Threat list |
+ Seen date |
+
+
+
+ {{r.ipv6}} |
+ {{r.subnet}} |
+ {{r.threatlist}} |
+ {{r.seen_date}} |
+
+
+
+
+
+
+
+
+ Threats
+
+
+
+
+ IPv6 |
+ Subnet |
+ Threat list |
+ Seen date |
+
+
+
+ {{r.ipv6}} |
+ {{r.subnet}} |
+ {{r.threatlist}} |
+ {{r.seen_date}} |
+
+
+
+
+
+
+
+
+ Geolocate
+
+
+
+
+ ASN |
+ City |
+ Country |
+ Latitude |
+ Longitude |
+ Organization |
+ IPv4 |
+ IPv6 |
+ Subnet |
+
+
+
+
+ {{r.asn}} |
+ {{r.city}} |
+ {{r.country_name}}/{{r.country}} |
+ {{r.latitude}} |
+ {{r.longitude}} |
+ {{r.organisation}} |
+ {{r.ip}} |
+ {{r.ipv6}} |
+ {{r.subnet}} |
+
+
+
+
+
+
+
+
+ inetnum
+
+
+
+
+ ASN |
+ Country |
+ Subnet |
+ Organisation |
+ Location |
+ Netname |
+ Seen Date |
+ Source |
+
+
+
+ {{r.asn}} |
+ {{r.country}} |
+ {{r.subnet}} |
+ {{r.organization}} |
+ {{r.location}} |
+ {{r.netname}} |
+ {{r.seen_date}} |
+ {{r.source}} |
+
+
+
+
+
+
+
+
+ Ports
+
+
+
+
+ ASN |
+ Country |
+ IPv4 |
+ Organisation |
+ Location |
+ OS |
+ Port |
+ Seen date |
+
+
+
+ {{r.asn}} |
+ {{r.country}} |
+ {{r.ip}} |
+ {{r.organization}} |
+ {{r.location}} |
+ {{r.os}} |
+ {{r.port}} |
+ {{r.seen_date}} |
+
+
+
+
+
+
+
+
+ Reverse
+
+
+
+
+ Domain |
+ IPv4 |
+ IPv6 |
+ Reverse |
+ Seen date |
+
+
+
+ {{r.domain}} |
+ {{r.ip}} |
+ {{r.ipv6}} |
+ {{r.reverse}} |
+ {{r.seen_date}} |
+
+
+
+
+
+
+
+
+ DataScan
+
+
+
+
+ ASN |
+ Country |
+ IPv4 |
+ Organisation |
+ Location |
+ Port |
+ Protocol |
+ OS |
+ Product |
+ Product Version |
+ Seen Date |
+ Data 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
+
+
+
+
+ Domain |
+ IPv4 |
+ IPv6 |
+ Seen 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}} |
+
+
+ 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}} |
+
+
+ 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