From cf7b8318a46d4a31c627a7ecec77824ba7c5b94c Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Sat, 5 Feb 2022 11:32:46 +0530 Subject: [PATCH 01/19] Initial Commit for IPQualityScore Expansion Module --- .../modules/expansion/ipqualityscore.py | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 misp_modules/modules/expansion/ipqualityscore.py diff --git a/misp_modules/modules/expansion/ipqualityscore.py b/misp_modules/modules/expansion/ipqualityscore.py new file mode 100644 index 0000000..ebdb395 --- /dev/null +++ b/misp_modules/modules/expansion/ipqualityscore.py @@ -0,0 +1,128 @@ +import json +import logging +import requests +import urllib.parse +from . import check_input_attribute, standard_error_message +from pymisp import MISPAttribute, MISPEvent, MISPTag, MISPObject, Distribution + +logger = logging.getLogger('ipqualityscore') +logger.setLevel(logging.DEBUG) + +misperrors = {'error': 'Error'} +mispattributes = {'input': ['hostname', 'domain', 'url', 'uri', 'ip-src', 'ip-dst', 'email', 'email-src', 'email-dst', 'target-email', 'whois-registrant-email', 'phone-number','whois-registrant-phone'], 'output': ['text'], 'format': 'misp_standard'} +moduleinfo = {'version': '0.1', 'author': 'David Mackler', 'description': 'Query IPQualityScore for IP reputation, Email Validation, Phone Number Validation and Malicious Domain/URL Scanner.', + 'module-type': ['hover', 'expansion']} +moduleconfig = ['apikey'] + +BASE_URL = 'https://ipqualityscore.com/api/json' +DEFAULT_DISTRIBUTION_SETTING = Distribution.your_organisation_only.value + +IP_API_ATTRIBUTE_TYPES = ['ip-src', 'ip-dst'] +URL_API_ATTRIBUTE_TYPES = ['hostname', 'domain', 'url', 'uri'] +EMAIL_API_ATTRIBUTE_TYPES = ['email', 'email-src', 'email-dst', 'target-email', 'whois-registrant-email'] +PHONE_API_ATTRIBUTE_TYPES = ['phone-number','whois-registrant-phone'] + +def _format_result(attribute, result, enrichment_type): + + event = MISPEvent() + + orig_attr = MISPAttribute() + orig_attr.from_dict(**attribute) + + event = _make_enriched_attr(event, result, orig_attr) + + return event + +def _make_enriched_attr(event, result, orig_attr): + + enriched_object = MISPObject('IPQualityScore Enrichment') + enriched_object.add_reference(orig_attr.uuid, 'related-to') + + enriched_attr = MISPAttribute() + enriched_attr.from_dict(**{ + 'value': orig_attr.value, + 'type': orig_attr.type, + 'distribution': 0, + 'object_relation': 'enriched-attr', + 'to_ids': orig_attr.to_ids + }) + + # enriched_attr = _make_tags(enriched_attr, result) + # enriched_object.add_attribute(**enriched_attr) + + + fraud_score_attr = MISPAttribute() + fraud_score_attr.from_dict(**{ + 'value': result.get('fraud_score'), + 'type': 'text', + 'object_relation': 'fraud_score', + 'distribution': 0 + }) + enriched_object.add_attribute(**fraud_score_attr) + + latitude = MISPAttribute() + latitude.from_dict(**{ + 'value': result.get('latitude'), + 'type': 'text', + 'object_relation': 'latitude', + 'distribution': 0 + }) + enriched_object.add_attribute(**latitude) + + event.add_attribute(**orig_attr) + event.add_object(**enriched_object) + + longitude = MISPAttribute() + longitude.from_dict(**{ + 'value': result.get('longitude'), + 'type': 'text', + 'object_relation': 'longitude', + 'distribution': 0 + }) + enriched_object.add_attribute(**longitude) + + return event + +def handler(q=False): + if q is False: + return False + request = json.loads(q) + + # check if the apikey is pprovided + if not request.get('config') or not request['config'].get('apikey'): + misperrors['error'] = 'IPQualityScore apikey is missing' + return misperrors + apikey = request['config'].get('apikey') + # check attribute is added to the event + 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.'} + + input_attribute = request['attribute'] + input_attribute_type = input_attribute['type'] + input_attribute_value = attribute['value'] + # check if the attribute type is supported by IPQualityScore + if input_attribute_type not in mispattributes['input']: + return {'error': 'Unsupported attributes type for IPqualityScore Enrichment'} + + if input_attribute_type in IP_API_ATTRIBUTE_TYPES: + url = f"{BASE_URL}/ip/{input_attribute_value}" + headers = {"IPQS-KEY": apikey} + response = self.get(url, headers) + data = response.data + if str(data.get('success')) == "True": + event = _format_result(input_attribute, data, "ip") + event = json.loads(event.to_json()) + ret_result = {key: event[key] for key in ('Attribute', 'Object') if key + in event} + return {'results': ret_result} + else: + return {'error', str(data.get('message')) + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo + \ No newline at end of file From 17541e29386938cd8a7ba1a61aabef809a9e6630 Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Sat, 5 Feb 2022 11:33:43 +0530 Subject: [PATCH 02/19] Added ipqualityscore to All list --- misp_modules/modules/expansion/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 7591d7d..23f3d32 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -18,7 +18,7 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', 'malwarebazaar', 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion', 'censys_enrich', 'trustar_enrich', 'recordedfuture', 'html_to_markdown', 'socialscan', 'passive-ssh', - 'qintel_qsentry', 'mwdb'] + 'qintel_qsentry', 'mwdb', 'ipqualityscore'] minimum_required_fields = ('type', 'uuid', 'value') From 47dde7943b95e6dc796d9fee2765b80e610b192f Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Wed, 9 Feb 2022 10:20:42 +0530 Subject: [PATCH 03/19] delete --- .../modules/expansion/ipqualityscore.py | 128 ------------------ 1 file changed, 128 deletions(-) delete mode 100644 misp_modules/modules/expansion/ipqualityscore.py diff --git a/misp_modules/modules/expansion/ipqualityscore.py b/misp_modules/modules/expansion/ipqualityscore.py deleted file mode 100644 index ebdb395..0000000 --- a/misp_modules/modules/expansion/ipqualityscore.py +++ /dev/null @@ -1,128 +0,0 @@ -import json -import logging -import requests -import urllib.parse -from . import check_input_attribute, standard_error_message -from pymisp import MISPAttribute, MISPEvent, MISPTag, MISPObject, Distribution - -logger = logging.getLogger('ipqualityscore') -logger.setLevel(logging.DEBUG) - -misperrors = {'error': 'Error'} -mispattributes = {'input': ['hostname', 'domain', 'url', 'uri', 'ip-src', 'ip-dst', 'email', 'email-src', 'email-dst', 'target-email', 'whois-registrant-email', 'phone-number','whois-registrant-phone'], 'output': ['text'], 'format': 'misp_standard'} -moduleinfo = {'version': '0.1', 'author': 'David Mackler', 'description': 'Query IPQualityScore for IP reputation, Email Validation, Phone Number Validation and Malicious Domain/URL Scanner.', - 'module-type': ['hover', 'expansion']} -moduleconfig = ['apikey'] - -BASE_URL = 'https://ipqualityscore.com/api/json' -DEFAULT_DISTRIBUTION_SETTING = Distribution.your_organisation_only.value - -IP_API_ATTRIBUTE_TYPES = ['ip-src', 'ip-dst'] -URL_API_ATTRIBUTE_TYPES = ['hostname', 'domain', 'url', 'uri'] -EMAIL_API_ATTRIBUTE_TYPES = ['email', 'email-src', 'email-dst', 'target-email', 'whois-registrant-email'] -PHONE_API_ATTRIBUTE_TYPES = ['phone-number','whois-registrant-phone'] - -def _format_result(attribute, result, enrichment_type): - - event = MISPEvent() - - orig_attr = MISPAttribute() - orig_attr.from_dict(**attribute) - - event = _make_enriched_attr(event, result, orig_attr) - - return event - -def _make_enriched_attr(event, result, orig_attr): - - enriched_object = MISPObject('IPQualityScore Enrichment') - enriched_object.add_reference(orig_attr.uuid, 'related-to') - - enriched_attr = MISPAttribute() - enriched_attr.from_dict(**{ - 'value': orig_attr.value, - 'type': orig_attr.type, - 'distribution': 0, - 'object_relation': 'enriched-attr', - 'to_ids': orig_attr.to_ids - }) - - # enriched_attr = _make_tags(enriched_attr, result) - # enriched_object.add_attribute(**enriched_attr) - - - fraud_score_attr = MISPAttribute() - fraud_score_attr.from_dict(**{ - 'value': result.get('fraud_score'), - 'type': 'text', - 'object_relation': 'fraud_score', - 'distribution': 0 - }) - enriched_object.add_attribute(**fraud_score_attr) - - latitude = MISPAttribute() - latitude.from_dict(**{ - 'value': result.get('latitude'), - 'type': 'text', - 'object_relation': 'latitude', - 'distribution': 0 - }) - enriched_object.add_attribute(**latitude) - - event.add_attribute(**orig_attr) - event.add_object(**enriched_object) - - longitude = MISPAttribute() - longitude.from_dict(**{ - 'value': result.get('longitude'), - 'type': 'text', - 'object_relation': 'longitude', - 'distribution': 0 - }) - enriched_object.add_attribute(**longitude) - - return event - -def handler(q=False): - if q is False: - return False - request = json.loads(q) - - # check if the apikey is pprovided - if not request.get('config') or not request['config'].get('apikey'): - misperrors['error'] = 'IPQualityScore apikey is missing' - return misperrors - apikey = request['config'].get('apikey') - # check attribute is added to the event - 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.'} - - input_attribute = request['attribute'] - input_attribute_type = input_attribute['type'] - input_attribute_value = attribute['value'] - # check if the attribute type is supported by IPQualityScore - if input_attribute_type not in mispattributes['input']: - return {'error': 'Unsupported attributes type for IPqualityScore Enrichment'} - - if input_attribute_type in IP_API_ATTRIBUTE_TYPES: - url = f"{BASE_URL}/ip/{input_attribute_value}" - headers = {"IPQS-KEY": apikey} - response = self.get(url, headers) - data = response.data - if str(data.get('success')) == "True": - event = _format_result(input_attribute, data, "ip") - event = json.loads(event.to_json()) - ret_result = {key: event[key] for key in ('Attribute', 'Object') if key - in event} - return {'results': ret_result} - else: - return {'error', str(data.get('message')) - -def introspection(): - return mispattributes - - -def version(): - moduleinfo['config'] = moduleconfig - return moduleinfo - \ No newline at end of file From 85bd1b69ad791ada6ebaed73fe7462e0ff5c0be3 Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Wed, 9 Feb 2022 10:21:40 +0530 Subject: [PATCH 04/19] Initial Commit for IPQualityScore Expansion Module --- .../expansion/ipqs_fraud_and_risk_scoring.py | 635 ++++++++++++++++++ 1 file changed, 635 insertions(+) create mode 100644 misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py diff --git a/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py b/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py new file mode 100644 index 0000000..f3b2afb --- /dev/null +++ b/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py @@ -0,0 +1,635 @@ +import json +import logging +import requests +from requests.exceptions import ( + HTTPError, + ProxyError, + InvalidURL, + ConnectTimeout +) +from . import check_input_attribute, standard_error_message +from pymisp import MISPEvent, MISPAttribute, MISPObject, MISPTag, Distribution + +ip_query_input_type = [ + 'ip-src', + 'ip-dst' +] +url_query_input_type = [ + 'hostname', + 'domain', + 'url', + 'uri' +] +email_query_input_type = [ + 'email', + 'email-src', + 'email-dst', + 'target-email', + 'whois-registrant-email' +] +phone_query_input_type = [ + 'phone-number', + 'whois-registrant-phone' +] + +misperrors = { + 'error': 'Error' +} +mispattributes = { + 'input': ip_query_input_type + url_query_input_type + email_query_input_type + phone_query_input_type, + 'format': 'misp_standard' +} +moduleinfo = { + 'version': '0.1', + 'author': 'David Mackler', + 'description': 'Query IPQualityScore for IP reputation, Email Validation, Phone Number Validation and Malicious ' + 'Domain/URL Scanner.', + 'module-type': ['expansion', 'hover'] +} +moduleconfig = ['apikey'] + +logger = logging.getLogger('ipqualityscore') +logger.setLevel(logging.DEBUG) +BASE_URL = 'https://ipqualityscore.com/api/json' +DEFAULT_DISTRIBUTION_SETTING = Distribution.your_organisation_only.value +IP_ENRICH = 'ip' +URL_ENRICH = 'url' +EMAIL_ENRICH = 'email' +PHONE_ENRICH = 'phone' + + +class RequestHandler: + """A class for handling any outbound requests from this module.""" + + def __init__(self, apikey): + self.session = requests.Session() + self.api_key = apikey + + def get(self, url: str, headers: dict = None, params: dict = None) -> requests.Response: + """General get method to fetch the response from IPQualityScore.""" + try: + response = self.session.get( + url, headers=headers, params=params + ).json() + if str(response["success"]) != "True": + msg = response["message"] + logger.error(f"Error: {msg}") + misperrors["error"] = msg + else: + return response + except (ConnectTimeout, ProxyError, InvalidURL) as error: + msg = "Error connecting with the IPQualityScore." + logger.error(f"{msg} Error: {error}") + misperrors["error"] = msg + + def ipqs_lookup(self, reputation_type: str, ioc: str) -> requests.Response: + """Do a lookup call.""" + url = f"{BASE_URL}/{reputation_type}" + payload = {reputation_type: ioc} + headers = {"IPQS-KEY": self.api_key} + try: + response = self.get(url, headers, payload) + except HTTPError as error: + msg = f"Error when requesting data from IPQualityScore. {error.response}: {error.response.reason}" + logger.error(msg) + misperrors["error"] = msg + raise + return response + + +def parse_attribute(comment, feature, value): + """Generic Method for parsing the attributes in the object""" + attribute = { + 'type': 'text', + 'value': value, + 'comment': comment, + 'distribution': DEFAULT_DISTRIBUTION_SETTING, + 'object_relation': feature + } + return attribute + + +class IPQualityScoreParser: + """A class for handling the enrichment objects""" + + def __init__(self, attribute): + self.rf_white = "#CCCCCC" + self.rf_grey = " #CDCDCD" + self.rf_yellow = "#FFCF00" + self.rf_red = "#D10028" + self.clean = "CLEAN" + self.low = "LOW RISK" + self.medium = "MODERATE RISK" + self.high = "HIGH RISK" + self.critical = "CRITICAL" + self.invalid = "INVALID" + self.suspicious = "SUSPICIOUS" + self.malware = "MALWARE" + self.phishing = "PHISHING" + self.disposable = "DISPOSABLE" + self.attribute = attribute + self.misp_event = MISPEvent() + self.misp_event.add_attribute(**attribute) + self.ipqs_object = MISPObject('IPQS Fraud ans Risk Scoring Object') + self.ipqs_object.template_uuid = "57d066e6-6d66-42a7-a1ad-e075e39b2b5e" + self.ipqs_object.template_id = "1" + self.ipqs_object.description = "IPQS Fraud ans Risk Scoring Data" + setattr(self.ipqs_object, 'meta-category', 'network') + description = ( + "An object containing the enriched attribute and " + "related entities from IPQualityScore." + ) + self.ipqs_object.from_dict( + **{"meta-category": "misc", "description": description, "distribution": DEFAULT_DISTRIBUTION_SETTING} + ) + + temp_attr = MISPAttribute() + temp_attr.from_dict(**attribute) + self.enriched_attribute = MISPAttribute() + self.enriched_attribute.from_dict( + **{"value": temp_attr.value, "type": temp_attr.type, "distribution": DEFAULT_DISTRIBUTION_SETTING} + ) + self.ipqs_object.distribution = DEFAULT_DISTRIBUTION_SETTING + self.ip_data_items = [ + 'fraud_score', + 'country_code', + 'region', + 'city', + 'zip_code', + 'ISP', + 'ASN', + 'organization', + 'is_crawler', + 'timezone', + 'mobile', + 'host', + 'proxy', + 'vpn', + 'tor', + 'active_vpn', + 'active_tor', + 'recent_abuse', + 'bot_status', + 'connection_type', + 'abuse_velocity', + 'latitude', + 'longitude' + ] + self.ip_data_items_friendly_names = { + 'fraud_score': 'IPQS: Fraud Score', + 'country_code': 'IPQS: Country Code', + 'region': 'IPQS: Region', + 'city': 'IPQS: City', + 'zip_code': 'IPQS: Zip Code', + 'ISP': 'IPQS: ISP', + 'ASN': 'IPQS: ASN', + 'organization': 'IPQS: Organization', + 'is_crawler': 'IPQS: Is Crawler', + 'timezone': 'IPQS: Timezone', + 'mobile': 'IPQS: Mobile', + 'host': 'IPQS: Host', + 'proxy': 'IPQS: Proxy', + 'vpn': 'IPQS: VPN', + 'tor': 'IPQS: TOR', + 'active_vpn': 'IPQS: Active VPN', + 'active_tor': 'IPQS: Active TOR', + 'recent_abuse': 'IPQS: Recent Abuse', + 'bot_status': 'IPQS: Bot Status', + 'connection_type': 'IPQS: Connection Type', + 'abuse_velocity': 'IPQS: Abuse Velocity', + 'latitude': 'IPQS: Latitude', + 'longitude': 'IPQS: Longitude' + } + self.url_data_items = [ + 'unsafe', + 'domain', + 'ip_address', + 'server', + 'domain_rank', + 'dns_valid', + 'parking', + 'spamming', + 'malware', + 'phishing', + 'suspicious', + 'adult', + 'risk_score', + 'category', + 'domain_age' + ] + self.url_data_items_friendly_names = { + 'unsafe': 'IPQS: Unsafe', + 'domain': 'IPQS: Domain', + 'ip_address': 'IPQS: IP Address', + 'server': 'IPQS: Server', + 'domain_rank': 'IPQS: Domain Rank', + 'dns_valid': 'IPQS: DNS Valid', + 'parking': 'IPQS: Parking', + 'spamming': 'IPQS: Spamming', + 'malware': 'IPQS: Malware', + 'phishing': 'IPQS: Phishing', + 'suspicious': 'IPQS: Suspicious', + 'adult': 'IPQS: Adult', + 'risk_score': 'IPQS: Risk Score', + 'category': 'IPQS: Category', + 'domain_age': 'IPQS: Domain Age' + } + self.email_data_items = [ + 'valid', + 'disposable', + 'smtp_score', + 'overall_score', + 'first_name', + 'generic', + 'common', + 'dns_valid', + 'honeypot', + 'deliverability', + 'frequent_complainer', + 'spam_trap_score', + 'catch_all', + 'timed_out', + 'suspect', + 'recent_abuse', + 'fraud_score', + 'suggested_domain', + 'leaked', + 'sanitized_email', + 'domain_age', + 'first_seen' + ] + self.email_data_items_friendly_names = { + 'valid': 'IPQS: Valid', + 'disposable': 'IPQS: Disposable', + 'smtp_score': 'IPQS: SMTP Score', + 'overall_score': 'IPQS: Overall Score', + 'first_name': 'IPQS: First Name', + 'generic': 'IPQS: Generic', + 'common': 'IPQS: Common', + 'dns_valid': 'IPQS: DNS Valid', + 'honeypot': 'IPQS: Honeypot', + 'deliverability': 'IPQS: Deliverability', + 'frequent_complainer': 'IPQS: Frequent Complainer', + 'spam_trap_score': 'IPQS: Spam Trap Score', + 'catch_all': 'IPQS: Catch All', + 'timed_out': 'IPQS: Timed Out', + 'suspect': 'IPQS: Suspect', + 'recent_abuse': 'IPQS: Recent Abuse', + 'fraud_score': 'IPQS: Fraud Score', + 'suggested_domain': 'IPQS: Suggested Domain', + 'leaked': 'IPQS: Leaked', + 'sanitized_email': 'IPQS: Sanitized Email', + 'domain_age': 'IPQS: Domain Age', + 'first_seen': 'IPQS: First Seen' + } + self.phone_data_items = [ + 'formatted', + 'local_format', + 'valid', + 'fraud_score', + 'recent_abuse', + 'VOIP', + 'prepaid', + 'risky', + 'active', + 'carrier', + 'line_type', + 'country', + 'city', + 'zip_code', + 'region', + 'dialing_code', + 'active_status', + 'leaked', + 'name', + 'timezone', + 'do_not_call', + ] + self.phone_data_items_friendly_names = { + 'formatted': 'IPQS: Formatted', + 'local_format': 'IPQS: Local Format', + 'valid': 'IPQS: Valid', + 'fraud_score': 'IPQS: Fraud Score', + 'recent_abuse': 'IPQS: Recent Abuse', + 'VOIP': 'IPQS: VOIP', + 'prepaid': 'IPQS: Prepaid', + 'risky': 'IPQS: Risky', + 'active': 'IPQS: Active', + 'carrier': 'IPQS: Carrier', + 'line_type': 'IPQS: Line Type', + 'country': 'IPQS: Country', + 'city': 'IPQS: City', + 'zip_code': 'IPQS: Zip Code', + 'region': 'IPQS: Region', + 'dialing_code': 'IPQS: Dialing Code', + 'active_status': 'IPQS: Active Status', + 'leaked': 'IPQS: Leaked', + 'name': 'IPQS: Name', + 'timezone': 'IPQS: Timezone', + 'do_not_call': 'IPQS: Do Not Call', + } + self.timestamp_items_friendly_name = { + 'human': ' Human', + 'timestamp': ' Timestamp', + 'iso': ' ISO' + } + self.timestamp_items = [ + 'human', + 'timestamp', + 'iso' + ] + + def criticality_color(self, criticality) -> str: + """method which maps the color to the criticality level""" + mapper = { + self.clean: self.rf_grey, + self.low: self.rf_grey, + self.medium: self.rf_yellow, + self.suspicious: self.rf_yellow, + self.high: self.rf_red, + self.critical: self.rf_red, + self.invalid: self.rf_red, + self.disposable: self.rf_red, + self.malware: self.rf_red, + self.phishing: self.rf_red + } + return mapper.get(criticality, self.rf_white) + + def add_tag(self, tag_name: str, hex_color: str = None) -> None: + """Helper method for adding a tag to the enriched attribute.""" + tag = MISPTag() + tag_properties = {"name": tag_name} + if hex_color: + tag_properties["colour"] = hex_color + tag.from_dict(**tag_properties) + self.enriched_attribute.add_tag(tag) + + def ipqs_parser(self, query_response, enrich_type): + """ helper method to call the enrichment function according to the type""" + if enrich_type == IP_ENRICH: + self.ip_reputation_data(query_response) + elif enrich_type == URL_ENRICH: + self.url_reputation_data(query_response) + elif enrich_type == EMAIL_ENRICH: + self.email_reputation_data(query_response) + elif enrich_type == PHONE_ENRICH: + self.phone_reputation_data(query_response) + + def ip_reputation_data(self, query_response): + """method to create object for IP address""" + comment = "Results from IPQualityScore IP Reputation API" + for ip_data_item in self.ip_data_items: + if ip_data_item in query_response: + data_item = self.ip_data_items_friendly_names[ip_data_item] + data_item_value = str(query_response[ip_data_item]) + self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value)) + if ip_data_item == "fraud_score": + fraud_score = int(data_item_value) + tag_name = f'IPQS:Fraud Score="{fraud_score}"' + self.add_tag(tag_name) + self.ip_address_risk_scoring(fraud_score) + + self.ipqs_object.add_attribute( + "Enriched attribute", **self.enriched_attribute + ) + self.ipqs_object.add_reference(self.attribute['uuid'], 'related-to') + self.misp_event.add_object(self.ipqs_object) + + def ip_address_risk_scoring(self, score): + """method to create calculate verdict for IP Address""" + risk_criticality = "" + if score == 100: + risk_criticality = self.critical + elif 85 <= score <= 99: + risk_criticality = self.high + elif 75 <= score <= 84: + risk_criticality = self.medium + elif 60 <= score <= 74: + risk_criticality = self.suspicious + elif score <= 59: + risk_criticality = self.clean + + hex_color = self.criticality_color(risk_criticality) + tag_name = f'IPQS:VERDICT="{risk_criticality}"' + self.add_tag(tag_name, hex_color) + + def url_reputation_data(self, query_response): + """method to create object for URL/Domain""" + malware = False + phishing = False + risk_score = 0 + comment = "Results from IPQualityScore Malicious URL Scanner API" + for url_data_item in self.url_data_items: + if url_data_item in query_response: + data_item_value = "" + if url_data_item == "domain_age": + for timestamp_item in self.timestamp_items: + data_item = self.url_data_items_friendly_names[url_data_item] + \ + self.timestamp_items_friendly_name[timestamp_item] + data_item_value = str(query_response[url_data_item][timestamp_item]) + self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value)) + else: + data_item = self.url_data_items_friendly_names[url_data_item] + data_item_value = str(query_response[url_data_item]) + self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value)) + + if url_data_item == "malware": + malware = data_item_value + if url_data_item == "phishing": + phishing = data_item_value + if url_data_item == "risk_score": + risk_score = int(data_item_value) + tag_name = f'IPQS:Risk Score="{risk_score}"' + self.add_tag(tag_name) + + self.url_risk_scoring(risk_score, malware, phishing) + self.ipqs_object.add_attribute( + "Enriched attribute", **self.enriched_attribute + ) + self.ipqs_object.add_reference(self.attribute['uuid'], 'related-to') + self.misp_event.add_object(self.ipqs_object) + + def url_risk_scoring(self, score, malware, phishing): + """method to create calculate verdict for URL/Domain""" + risk_criticality = "" + if malware == 'True': + risk_criticality = self.malware + elif phishing == 'True': + risk_criticality = self.phishing + elif score >= 90: + risk_criticality = self.high + elif 80 <= score <= 89: + risk_criticality = self.medium + elif 70 <= score <= 79: + risk_criticality = self.low + elif 55 <= score <= 69: + risk_criticality = self.suspicious + elif score <= 54: + risk_criticality = self.clean + + hex_color = self.criticality_color(risk_criticality) + tag_name = f'IPQS:VERDICT="{risk_criticality}"' + self.add_tag(tag_name, hex_color) + + def email_reputation_data(self, query_response): + """method to create object for Email Address""" + comment = "Results from IPQualityScore Email Verification API" + disposable = False + valid = False + fraud_score = 0 + for email_data_item in self.email_data_items: + if email_data_item in query_response: + data_item_value = "" + if email_data_item not in ("domain_age", "first_seen"): + data_item = self.email_data_items_friendly_names[email_data_item] + data_item_value = str(query_response[email_data_item]) + self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value)) + else: + for timestamp_item in self.timestamp_items: + data_item = self.email_data_items_friendly_names[email_data_item] + \ + self.timestamp_items_friendly_name[timestamp_item] + data_item_value = str(query_response[email_data_item][timestamp_item]) + self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value)) + + if email_data_item == "disposable": + disposable = data_item_value + if email_data_item == "valid": + valid = data_item_value + if email_data_item == "fraud_score": + fraud_score = int(data_item_value) + tag_name = f'IPQS:Fraud Score="{fraud_score}"' + self.add_tag(tag_name) + + self.email_address_risk_scoring(fraud_score, disposable, valid) + self.ipqs_object.add_attribute( + "Enriched attribute", **self.enriched_attribute + ) + self.ipqs_object.add_reference(self.attribute['uuid'], 'related-to') + self.misp_event.add_object(self.ipqs_object) + + def email_address_risk_scoring(self, score, disposable, valid): + """method to create calculate verdict for Email Address""" + risk_criticality = "" + if valid == "False": + risk_criticality = self.invalid + elif disposable == "True": + risk_criticality = self.disposable + elif score == 100: + risk_criticality = self.high + elif 88 <= score <= 99: + risk_criticality = self.medium + elif 80 <= score <= 87: + risk_criticality = self.low + elif score <= 79: + risk_criticality = self.clean + hex_color = self.criticality_color(risk_criticality) + tag_name = f'IPQS:VERDICT="{risk_criticality}"' + + self.add_tag(tag_name, hex_color) + + def phone_reputation_data(self, query_response): + """method to create object for Phone Number""" + fraud_score = 0 + valid = False + active = False + comment = "Results from IPQualityScore Phone Number Validation API" + for phone_data_item in self.phone_data_items: + if phone_data_item in query_response: + data_item = self.phone_data_items_friendly_names[phone_data_item] + data_item_value = str(query_response[phone_data_item]) + self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value)) + if phone_data_item == "active": + active = data_item_value + if phone_data_item == "valid": + valid = data_item_value + if phone_data_item == "fraud_score": + fraud_score = int(data_item_value) + tag_name = f'IPQS:Fraud Score="{fraud_score}"' + self.add_tag(tag_name) + + self.phone_address_risk_scoring(fraud_score, valid, active) + self.ipqs_object.add_attribute( + "Enriched attribute", **self.enriched_attribute + ) + self.ipqs_object.add_reference(self.attribute['uuid'], 'related-to') + self.misp_event.add_object(self.ipqs_object) + + def phone_address_risk_scoring(self, score, valid, active): + """method to create calculate verdict for Phone Number""" + risk_criticality = "" + if valid == "False": + risk_criticality = self.medium + elif active == "False": + risk_criticality = self.medium + elif 90 <= score <= 100: + risk_criticality = self.high + elif 80 <= score <= 89: + risk_criticality = self.low + elif 50 <= score <= 79: + risk_criticality = self.suspicious + elif score <= 49: + risk_criticality = self.clean + hex_color = self.criticality_color(risk_criticality) + tag_name = f'IPQS:VERDICT="{risk_criticality}"' + self.add_tag(tag_name, hex_color) + + def get_results(self): + """returns the dictionary object to MISP Instance""" + event = json.loads(self.misp_event.to_json()) + results = {key: event[key] for key in ('Attribute', 'Object')} + return {'results': results} + + +def handler(q=False): + """The function which accepts a JSON document to expand the values and return a dictionary of the expanded + values. """ + if q is False: + return False + request = json.loads(q) + # check if the apikey is provided + if not request.get('config') or not request['config'].get('apikey'): + misperrors['error'] = 'IPQualityScore apikey is missing' + return misperrors + apikey = request['config'].get('apikey') + # check attribute is added to the event + 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'] + attribute_type = attribute['type'] + attribute_value = attribute['value'] + + # check if the attribute type is supported by IPQualityScore + if attribute_type not in mispattributes['input']: + return {'error': 'Unsupported attributes type for IPqualityScore Enrichment'} + request_handler = RequestHandler(apikey) + enrich_type = "" + if attribute_type in ip_query_input_type: + enrich_type = IP_ENRICH + json_response = request_handler.ipqs_lookup(IP_ENRICH, attribute_value) + elif attribute_type in url_query_input_type: + enrich_type = URL_ENRICH + json_response = request_handler.ipqs_lookup(URL_ENRICH, attribute_value) + elif attribute_type in email_query_input_type: + enrich_type = EMAIL_ENRICH + json_response = request_handler.ipqs_lookup(EMAIL_ENRICH, attribute_value) + elif attribute_type in phone_query_input_type: + enrich_type = PHONE_ENRICH + json_response = request_handler.ipqs_lookup(PHONE_ENRICH, attribute_value) + + parser = IPQualityScoreParser(attribute) + parser.ipqs_parser(json_response, enrich_type) + return parser.get_results() + + +def introspection(): + """The function that returns a dict of the supported attributes (input and output) by your expansion module.""" + return mispattributes + + +def version(): + """The function that returns a dict with the version and the associated meta-data including potential + configurations required of the module. """ + moduleinfo['config'] = moduleconfig + return moduleinfo + From fedf731e074d2f34c58a3ed6578b4a631841b324 Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Wed, 9 Feb 2022 10:22:16 +0530 Subject: [PATCH 05/19] added ipqs_fraud_and_risk_scoring to modules list --- misp_modules/modules/expansion/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 23f3d32..15eb8d6 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -18,7 +18,7 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', 'malwarebazaar', 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion', 'censys_enrich', 'trustar_enrich', 'recordedfuture', 'html_to_markdown', 'socialscan', 'passive-ssh', - 'qintel_qsentry', 'mwdb', 'ipqualityscore'] + 'qintel_qsentry', 'mwdb', 'ipqs_fraud_and_risk_scoring'] minimum_required_fields = ('type', 'uuid', 'value') From 430a838332aaa3e95d682ec6784669e7cf524307 Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Sat, 12 Feb 2022 10:20:48 +0530 Subject: [PATCH 06/19] Update ipqs_fraud_and_risk_scoring.py --- .../expansion/ipqs_fraud_and_risk_scoring.py | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py b/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py index f3b2afb..754f94a 100644 --- a/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py +++ b/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py @@ -130,10 +130,10 @@ class IPQualityScoreParser: self.attribute = attribute self.misp_event = MISPEvent() self.misp_event.add_attribute(**attribute) - self.ipqs_object = MISPObject('IPQS Fraud ans Risk Scoring Object') + self.ipqs_object = MISPObject('IPQS Fraud and Risk Scoring Object') self.ipqs_object.template_uuid = "57d066e6-6d66-42a7-a1ad-e075e39b2b5e" self.ipqs_object.template_id = "1" - self.ipqs_object.description = "IPQS Fraud ans Risk Scoring Data" + self.ipqs_object.description = "IPQS Fraud and Risk Scoring Data" setattr(self.ipqs_object, 'meta-category', 'network') description = ( "An object containing the enriched attribute and " @@ -385,8 +385,8 @@ class IPQualityScoreParser: self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value)) if ip_data_item == "fraud_score": fraud_score = int(data_item_value) - tag_name = f'IPQS:Fraud Score="{fraud_score}"' - self.add_tag(tag_name) + # tag_name = f'IPQS:Fraud Score="{fraud_score}"' + # self.add_tag(tag_name) self.ip_address_risk_scoring(fraud_score) self.ipqs_object.add_attribute( @@ -439,8 +439,8 @@ class IPQualityScoreParser: phishing = data_item_value if url_data_item == "risk_score": risk_score = int(data_item_value) - tag_name = f'IPQS:Risk Score="{risk_score}"' - self.add_tag(tag_name) + #tag_name = f'IPQS:Risk Score="{risk_score}"' + #self.add_tag(tag_name) self.url_risk_scoring(risk_score, malware, phishing) self.ipqs_object.add_attribute( @@ -497,8 +497,8 @@ class IPQualityScoreParser: valid = data_item_value if email_data_item == "fraud_score": fraud_score = int(data_item_value) - tag_name = f'IPQS:Fraud Score="{fraud_score}"' - self.add_tag(tag_name) + #tag_name = f'IPQS:Fraud Score="{fraud_score}"' + #self.add_tag(tag_name) self.email_address_risk_scoring(fraud_score, disposable, valid) self.ipqs_object.add_attribute( @@ -544,8 +544,8 @@ class IPQualityScoreParser: valid = data_item_value if phone_data_item == "fraud_score": fraud_score = int(data_item_value) - tag_name = f'IPQS:Fraud Score="{fraud_score}"' - self.add_tag(tag_name) + #tag_name = f'IPQS:Fraud Score="{fraud_score}"' + #self.add_tag(tag_name) self.phone_address_risk_scoring(fraud_score, valid, active) self.ipqs_object.add_attribute( @@ -632,4 +632,3 @@ def version(): configurations required of the module. """ moduleinfo['config'] = moduleconfig return moduleinfo - From 9e0849b7935a7d6c9ebf91a862fe9438080a624d Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Sat, 12 Feb 2022 10:21:36 +0530 Subject: [PATCH 07/19] added IPQS logo --- documentation/logos/ipqualityscore.png | Bin 0 -> 6769 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 documentation/logos/ipqualityscore.png diff --git a/documentation/logos/ipqualityscore.png b/documentation/logos/ipqualityscore.png new file mode 100644 index 0000000000000000000000000000000000000000..da52d96680f76ed4c92b0b6aaab0ecc0121bfa68 GIT binary patch literal 6769 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 literal 0 HcmV?d00001 From cfc70ec1768181ea1f85f5e41db916709b6ff126 Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Sat, 12 Feb 2022 10:22:19 +0530 Subject: [PATCH 08/19] added documentation --- .../expansion/ipqs_fraud_and_risk_scoring.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 documentation/website/expansion/ipqs_fraud_and_risk_scoring.json diff --git a/documentation/website/expansion/ipqs_fraud_and_risk_scoring.json b/documentation/website/expansion/ipqs_fraud_and_risk_scoring.json new file mode 100644 index 0000000..8a1c097 --- /dev/null +++ b/documentation/website/expansion/ipqs_fraud_and_risk_scoring.json @@ -0,0 +1,13 @@ +{ + "description": "IPQualityScore MISP Expansion Module.", + "logo": "ipqualityscore.png", + "requirements": [ + "A IPQualityScore API Key." + ], + "input": "A MISP attribute of type IP Address(ip-src, ip-dst), Domain(hostname, domain), URL(url, uri), Email Address(email, email-src, email-dst, target-email, whois-registrant-email) and Phone Number(phone-number, whois-registrant-phone).", + "output": "IPQualityScore object, resulting from the query on the IPQualityScore API.", + "references": [ + "https://www.ipqualityscore.com/" + ], + "features": "This Module takes the IP Address, Domain, URL, Email and Phone Number MISP Attributes as input to query the IPQualityScore API.\n The results of the IPQualityScore API are than returned as IPQS Fraud and Risk Scoring Object. \n The object contains a copy of the enriched attribute with added tags presenting the verdict based on fraud score,risk score and other attributes from IPQualityScore." +} \ No newline at end of file From 023f6653b9d378a2b6fed12f5235e2f9ceeeba37 Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Sat, 12 Feb 2022 10:36:03 +0530 Subject: [PATCH 09/19] Update ipqs_fraud_and_risk_scoring.json --- .../website/expansion/ipqs_fraud_and_risk_scoring.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/website/expansion/ipqs_fraud_and_risk_scoring.json b/documentation/website/expansion/ipqs_fraud_and_risk_scoring.json index 8a1c097..d0d4665 100644 --- a/documentation/website/expansion/ipqs_fraud_and_risk_scoring.json +++ b/documentation/website/expansion/ipqs_fraud_and_risk_scoring.json @@ -1,5 +1,5 @@ { - "description": "IPQualityScore MISP Expansion Module.", + "description": "IPQualityScore MISP Expansion Module for IP reputation, Email Validation, Phone Number Validation, Malicious Domain and Malicious URL Scanner.", "logo": "ipqualityscore.png", "requirements": [ "A IPQualityScore API Key." @@ -10,4 +10,4 @@ "https://www.ipqualityscore.com/" ], "features": "This Module takes the IP Address, Domain, URL, Email and Phone Number MISP Attributes as input to query the IPQualityScore API.\n The results of the IPQualityScore API are than returned as IPQS Fraud and Risk Scoring Object. \n The object contains a copy of the enriched attribute with added tags presenting the verdict based on fraud score,risk score and other attributes from IPQualityScore." -} \ No newline at end of file +} From 3856f9fe1d1160673b5c5f94d352e5216f0c30b6 Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Sat, 12 Feb 2022 10:38:48 +0530 Subject: [PATCH 10/19] Update ipqs_fraud_and_risk_scoring.py --- misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py b/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py index 754f94a..9cb50a3 100644 --- a/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py +++ b/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py @@ -42,8 +42,8 @@ mispattributes = { moduleinfo = { 'version': '0.1', 'author': 'David Mackler', - 'description': 'Query IPQualityScore for IP reputation, Email Validation, Phone Number Validation and Malicious ' - 'Domain/URL Scanner.', + 'description': 'Query IPQualityScore for IP reputation, Email Validation, Phone Number Validation,' + 'Malicious Domain and Malicious URL Scanner.', 'module-type': ['expansion', 'hover'] } moduleconfig = ['apikey'] From e7645a195aa18c98bbc50eca45f2873c4aa80fb1 Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Sat, 12 Feb 2022 11:01:54 +0530 Subject: [PATCH 11/19] Update test_expansions.py --- tests/test_expansions.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index b8764f7..7459e99 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -293,6 +293,29 @@ class TestExpansions(unittest.TestCase): response = self.misp_modules_post(query) self.assertEqual(self.get_object(response), 'asn') + def test_ipqs_fraud_and_risk_scoring(self): + module_name = "ipqs_fraud_and_risk_scoring" + query = {"module": module_name, + "attribute": {"type": "domain", + "value": "noreply@ipqualityscore.com", + "uuid": "ea89a33b-4ab7-4515-9f02-922a0bee333d"}, + "config": {}} + if module_name in self.configs: + query['config'] = self.configs[module_name] + response = self.misp_modules_post(query) + try: + self.assertEqual(self.get_object(response), 'IPQS:) + except Exception: + self.assertIn( + self.get_errors(response), + ( + "Invalid or unauthorized key. Please check the API key and try again." + ) + ) + else: + response = self.misp_modules_post(query) + self.assertEqual(self.get_errors(response), 'An API key for IPQualityScore is required.') + def test_macaddess_io(self): module_name = 'macaddress_io' query = {"module": module_name, "mac-address": "44:38:39:ff:ef:57"} From 8c1db02a655dcc27da3a87e3e5dcfe3d723b5c8d Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Sat, 12 Feb 2022 11:06:38 +0530 Subject: [PATCH 12/19] Update test_expansions.py --- tests/test_expansions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 7459e99..649c05a 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -304,7 +304,7 @@ class TestExpansions(unittest.TestCase): query['config'] = self.configs[module_name] response = self.misp_modules_post(query) try: - self.assertEqual(self.get_object(response), 'IPQS:) + self.assertEqual(self.get_object(response), 'IPQS:') except Exception: self.assertIn( self.get_errors(response), From 0d63c5e0a2a197221ca77d63cf17d5434f2c5ac7 Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Sat, 12 Feb 2022 11:14:57 +0530 Subject: [PATCH 13/19] Update test_expansions.py --- tests/test_expansions.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 649c05a..db5d160 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -296,22 +296,14 @@ class TestExpansions(unittest.TestCase): def test_ipqs_fraud_and_risk_scoring(self): module_name = "ipqs_fraud_and_risk_scoring" query = {"module": module_name, - "attribute": {"type": "domain", + "attribute": {"type": "email", "value": "noreply@ipqualityscore.com", "uuid": "ea89a33b-4ab7-4515-9f02-922a0bee333d"}, "config": {}} if module_name in self.configs: query['config'] = self.configs[module_name] response = self.misp_modules_post(query) - try: - self.assertEqual(self.get_object(response), 'IPQS:') - except Exception: - self.assertIn( - self.get_errors(response), - ( - "Invalid or unauthorized key. Please check the API key and try again." - ) - ) + self.assertEqual(self.get_values(response), 'noreply@ipqualityscore.com') else: response = self.misp_modules_post(query) self.assertEqual(self.get_errors(response), 'An API key for IPQualityScore is required.') From 59a6ca2fb43067afb89b1e678d522758cceff197 Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Sat, 12 Feb 2022 11:34:42 +0530 Subject: [PATCH 14/19] Update test_expansions.py --- tests/test_expansions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index db5d160..35e384d 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -303,10 +303,10 @@ class TestExpansions(unittest.TestCase): if module_name in self.configs: query['config'] = self.configs[module_name] response = self.misp_modules_post(query) - self.assertEqual(self.get_values(response), 'noreply@ipqualityscore.com') + self.assertEqual(self.get_values(response)['message'], 'Success.') else: response = self.misp_modules_post(query) - self.assertEqual(self.get_errors(response), 'An API key for IPQualityScore is required.') + self.assertEqual(self.get_errors(response), 'Invalid or unauthorized key. Please check the API key and try again.') def test_macaddess_io(self): module_name = 'macaddress_io' From f5577aac78e25df5f19b86566534b7537ff917d3 Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Sat, 12 Feb 2022 11:45:45 +0530 Subject: [PATCH 15/19] Update test_expansions.py --- tests/test_expansions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_expansions.py b/tests/test_expansions.py index 35e384d..d75756f 100644 --- a/tests/test_expansions.py +++ b/tests/test_expansions.py @@ -306,7 +306,7 @@ class TestExpansions(unittest.TestCase): self.assertEqual(self.get_values(response)['message'], 'Success.') else: response = self.misp_modules_post(query) - self.assertEqual(self.get_errors(response), 'Invalid or unauthorized key. Please check the API key and try again.') + self.assertEqual(self.get_errors(response), 'IPQualityScore apikey is missing') def test_macaddess_io(self): module_name = 'macaddress_io' From 2f1d35774d6a3a90f6a76e0b48fdf5609c37286d Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Tue, 15 Feb 2022 18:52:14 +0530 Subject: [PATCH 16/19] Update ipqs_fraud_and_risk_scoring.py --- .../expansion/ipqs_fraud_and_risk_scoring.py | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py b/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py index 9cb50a3..bb58284 100644 --- a/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py +++ b/misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py @@ -42,7 +42,7 @@ mispattributes = { moduleinfo = { 'version': '0.1', 'author': 'David Mackler', - 'description': 'Query IPQualityScore for IP reputation, Email Validation, Phone Number Validation,' + 'description': 'IPQualityScore MISP Expansion Module for IP reputation, Email Validation, Phone Number Validation,' 'Malicious Domain and Malicious URL Scanner.', 'module-type': ['expansion', 'hover'] } @@ -124,9 +124,9 @@ class IPQualityScoreParser: self.critical = "CRITICAL" self.invalid = "INVALID" self.suspicious = "SUSPICIOUS" - self.malware = "MALWARE" - self.phishing = "PHISHING" - self.disposable = "DISPOSABLE" + self.malware = "CRITICAL" + self.phishing = "CRITICAL" + self.disposable = "CRITICAL" self.attribute = attribute self.misp_event = MISPEvent() self.misp_event.add_attribute(**attribute) @@ -385,8 +385,6 @@ class IPQualityScoreParser: self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value)) if ip_data_item == "fraud_score": fraud_score = int(data_item_value) - # tag_name = f'IPQS:Fraud Score="{fraud_score}"' - # self.add_tag(tag_name) self.ip_address_risk_scoring(fraud_score) self.ipqs_object.add_attribute( @@ -439,8 +437,6 @@ class IPQualityScoreParser: phishing = data_item_value if url_data_item == "risk_score": risk_score = int(data_item_value) - #tag_name = f'IPQS:Risk Score="{risk_score}"' - #self.add_tag(tag_name) self.url_risk_scoring(risk_score, malware, phishing) self.ipqs_object.add_attribute( @@ -497,8 +493,6 @@ class IPQualityScoreParser: valid = data_item_value if email_data_item == "fraud_score": fraud_score = int(data_item_value) - #tag_name = f'IPQS:Fraud Score="{fraud_score}"' - #self.add_tag(tag_name) self.email_address_risk_scoring(fraud_score, disposable, valid) self.ipqs_object.add_attribute( @@ -510,10 +504,10 @@ class IPQualityScoreParser: def email_address_risk_scoring(self, score, disposable, valid): """method to create calculate verdict for Email Address""" risk_criticality = "" - if valid == "False": - risk_criticality = self.invalid - elif disposable == "True": + if disposable == "True": risk_criticality = self.disposable + elif valid == "False": + risk_criticality = self.invalid elif score == 100: risk_criticality = self.high elif 88 <= score <= 99: @@ -544,8 +538,7 @@ class IPQualityScoreParser: valid = data_item_value if phone_data_item == "fraud_score": fraud_score = int(data_item_value) - #tag_name = f'IPQS:Fraud Score="{fraud_score}"' - #self.add_tag(tag_name) + self.phone_address_risk_scoring(fraud_score, valid, active) self.ipqs_object.add_attribute( From 9b4b1a1c4f06d59d4a167b4838e7bb1e9d4fea76 Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Tue, 15 Feb 2022 19:01:13 +0530 Subject: [PATCH 17/19] Update __init__.py --- misp_modules/modules/expansion/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 15eb8d6..1142252 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -18,7 +18,7 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', 'malwarebazaar', 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion', 'censys_enrich', 'trustar_enrich', 'recordedfuture', 'html_to_markdown', 'socialscan', 'passive-ssh', - 'qintel_qsentry', 'mwdb', 'ipqs_fraud_and_risk_scoring'] + 'qintel_qsentry', 'mwdb', 'hashlookup', 'mmdb_lookup', 'ipqs_fraud_and_risk_scoring'] minimum_required_fields = ('type', 'uuid', 'value') From 82eee0074b2a408ef8eeefc9816422b0dd9c34ab Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Tue, 15 Feb 2022 19:11:36 +0530 Subject: [PATCH 18/19] Update __init__.py --- misp_modules/modules/expansion/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 1142252..15eb8d6 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -18,7 +18,7 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', 'malwarebazaar', 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion', 'censys_enrich', 'trustar_enrich', 'recordedfuture', 'html_to_markdown', 'socialscan', 'passive-ssh', - 'qintel_qsentry', 'mwdb', 'hashlookup', 'mmdb_lookup', 'ipqs_fraud_and_risk_scoring'] + 'qintel_qsentry', 'mwdb', 'ipqs_fraud_and_risk_scoring'] minimum_required_fields = ('type', 'uuid', 'value') From 4a19d35da028719e707641fe4de481517384dace Mon Sep 17 00:00:00 2001 From: Rambatla Venkat Rao <68921481+RamboV@users.noreply.github.com> Date: Tue, 15 Feb 2022 19:19:51 +0530 Subject: [PATCH 19/19] updated to add the latest modules --- misp_modules/modules/expansion/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misp_modules/modules/expansion/__init__.py b/misp_modules/modules/expansion/__init__.py index 15eb8d6..1142252 100644 --- a/misp_modules/modules/expansion/__init__.py +++ b/misp_modules/modules/expansion/__init__.py @@ -18,7 +18,7 @@ __all__ = ['cuckoo_submit', 'vmray_submit', 'bgpranking', 'circl_passivedns', 'c 'assemblyline_submit', 'assemblyline_query', 'ransomcoindb', 'malwarebazaar', 'lastline_query', 'lastline_submit', 'sophoslabs_intelix', 'cytomic_orion', 'censys_enrich', 'trustar_enrich', 'recordedfuture', 'html_to_markdown', 'socialscan', 'passive-ssh', - 'qintel_qsentry', 'mwdb', 'ipqs_fraud_and_risk_scoring'] + 'qintel_qsentry', 'mwdb', 'hashlookup', 'mmdb_lookup', 'ipqs_fraud_and_risk_scoring'] minimum_required_fields = ('type', 'uuid', 'value')