diff --git a/analyzers/VirusTotal/VirusTotal_DownloadSample.json b/analyzers/VirusTotal/VirusTotal_DownloadSample.json index 3f3ab4321..b598f5150 100644 --- a/analyzers/VirusTotal/VirusTotal_DownloadSample.json +++ b/analyzers/VirusTotal/VirusTotal_DownloadSample.json @@ -1,6 +1,6 @@ { "name": "VirusTotal_DownloadSample", - "version": "3.0", + "version": "3.1", "author": "LDO-CERT", "url": "https://github.com/TheHive-Project/Cortex-Analyzers", "license": "AGPL-V3", diff --git a/analyzers/VirusTotal/VirusTotal_GetReport.json b/analyzers/VirusTotal/VirusTotal_GetReport.json index 91b7cc299..ad4ab2bb2 100644 --- a/analyzers/VirusTotal/VirusTotal_GetReport.json +++ b/analyzers/VirusTotal/VirusTotal_GetReport.json @@ -1,6 +1,6 @@ { "name": "VirusTotal_GetReport", - "version": "3.0", + "version": "3.1", "author": "CERT-BDF", "url": "https://github.com/TheHive-Project/Cortex-Analyzers", "license": "AGPL-V3", diff --git a/analyzers/VirusTotal/VirusTotal_Rescan.json b/analyzers/VirusTotal/VirusTotal_Rescan.json index 0e07ecbd2..82a4ced16 100644 --- a/analyzers/VirusTotal/VirusTotal_Rescan.json +++ b/analyzers/VirusTotal/VirusTotal_Rescan.json @@ -1,6 +1,6 @@ { "name": "VirusTotal_Rescan", - "version": "3.0", + "version": "3.1", "author": "CERT-LDO", "url": "https://github.com/TheHive-Project/Cortex-Analyzers", "license": "AGPL-V3", diff --git a/analyzers/VirusTotal/VirusTotal_Scan.json b/analyzers/VirusTotal/VirusTotal_Scan.json index bbfda7307..c923e2145 100644 --- a/analyzers/VirusTotal/VirusTotal_Scan.json +++ b/analyzers/VirusTotal/VirusTotal_Scan.json @@ -1,6 +1,6 @@ { "name": "VirusTotal_Scan", - "version": "3.0", + "version": "3.1", "author": "CERT-BDF", "url": "https://github.com/TheHive-Project/Cortex-Analyzers", "license": "AGPL-V3", diff --git a/analyzers/VirusTotal/requirements.txt b/analyzers/VirusTotal/requirements.txt index 3acdae127..6a1612f9c 100644 --- a/analyzers/VirusTotal/requirements.txt +++ b/analyzers/VirusTotal/requirements.txt @@ -1,6 +1,6 @@ cortexutils future requests -virustotal-api +vt-py python-magic filetype diff --git a/analyzers/VirusTotal/virustotal.py b/analyzers/VirusTotal/virustotal.py index 6f8824e52..8635e1dcf 100755 --- a/analyzers/VirusTotal/virustotal.py +++ b/analyzers/VirusTotal/virustotal.py @@ -1,6 +1,8 @@ #!/usr/bin/env python3 # encoding: utf-8 +# report -> domain + import os import time import hashlib @@ -8,10 +10,14 @@ import tempfile import mimetypes import filetype +import json +import urllib +import urllib.parse from datetime import datetime -from virus_total_apis import PublicApi, PrivateApi +from vt import Client, error from cortexutils.analyzer import Analyzer +from base64 import urlsafe_b64encode, b64decode class VirusTotalAnalyzer(Analyzer): @@ -36,103 +42,53 @@ def __init__(self): ) self.obs_path = None self.proxies = self.get_param("config.proxy", None) - if ( - self.download_sample - or self.download_sample_if_highlighted - or self.service == "download" - ): - self.vt_pay = PrivateApi(self.virustotal_key, self.proxies) - self.vt = PublicApi(self.virustotal_key, self.proxies) + self.vt = Client(apikey=self.virustotal_key, proxy=self.proxies) def get_file(self, hash): self.obs_path = "{}/{}".format(tempfile.gettempdir(), hash) - response = self.vt_pay.get_file(hash) - if response.get("response_code", None) == 200: - with open(self.obs_path, "wb") as f: - f.write(response["results"]) - kind = filetype.guess(self.obs_path) - if kind and kind.extension != None: - os.rename(self.obs_path, "{}.{}".format(self.obs_path, kind.extension)) - self.obs_path = "{}.{}".format(self.obs_path, kind.extension) - - def wait_file_report(self, id): - results = self.check_response(self.vt.get_file_report(id)) - code = results.get("response_code", None) - if code == 1: - if self.data_type == "hash" and ( - self.download_sample - or ( - self.download_sample_if_highlighted - and self.highlighted_antivirus - and any( - [ - results.get("scans", {}).get(av, {}).get("detected", None) - == False - for av in self.highlighted_antivirus - ] - ) - ) - ): - self.get_file(self.get_param("data", None, "Data is missing")) - self.report(results) - else: - time.sleep(self.polling_interval) - self.wait_file_report(id) - - def wait_url_report(self, id): - results = self.check_response(self.vt.get_url_report(id)) - code = results.get("response_code", None) - if code == 1 and (results.get("scan_id") == id): - self.report(results) - else: - time.sleep(self.polling_interval) - self.wait_url_report(id) - - def check_response(self, response): - if type(response) is not dict: - self.error("Bad response : " + str(response)) - status = response.get("response_code", -1) - if status == 204: - self.error("VirusTotal api rate limit exceeded (Status 204).") - if status != 200: - self.error("Bad status : " + str(status)) - results = response.get("results", {}) - if "Missing IP address" in results.get("verbose_msg", ""): - results["verbose_msg"] = "IP address not available in VirusTotal" - return results - - # 0 => not found - # -2 => in queue - # 1 => ready - - def read_scan_response(self, response, func): - results = self.check_response(response) - code = results.get("response_code", None) - scan_id = results.get("scan_id", None) - if code == 1 and scan_id is not None: - func(scan_id) - else: - self.error("Scan not found") + with open(self.obs_path, "wb") as obs_file: + self.vt.download_file(hash, obs_file) + + def file_to_sha256(self, file): + sha256_hash = hashlib.sha256() + for byte_block in iter(lambda: file.read(4096), b""): + sha256_hash.update(byte_block) + return sha256_hash.hexdigest() def artifacts(self, raw): artifacts = [] + if self.obs_path: - tags = [] - # This will work only in scan/rescan workflow, not in download only + tags = ["autoImport:true"] + # This will work only in scan/rescan workflow, not in download if self.highlighted_antivirus: for av in self.highlighted_antivirus: - detected = raw.get("scans", {}).get(av, {}).get("detected", None) - if detected == False: + category = ( + raw["attributes"] + .get("last_analysis_results", {}) + .get(av, {}) + .get("category", None) + ) + if category != "malicious" or category != "suspicious": tags.append("to_{}".format(av)) artifacts.append(self.build_artifact("file", self.obs_path, tags=tags)) + + for ioc_type in raw.get("iocs", []): + for ioc in raw.get("iocs").get(ioc_type): + artifacts.append(self.build_artifact(ioc_type, ioc.get("data"), tags=ioc.get("tags"))) + return artifacts def summary(self, raw): taxonomies = [] - level = "info" namespace = "VT" predicate = "GetReport" - value = "0" + stats_field = "last_analysis_stats" + results_field = "last_analysis_results" + + if self.service == "scan" or self.service == "rescan": + stats_field = "stats" + results_field = "results" if self.service == "scan": predicate = "Scan" @@ -143,68 +99,128 @@ def summary(self, raw): result = {"has_result": True} - if raw["response_code"] != 1: + if "id" not in raw: result["has_result"] = False - result["positives"] = raw.get("positives", 0) - result["total"] = raw.get("total", 0) - - if "scan_date" in raw: - result["scan_date"] = raw["scan_date"] + if stats_field in raw["attributes"]: + result["malicious"] = raw["attributes"][stats_field].get("malicious", 0) + result["suspicious"] = raw["attributes"][stats_field].get("suspicious", 0) + result["type-unsupported"] = raw["attributes"][stats_field].get( + "type-unsupported", 0 + ) + result["confirmed-timeout"] = raw["attributes"][stats_field].get( + "confirmed-timeout", 0 + ) + result["timeout"] = raw["attributes"][stats_field].get("timeout", 0) + result["failure"] = raw["attributes"][stats_field].get("failure", 0) + result["undetected"] = raw["attributes"][stats_field].get("undetected", 0) + + total = 0 + for category, value in raw["attributes"][stats_field].items(): + total += value + result["total"] = total + + if stats_field in raw["attributes"]: + value = "{}/{}".format( + result["malicious"] + result["suspicious"], result["total"] + ) + if result["malicious"] > 0: + level = "malicious" + elif result["suspicious"] > 0: + level = "suspicious" + elif ( + result.get("type-unsupported", 0) > 1 + or result.get("confirmed-timeout", 0) > 1 + or result.get("timeout", 0) > 1 + or result.get("failure", 0) > 1 + or result.get("undetected", 0) + ): + level = "info" + else: + level = "safe" + taxonomies.append( + self.build_taxonomy(level, namespace, predicate, value) + ) if self.service == "get": - if "scans" in raw: - result["scans"] = len(raw["scans"]) - value = "{}/{}".format(result["positives"], result["total"]) - if result["positives"] == 0: - level = "safe" - elif result["positives"] < 5: - level = "suspicious" - else: - level = "malicious" - - if "resolutions" in raw: - result["resolutions"] = len(raw["resolutions"]) - value = "{} resolution(s)".format(result["resolutions"]) - if result["resolutions"] == 0: - level = "safe" - elif result["resolutions"] < 5: - level = "suspicious" - else: - level = "malicious" - - if "detected_urls" in raw: - result["detected_urls"] = len(raw["detected_urls"]) - value = "{} detected_url(s)".format(result["detected_urls"]) - if result["detected_urls"] == 0: - level = "safe" - elif result["detected_urls"] < 5: - level = "suspicious" - else: - level = "malicious" - - if "detected_downloaded_samples" in raw: - result["detected_downloaded_samples"] = len( - raw["detected_downloaded_samples"] - ) - - if self.service in ["scan", "rescan"]: - if "scans" in raw: - result["scans"] = len(raw["scans"]) - value = "{}/{}".format(result["positives"], result["total"]) - if result["positives"] == 0: - level = "safe" - elif result["positives"] < 5: - level = "suspicious" - else: - level = "malicious" - - taxonomies.append(self.build_taxonomy(level, namespace, predicate, value)) + data_type = "files" + if raw["type"] == "url": + data_type = "urls" + elif raw["type"] == "domain": + data_type = "domains" + elif raw["type"] == "ip_address": + data_type = "ip_addresses" + + if data_type in ["files", "urls"]: + if "contacted_domains" in raw["relations"]: + nb_domains = raw["relations"]["contacted_domains"]["meta"]["count"] + value = "{} contacted domain(s)".format(nb_domains) + if nb_domains == 0: + level = "safe" + elif nb_domains < 5: + level = "suspicious" + else: + level = "malicious" + taxonomies.append( + self.build_taxonomy(level, namespace, predicate, value) + ) + + if data_type in ["ip_addresses", "domains"]: + try: + result["resolutions"] = self.vt.get_object( + "/{}/{}/{}".format(data_type, raw["id"], "resolutions") + ).to_dict() + value = "{} resolution(s)".format(result["meta"]["count"]) + if result["meta"]["count"] == 0: + level = "safe" + elif result["meta"]["count"] < 5: + level = "suspicious" + else: + level = "malicious" + taxonomies.append( + self.build_taxonomy(level, namespace, predicate, value) + ) + except Exception: + pass # Premium api key required + + if data_type in ["files"]: + try: + result["embedded_urls"] = self.vt.get_object( + "/{}/{}/{}".format(data_type, raw["id"], "embedded_urls") + ).to_dict() + value = "{} embedded url(s)".format(result["meta"]["count"]) + if result["meta"]["count"] == 0: + level = "safe" + elif result["meta"]["count"] < 5: + level = "suspicious" + else: + level = "malicious" + taxonomies.append( + self.build_taxonomy(level, namespace, predicate, value) + ) + except Exception: + pass # Premium api key required + + if data_type in ["files", "ip_addresses", "domains"]: + if "downloaded_files" in raw["relations"]: + nb_files = raw["relations"]["downloaded_files"]["meta"]["count"] + value = "{} downloaded file(s)".format(nb_files) + if nb_files == 0: + level = "safe" + elif nb_files < 5: + level = "suspicious" + else: + level = "malicious" + taxonomies.append( + self.build_taxonomy(level, namespace, predicate, value) + ) if self.highlighted_antivirus: - for av in self.highlighted_antivirus: - detected = raw.get("scans", {}).get(av, {}).get("detected", None) - if detected == False: + for av in (av for av in self.highlighted_antivirus if av): + category = ( + raw["attributes"][results_field].get(av, {}).get("category", None) + ) + if category != "malicious" or category != "suspicious": taxonomies.append( self.build_taxonomy("info", namespace, av, "Not detected!") ) @@ -212,26 +228,43 @@ def summary(self, raw): return {"taxonomies": taxonomies} def run(self): + results = dict() + iocs = dict() + iocs['ip'] = list() + iocs['domain'] = list() + iocs['url'] = list() + iocs['other'] = list() + if self.service == "scan": if self.data_type == "file": - filename = self.get_param("filename", "noname.ext") filepath = self.get_param("file", None, "File is missing") - self.read_scan_response( - self.vt.scan_file(filepath, from_disk=True, filename=filename), - self.wait_file_report, - ) + with open(filepath, "rb") as f: + resp = self.vt.scan_file(file=f, wait_for_completion=True) + results = resp.to_dict() + file_hash = b64decode(results.get("id")).decode().split(':')[0] + self.get_relation("contacted_domains", "files", file_hash, results, iocs) + self.get_relation("contacted_ips", "files", file_hash, results, iocs) + self.get_relation("contacted_urls", "files", file_hash, results, iocs) + elif self.data_type == "url": - data = self.get_param("data", None, "Data is missing") - self.read_scan_response(self.vt.scan_url(data), self.wait_url_report) + url = self.get_param("data", None, "Data is missing") + resp = self.vt.scan_url(url=url, wait_for_completion=True) + results = resp.to_dict() + url_b64 = results.get("id").split("-")[1] + self.get_relation("contacted_domains", "files", url_b64, results, iocs) + self.get_relation("contacted_ips", "files", url_b64, results, iocs) + self.get_relation("last_serving_ip_address", "files", url_b64, results, iocs) else: self.error("Invalid data type") elif self.service == "rescan": if self.data_type == "hash": data = self.get_param("data", None, "Data is missing") - self.read_scan_response( - self.vt.rescan_file(data), self.wait_file_report - ) + resp = self.vt.post("/files/{}/analyse".format(data)).text() + results = json.loads(resp) + self.get_relation("contacted_domains", "files", data, results, iocs) + self.get_relation("contacted_ips", "files", data, results, iocs) + self.get_relation("contacted_urls", "files", data, results, iocs) else: self.error("Invalid data type") @@ -242,52 +275,68 @@ def run(self): self.report({"message": "file downloaded"}) elif self.service == "get": - if self.data_type == "domain": - data = self.get_param("data", None, "Data is missing") - results = self.check_response(self.vt.get_domain_report(data)) - - elif self.data_type == "fqdn": - data = self.get_param("data", None, "Data is missing") - results = self.check_response(self.vt.get_domain_report(data)) - - elif self.data_type == "ip": - data = self.get_param("data", None, "Data is missing") - results = self.check_response(self.vt.get_ip_report(data)) - - elif self.data_type == "file": - hashes = self.get_param("attachment.hashes", None) - if hashes is None: + try: + if self.data_type == "domain" or self.data_type == "fqdn": + data = self.get_param("data", None, "Data is missing") + results = self.vt.get_object("/domains/{}".format(data)).to_dict() + self.get_relation("urls", "domains", data, results, iocs) + self.get_relation("downloaded_files", "domains", data, results, iocs) + self.get_relation("referrer_files", "domains", data, results, iocs) + + elif self.data_type == "ip": + data = self.get_param("data", None, "Data is missing") + results = self.vt.get_object("/ip_addresses/{}".format(data)).to_dict() + self.get_relation("urls", "ip_addresses", data, results, iocs) + + elif self.data_type == "file": filepath = self.get_param("file", None, "File is missing") - hash = hashlib.sha256(open(filepath, "rb").read()).hexdigest() + with open(filepath, "rb") as f: + file_hash = self.file_to_sha256(f) + results = self.vt.get_object("/files/{}".format(file_hash)).to_dict() + self.get_relation("contacted_domains", "files", file_hash, results, iocs) + self.get_relation("contacted_ips", "files", file_hash, results, iocs) + self.get_relation("contacted_urls", "files", file_hash, results, iocs) + + elif self.data_type == "hash": + data = self.get_param("data", None, "Data is missing") + results = self.vt.get_object("/files/{}".format(data)).to_dict() + self.get_relation("contacted_domains", "files", data, results, iocs) + self.get_relation("contacted_ips", "files", data, results, iocs) + self.get_relation("contacted_urls", "files", data, results, iocs) + + elif self.data_type == "url": + url = self.get_param("data", None, "Data is missing") + url_b64 = urlsafe_b64encode(url.encode()).decode().split("=")[0] + results = self.vt.get_object("/urls/{}".format(url_b64)).to_dict() + self.get_relation("contacted_domains", "urls", url_b64, results, iocs) + self.get_relation("contacted_ips", "urls", url_b64, results, iocs) + self.get_relation("last_serving_ip_address", "urls", url_b64, results, iocs) else: - hash = next(h for h in hashes if len(h) == 64) - results = self.check_response(self.vt.get_file_report(hash)) - - elif self.data_type == "hash": - data = self.get_param("data", None, "Data is missing") - results = self.check_response(self.vt.get_file_report(data)) - - elif self.data_type == "url": - data = self.get_param("data", None, "Data is missing") - results = self.check_response(self.vt.get_url_report(data)) - else: - self.error("Invalid data type") - - # if aged and enabled rescan - if self.data_type == "hash" and self.rescan_hash_older_than_days: - if ( - datetime.strptime(results["scan_date"], "%Y-%m-%d %H:%M:%S") - - datetime.now() - ).days > self.rescan_hash_older_than_days: - self.read_scan_response( - self.vt.rescan_file(data), self.wait_file_report - ) + self.error("Invalid data type") + self.get_yararuleset(results, iocs) + self.get_ids_results(results, iocs) + + # if aged and enabled rescan + if self.data_type == "hash" and self.rescan_hash_older_than_days: + if ( + datetime.fromtimestamp(results["attributes"]["last_analysis_date"]) + - datetime.now() + ).days > self.rescan_hash_older_than_days: + filepath = self.get_param("file", None, "File is missing") + with open(filepath, "rb") as f: + self.vt.scan_file(file=f, wait_for_completion=True) + except Exception: + self.report({"message": "Report not found."}) + return # download if hash, dangerous and not seen by av if ( self.data_type == "hash" and (results.get("response_code", None) == 1) - and (results.get("positives", 0) >= 5) + and ( + results["attributes"]["last_analysis_stats"].get("malicious", 0) + >= 5 + ) and ( self.download_sample or ( @@ -306,11 +355,49 @@ def run(self): ) ): self.get_file(data) - self.report(results) - else: self.error("Invalid service") - + results['iocs'] = iocs + self.report(results) + + def get_yararuleset(self, results, iocs): + for yara_result in results["attributes"].get( "crowdsourced_yara_results", []): + yara_ruleset = self.vt.get_object( + "/yara_rulesets/{}".format(yara_result["ruleset_id"]) + ).to_dict() + iocs["other"].append({ + "data": yara_ruleset["attributes"]["rules"], + "tags": [ + "detection:YARA", + "ruleset:{}".format(yara_ruleset["attributes"]["name"]) + ] + }) + + def get_ids_results(self, results, iocs): + for ids_result in results["attributes"].get("crowdsourced_ids_results", []): + iocs["other"].append({ + "data": ids_result["rule_raw"], + "tags": [ + "detection:IDS", + "rule-src:{}".format(ids_result["rule_source"]) + ] + }) + + def get_relation(self, relation, data_type, data, results, iocs): + try: + result = self.vt.get_json( + "/{}/{}/{}".format(data_type, data, relation) + ) + if not "relations" in results: + results["relations"] = {} + results['relations'][relation] = result + for url in result['data']: + iocs["url"].append({ + "data": url['attributes']['url'], + "tags": ["known-relationship:{}".format(data_type.replace("_", "-"))] + }) + except Exception: + pass #Premium api required if __name__ == "__main__": VirusTotalAnalyzer().run() diff --git a/thehive-templates/EmlParser_2_0/short.html b/thehive-templates/EmlParser_2_0/short.html deleted file mode 100644 index 57f9d29cf..000000000 --- a/thehive-templates/EmlParser_2_0/short.html +++ /dev/null @@ -1,3 +0,0 @@ - - {{t.namespace}}:{{t.predicate}}={{t.value}} - diff --git a/thehive-templates/VirusTotal_DownloadSample_3_1/long.html b/thehive-templates/VirusTotal_DownloadSample_3_1/long.html new file mode 100644 index 000000000..8e36bfb8a --- /dev/null +++ b/thehive-templates/VirusTotal_DownloadSample_3_1/long.html @@ -0,0 +1,21 @@ + +
+
+ VirusTotal GetReport: download failed +
+
+ The file could not be downloaded. Check if your VirusTotal API key is linked to an Entreprise account. +
+
+ + +
+
+
+ VirusTotal GetReport: Success +
+
+ The file has been downloaded successfully and automatically added in the report and the Case. +
+
+
\ No newline at end of file diff --git a/thehive-templates/VirusTotal_GetReport_3_0/long.html b/thehive-templates/VirusTotal_GetReport_3_0/long.html deleted file mode 100644 index ef25f7b79..000000000 --- a/thehive-templates/VirusTotal_GetReport_3_0/long.html +++ /dev/null @@ -1,195 +0,0 @@ -
-
- {{(artifact.data || artifact.attachment.name) | fang}} -
-
- {{content.errorMessage}} -
-
- -
-
-
-
- Summary -
-
-
-
Score
-
{{content.positives || 0}}/{{content.total}}
-
-
-
Last analysis date
-
{{content.scan_date}}
-
-
-
Autonomous System
-
{{content.as_owner}}
-
-
-
Categories
-
{{content.categories.join(', ')}}
-
-
-
Sub domains
-
{{content.subdomains.join(', ')}}
-
-
-
Resolutions
-
-
- This domain has been seen to resolve to the following IP addresses. -
-
- The following domains resolved to the given IP address. -
-
- {{::resolution.last_resolved | amDateFormat:'DD-MM-YYYY'}}: - {{(resolution.ip_address | fang) || (resolution.hostname | fang)}} -
-
-
-
-
Virus Total
-
- - - View Full Report - -
-
-
-
- -
-
- Latest detected URLs - - Show All ({{::content.detected_urls.length}}) - Show less - -
-
-

Latest URLs hosted in this IP address - detected by at least one URL scanner or malicious URL dataset. -

- - - - - - - - - - - -
ScoreScan DateURL
- {{::url.positives}}/{{::url.total}} - {{url.scan_date}}{{url.url | fang}}
-
-
- -
-
- Latest detected files that were downloaded from this IP address - - Show All ({{::content.detected_downloaded_samples.length}}) - Show less - -
-
-

Latest files that are - detected by at least one antivirus solution and were downloaded by VirusTotal from the IP address provided. -

- - - - - - - - - - - -
ScoreDateSHA256
- {{hash.positives}}/{{hash.total}} - {{hash.date}}{{hash.sha256}}
-
-
- -
-
- Latest detected files that embed this IP address in their strings - - Show All ({{::content.detected_referrer_samples.length}}) - Show less - -
-
-

Latest files that are - detected by at least one antivirus solution and embed URL pattern strings with the IP address provided. -

- - - - - - - - - -
ScoreSHA256
- {{hash.positives}}/{{hash.total}} - {{hash.sha256}}
-
-
- -
-
- Scans -
-
- - - - - - - - - - - - - - - - - - -
ScannerDetectedResultDetailsUpdateVersion
- {{scanner}} - - - {{result.result}} - - - View details - {{result.update}}{{result.version}}
-
-
- -
-
-
-
- {{(artifact.data || artifact.attachment.name) | fang}} -
-
- {{content.verbose_msg}} -
-
-
-
diff --git a/thehive-templates/VirusTotal_GetReport_3_0/short.html b/thehive-templates/VirusTotal_GetReport_3_0/short.html deleted file mode 100644 index 5fc0dabfb..000000000 --- a/thehive-templates/VirusTotal_GetReport_3_0/short.html +++ /dev/null @@ -1,3 +0,0 @@ - - {{t.namespace}}:{{t.predicate}}="{{t.value}}" - diff --git a/thehive-templates/VirusTotal_GetReport_3_1/long.html b/thehive-templates/VirusTotal_GetReport_3_1/long.html new file mode 100644 index 000000000..051b56285 --- /dev/null +++ b/thehive-templates/VirusTotal_GetReport_3_1/long.html @@ -0,0 +1,551 @@ + +
+
+ VirusTotal GetReport +
+
{{content.errorMessage}}
+
+ + +
+
+
+ Summary +
+
+
+
+
+
Message
+
+ {{content.message}} +
+
+
+
Malicious
+
+ {{content.attributes.last_analysis_stats.malicious}}/{{total}} +
+
+
+
Suspicious
+
+ {{content.attributes.last_analysis_stats.suspicious}}/{{total}} +
+
+
+
Undefined
+
+ {{others}}/{{total}} +
+
+
+
+
+
Size
+
{{content.attributes.size}}B
+
+
+
Last analysis date
+
+ {{content.attributes.last_analysis_date*1000 | date: 'yyyy-MM-dd HH:mm:ss'}} +
+
+
+
+
+
+
Names
+
{{name}}
+
+
+
Url
+
{{content.attributes.url}}
+
+
+
SHA-256
+
{{content.id}}
+
+
+
+
Resolution
+
{{content.id}}
+
+
+
+ +
+
+ +
+
+ Crowdsourced YARA results +
+
+
+
+
+ +
+
+ Matches rule {{result.rule_name}} by + {{result.author}} from {{result.ruleset_name}} at + {{result.source}} +
+ +
+
+ +
+
+ {{result.description}} +
+
+
+
+
+
+
+
+ +
+
+ Crowdsourced IDS stats +
+
+
+
LOW: {{content.attributes.crowdsourced_ids_stats.low}}
+
INFO: {{content.attributes.crowdsourced_ids_stats.info}}
+
+ MEDIUM: {{content.attributes.crowdsourced_ids_stats.medium}} +
+
HIGH: {{content.attributes.crowdsourced_ids_stats.high}}
+
+
+
+ +
+
+ Crowdsourced IDS results +
+
+
+
+
+ + +
+ +
+ Matches rule {{result.rule_msg}} from {{result.rule_source}} + at + {{result.rule_url}} +
+ +
+
+ +
+
+ Category: {{result.rule_category}} +
+ Destinations: + + {{data.dest_ip}}:{{data.dest_port}} + +
+
+
+
+
+
+
+
+
+ +
+
+ Sandbox Verdicts +
+
+
+
+
+
{{verdict.sandbox_name}}
+
+
+
+
Category
+
{{verdict.category}}
+
+
+
+
+
Classification(s)
+
+ {{classification}} +
+
+
+
+
+
+
+
+ +
+
+
+ Contacted URLs +
+
+ (Last 10) +
+
+
+ + + + + + + + + + + + + + + + + +
ScannedDetectionsStatusURL
+ {{url.attributes.last_analysis_date*1000 | date: 'yyyy-MM-dd'}} + + + {{url.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{url.attributes.last_http_response_code || "-"}}{{url.attributes.url || "-"}}
+
+
+ +
+
+
+ Contacted Domains +
+
+ (Last 10) +
+
+
+ + + + + + + + + + + + + + + + + + +
DomainDetectionsCreatedRegistrar
+ {{domain.id}} + + + {{domain.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{domain.attributes.creation_date*1000 | date: + 'yyyy-MM-dd'}} + -{{domain.attributes.registrar || "-"}}
+
+
+ +
+
+ Last Serving IP Address +
+
+ + + + + + + + + + + + + + + + + +
IPDetectionsAutonomous SystemCountry
+ {{ip.id}} + + + {{ip.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{ip.attributes.asn || "-"}}{{ip.attributes.country || "-"}}
+
+
+ +
+
+
+ Contacted IP Addresses +
+
+ (Last 10) +
+
+
+ + + + + + + + + + + + + + + + + +
IPDetectionsAutonomous SystemCountry
+ {{ip.id}} + + + {{ip.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{ip.attributes.asn || "-"}}{{ip.attributes.country || "-"}}
+
+
+ +
+
+
+ Detected URLs +
+
+ (Last 10) +
+
+
+ + + + + + + + + + + + + + + + + +
ScannedDetectionsStatusURL
+ {{url.attributes.last_analysis_date*1000 | date: 'yyyy-MM-dd'}} + + + {{url.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{url.attributes.last_http_response_code || "-"}}{{url.attributes.url || "-"}}
+
+
+ +
+
+
+ Detected files downloaded from this domain +
+
+ (Last 10) +
+
+
+ + + + + + + + + + + + + + + +
ScannedDetectionsSHA256
+ {{file.attributes.last_analysis_date*1000 | date: 'yyyy-MM-dd'}} + + + {{file.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{file.attributes.sha256 || "-"}}
+
+
+ +
+
+
+ Detected files which refer to this domain +
+
+ (Last 10) +
+
+
+ + + + + + + + + + + + + + + +
ScannedDetectionsSHA256
+ {{file.attributes.last_analysis_date*1000 | date: 'yyyy-MM-dd'}} + + + {{file.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{file.attributes.sha256 || "-"}}
+
+
+ +
+
+ Scans +
+
+ + + + + + + + + + + + + + + + + + + + + +
ScannerDetectedResultMethodUpdateVersion
+ {{scanner.engine_name}} + + + + + {{scanner.result || "null"}} + {{scanner.method || "null"}} + {{scanner.engine_update.slice(0,4)+"/"+scanner.engine_update.slice(4,6)+"/"+scanner.engine_update.slice(6,8)}} + {{scanner.engine_version || ""}}
+
+
+
diff --git a/thehive-templates/VirusTotal_Rescan_3_1/long.html b/thehive-templates/VirusTotal_Rescan_3_1/long.html new file mode 100644 index 000000000..44b9a4cfd --- /dev/null +++ b/thehive-templates/VirusTotal_Rescan_3_1/long.html @@ -0,0 +1,204 @@ + +
+
+ VirusTotal GetReport +
+
{{content.errorMessage}}
+
+ + +
+
+
+ Summary +
+
+
+
Id (base64)
+
{{content.data.id}}
+
+
+
+ +
+
+ Contacted URLs +
+
+ + + + + + + + + + + + + + + + + +
ScannedDetectionsStatusURL
+ {{url.attributes.last_analysis_date*1000 | date: 'yyyy-MM-dd'}} + + + {{url.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{url.attributes.last_http_response_code || "-"}}{{url.attributes.url || "-"}}
+
+
+ +
+
+ Contacted Domains +
+
+ + + + + + + + + + + + + + + + + + +
DomainDetectionsCreatedRegistrar
+ {{domain.id}} + + + {{domain.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{domain.attributes.creation_date*1000 | date: 'yyyy-MM-dd'}}-{{domain.attributes.registrar || "-"}}
+
+
+ +
+
+ Contacted IP Addresses +
+
+ + + + + + + + + + + + + + + + + +
IPDetectionsAutonomous SystemCountry
+ {{ip.id}} + + + {{ip.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{ip.attributes.asn || "-"}}{{ip.attributes.country || "-"}}
+
+
+ +
+
+ Last Serving IP Address +
+
+ + + + + + + + + + + + + + + + + +
IPDetectionsAutonomous SystemCountry
+ {{ip.id}} + + + {{ip.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{ip.attributes.asn || "-"}}{{ip.attributes.country || "-"}}
+
+
+ +
+
+ Scans +
+
+ + + + + + + + + + + + + + + + + + + + + +
ScannerDetectedResultMethodUpdateVersion
+ {{scanner.engine_name}} + + + + + {{scanner.result || "null"}} + {{scanner.method || "null"}} + {{scanner.engine_update.slice(0,4)+"/"+scanner.engine_update.slice(4,6)+"/"+scanner.engine_update.slice(6,8)}} + {{scanner.engine_version || ""}}
+
+
+
\ No newline at end of file diff --git a/thehive-templates/VirusTotal_Scan_3_0/long.html b/thehive-templates/VirusTotal_Scan_3_0/long.html deleted file mode 100644 index d3275fc6a..000000000 --- a/thehive-templates/VirusTotal_Scan_3_0/long.html +++ /dev/null @@ -1,78 +0,0 @@ -
-
- {{(artifact.data || artifact.attachment.name) | fang}} -
-
- {{content.errorMessage}} -
-
- - -
-
-
-
Summary
-
-
-
Score
-
{{content.positives}}/{{content.total}}
-
-
-
Last analysis date
-
{{content.scan_date}}
-
-
-
Virus Total
-
- - - - View Full Report - -
-
-
-
- -
-
Scans
-
- - - - - - - - - - - - - - - - - - -
ScannerDetectedResultDetailsUpdateVersion
{{scanner}} - - {{result.result}} - View details - {{result.update}}{{result.version}}
-
-
- - -
-
-
-
- {{(artifact.data || artifact.attachment.name)| fang}} -
-
- {{content.verbose_msg}} -
-
-
-
diff --git a/thehive-templates/VirusTotal_Scan_3_0/short.html b/thehive-templates/VirusTotal_Scan_3_0/short.html deleted file mode 100644 index 5fc0dabfb..000000000 --- a/thehive-templates/VirusTotal_Scan_3_0/short.html +++ /dev/null @@ -1,3 +0,0 @@ - - {{t.namespace}}:{{t.predicate}}="{{t.value}}" - diff --git a/thehive-templates/VirusTotal_Scan_3_1/long.html b/thehive-templates/VirusTotal_Scan_3_1/long.html new file mode 100644 index 000000000..10fd7e1d0 --- /dev/null +++ b/thehive-templates/VirusTotal_Scan_3_1/long.html @@ -0,0 +1,264 @@ + +
+
+ VirusTotal GetReport +
+
{{content.errorMessage}}
+
+ + +
+
+
+ Summary +
+
+
+
Malicious
+
+ {{content.attributes.stats.malicious}}/{{total}} +
+
+
+
Suspicious
+
+ {{content.attributes.stats.suspicious}}/{{total}} +
+
+
+
Undefined
+
+ {{others}}/{{total}} +
+
+
+
Last analysis date
+
{{content.attributes.date*1000 | date: 'yyyy-MM-dd HH:mm:ss'}}
+
+
+
VirusTotal Report
+
+ https://www.virustotal.com/gui/file-analysis/{{content.id}} + https://www.virustotal.com/gui/url/{{content.id.split("-")[1]}} +
+
+
+
+ +
+
+
+ Contacted URLs +
+
+ (Last 10) +
+
+
+ + + + + + + + + + + + + + + + + +
ScannedDetectionsStatusURL
+ {{url.attributes.last_analysis_date*1000 | date: 'yyyy-MM-dd'}} + + + {{url.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{url.attributes.last_http_response_code || "-"}}{{url.attributes.url || "-"}}
+
+
+ +
+
+
+ Contacted Domains +
+
+ (Last 10) +
+
+
+ + + + + + + + + + + + + + + + + + +
DomainDetectionsCreatedRegistrar
+ {{domain.id}} + + + {{domain.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{domain.attributes.creation_date*1000 | date: 'yyyy-MM-dd'}}-{{domain.attributes.registrar || "-"}}
+
+
+ +
+
+ Last Serving IP Address +
+
+ + + + + + + + + + + + + + + + + +
IPDetectionsAutonomous SystemCountry
+ {{ip.id}} + + + {{ip.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{ip.attributes.asn || "-"}}{{ip.attributes.country || "-"}}
+
+
+ +
+
+
+ Contacted IP Addresses +
+
+ (Last 10) +
+
+
+ + + + + + + + + + + + + + + + + +
IPDetectionsAutonomous SystemCountry
+ {{ip.id}} + + + {{ip.attributes.last_analysis_stats.malicious || "0"}} + / {{total}} + {{ip.attributes.asn || "-"}}{{ip.attributes.country || "-"}}
+
+
+ +
+
+ Scans +
+
+ + + + + + + + + + + + + + + + + + + + + +
ScannerDetectedResultMethodUpdateVersion
+ {{scanner.engine_name}} + + + + + {{scanner.result || "null"}} + {{scanner.method || "null"}} + {{scanner.engine_update.slice(0,4)+"/"+scanner.engine_update.slice(4,6)+"/"+scanner.engine_update.slice(6,8)}} + {{scanner.engine_version || ""}}
+
+
+
\ No newline at end of file