From 410aaaeb2896bfb69d14b7ac2f293a22f914fadc Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Fri, 23 Oct 2020 21:19:26 +0200 Subject: [PATCH 1/4] add: First shot of an expansio module to query cve-search with a cpe to get the related vulnerabilities --- misp_modules/modules/expansion/cpe.py | 122 ++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 misp_modules/modules/expansion/cpe.py diff --git a/misp_modules/modules/expansion/cpe.py b/misp_modules/modules/expansion/cpe.py new file mode 100644 index 0000000..74cb1b6 --- /dev/null +++ b/misp_modules/modules/expansion/cpe.py @@ -0,0 +1,122 @@ +import json +import requests +from . import check_input_attribute, standard_error_message + +misperrors = {'error': 'Error'} +mispattributes = {'input': ['vulnerability'], 'format': 'misp_standard'} +moduleinfo = { + 'version': '1', + 'author': 'Christian Studer', + 'description': 'An expansion module to enrich a CPE attribute with the related vulnerabilities.', + 'module-type': ['expansion', 'hover'] +} +moduleconfig = ["custom_API_URL"] + + +class VulnerabilityParser(): + def __init__(self, attribute, api_url): + self.attribute = attribute + self.api_url = api_url + self.misp_event = MISPEvent() + self.misp_event.add_attribute(**attribute) + self.vulnerability_mapping = { + 'id': { + 'type': 'vulnerability', + 'object_relation': 'id' + }, + 'summary': { + 'type': 'text', + 'object_relation': 'summary' + }, + 'vulnerable_configuration': { + 'type': 'cpe', + 'object_relation': 'vulnerable_configuration' + }, + 'vulnerable_configuration_cpe_2_2': { + 'type': 'cpe', + 'object_relation': 'vulnerable_configuration' + }, + 'Modified': { + 'type': 'datetime', + 'object_relation': 'modified' + }, + 'Published': { + 'type': 'datetime', + 'object_relation': 'published' + }, + 'references': { + 'type': 'link', + 'object_relation': 'references' + }, + 'cvss': { + 'type': 'float', + 'object_relation': 'cvss-score' + } + } + + def parse_vulnerabilities(self, vulnerabilities): + for vulnerability in vulnerabilities: + vulnerability_object = MISPObject('vulnerability') + for feature in ('id', 'summary', 'Modified', 'Published', 'cvss'): + if vulnerability.get(feature): + attribute = {'value': vulnerability[feature]} + atttribute.update(self.vulnerability_mapping[feature]) + vulnerability_object.add_attribute(**attribute) + if vulnerability.get('Published'): + vulnerability_object.add_attribute(**{ + 'type': 'text', + 'object_relation': 'state', + 'value': 'Published' + }) + for feature in ('references', 'vulnerable_configuration', 'vulnerable_configuration_cpe_2_2'): + if vulnerability.get(feature): + for value in vulnerability[feature]: + if isinstance(value, dict): + value = value['title'] + attribute = {'value': value} + attribute.update(self.vulnerability_mapping[feature]) + vulnerability_object.add_attribute(**attribute) + vulnerability_object.add_reference(self.attribute['uuid'], 'related-to') + self.misp_event.add_object(vulnerability_object) + + def get_result(self): + event = json.loads(self.misp_event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])} + return {'results': results} + + +def check_url(url): + return "{}/".format(url) if not url.endswith('/') else url + + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + if not request.get('attribute') or not check_input_attribute(request['attribute']): + return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'} + attribute = request['attribute'] + if attribute.get('type') != 'cpe': + return {'error': 'Wrong input attribute type.'} + if not request.get('config') or not request['config'].get('custom_API_URL'): + return {'error': 'Missing API URL'} + api_url = check_url(request['config']['custom_API_URL']) + response = requests.get("{}{}".format(api_url, attribute['value'])) + if response.status_code == 200: + vulnerabilities = response.json() + if not vulnerabilities: + return {'error': 'No related vulnerability for this CPE.'} + else: + return {'error': 'API not accessible.'} + parser = VulnerabilitiesParser(attribute, api_url) + parser.parse_vulnerabilities(vulnerabilities) + return parser.get_result() + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo From 88c8d9077c3255c9c380b7cf623f9139ab30e9c2 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Sat, 24 Oct 2020 02:40:31 +0200 Subject: [PATCH 2/4] fix: [cpe] Typos and variable name issues fixed + Making the module available in MISP --- misp_modules/modules/expansion/__init__.py | 4 ++-- misp_modules/modules/expansion/cpe.py | 23 ++++++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 1b6d2bb..fcd7774 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -5,8 +5,8 @@ import sys sys.path.append('{}/lib'.format('/'.join((os.path.realpath(__file__)).split('/')[:-3]))) __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'circl_passivessl', - 'countrycode', 'cve', 'cve_advanced', 'dns', 'btc_steroids', 'domaintools', 'eupi', 'eql', - 'farsight_passivedns', 'ipasn', 'passivetotal', 'sourcecache', 'virustotal', + 'countrycode', 'cve', 'cve_advanced', 'cpe', 'dns', 'btc_steroids', 'domaintools', 'eupi', + 'eql', 'farsight_passivedns', 'ipasn', 'passivetotal', 'sourcecache', 'virustotal', 'whois', 'shodan', 'reversedns', 'geoip_asn', 'geoip_city', 'geoip_country', 'wiki', 'iprep', 'threatminer', 'otx', 'threatcrowd', 'vulndb', 'crowdstrike_falcon', 'yara_syntax_validator', 'hashdd', 'onyphe', 'onyphe_full', 'rbl', diff --git a/misp_modules/modules/expansion/cpe.py b/misp_modules/modules/expansion/cpe.py index 74cb1b6..9298876 100644 --- a/misp_modules/modules/expansion/cpe.py +++ b/misp_modules/modules/expansion/cpe.py @@ -1,19 +1,21 @@ import json import requests from . import check_input_attribute, standard_error_message +from pymisp import MISPEvent, MISPObject misperrors = {'error': 'Error'} -mispattributes = {'input': ['vulnerability'], 'format': 'misp_standard'} +mispattributes = {'input': ['cpe'], 'format': 'misp_standard'} moduleinfo = { 'version': '1', 'author': 'Christian Studer', - 'description': 'An expansion module to enrich a CPE attribute with the related vulnerabilities.', + 'description': 'An expansion module to enrich a CPE attribute with its related vulnerabilities.', 'module-type': ['expansion', 'hover'] } -moduleconfig = ["custom_API_URL"] +moduleconfig = ["custom_API_URL", "limit"] +cveapi_url = 'https://cve.circl.lu/api/cvefor/' -class VulnerabilityParser(): +class VulnerabilitiesParser(): def __init__(self, attribute, api_url): self.attribute = attribute self.api_url = api_url @@ -60,7 +62,7 @@ class VulnerabilityParser(): for feature in ('id', 'summary', 'Modified', 'Published', 'cvss'): if vulnerability.get(feature): attribute = {'value': vulnerability[feature]} - atttribute.update(self.vulnerability_mapping[feature]) + attribute.update(self.vulnerability_mapping[feature]) vulnerability_object.add_attribute(**attribute) if vulnerability.get('Published'): vulnerability_object.add_attribute(**{ @@ -81,7 +83,7 @@ class VulnerabilityParser(): def get_result(self): event = json.loads(self.misp_event.to_json()) - results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])} + results = {key: event[key] for key in ('Attribute', 'Object')} return {'results': results} @@ -98,10 +100,11 @@ def handler(q=False): attribute = request['attribute'] if attribute.get('type') != 'cpe': return {'error': 'Wrong input attribute type.'} - if not request.get('config') or not request['config'].get('custom_API_URL'): - return {'error': 'Missing API URL'} - api_url = check_url(request['config']['custom_API_URL']) - response = requests.get("{}{}".format(api_url, attribute['value'])) + api_url = check_url(request['config']['custom_API_URL']) if request['config'].get('custom_API_URL') else cveapi_url + url = f"{api_url}{attribute['value']}" + if request['config'].get('limit'): + url = f"{url}/{request['config']['limit']}" + response = requests.get(url) if response.status_code == 200: vulnerabilities = response.json() if not vulnerabilities: From 6660e2fc11748f61d35dc2542ac088b1d4341f57 Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Sat, 24 Oct 2020 23:52:06 +0200 Subject: [PATCH 3/4] add: Added documentation for the cpe module --- README.md | 1 + doc/README.md | 21 +++++++++++++++++++++ misp_modules/modules/expansion/cpe.py | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 26dce03..6a38688 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ For more information: [Extending MISP with Python modules](https://www.misp-proj * [CIRCL Passive SSL](misp_modules/modules/expansion/circl_passivessl.py) - a hover and expansion module to expand IP addresses with the X.509 certificate(s) seen. * [countrycode](misp_modules/modules/expansion/countrycode.py) - a hover module to tell you what country a URL belongs to. * [CrowdStrike Falcon](misp_modules/modules/expansion/crowdstrike_falcon.py) - an expansion module to expand using CrowdStrike Falcon Intel Indicator API. +* [CPE](misp_modules/modules/expansion/cpe.py) - An expansion module to query the CVE Search API with a cpe code, to get its related vulnerabilities. * [CVE](misp_modules/modules/expansion/cve.py) - a hover module to give more information about a vulnerability (CVE). * [CVE advanced](misp_modules/modules/expansion/cve_advanced.py) - An expansion module to query the CIRCL CVE search API for more information about a vulnerability (CVE). * [Cuckoo submit](misp_modules/modules/expansion/cuckoo_submit.py) - A hover module to submit malware sample, url, attachment, domain to Cuckoo Sandbox. diff --git a/doc/README.md b/doc/README.md index 1225780..2245c3c 100644 --- a/doc/README.md +++ b/doc/README.md @@ -222,6 +222,27 @@ Module to expand country codes. ----- +#### [cpe](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/cpe.py) + + + +An expansion module to query the CVE search API with a cpe code to get its related vulnerabilities. +- **features**: +>The module takes a cpe attribute as input and queries the CVE search API to get its related vulnerabilities. +>The list of vulnerabilities is then parsed and returned as vulnerability objects. +> +>Users can use their own CVE search API url by defining a value to the custom_API_URL parameter. If no custom API url is given, the default cve.circl.lu api url is used. +> +>In order to limit the amount of data returned by CVE serach, users can also the limit parameter. With the limit set, the API returns only the requested number of vulnerabilities, sorted from the highest cvss score to the lowest one. +- **input**: +>CPE attribute. +- **output**: +>The vulnerabilities related to the CPE. +- **references**: +>https://cve.circl.lu/api/ + +----- + #### [crowdstrike_falcon](https://github.com/MISP/misp-modules/tree/master/misp_modules/modules/expansion/crowdstrike_falcon.py) diff --git a/misp_modules/modules/expansion/cpe.py b/misp_modules/modules/expansion/cpe.py index 9298876..8ea0f65 100644 --- a/misp_modules/modules/expansion/cpe.py +++ b/misp_modules/modules/expansion/cpe.py @@ -88,7 +88,7 @@ class VulnerabilitiesParser(): def check_url(url): - return "{}/".format(url) if not url.endswith('/') else url + return url if url.endswith('/') else f"{url}/" def handler(q=False): From 260bddb3cf4f8488521ee89cb2414eed48b97e1c Mon Sep 17 00:00:00 2001 From: chrisr3d Date: Mon, 2 Nov 2020 19:03:26 +0100 Subject: [PATCH 4/4] chg: [cpe] Changed CVE-Search API default url --- misp_modules/modules/expansion/cpe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/cpe.py b/misp_modules/modules/expansion/cpe.py index 8ea0f65..cd5e5fe 100644 --- a/misp_modules/modules/expansion/cpe.py +++ b/misp_modules/modules/expansion/cpe.py @@ -12,7 +12,7 @@ moduleinfo = { 'module-type': ['expansion', 'hover'] } moduleconfig = ["custom_API_URL", "limit"] -cveapi_url = 'https://cve.circl.lu/api/cvefor/' +cveapi_url = 'https://cvepremium.circl.lu/api/cvefor/' class VulnerabilitiesParser():