diff --git a/analyzers/AnyRun/AnyRun_Sandbox_Analysis.json b/analyzers/AnyRun/AnyRun_Sandbox_Analysis.json new file mode 100644 index 000000000..d48a1eea0 --- /dev/null +++ b/analyzers/AnyRun/AnyRun_Sandbox_Analysis.json @@ -0,0 +1,28 @@ +{ + "name": "AnyRun_Sandbox_Analysis", + "version": "1.0", + "author": "Andrea Garavaglia, Davide Arcuri, LDO-CERT", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "license": "AGPL-V3", + "description": "Any.Run Sandbox file analysis", + "dataTypeList": ["file", "url"], + "command": "AnyRun/anyrun_analyzer.py", + "baseConfig": "AnyRun", + "configurationItems": [ + { + "name": "token", + "description": "API token", + "type": "string", + "multi": false, + "required": false + }, + { + "name": "verify_ssl", + "description": "Verify SSL certificate", + "type": "boolean", + "multi": false, + "required": true, + "defaultValue": true + } + ] +} diff --git a/analyzers/AnyRun/anyrun_analyzer.py b/analyzers/AnyRun/anyrun_analyzer.py new file mode 100755 index 000000000..0e705eec7 --- /dev/null +++ b/analyzers/AnyRun/anyrun_analyzer.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# encoding: utf-8 +import time +import requests +from os.path import basename +from cortexutils.analyzer import Analyzer +from requests.packages.urllib3.exceptions import InsecureRequestWarning + + +class AnyRunAnalyzer(Analyzer): + def __init__(self): + Analyzer.__init__(self) + self.url = "https://api.any.run/v1" + self.token = self.get_param("config.token", None, "Service token is missing") + self.verify_ssl = self.get_param("config.verify_ssl", True, None) + if not self.verify_ssl: + requests.packages.urllib3.disable_warnings(InsecureRequestWarning) + + def summary(self, raw): + taxonomies = [] + level = "safe" + namespace = "AnyRun" + predicate = "Sandbox" + value = ( + raw.get("analysis", {}).get("scores", {}).get("verdict", {}).get("score", 0) + ) + if 50 < value < 100: + level = "suspicious" + elif value == 100: + level = "malicious" + + taxonomies.append( + self.build_taxonomy(level, namespace, predicate, "{0}/100".format(value)) + ) + + return {"taxonomies": taxonomies} + + def run(self): + Analyzer.run(self) + + try: + headers = {"Authorization": "API-Key {0}".format(self.token)} + + status_code = None + tries = 0 + if self.data_type == "file": + filepath = self.get_param("file", None, "File is missing") + filename = self.get_param("filename", basename(filepath)) + while status_code in (None, 429) and tries <= 15: + with open(filepath, "rb") as sample: + files = {"file": (filename, sample)} + response = requests.post( + "{0}/analysis".format(self.url), + files=files, + headers=headers, + verify=self.verify_ssl, + ) + status_code = response.status_code + if status_code == 200: + task_id = response.json()["data"]["taskid"] + elif status_code == 201: + task_id = response.json()["taskid"] + elif status_code == 429: + # it not support parallel runs, so we wait and resubmit later + time.sleep(60) + tries += 1 + else: + self.error(response.json()["message"]) + elif self.data_type == "url": + url = self.get_param("data", None, "Url is missing") + data = {"obj_type": "url", "obj_url": url} + while status_code in (None, 429) and tries <= 15: + response = requests.post( + "{0}/analysis".format(self.url), + data=data, + headers=headers, + verify=self.verify_ssl, + ) + status_code = response.status_code + if status_code == 200: + task_id = response.json()["data"]["taskid"] + elif status_code == 201: + task_id = response.json()["taskid"] + elif status_code == 429: + # it not support parallel runs, so we wait and resubmit later + time.sleep(60) + tries += 1 + else: + self.error(response.json()["message"]) + else: + self.error("Invalid data type!") + + finished = False + tries = 0 + while not finished and tries <= 15: # wait max 15 mins + time.sleep(60) + response = requests.get( + "{0}/analysis/{1}".format(self.url, task_id), + headers=headers, + verify=self.verify_ssl, + ) + if response.status_code == 200: + finished = ( + True if response.json()["data"]["status"] == "done" else False + ) + elif 400 < response.status_code < 500: + self.error(response.json()["message"]) + tries += 1 + if not finished: + self.error("AnyRun analysis timed out") + + # this items could be huge, we provide link to the report so avoid them in cortex + final_report = response.json()["data"] + final_report.pop("environments", None) + final_report.pop("modified", None) + for incident in final_report.get("incidents", []): + incident.pop("events", None) + for process in final_report.get("processes", []): + process.pop("modules", None) + self.report(final_report) + + except requests.exceptions.RequestException as e: + self.error(str(e)) + + except Exception as e: + self.unexpectedError(e) + + +if __name__ == "__main__": + AnyRunAnalyzer().run() diff --git a/analyzers/AnyRun/requirements.txt b/analyzers/AnyRun/requirements.txt new file mode 100644 index 000000000..6aabc3cfa --- /dev/null +++ b/analyzers/AnyRun/requirements.txt @@ -0,0 +1,2 @@ +cortexutils +requests diff --git a/thehive-templates/AnyRun_Sandbox_1_0/long.html b/thehive-templates/AnyRun_Sandbox_1_0/long.html new file mode 100644 index 000000000..741127bba --- /dev/null +++ b/thehive-templates/AnyRun_Sandbox_1_0/long.html @@ -0,0 +1,132 @@ +