diff --git a/README.md b/README.md index d8e0adb..8bfa50d 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,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/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 0c5f77a..e388338 100644 --- a/REQUIREMENTS +++ b/REQUIREMENTS @@ -177,6 +177,7 @@ validators==0.14.0 vt-graph-api==2.2.0 vt-py==0.17.5 vulners==2.0.10 +vysion==1.0.10 wand==0.6.11 websocket-client==1.5.1 ; python_version >= '3.7' websockets==11.0.3 ; python_version >= '3.7' diff --git a/docs/index.md b/docs/index.md index e2c5a13..8dedd8e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -75,6 +75,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](misp-modules/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/docs/logos/vysion.png b/docs/logos/vysion.png new file mode 100644 index 0000000..e5fb194 Binary files /dev/null and b/docs/logos/vysion.png differ diff --git a/documentation/README.md b/documentation/README.md index 0016f25..ecc1897 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -1904,6 +1904,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/logos/vysion.png b/documentation/logos/vysion.png new file mode 100644 index 0000000..e5fb194 Binary files /dev/null and b/documentation/logos/vysion.png differ diff --git a/documentation/website/expansion/vysion.json b/documentation/website/expansion/vysion.json new file mode 100644 index 0000000..9f51ddf --- /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 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." +} \ No newline at end of file diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 77ff4f8..9b5150c 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -20,7 +20,7 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'trustar_enrich', 'recordedfuture', 'html_to_markdown', 'socialscan', 'passive-ssh', 'qintel_qsentry', 'mwdb', 'hashlookup', 'mmdb_lookup', 'ipqs_fraud_and_risk_scoring', 'clamav', 'jinja_template_rendering','hyasinsight', 'variotdbs', 'crowdsec', - 'extract_url_components', 'ipinfo', 'whoisfreaks', 'ip2locationio'] + 'extract_url_components', 'ipinfo', 'whoisfreaks', 'ip2locationio', '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 0000000..bf1a6a1 --- /dev/null +++ b/misp_modules/modules/expansion/vysion.py @@ -0,0 +1,212 @@ +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": "The request is missing required attribute information, which should contain at least a type, a value, and a 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.search( + attribute_value + ) # TODO 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, exact=True) + elif attribute_type == "btc": + result = client.search(attribute_value) # TODO + elif attribute_type == "phone-number": + result = client.search(attribute_value) # TODO + + if result is None: + return {"results": {}} + elif isinstance(result, dto.VysionError): + LOGGER.error(str(result)) + 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