From af0268b51019b44745a16704b8172a58db776214 Mon Sep 17 00:00:00 2001 From: Ants Date: Mon, 9 Oct 2023 15:35:58 +0300 Subject: [PATCH] Added Malware Hash Registry (MHR) pre processing --- processing/cuckoo/processing/__init__.py | 2 +- processing/cuckoo/processing/config.py | 11 +- .../data/conftemplates/mhr.yaml.jinja2 | 21 ++++ processing/cuckoo/processing/pre/mhr.py | 115 ++++++++++++++++++ .../analysis/components/mhr.html.jinja2 | 16 +++ .../web/templates/analysis/index.html.jinja2 | 9 ++ .../partial/analysis-index.html.jinja2 | 9 +- 7 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 processing/cuckoo/processing/data/conftemplates/mhr.yaml.jinja2 create mode 100644 processing/cuckoo/processing/pre/mhr.py create mode 100644 web/cuckoo/web/templates/analysis/components/mhr.html.jinja2 diff --git a/processing/cuckoo/processing/__init__.py b/processing/cuckoo/processing/__init__.py index 269fd359..7b12e6c2 100644 --- a/processing/cuckoo/processing/__init__.py +++ b/processing/cuckoo/processing/__init__.py @@ -1,4 +1,4 @@ # Copyright (C) 2019-2023 Estonian Information System Authority. # See the file 'LICENSE' for copying permission. -__version__ = "0.1.2" +__version__ = "0.1.3" diff --git a/processing/cuckoo/processing/config.py b/processing/cuckoo/processing/config.py index c631e54d..8323989d 100644 --- a/processing/cuckoo/processing/config.py +++ b/processing/cuckoo/processing/config.py @@ -1,4 +1,4 @@ -# Copyright (C) 2019-2021 Estonian Information System Authority. +# Copyright (C) 2019-2023 Estonian Information System Authority. # See the file 'LICENSE' for copying permission. from cuckoo.common import config @@ -74,6 +74,15 @@ def constraints(self, value): "submitter": config.String(), "rescan_time": config.Int(default_val=15, min_value=1), }, + "mhr.yaml": { + "enabled": config.Boolean(default_val=False), + "timeout": config.Int(default_val=60, min_value=0), + "url": config.HTTPUrl(default_val="https://hash.cymru.com/v2/"), + "user": config.String(allow_empty=True), + "password": config.String(allow_empty=True), + "min_suspicious": config.Int(default_val=10, min_value=1), + "min_malicious": config.Int(default_val=17, min_value=1), + }, "misp.yaml": { "processing": { "enabled": config.Boolean(default_val=False), diff --git a/processing/cuckoo/processing/data/conftemplates/mhr.yaml.jinja2 b/processing/cuckoo/processing/data/conftemplates/mhr.yaml.jinja2 new file mode 100644 index 00000000..82022717 --- /dev/null +++ b/processing/cuckoo/processing/data/conftemplates/mhr.yaml.jinja2 @@ -0,0 +1,21 @@ +# Enable or disable MHR usage during processing. Module performs hash and url lookups for submissions. +enabled: {{ enabled }} + +# What percentage of AV engines should mark a sample as malicious for Cuckoo to +# mark it as suspicious? Take into account that false positives happen. +min_suspicious: {{ min_suspicious }} + +# What percentage of AV engines should mark a sample as malicious for Cuckoo to +# mark it as malicious? Take into account that false positives happen. +min_malicious: {{ min_malicious }} + +# How much time we can wait to establish MHR connection and get the report. +timeout: {{ timeout }} + + +# URL to your Malware Hash Registry (MHR) REST API +url: {{ url }} + +# Registration is needed: https://hash.cymru.com/signup +user: {{ user }} +password: {{ password }} diff --git a/processing/cuckoo/processing/pre/mhr.py b/processing/cuckoo/processing/pre/mhr.py new file mode 100644 index 00000000..4da434e8 --- /dev/null +++ b/processing/cuckoo/processing/pre/mhr.py @@ -0,0 +1,115 @@ +# Copyright (C) 2019-2023 Estonian Information System Authority. +# See the file 'LICENSE' for copying permission. +import logging +import time +from urllib.parse import urljoin +import requests +from requests.auth import HTTPBasicAuth +import json + +from cuckoo.common.config import cfg + +from ..abtracts import Processor +from ..signatures.signature import Scores, IOC + + +class MHRInfoGather(Processor): + + CATEGORY = ["file"] + KEY = "mhr" + + @classmethod + def enabled(cls): + return cfg("mhr", "enabled", subpkg="processing") + + def init(self): + self.url = cfg( + "mhr", "url", subpkg="processing" + ) + self.timeout = cfg( + "mhr", "timeout", subpkg="processing" + ) + self.user = cfg( + "mhr", "user", subpkg="processing" + ) + self.password = cfg( + "mhr", "password", subpkg="processing" + ) + self.min_suspicious = cfg( + "mhr", "min_suspicious", subpkg="processing" + ) + self.min_malicious = cfg( + "mhr", "min_malicious", subpkg="processing" + ) + + def _get_results(self, sha256): + results = self._request_json(urljoin(self.url, sha256)) + + if results: + return results + else: + return None + + def _request_json(self, url, **kwargs): + """Wrapper around doing a request and parsing its JSON output.""" + try: + r = requests.get(url, auth=HTTPBasicAuth(self.user, self.password), + timeout=self.timeout, **kwargs) + return r.json() if r.status_code == 200 else {} + except (requests.ConnectionError, ValueError) as e: + self.ctx.log.error( + f"Unable to fetch MHR results: {e}" + ) + return {} + + def _handle_file_target(self): + info = None + try: + target = self.ctx.result.get("target") + info = self._get_results(target.sha256) + + if not info: + return None + except Exception as e: + self.ctx.log.warning( + f"Error while making MHR request: {e}" + ) + return info + + + def start(self): + info = None + antivirus_detection_rate = None + if self.ctx.analysis.category == "file": + info = self._handle_file_target() + + if not info: + return {} + + + score = 0 + if info["antivirus_detection_rate"]: + if info["antivirus_detection_rate"] >= self.min_malicious: + score = Scores.KNOWN_BAD + elif info["antivirus_detection_rate"] >= self.min_suspicious: + # Suspicious. Decide what scores to use Cuckoo-wide and document. + score = Scores.SUSPICIOUS + else: + return {} + + if score: + iocs = [ + IOC(antivirus="MHR", result=info["antivirus_detection_rate"]) + ] + + self.ctx.signature_tracker.add_signature( + name="mhr", + score=score, + short_description="MHR sources report this target as " + "malicious", + description=f"{info['antivirus_detection_rate']} percentage of tested MHR antivirus engines" + f" detect this target as malicious", + iocs=iocs + ) + + return info diff --git a/web/cuckoo/web/templates/analysis/components/mhr.html.jinja2 b/web/cuckoo/web/templates/analysis/components/mhr.html.jinja2 new file mode 100644 index 00000000..19dd5973 --- /dev/null +++ b/web/cuckoo/web/templates/analysis/components/mhr.html.jinja2 @@ -0,0 +1,16 @@ +{% call ui_box(title="Malware Hash Registry (MHR)", has_padding=False) %} + + + + + + + + + + + + +
Antivirus Detection RateLast Run
+ {{ pre.mhr.last_run_date|formatisodatetime }}
+{% endcall %} diff --git a/web/cuckoo/web/templates/analysis/index.html.jinja2 b/web/cuckoo/web/templates/analysis/index.html.jinja2 index 047e1055..bc81efee 100644 --- a/web/cuckoo/web/templates/analysis/index.html.jinja2 +++ b/web/cuckoo/web/templates/analysis/index.html.jinja2 @@ -137,6 +137,15 @@ {% endif %} {% endif %} + {# tab: static.mhr #} + {% if pre.mhr is defined %} + {% if pre.mhr.antivirus_detection_rate %} + + {% endif %} + {% endif %} + {# tab: pre.misp #} {% if pre.misp %}