diff --git a/analyzers/SEKOIAIntelligenceCenter/IntelligenceCenter_observables.json b/analyzers/SEKOIAIntelligenceCenter/IntelligenceCenter_observables.json new file mode 100644 index 000000000..ed23704e5 --- /dev/null +++ b/analyzers/SEKOIAIntelligenceCenter/IntelligenceCenter_observables.json @@ -0,0 +1,50 @@ +{ + "name": "SEKOIAIntelligenceCenter_Observables", + "version": "1.0", + "author": "SEKOIA", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "license": "AGPL-V3", + "description": "Query the Intelligence Center to retrieve known observables", + "dataTypeList": [ + "domain", + "fqdn", + "url", + "hash", + "ip" + ], + "command": "SEKOIAIntelligenceCenter/sekoia_intelligence_center_analyzer.py", + "baseConfig": "SEKOIAIntelligenceCenter", + "config": { + "service": "observables" + }, + "configurationItems": [ + { + "name": "api_key", + "description": "Intelligence center API key", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "url", + "description": "Intelligence center URL", + "type": "string", + "multi": false, + "required": false + } + ], + "registration_required": true, + "subscription_required": true, + "free_subscription": false, + "service_homepage": "https://sekoia.io/", + "service_logo": { + "path": "assets/sekoia_logo.png", + "caption": "logo" + }, + "screenshots": [ + { + "path": "assets/SEKOIAIntelligenceCenter_Context_long.png", + "caption": "SEKOIAIntelligenceCenter_Context long report sample" + } + ] +} \ No newline at end of file diff --git a/analyzers/SEKOIAIntelligenceCenter/README.md b/analyzers/SEKOIAIntelligenceCenter/README.md index 3d22a3ab2..31d07e341 100644 --- a/analyzers/SEKOIAIntelligenceCenter/README.md +++ b/analyzers/SEKOIAIntelligenceCenter/README.md @@ -1,10 +1,11 @@ Get more context around domain names, IP adresses, urls and file hashes using the [SEKOIA.IO](https://sekoia.io) Intelligence Database. - The analyzer comes in 2 flavors: + The analyzer comes in 3 flavors: - SEKOIAIntelligenceCenter_**Indicators**: Find indicators matching the observable provided. - SEKOIAIntelligenceCenter_**Context**: Get indicators and their context for the observable provided. + - SEKOIAIntelligenceCenter_**Observables**: Query the Intelligence Center to retrieve known observables. #### Requirements You need an active [SEKOIA.IO Intelligence Center](https://sekoia.io/) subscription to use the analyzer: diff --git a/analyzers/SEKOIAIntelligenceCenter/assets/SEKOIAIntelligenceCenter_Observables_long.png b/analyzers/SEKOIAIntelligenceCenter/assets/SEKOIAIntelligenceCenter_Observables_long.png new file mode 100644 index 000000000..53791c2ae Binary files /dev/null and b/analyzers/SEKOIAIntelligenceCenter/assets/SEKOIAIntelligenceCenter_Observables_long.png differ diff --git a/analyzers/SEKOIAIntelligenceCenter/sekoia_intelligence_center_analyzer.py b/analyzers/SEKOIAIntelligenceCenter/sekoia_intelligence_center_analyzer.py index 867078ee9..b3ad63c9c 100755 --- a/analyzers/SEKOIAIntelligenceCenter/sekoia_intelligence_center_analyzer.py +++ b/analyzers/SEKOIAIntelligenceCenter/sekoia_intelligence_center_analyzer.py @@ -21,6 +21,8 @@ class IntelligenceCenterAnalyzer(Analyzer): @property def url(self): + if self.service == "observables": + return "{}/api/v2/inthreat/observables/search?with_indicated_threats=1".format(self.base_url) path = "" if self.service == "context": path = "/context" @@ -36,20 +38,30 @@ def __init__(self): self.base_url = self.DEFAULT_URL def run(self): - ic_type = self.get_ic_type() - value = self.get_data() - results = self.perform_request({"type": ic_type, "value": value}) + payload = self.get_payload() + results = self.perform_request(payload) self.report({"results": results}) def summary(self, raw): count = len(raw.get("results", [])) value = "{} result{}".format(count, "s" if count > 1 else "") + + taxonomies = [] if count == 0: - level = "safe" + taxonomies.append(self.build_taxonomy("safe", "SEKOIA", self.service, value)) + elif self.service == "observables": + has_threats = any(res.get("x_ic_indicated_threats") for res in raw["results"]) + if has_threats: + taxonomies.append(self.build_taxonomy("malicious", "SEKOIA", self.service, value)) else: - level = "malicious" + taxonomies.append(self.build_taxonomy("malicious", "SEKOIA", self.service, value)) + + return {"taxonomies": taxonomies} - return {"taxonomies": [self.build_taxonomy(level, "SEKOIA", self.service, value)]} + def get_payload(self): + if self.service == "observables": + return {"term": self.get_data()} + return {"type": self.get_ic_type(), "value": self.get_data()} def get_ic_type(self): if self.data_type not in self.TYPES_MAPPING.keys(): @@ -70,11 +82,8 @@ def perform_request(self, payload): The main error codes are handled here """ - headers = {"Authorization": "Bearer {}".format(self.api_key)} try: - response = requests.get(self.url, params=payload, headers=headers) - response.raise_for_status() - return response.json()["items"] + return self._send_request(payload) except HTTPError as ex: if ex.response.status_code == 401: self.error("Unauthorized to query the API. Is the API key valid ?") @@ -86,6 +95,15 @@ def perform_request(self, payload): self.error("Quota exhausted.") self.error("API returned with the error code {}".format(str(ex.response.status_code))) + def _send_request(self, payload): + headers = {"Authorization": "Bearer {}".format(self.api_key)} + if self.service == "observables": + response = requests.post(self.url, json=payload, headers=headers) + else: + response = requests.get(self.url, params=payload, headers=headers) + response.raise_for_status() + return response.json()["items"] + if __name__ == "__main__": IntelligenceCenterAnalyzer().run() diff --git a/thehive-templates/SEKOIAIntelligenceCenter_Observables_1_0/long.html b/thehive-templates/SEKOIAIntelligenceCenter_Observables_1_0/long.html new file mode 100644 index 000000000..48c25f660 --- /dev/null +++ b/thehive-templates/SEKOIAIntelligenceCenter_Observables_1_0/long.html @@ -0,0 +1,75 @@ +