diff --git a/README.md b/README.md index 0d21fe8d..c9e6b619 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [VMware NSX](misp_modules/modules/expansion/vmware_nsx.py) - a module to enrich a file or URL with VMware NSX Defender. * [VulnDB](misp_modules/modules/expansion/vulndb.py) - a module to query [VulnDB](https://www.riskbasedsecurity.com/). * [Vulners](misp_modules/modules/expansion/vulners.py) - an expansion module to expand information about CVEs using Vulners API. +* [Vysion](misp_modules/modules/expansion/vysion.py) - an expansion module to add dark web intelligence using Vysion API. * [whois](misp_modules/modules/expansion/whois.py) - a module to query a local instance of [uwhois](https://github.com/rafiot/uwhoisd). * [whoisfreaks](misp_modules/modules/expansion/whoisfreaks.py) - An expansion module for [whoisfreaks](https://whoisfreaks.com/) that will provide an enriched analysis of the provided domain, including WHOIS and DNS information. * [wikidata](misp_modules/modules/expansion/wiki.py) - a [wikidata](https://www.wikidata.org) expansion module. diff --git a/REQUIREMENTS b/REQUIREMENTS index 96b977b7..366eaeb6 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -170,6 +170,7 @@ urllib3==2.2.2; python_version >= '3.8' vt-graph-api==2.2.0 vt-py==0.18.2; python_full_version >= '3.7.0' vulners==2.1.7; python_version >= '3.8' +vysion== 2.0.2; python_version >= '3.8' wand==0.6.13 websocket-client==1.8.0; python_version >= '3.8' websockets==12.0; python_version >= '3.8' diff --git a/docs/index.md b/docs/index.md index 6b8c7fe5..5e21e39a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -76,6 +76,7 @@ For more information: [Extending MISP with Python modules](https://www.circl.lu/ * [VMray](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/vmray_submit.py) - a module to submit a sample to VMray. * [VulnDB](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/vulndb.py) - a module to query [VulnDB](https://www.riskbasedsecurity.com/). * [Vulners](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/vulners.py) - an expansion module to expand information about CVEs using Vulners API. +* [Vysion](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/vysion.py) - an expansion module to add dark web intelligence using Vysion API. * [whois](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/whois.py) - a module to query a local instance of [uwhois](https://github.com/rafiot/uwhoisd). * [wikidata](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/wiki.py) - a [wikidata](https://www.wikidata.org) expansion module. * [xforce](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/xforceexchange.py) - an IBM X-Force Exchange expansion module. diff --git a/documentation/README.md b/documentation/README.md index 1b77dd80..e2f93bda 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -1944,6 +1944,26 @@ An expansion hover module to expand information about CVE id using Vulners API. ----- +#### [Vysion](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/vysion.py) + + + +Module to enrich the information by making use of the Vysion API. +- **features**: +>This module gets correlated information from our dark web intelligence database. With this you will get several objects containing information related to, for example, an organization victim of a ransomware attack. +>MISP objects containing title, link to our webapp and TOR, i2p or clearnet URLs. +- **input**: +>MISP Attribute which include: company(target-org), country, info. +- **output**: +>MISP objects containing title, link to our webapp and TOR, i2p or clearnet URLs. +- **references**: +>https://vysion.ai/ +- **requirements**: +> Vysion python library +> Vysion API Key + +----- + #### [whois](https://github.com/MISP/misp-modules/tree/main/misp_modules/modules/expansion/whois.py) Module to query a local instance of uwhois (https://github.com/rafiot/uwhoisd). diff --git a/documentation/website/expansion/vysion.json b/documentation/website/expansion/vysion.json new file mode 100644 index 00000000..4ec4c636 --- /dev/null +++ b/documentation/website/expansion/vysion.json @@ -0,0 +1,16 @@ +{ + "description": "Module to enrich the information by making use of the Vysion API.", + "logo": "vysion.png", + "requirements": [ + "Vysion python library", + "Vysion API Key" + ], + "input": "MISP Attribute which include: company(target-org), country, info.", + "output": "MISP objects containing title, link to our webapp and TOR, i2p or clearnet URLs.", + "references": [ + "https://vysion.ai/", + "https://developers.vysion.ai/", + "https://github.com/ByronLabs/vysion-cti/tree/main" + ], + "features": "This module gets correlated information from Byron Labs' dark web intelligence database. With this you will get several objects containing information related to, for example, an organization victim of a ransomware attack." +} \ No newline at end of file diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 70e48132..05021577 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -21,7 +21,7 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'qintel_qsentry', 'mwdb', 'hashlookup', 'mmdb_lookup', 'ipqs_fraud_and_risk_scoring', 'clamav', 'jinja_template_rendering','hyasinsight', 'variotdbs', 'crowdsec', 'extract_url_components', 'ipinfo', 'whoisfreaks', 'ip2locationio', 'stairwell', - 'google_threat_intelligence', 'vulnerability_lookup'] + 'google_threat_intelligence', 'vulnerability_lookup', 'vysion'] minimum_required_fields = ('type', 'uuid', 'value') diff --git a/misp_modules/modules/expansion/vysion.py b/misp_modules/modules/expansion/vysion.py new file mode 100644 index 00000000..8e7c71fd --- /dev/null +++ b/misp_modules/modules/expansion/vysion.py @@ -0,0 +1,207 @@ +import json +from pymisp import MISPAttribute, MISPEvent +from urllib.parse import urlparse + +import logging + +import vysion.client as vysion + +import vysion.dto as dto +from vysion.dto.util import MISPProcessor + +misperrors = {"error": "Error"} +mispattributes = { + "input": [ + "email", + "domain", + "hostname", + "url", + "text", + "btc", + "phone-number", + "target-org", + ], + "format": "misp_standard", +} + +# possible module-types: 'expansion', 'hover' or both +moduleinfo = { + "version": "1", + "author": "Byron Labs", + "description": "Enrich observables with the Vysion API", + "module-type": ["expansion"], +} + +# config fields that your code expects from the site admin +moduleconfig = [ + "apikey", + "event_limit", + "proxy_host", + "proxy_port", + "proxy_username", + "proxy_password", +] + +LOGGER = logging.getLogger("vysion") +LOGGER.setLevel(logging.INFO) +LOGGER.info("Starting Vysion") + +DEFAULT_RESULTS_LIMIT = 10 + + +def get_proxy_settings(config: dict) -> dict: + """Returns proxy settings in the requests format. + If no proxy settings are set, return None.""" + proxies = None + host = config.get("proxy_host") + port = config.get("proxy_port") + username = config.get("proxy_username") + password = config.get("proxy_password") + + if host: + if not port: + misperrors["error"] = ( + "The vysion_proxy_host config is set, " + "please also set the vysion_proxy_port." + ) + raise KeyError + parsed = urlparse(host) + if "http" in parsed.scheme: + scheme = "http" + else: + scheme = parsed.scheme + netloc = parsed.netloc + host = f"{netloc}:{port}" + + if username: + if not password: + misperrors["error"] = ( + "The vysion_proxy_username config is set, " + "please also set the vysion_proxy_password." + ) + raise KeyError + auth = f"{username}:{password}" + host = auth + "@" + host + + proxies = {"http": f"{scheme}://{host}", "https": f"{scheme}://{host}"} + return proxies + + +def parse_error(status_code: int) -> str: + + status_mapping = { + 500: "Vysion is blind.", + 400: "Incorrect request, please check the arguments.", + 403: "You don't have enough privileges to make the request.", + } + + if status_code in status_mapping: + return status_mapping[status_code] + + return "Vysion may not be accessible." + + +def handler(q=False): + + if q is False: + return False + + request = json.loads(q) + + if not request.get("config") or not request["config"].get("apikey"): + misperrors["error"] = "A Vysion api key is required for this module." + return misperrors + + if not request.get("attribute"): + return { + "error": f"{standard_error_message}, which should contain at least a type, a value and an uuid." + } + + if request["attribute"]["type"] not in mispattributes["input"]: + return {"error": "Unsupported attribute type."} + + # event_limit = request["config"].get("event_limit") + attribute = request["attribute"] + proxy_settings = get_proxy_settings(request.get("config")) + + try: + + client = vysion.Client( + api_key=request["config"]["apikey"], + headers={ + "x-tool": "MISPModuleVysionExpansion", + }, + proxy=proxy_settings["http"] if proxy_settings else None, + ) + + LOGGER.debug(attribute) + + misp_attribute = MISPAttribute() + misp_attribute.from_dict(**attribute) + + attribute_type = misp_attribute.type + attribute_value = misp_attribute.value + + # https://www.misp-project.org/datamodels/#types + + LOGGER.debug(attribute_type) + + result = None + + if attribute_type == "email": + result = client.find_email(attribute_value) + elif attribute_type == "domain": + result = client.search(attribute_value) + elif attribute_type == "url": + result = client.find_url(attribute_value) + elif attribute_type == "text": + result = client.search(attribute_value) + elif attribute_type == "target-org": + result = client.search(attribute_value) + elif attribute_type == "btc": + result = client.search(attribute_value) + elif attribute_type == "phone-number": + result = client.search(attribute_value) + + if result is None: + return {"results": {}} + + p = MISPProcessor() + misp_event: MISPEvent = p.process(result, ref_attribute=misp_attribute) + + LOGGER.info("Vysion client initialized") + + LOGGER.info("Vysion result obtained") + + return { + "results": { + "Object": [ + json.loads(object.to_json()) for object in misp_event.objects + ], + "Attribute": [ + json.loads(attribute.to_json()) + for attribute in misp_event.attributes + ], + "Tag": [ + json.loads(tag.to_json()) + for tag in misp_event.tags + ] + } + } + + except vysion.APIError as ex: + + LOGGER.error("Error in Vysion") + LOGGER.error(ex) + + misperrors["error"] = ex.message + return misperrors + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo["config"] = moduleconfig + return moduleinfo \ No newline at end of file