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 URLs hosted in this IP address
- detected by at least one URL scanner or malicious URL dataset.
-
-
-
- Score |
- Scan Date |
- URL |
-
-
-
- {{::url.positives}}/{{::url.total}}
- |
- {{url.scan_date}} |
- {{url.url | fang}} |
-
-
-
-
-
-
-
-
-
Latest files that are
- detected by at least one antivirus solution and were downloaded by VirusTotal from the IP address provided.
-
-
-
- Score |
- Date |
- SHA256 |
-
-
-
- {{hash.positives}}/{{hash.total}}
- |
- {{hash.date}} |
- {{hash.sha256}} |
-
-
-
-
-
-
-
-
-
Latest files that are
- detected by at least one antivirus solution and embed URL pattern strings with the IP address provided.
-
-
-
- Score |
- SHA256 |
-
-
-
- {{hash.positives}}/{{hash.total}}
- |
- {{hash.sha256}} |
-
-
-
-
-
-
-
- Scans
-
-
-
-
-
- Scanner |
- Detected |
- Result |
- Details |
- Update |
- Version |
-
-
-
- {{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)
+
+
+
+
+
+
+ Scanned |
+ Detections |
+ Status |
+ URL |
+
+
+
+
+
+ {{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)
+
+
+
+
+
+
+ Domain |
+ Detections |
+ Created |
+ Registrar |
+
+
+
+
+
+ {{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
+
+
+
+
+
+ IP |
+ Detections |
+ Autonomous System |
+ Country |
+
+
+
+
+
+ {{ip.id}}
+ |
+
+
+ {{ip.attributes.last_analysis_stats.malicious || "0"}}
+ / {{total}}
+ |
+ {{ip.attributes.asn || "-"}} |
+ {{ip.attributes.country || "-"}} |
+
+
+
+
+
+
+
+
+
+ Contacted IP Addresses
+
+
+ (Last 10)
+
+
+
+
+
+
+ IP |
+ Detections |
+ Autonomous System |
+ Country |
+
+
+
+
+
+ {{ip.id}}
+ |
+
+
+ {{ip.attributes.last_analysis_stats.malicious || "0"}}
+ / {{total}}
+ |
+ {{ip.attributes.asn || "-"}} |
+ {{ip.attributes.country || "-"}} |
+
+
+
+
+
+
+
+
+
+ Detected URLs
+
+
+ (Last 10)
+
+
+
+
+
+
+ Scanned |
+ Detections |
+ Status |
+ URL |
+
+
+
+
+
+ {{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)
+
+
+
+
+
+
+ Scanned |
+ Detections |
+ SHA256 |
+
+
+
+
+
+ {{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)
+
+
+
+
+
+
+ Scanned |
+ Detections |
+ SHA256 |
+
+
+
+
+
+ {{file.attributes.last_analysis_date*1000 | date: 'yyyy-MM-dd'}}
+ |
+
+
+ {{file.attributes.last_analysis_stats.malicious || "0"}}
+ / {{total}}
+ |
+ {{file.attributes.sha256 || "-"}} |
+
+
+
+
+
+
+
+
+ Scans
+
+
+
+
+
+ Scanner |
+ Detected |
+ Result |
+ Method |
+ Update |
+ Version |
+
+
+
+
+
+ {{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
+
+
+
+
+
+ Scanned |
+ Detections |
+ Status |
+ URL |
+
+
+
+
+
+ {{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
+
+
+
+
+
+ Domain |
+ Detections |
+ Created |
+ Registrar |
+
+
+
+
+
+ {{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
+
+
+
+
+
+ IP |
+ Detections |
+ Autonomous System |
+ Country |
+
+
+
+
+
+ {{ip.id}}
+ |
+
+
+ {{ip.attributes.last_analysis_stats.malicious || "0"}}
+ / {{total}}
+ |
+ {{ip.attributes.asn || "-"}} |
+ {{ip.attributes.country || "-"}} |
+
+
+
+
+
+
+
+
+ Last Serving IP Address
+
+
+
+
+
+ IP |
+ Detections |
+ Autonomous System |
+ Country |
+
+
+
+
+
+ {{ip.id}}
+ |
+
+
+ {{ip.attributes.last_analysis_stats.malicious || "0"}}
+ / {{total}}
+ |
+ {{ip.attributes.asn || "-"}} |
+ {{ip.attributes.country || "-"}} |
+
+
+
+
+
+
+
+
+ Scans
+
+
+
+
+
+ Scanner |
+ Detected |
+ Result |
+ Method |
+ Update |
+ Version |
+
+
+
+
+
+ {{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
-
-
-
-
- Scanner |
- Detected |
- Result |
- Details |
- Update |
- Version |
-
-
- {{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}}
+
+
+
+
+
+
+
+
+
+ Contacted URLs
+
+
+ (Last 10)
+
+
+
+
+
+
+ Scanned |
+ Detections |
+ Status |
+ URL |
+
+
+
+
+
+ {{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)
+
+
+
+
+
+
+ Domain |
+ Detections |
+ Created |
+ Registrar |
+
+
+
+
+
+ {{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
+
+
+
+
+
+ IP |
+ Detections |
+ Autonomous System |
+ Country |
+
+
+
+
+
+ {{ip.id}}
+ |
+
+
+ {{ip.attributes.last_analysis_stats.malicious || "0"}}
+ / {{total}}
+ |
+ {{ip.attributes.asn || "-"}} |
+ {{ip.attributes.country || "-"}} |
+
+
+
+
+
+
+
+
+
+ Contacted IP Addresses
+
+
+ (Last 10)
+
+
+
+
+
+
+ IP |
+ Detections |
+ Autonomous System |
+ Country |
+
+
+
+
+
+ {{ip.id}}
+ |
+
+
+ {{ip.attributes.last_analysis_stats.malicious || "0"}}
+ / {{total}}
+ |
+ {{ip.attributes.asn || "-"}} |
+ {{ip.attributes.country || "-"}} |
+
+
+
+
+
+
+
+
+ Scans
+
+
+
+
+
+ Scanner |
+ Detected |
+ Result |
+ Method |
+ Update |
+ Version |
+
+
+
+
+
+ {{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