misp-modules 2.4.196__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. misp_modules/__init__.py +379 -0
  2. misp_modules/helpers/__init__.py +1 -0
  3. misp_modules/helpers/cache.py +84 -0
  4. misp_modules/lib/__init__.py +4 -0
  5. misp_modules/lib/_vmray/__init__.py +0 -0
  6. misp_modules/lib/_vmray/parser.py +1417 -0
  7. misp_modules/lib/_vmray/rest_api.py +148 -0
  8. misp_modules/lib/cof2misp/LICENSE-2.0.txt +202 -0
  9. misp_modules/lib/cof2misp/__init__.py +0 -0
  10. misp_modules/lib/cof2misp/cof.py +165 -0
  11. misp_modules/lib/joe_mapping.py +114 -0
  12. misp_modules/lib/joe_parser.py +573 -0
  13. misp_modules/lib/lastline_api.py +841 -0
  14. misp_modules/lib/qintel_helper.py +263 -0
  15. misp_modules/lib/stix2misp.py +2080 -0
  16. misp_modules/lib/stix2misp_mapping.py +460 -0
  17. misp_modules/lib/synonymsToTagNames.json +1 -0
  18. misp_modules/lib/vt_graph_parser/__init__.py +8 -0
  19. misp_modules/lib/vt_graph_parser/errors.py +20 -0
  20. misp_modules/lib/vt_graph_parser/helpers/__init__.py +7 -0
  21. misp_modules/lib/vt_graph_parser/helpers/parsers.py +88 -0
  22. misp_modules/lib/vt_graph_parser/helpers/rules.py +304 -0
  23. misp_modules/lib/vt_graph_parser/helpers/wrappers.py +58 -0
  24. misp_modules/lib/vt_graph_parser/importers/__init__.py +7 -0
  25. misp_modules/lib/vt_graph_parser/importers/base.py +98 -0
  26. misp_modules/lib/vt_graph_parser/importers/pymisp_response.py +73 -0
  27. misp_modules/modules/__init__.py +4 -0
  28. misp_modules/modules/action_mod/__init__.py +1 -0
  29. misp_modules/modules/action_mod/_utils/__init__.py +1 -0
  30. misp_modules/modules/action_mod/_utils/utils.py +70 -0
  31. misp_modules/modules/action_mod/mattermost.py +113 -0
  32. misp_modules/modules/action_mod/slack.py +96 -0
  33. misp_modules/modules/action_mod/testaction.py +68 -0
  34. misp_modules/modules/expansion/__init__.py +36 -0
  35. misp_modules/modules/expansion/_dnsdb_query/COPYRIGHT +27 -0
  36. misp_modules/modules/expansion/_dnsdb_query/LICENSE +202 -0
  37. misp_modules/modules/expansion/_dnsdb_query/README.md +162 -0
  38. misp_modules/modules/expansion/_dnsdb_query/__init__.py +0 -0
  39. misp_modules/modules/expansion/_dnsdb_query/dnsdb_query.py +327 -0
  40. misp_modules/modules/expansion/_ransomcoindb/__init__.py +0 -0
  41. misp_modules/modules/expansion/_ransomcoindb/ransomcoindb.py +96 -0
  42. misp_modules/modules/expansion/_vulnerability_parser/__init__.py +0 -0
  43. misp_modules/modules/expansion/_vulnerability_parser/vulnerability_parser.py +118 -0
  44. misp_modules/modules/expansion/abuseipdb.py +146 -0
  45. misp_modules/modules/expansion/apiosintds.py +365 -0
  46. misp_modules/modules/expansion/apivoid.py +133 -0
  47. misp_modules/modules/expansion/assemblyline_query.py +179 -0
  48. misp_modules/modules/expansion/assemblyline_submit.py +100 -0
  49. misp_modules/modules/expansion/backscatter_io.py +84 -0
  50. misp_modules/modules/expansion/btc_scam_check.py +54 -0
  51. misp_modules/modules/expansion/btc_steroids.py +238 -0
  52. misp_modules/modules/expansion/censys_enrich.py +287 -0
  53. misp_modules/modules/expansion/circl_passivedns.py +85 -0
  54. misp_modules/modules/expansion/circl_passivessl.py +113 -0
  55. misp_modules/modules/expansion/clamav.py +129 -0
  56. misp_modules/modules/expansion/cluster25_expand.py +239 -0
  57. misp_modules/modules/expansion/countrycode.py +69 -0
  58. misp_modules/modules/expansion/cpe.py +140 -0
  59. misp_modules/modules/expansion/crowdsec.py +235 -0
  60. misp_modules/modules/expansion/crowdstrike_falcon.py +148 -0
  61. misp_modules/modules/expansion/cuckoo_submit.py +161 -0
  62. misp_modules/modules/expansion/cve.py +57 -0
  63. misp_modules/modules/expansion/cve_advanced.py +185 -0
  64. misp_modules/modules/expansion/cytomic_orion.py +196 -0
  65. misp_modules/modules/expansion/dbl_spamhaus.py +78 -0
  66. misp_modules/modules/expansion/dns.py +71 -0
  67. misp_modules/modules/expansion/docx_enrich.py +71 -0
  68. misp_modules/modules/expansion/domaintools.py +290 -0
  69. misp_modules/modules/expansion/eql.py +91 -0
  70. misp_modules/modules/expansion/eupi.py +86 -0
  71. misp_modules/modules/expansion/extract_url_components.py +83 -0
  72. misp_modules/modules/expansion/farsight_passivedns.py +243 -0
  73. misp_modules/modules/expansion/geoip_asn.py +75 -0
  74. misp_modules/modules/expansion/geoip_city.py +75 -0
  75. misp_modules/modules/expansion/geoip_country.py +73 -0
  76. misp_modules/modules/expansion/google_safe_browsing.py +86 -0
  77. misp_modules/modules/expansion/google_search.py +57 -0
  78. misp_modules/modules/expansion/google_threat_intelligence.py +453 -0
  79. misp_modules/modules/expansion/greynoise.py +346 -0
  80. misp_modules/modules/expansion/hashdd.py +53 -0
  81. misp_modules/modules/expansion/hashlookup.py +118 -0
  82. misp_modules/modules/expansion/hibp.py +62 -0
  83. misp_modules/modules/expansion/html_to_markdown.py +63 -0
  84. misp_modules/modules/expansion/hyasinsight.py +881 -0
  85. misp_modules/modules/expansion/intel471.py +73 -0
  86. misp_modules/modules/expansion/intelmq_eventdb.py.experimental +67 -0
  87. misp_modules/modules/expansion/ip2locationio.py +90 -0
  88. misp_modules/modules/expansion/ipasn.py +70 -0
  89. misp_modules/modules/expansion/ipinfo.py +112 -0
  90. misp_modules/modules/expansion/ipqs_fraud_and_risk_scoring.py +633 -0
  91. misp_modules/modules/expansion/iprep.py +98 -0
  92. misp_modules/modules/expansion/jinja_template_rendering.py +54 -0
  93. misp_modules/modules/expansion/joesandbox_query.py +91 -0
  94. misp_modules/modules/expansion/joesandbox_submit.py +147 -0
  95. misp_modules/modules/expansion/lastline_query.py +148 -0
  96. misp_modules/modules/expansion/lastline_submit.py +180 -0
  97. misp_modules/modules/expansion/macaddress_io.py +130 -0
  98. misp_modules/modules/expansion/macvendors.py +55 -0
  99. misp_modules/modules/expansion/malshare_upload.py +107 -0
  100. misp_modules/modules/expansion/malwarebazaar.py +67 -0
  101. misp_modules/modules/expansion/mcafee_insights_enrich.py +249 -0
  102. misp_modules/modules/expansion/mmdb_lookup.py +138 -0
  103. misp_modules/modules/expansion/module.py.skeleton +44 -0
  104. misp_modules/modules/expansion/mwdb.py +152 -0
  105. misp_modules/modules/expansion/ocr_enrich.py +74 -0
  106. misp_modules/modules/expansion/ods_enrich.py +71 -0
  107. misp_modules/modules/expansion/odt_enrich.py +61 -0
  108. misp_modules/modules/expansion/onyphe.py +243 -0
  109. misp_modules/modules/expansion/onyphe_full.py +387 -0
  110. misp_modules/modules/expansion/otx.py +167 -0
  111. misp_modules/modules/expansion/passive_ssh.py +150 -0
  112. misp_modules/modules/expansion/passivetotal.py +358 -0
  113. misp_modules/modules/expansion/pdf_enrich.py +58 -0
  114. misp_modules/modules/expansion/pptx_enrich.py +65 -0
  115. misp_modules/modules/expansion/qintel_qsentry.py +229 -0
  116. misp_modules/modules/expansion/qrcode.py +99 -0
  117. misp_modules/modules/expansion/ransomcoindb.py +82 -0
  118. misp_modules/modules/expansion/rbl.py +126 -0
  119. misp_modules/modules/expansion/recordedfuture.py +544 -0
  120. misp_modules/modules/expansion/reversedns.py +77 -0
  121. misp_modules/modules/expansion/securitytrails.py +571 -0
  122. misp_modules/modules/expansion/shodan.py +244 -0
  123. misp_modules/modules/expansion/sigma_queries.py +61 -0
  124. misp_modules/modules/expansion/sigma_syntax_validator.py +49 -0
  125. misp_modules/modules/expansion/sigmf_expand.py +303 -0
  126. misp_modules/modules/expansion/socialscan.py +108 -0
  127. misp_modules/modules/expansion/sophoslabs_intelix.py +146 -0
  128. misp_modules/modules/expansion/sourcecache.py +57 -0
  129. misp_modules/modules/expansion/stairwell.py +156 -0
  130. misp_modules/modules/expansion/stix2_pattern_syntax_validator.py +56 -0
  131. misp_modules/modules/expansion/threatcrowd.py +170 -0
  132. misp_modules/modules/expansion/threatfox.py +75 -0
  133. misp_modules/modules/expansion/threatminer.py +152 -0
  134. misp_modules/modules/expansion/triage_submit.py +119 -0
  135. misp_modules/modules/expansion/trustar_enrich.py +235 -0
  136. misp_modules/modules/expansion/urlhaus.py +164 -0
  137. misp_modules/modules/expansion/urlscan.py +269 -0
  138. misp_modules/modules/expansion/variotdbs.py +133 -0
  139. misp_modules/modules/expansion/virustotal.py +311 -0
  140. misp_modules/modules/expansion/virustotal_public.py +272 -0
  141. misp_modules/modules/expansion/virustotal_upload.py +91 -0
  142. misp_modules/modules/expansion/vmray_submit.py +161 -0
  143. misp_modules/modules/expansion/vmware_nsx.py +628 -0
  144. misp_modules/modules/expansion/vulndb.py +292 -0
  145. misp_modules/modules/expansion/vulnerability_lookup.py +324 -0
  146. misp_modules/modules/expansion/vulners.py +81 -0
  147. misp_modules/modules/expansion/vysion.py +221 -0
  148. misp_modules/modules/expansion/whois.py +72 -0
  149. misp_modules/modules/expansion/whoisfreaks.py +232 -0
  150. misp_modules/modules/expansion/wiki.py +58 -0
  151. misp_modules/modules/expansion/xforceexchange.py +189 -0
  152. misp_modules/modules/expansion/xlsx_enrich.py +63 -0
  153. misp_modules/modules/expansion/yara_query.py +70 -0
  154. misp_modules/modules/expansion/yara_syntax_validator.py +49 -0
  155. misp_modules/modules/expansion/yeti.py +196 -0
  156. misp_modules/modules/export_mod/__init__.py +3 -0
  157. misp_modules/modules/export_mod/cef_export.py +93 -0
  158. misp_modules/modules/export_mod/cisco_firesight_manager_ACL_rule_export.py +150 -0
  159. misp_modules/modules/export_mod/defender_endpoint_export.py +141 -0
  160. misp_modules/modules/export_mod/goamlexport.py +255 -0
  161. misp_modules/modules/export_mod/liteexport.py +97 -0
  162. misp_modules/modules/export_mod/mass_eql_export.py +106 -0
  163. misp_modules/modules/export_mod/nexthinkexport.py +131 -0
  164. misp_modules/modules/export_mod/osqueryexport.py +125 -0
  165. misp_modules/modules/export_mod/pdfexport.py +107 -0
  166. misp_modules/modules/export_mod/testexport.py +73 -0
  167. misp_modules/modules/export_mod/threatStream_misp_export.py +114 -0
  168. misp_modules/modules/export_mod/threat_connect_export.py +125 -0
  169. misp_modules/modules/export_mod/virustotal_collections.py +141 -0
  170. misp_modules/modules/export_mod/vt_graph.py +120 -0
  171. misp_modules/modules/export_mod/yara_export.py +290 -0
  172. misp_modules/modules/import_mod/__init__.py +22 -0
  173. misp_modules/modules/import_mod/cof2misp.py +264 -0
  174. misp_modules/modules/import_mod/csvimport.py +319 -0
  175. misp_modules/modules/import_mod/cuckooimport.py +749 -0
  176. misp_modules/modules/import_mod/email_import.py +294 -0
  177. misp_modules/modules/import_mod/goamlimport.py +188 -0
  178. misp_modules/modules/import_mod/import_blueprint.py +93 -0
  179. misp_modules/modules/import_mod/joe_import.py +74 -0
  180. misp_modules/modules/import_mod/lastline_import.py +160 -0
  181. misp_modules/modules/import_mod/mispjson.py +73 -0
  182. misp_modules/modules/import_mod/ocr.py +122 -0
  183. misp_modules/modules/import_mod/openiocimport.py +99 -0
  184. misp_modules/modules/import_mod/taxii21.py +383 -0
  185. misp_modules/modules/import_mod/testimport.py +73 -0
  186. misp_modules/modules/import_mod/threatanalyzer_import.py +552 -0
  187. misp_modules/modules/import_mod/url_import.py +97 -0
  188. misp_modules/modules/import_mod/vmray_import.py +96 -0
  189. misp_modules/modules/import_mod/vmray_summary_json_import.py +87 -0
  190. misp_modules-2.4.196.dist-info/LICENSE +661 -0
  191. misp_modules-2.4.196.dist-info/METADATA +281 -0
  192. misp_modules-2.4.196.dist-info/RECORD +194 -0
  193. misp_modules-2.4.196.dist-info/WHEEL +4 -0
  194. misp_modules-2.4.196.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,633 @@
1
+ import json
2
+ import logging
3
+ import requests
4
+ from requests.exceptions import (
5
+ HTTPError,
6
+ ProxyError,
7
+ InvalidURL,
8
+ ConnectTimeout
9
+ )
10
+ from . import check_input_attribute, standard_error_message
11
+ from pymisp import MISPEvent, MISPAttribute, MISPObject, MISPTag, Distribution
12
+
13
+ ip_query_input_type = [
14
+ 'ip-src',
15
+ 'ip-dst'
16
+ ]
17
+ url_query_input_type = [
18
+ 'hostname',
19
+ 'domain',
20
+ 'url',
21
+ 'uri'
22
+ ]
23
+ email_query_input_type = [
24
+ 'email',
25
+ 'email-src',
26
+ 'email-dst',
27
+ 'target-email',
28
+ 'whois-registrant-email'
29
+ ]
30
+ phone_query_input_type = [
31
+ 'phone-number',
32
+ 'whois-registrant-phone'
33
+ ]
34
+
35
+ misperrors = {
36
+ 'error': 'Error'
37
+ }
38
+ mispattributes = {
39
+ 'input': ip_query_input_type + url_query_input_type + email_query_input_type + phone_query_input_type,
40
+ 'format': 'misp_standard'
41
+ }
42
+ moduleinfo = {
43
+ 'version': '0.1',
44
+ 'author': 'David Mackler',
45
+ 'description': 'IPQualityScore MISP Expansion Module for IP reputation, Email Validation, Phone Number Validation, Malicious Domain and Malicious URL Scanner.',
46
+ 'module-type': ['expansion', 'hover'],
47
+ 'name': 'IPQualityScore Lookup',
48
+ 'logo': 'ipqualityscore.png',
49
+ 'requirements': ['A IPQualityScore API Key.'],
50
+ '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.',
51
+ 'references': ['https://www.ipqualityscore.com/'],
52
+ '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).',
53
+ 'output': 'IPQualityScore object, resulting from the query on the IPQualityScore API.',
54
+ }
55
+ moduleconfig = ['apikey']
56
+
57
+ logger = logging.getLogger('ipqualityscore')
58
+ logger.setLevel(logging.DEBUG)
59
+ BASE_URL = 'https://ipqualityscore.com/api/json'
60
+ DEFAULT_DISTRIBUTION_SETTING = Distribution.your_organisation_only.value
61
+ IP_ENRICH = 'ip'
62
+ URL_ENRICH = 'url'
63
+ EMAIL_ENRICH = 'email'
64
+ PHONE_ENRICH = 'phone'
65
+
66
+
67
+ class RequestHandler:
68
+ """A class for handling any outbound requests from this module."""
69
+
70
+ def __init__(self, apikey):
71
+ self.session = requests.Session()
72
+ self.api_key = apikey
73
+
74
+ def get(self, url: str, headers: dict = None, params: dict = None) -> requests.Response:
75
+ """General get method to fetch the response from IPQualityScore."""
76
+ try:
77
+ response = self.session.get(
78
+ url, headers=headers, params=params
79
+ ).json()
80
+ if str(response["success"]) != "True":
81
+ msg = response["message"]
82
+ logger.error(f"Error: {msg}")
83
+ misperrors["error"] = msg
84
+ else:
85
+ return response
86
+ except (ConnectTimeout, ProxyError, InvalidURL) as error:
87
+ msg = "Error connecting with the IPQualityScore."
88
+ logger.error(f"{msg} Error: {error}")
89
+ misperrors["error"] = msg
90
+
91
+ def ipqs_lookup(self, reputation_type: str, ioc: str) -> requests.Response:
92
+ """Do a lookup call."""
93
+ url = f"{BASE_URL}/{reputation_type}"
94
+ payload = {reputation_type: ioc}
95
+ headers = {"IPQS-KEY": self.api_key}
96
+ try:
97
+ response = self.get(url, headers, payload)
98
+ except HTTPError as error:
99
+ msg = f"Error when requesting data from IPQualityScore. {error.response}: {error.response.reason}"
100
+ logger.error(msg)
101
+ misperrors["error"] = msg
102
+ raise
103
+ return response
104
+
105
+
106
+ def parse_attribute(comment, feature, value):
107
+ """Generic Method for parsing the attributes in the object"""
108
+ attribute = {
109
+ 'type': 'text',
110
+ 'value': value,
111
+ 'comment': comment,
112
+ 'distribution': DEFAULT_DISTRIBUTION_SETTING,
113
+ 'object_relation': feature
114
+ }
115
+ return attribute
116
+
117
+
118
+ class IPQualityScoreParser:
119
+ """A class for handling the enrichment objects"""
120
+
121
+ def __init__(self, attribute):
122
+ self.rf_white = "#CCCCCC"
123
+ self.rf_grey = " #CDCDCD"
124
+ self.rf_yellow = "#FFCF00"
125
+ self.rf_red = "#D10028"
126
+ self.clean = "CLEAN"
127
+ self.low = "LOW RISK"
128
+ self.medium = "MODERATE RISK"
129
+ self.high = "HIGH RISK"
130
+ self.critical = "CRITICAL"
131
+ self.invalid = "INVALID"
132
+ self.suspicious = "SUSPICIOUS"
133
+ self.malware = "CRITICAL"
134
+ self.phishing = "CRITICAL"
135
+ self.disposable = "CRITICAL"
136
+ self.attribute = attribute
137
+ self.misp_event = MISPEvent()
138
+ self.misp_event.add_attribute(**attribute)
139
+ self.ipqs_object = MISPObject('IPQS Fraud and Risk Scoring Object')
140
+ self.ipqs_object.template_uuid = "57d066e6-6d66-42a7-a1ad-e075e39b2b5e"
141
+ self.ipqs_object.template_id = "1"
142
+ self.ipqs_object.description = "IPQS Fraud and Risk Scoring Data"
143
+ setattr(self.ipqs_object, 'meta-category', 'network')
144
+ description = (
145
+ "An object containing the enriched attribute and "
146
+ "related entities from IPQualityScore."
147
+ )
148
+ self.ipqs_object.from_dict(
149
+ **{"meta-category": "misc", "description": description, "distribution": DEFAULT_DISTRIBUTION_SETTING}
150
+ )
151
+
152
+ temp_attr = MISPAttribute()
153
+ temp_attr.from_dict(**attribute)
154
+ self.enriched_attribute = MISPAttribute()
155
+ self.enriched_attribute.from_dict(
156
+ **{"value": temp_attr.value, "type": temp_attr.type, "distribution": DEFAULT_DISTRIBUTION_SETTING}
157
+ )
158
+ self.ipqs_object.distribution = DEFAULT_DISTRIBUTION_SETTING
159
+ self.ip_data_items = [
160
+ 'fraud_score',
161
+ 'country_code',
162
+ 'region',
163
+ 'city',
164
+ 'zip_code',
165
+ 'ISP',
166
+ 'ASN',
167
+ 'organization',
168
+ 'is_crawler',
169
+ 'timezone',
170
+ 'mobile',
171
+ 'host',
172
+ 'proxy',
173
+ 'vpn',
174
+ 'tor',
175
+ 'active_vpn',
176
+ 'active_tor',
177
+ 'recent_abuse',
178
+ 'bot_status',
179
+ 'connection_type',
180
+ 'abuse_velocity',
181
+ 'latitude',
182
+ 'longitude'
183
+ ]
184
+ self.ip_data_items_friendly_names = {
185
+ 'fraud_score': 'IPQS: Fraud Score',
186
+ 'country_code': 'IPQS: Country Code',
187
+ 'region': 'IPQS: Region',
188
+ 'city': 'IPQS: City',
189
+ 'zip_code': 'IPQS: Zip Code',
190
+ 'ISP': 'IPQS: ISP',
191
+ 'ASN': 'IPQS: ASN',
192
+ 'organization': 'IPQS: Organization',
193
+ 'is_crawler': 'IPQS: Is Crawler',
194
+ 'timezone': 'IPQS: Timezone',
195
+ 'mobile': 'IPQS: Mobile',
196
+ 'host': 'IPQS: Host',
197
+ 'proxy': 'IPQS: Proxy',
198
+ 'vpn': 'IPQS: VPN',
199
+ 'tor': 'IPQS: TOR',
200
+ 'active_vpn': 'IPQS: Active VPN',
201
+ 'active_tor': 'IPQS: Active TOR',
202
+ 'recent_abuse': 'IPQS: Recent Abuse',
203
+ 'bot_status': 'IPQS: Bot Status',
204
+ 'connection_type': 'IPQS: Connection Type',
205
+ 'abuse_velocity': 'IPQS: Abuse Velocity',
206
+ 'latitude': 'IPQS: Latitude',
207
+ 'longitude': 'IPQS: Longitude'
208
+ }
209
+ self.url_data_items = [
210
+ 'unsafe',
211
+ 'domain',
212
+ 'ip_address',
213
+ 'server',
214
+ 'domain_rank',
215
+ 'dns_valid',
216
+ 'parking',
217
+ 'spamming',
218
+ 'malware',
219
+ 'phishing',
220
+ 'suspicious',
221
+ 'adult',
222
+ 'risk_score',
223
+ 'category',
224
+ 'domain_age'
225
+ ]
226
+ self.url_data_items_friendly_names = {
227
+ 'unsafe': 'IPQS: Unsafe',
228
+ 'domain': 'IPQS: Domain',
229
+ 'ip_address': 'IPQS: IP Address',
230
+ 'server': 'IPQS: Server',
231
+ 'domain_rank': 'IPQS: Domain Rank',
232
+ 'dns_valid': 'IPQS: DNS Valid',
233
+ 'parking': 'IPQS: Parking',
234
+ 'spamming': 'IPQS: Spamming',
235
+ 'malware': 'IPQS: Malware',
236
+ 'phishing': 'IPQS: Phishing',
237
+ 'suspicious': 'IPQS: Suspicious',
238
+ 'adult': 'IPQS: Adult',
239
+ 'risk_score': 'IPQS: Risk Score',
240
+ 'category': 'IPQS: Category',
241
+ 'domain_age': 'IPQS: Domain Age'
242
+ }
243
+ self.email_data_items = [
244
+ 'valid',
245
+ 'disposable',
246
+ 'smtp_score',
247
+ 'overall_score',
248
+ 'first_name',
249
+ 'generic',
250
+ 'common',
251
+ 'dns_valid',
252
+ 'honeypot',
253
+ 'deliverability',
254
+ 'frequent_complainer',
255
+ 'spam_trap_score',
256
+ 'catch_all',
257
+ 'timed_out',
258
+ 'suspect',
259
+ 'recent_abuse',
260
+ 'fraud_score',
261
+ 'suggested_domain',
262
+ 'leaked',
263
+ 'sanitized_email',
264
+ 'domain_age',
265
+ 'first_seen'
266
+ ]
267
+ self.email_data_items_friendly_names = {
268
+ 'valid': 'IPQS: Valid',
269
+ 'disposable': 'IPQS: Disposable',
270
+ 'smtp_score': 'IPQS: SMTP Score',
271
+ 'overall_score': 'IPQS: Overall Score',
272
+ 'first_name': 'IPQS: First Name',
273
+ 'generic': 'IPQS: Generic',
274
+ 'common': 'IPQS: Common',
275
+ 'dns_valid': 'IPQS: DNS Valid',
276
+ 'honeypot': 'IPQS: Honeypot',
277
+ 'deliverability': 'IPQS: Deliverability',
278
+ 'frequent_complainer': 'IPQS: Frequent Complainer',
279
+ 'spam_trap_score': 'IPQS: Spam Trap Score',
280
+ 'catch_all': 'IPQS: Catch All',
281
+ 'timed_out': 'IPQS: Timed Out',
282
+ 'suspect': 'IPQS: Suspect',
283
+ 'recent_abuse': 'IPQS: Recent Abuse',
284
+ 'fraud_score': 'IPQS: Fraud Score',
285
+ 'suggested_domain': 'IPQS: Suggested Domain',
286
+ 'leaked': 'IPQS: Leaked',
287
+ 'sanitized_email': 'IPQS: Sanitized Email',
288
+ 'domain_age': 'IPQS: Domain Age',
289
+ 'first_seen': 'IPQS: First Seen'
290
+ }
291
+ self.phone_data_items = [
292
+ 'formatted',
293
+ 'local_format',
294
+ 'valid',
295
+ 'fraud_score',
296
+ 'recent_abuse',
297
+ 'VOIP',
298
+ 'prepaid',
299
+ 'risky',
300
+ 'active',
301
+ 'carrier',
302
+ 'line_type',
303
+ 'country',
304
+ 'city',
305
+ 'zip_code',
306
+ 'region',
307
+ 'dialing_code',
308
+ 'active_status',
309
+ 'leaked',
310
+ 'name',
311
+ 'timezone',
312
+ 'do_not_call',
313
+ ]
314
+ self.phone_data_items_friendly_names = {
315
+ 'formatted': 'IPQS: Formatted',
316
+ 'local_format': 'IPQS: Local Format',
317
+ 'valid': 'IPQS: Valid',
318
+ 'fraud_score': 'IPQS: Fraud Score',
319
+ 'recent_abuse': 'IPQS: Recent Abuse',
320
+ 'VOIP': 'IPQS: VOIP',
321
+ 'prepaid': 'IPQS: Prepaid',
322
+ 'risky': 'IPQS: Risky',
323
+ 'active': 'IPQS: Active',
324
+ 'carrier': 'IPQS: Carrier',
325
+ 'line_type': 'IPQS: Line Type',
326
+ 'country': 'IPQS: Country',
327
+ 'city': 'IPQS: City',
328
+ 'zip_code': 'IPQS: Zip Code',
329
+ 'region': 'IPQS: Region',
330
+ 'dialing_code': 'IPQS: Dialing Code',
331
+ 'active_status': 'IPQS: Active Status',
332
+ 'leaked': 'IPQS: Leaked',
333
+ 'name': 'IPQS: Name',
334
+ 'timezone': 'IPQS: Timezone',
335
+ 'do_not_call': 'IPQS: Do Not Call',
336
+ }
337
+ self.timestamp_items_friendly_name = {
338
+ 'human': ' Human',
339
+ 'timestamp': ' Timestamp',
340
+ 'iso': ' ISO'
341
+ }
342
+ self.timestamp_items = [
343
+ 'human',
344
+ 'timestamp',
345
+ 'iso'
346
+ ]
347
+
348
+ def criticality_color(self, criticality) -> str:
349
+ """method which maps the color to the criticality level"""
350
+ mapper = {
351
+ self.clean: self.rf_grey,
352
+ self.low: self.rf_grey,
353
+ self.medium: self.rf_yellow,
354
+ self.suspicious: self.rf_yellow,
355
+ self.high: self.rf_red,
356
+ self.critical: self.rf_red,
357
+ self.invalid: self.rf_red,
358
+ self.disposable: self.rf_red,
359
+ self.malware: self.rf_red,
360
+ self.phishing: self.rf_red
361
+ }
362
+ return mapper.get(criticality, self.rf_white)
363
+
364
+ def add_tag(self, tag_name: str, hex_color: str = None) -> None:
365
+ """Helper method for adding a tag to the enriched attribute."""
366
+ tag = MISPTag()
367
+ tag_properties = {"name": tag_name}
368
+ if hex_color:
369
+ tag_properties["colour"] = hex_color
370
+ tag.from_dict(**tag_properties)
371
+ self.enriched_attribute.add_tag(tag)
372
+
373
+ def ipqs_parser(self, query_response, enrich_type):
374
+ """ helper method to call the enrichment function according to the type"""
375
+ if enrich_type == IP_ENRICH:
376
+ self.ip_reputation_data(query_response)
377
+ elif enrich_type == URL_ENRICH:
378
+ self.url_reputation_data(query_response)
379
+ elif enrich_type == EMAIL_ENRICH:
380
+ self.email_reputation_data(query_response)
381
+ elif enrich_type == PHONE_ENRICH:
382
+ self.phone_reputation_data(query_response)
383
+
384
+ def ip_reputation_data(self, query_response):
385
+ """method to create object for IP address"""
386
+ comment = "Results from IPQualityScore IP Reputation API"
387
+ for ip_data_item in self.ip_data_items:
388
+ if ip_data_item in query_response:
389
+ data_item = self.ip_data_items_friendly_names[ip_data_item]
390
+ data_item_value = str(query_response[ip_data_item])
391
+ self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value))
392
+ if ip_data_item == "fraud_score":
393
+ fraud_score = int(data_item_value)
394
+ self.ip_address_risk_scoring(fraud_score)
395
+
396
+ self.ipqs_object.add_attribute(
397
+ "Enriched attribute", **self.enriched_attribute
398
+ )
399
+ self.ipqs_object.add_reference(self.attribute['uuid'], 'related-to')
400
+ self.misp_event.add_object(self.ipqs_object)
401
+
402
+ def ip_address_risk_scoring(self, score):
403
+ """method to create calculate verdict for IP Address"""
404
+ risk_criticality = ""
405
+ if score == 100:
406
+ risk_criticality = self.critical
407
+ elif 85 <= score <= 99:
408
+ risk_criticality = self.high
409
+ elif 75 <= score <= 84:
410
+ risk_criticality = self.medium
411
+ elif 60 <= score <= 74:
412
+ risk_criticality = self.suspicious
413
+ elif score <= 59:
414
+ risk_criticality = self.clean
415
+
416
+ hex_color = self.criticality_color(risk_criticality)
417
+ tag_name = f'IPQS:VERDICT="{risk_criticality}"'
418
+ self.add_tag(tag_name, hex_color)
419
+
420
+ def url_reputation_data(self, query_response):
421
+ """method to create object for URL/Domain"""
422
+ malware = False
423
+ phishing = False
424
+ risk_score = 0
425
+ comment = "Results from IPQualityScore Malicious URL Scanner API"
426
+ for url_data_item in self.url_data_items:
427
+ if url_data_item in query_response:
428
+ data_item_value = ""
429
+ if url_data_item == "domain_age":
430
+ for timestamp_item in self.timestamp_items:
431
+ data_item = self.url_data_items_friendly_names[url_data_item] + \
432
+ self.timestamp_items_friendly_name[timestamp_item]
433
+ data_item_value = str(query_response[url_data_item][timestamp_item])
434
+ self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value))
435
+ else:
436
+ data_item = self.url_data_items_friendly_names[url_data_item]
437
+ data_item_value = str(query_response[url_data_item])
438
+ self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value))
439
+
440
+ if url_data_item == "malware":
441
+ malware = data_item_value
442
+ if url_data_item == "phishing":
443
+ phishing = data_item_value
444
+ if url_data_item == "risk_score":
445
+ risk_score = int(data_item_value)
446
+
447
+ self.url_risk_scoring(risk_score, malware, phishing)
448
+ self.ipqs_object.add_attribute(
449
+ "Enriched attribute", **self.enriched_attribute
450
+ )
451
+ self.ipqs_object.add_reference(self.attribute['uuid'], 'related-to')
452
+ self.misp_event.add_object(self.ipqs_object)
453
+
454
+ def url_risk_scoring(self, score, malware, phishing):
455
+ """method to create calculate verdict for URL/Domain"""
456
+ risk_criticality = ""
457
+ if malware == 'True':
458
+ risk_criticality = self.malware
459
+ elif phishing == 'True':
460
+ risk_criticality = self.phishing
461
+ elif score >= 90:
462
+ risk_criticality = self.high
463
+ elif 80 <= score <= 89:
464
+ risk_criticality = self.medium
465
+ elif 70 <= score <= 79:
466
+ risk_criticality = self.low
467
+ elif 55 <= score <= 69:
468
+ risk_criticality = self.suspicious
469
+ elif score <= 54:
470
+ risk_criticality = self.clean
471
+
472
+ hex_color = self.criticality_color(risk_criticality)
473
+ tag_name = f'IPQS:VERDICT="{risk_criticality}"'
474
+ self.add_tag(tag_name, hex_color)
475
+
476
+ def email_reputation_data(self, query_response):
477
+ """method to create object for Email Address"""
478
+ comment = "Results from IPQualityScore Email Verification API"
479
+ disposable = False
480
+ valid = False
481
+ fraud_score = 0
482
+ for email_data_item in self.email_data_items:
483
+ if email_data_item in query_response:
484
+ data_item_value = ""
485
+ if email_data_item not in ("domain_age", "first_seen"):
486
+ data_item = self.email_data_items_friendly_names[email_data_item]
487
+ data_item_value = str(query_response[email_data_item])
488
+ self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value))
489
+ else:
490
+ for timestamp_item in self.timestamp_items:
491
+ data_item = self.email_data_items_friendly_names[email_data_item] + \
492
+ self.timestamp_items_friendly_name[timestamp_item]
493
+ data_item_value = str(query_response[email_data_item][timestamp_item])
494
+ self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value))
495
+
496
+ if email_data_item == "disposable":
497
+ disposable = data_item_value
498
+ if email_data_item == "valid":
499
+ valid = data_item_value
500
+ if email_data_item == "fraud_score":
501
+ fraud_score = int(data_item_value)
502
+
503
+ self.email_address_risk_scoring(fraud_score, disposable, valid)
504
+ self.ipqs_object.add_attribute(
505
+ "Enriched attribute", **self.enriched_attribute
506
+ )
507
+ self.ipqs_object.add_reference(self.attribute['uuid'], 'related-to')
508
+ self.misp_event.add_object(self.ipqs_object)
509
+
510
+ def email_address_risk_scoring(self, score, disposable, valid):
511
+ """method to create calculate verdict for Email Address"""
512
+ risk_criticality = ""
513
+ if disposable == "True":
514
+ risk_criticality = self.disposable
515
+ elif valid == "False":
516
+ risk_criticality = self.invalid
517
+ elif score == 100:
518
+ risk_criticality = self.high
519
+ elif 88 <= score <= 99:
520
+ risk_criticality = self.medium
521
+ elif 80 <= score <= 87:
522
+ risk_criticality = self.low
523
+ elif score <= 79:
524
+ risk_criticality = self.clean
525
+ hex_color = self.criticality_color(risk_criticality)
526
+ tag_name = f'IPQS:VERDICT="{risk_criticality}"'
527
+
528
+ self.add_tag(tag_name, hex_color)
529
+
530
+ def phone_reputation_data(self, query_response):
531
+ """method to create object for Phone Number"""
532
+ fraud_score = 0
533
+ valid = False
534
+ active = False
535
+ comment = "Results from IPQualityScore Phone Number Validation API"
536
+ for phone_data_item in self.phone_data_items:
537
+ if phone_data_item in query_response:
538
+ data_item = self.phone_data_items_friendly_names[phone_data_item]
539
+ data_item_value = str(query_response[phone_data_item])
540
+ self.ipqs_object.add_attribute(**parse_attribute(comment, data_item, data_item_value))
541
+ if phone_data_item == "active":
542
+ active = data_item_value
543
+ if phone_data_item == "valid":
544
+ valid = data_item_value
545
+ if phone_data_item == "fraud_score":
546
+ fraud_score = int(data_item_value)
547
+
548
+
549
+ self.phone_address_risk_scoring(fraud_score, valid, active)
550
+ self.ipqs_object.add_attribute(
551
+ "Enriched attribute", **self.enriched_attribute
552
+ )
553
+ self.ipqs_object.add_reference(self.attribute['uuid'], 'related-to')
554
+ self.misp_event.add_object(self.ipqs_object)
555
+
556
+ def phone_address_risk_scoring(self, score, valid, active):
557
+ """method to create calculate verdict for Phone Number"""
558
+ risk_criticality = ""
559
+ if valid == "False":
560
+ risk_criticality = self.medium
561
+ elif active == "False":
562
+ risk_criticality = self.medium
563
+ elif 90 <= score <= 100:
564
+ risk_criticality = self.high
565
+ elif 80 <= score <= 89:
566
+ risk_criticality = self.low
567
+ elif 50 <= score <= 79:
568
+ risk_criticality = self.suspicious
569
+ elif score <= 49:
570
+ risk_criticality = self.clean
571
+ hex_color = self.criticality_color(risk_criticality)
572
+ tag_name = f'IPQS:VERDICT="{risk_criticality}"'
573
+ self.add_tag(tag_name, hex_color)
574
+
575
+ def get_results(self):
576
+ """returns the dictionary object to MISP Instance"""
577
+ event = json.loads(self.misp_event.to_json())
578
+ results = {key: event[key] for key in ('Attribute', 'Object')}
579
+ return {'results': results}
580
+
581
+
582
+ def handler(q=False):
583
+ """The function which accepts a JSON document to expand the values and return a dictionary of the expanded
584
+ values. """
585
+ if q is False:
586
+ return False
587
+ request = json.loads(q)
588
+ # check if the apikey is provided
589
+ if not request.get('config') or not request['config'].get('apikey'):
590
+ misperrors['error'] = 'IPQualityScore apikey is missing'
591
+ return misperrors
592
+ apikey = request['config'].get('apikey')
593
+ # check attribute is added to the event
594
+ if not request.get('attribute') or not check_input_attribute(request['attribute']):
595
+ return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'}
596
+
597
+ attribute = request['attribute']
598
+ attribute_type = attribute['type']
599
+ attribute_value = attribute['value']
600
+
601
+ # check if the attribute type is supported by IPQualityScore
602
+ if attribute_type not in mispattributes['input']:
603
+ return {'error': 'Unsupported attributes type for IPqualityScore Enrichment'}
604
+ request_handler = RequestHandler(apikey)
605
+ enrich_type = ""
606
+ if attribute_type in ip_query_input_type:
607
+ enrich_type = IP_ENRICH
608
+ json_response = request_handler.ipqs_lookup(IP_ENRICH, attribute_value)
609
+ elif attribute_type in url_query_input_type:
610
+ enrich_type = URL_ENRICH
611
+ json_response = request_handler.ipqs_lookup(URL_ENRICH, attribute_value)
612
+ elif attribute_type in email_query_input_type:
613
+ enrich_type = EMAIL_ENRICH
614
+ json_response = request_handler.ipqs_lookup(EMAIL_ENRICH, attribute_value)
615
+ elif attribute_type in phone_query_input_type:
616
+ enrich_type = PHONE_ENRICH
617
+ json_response = request_handler.ipqs_lookup(PHONE_ENRICH, attribute_value)
618
+
619
+ parser = IPQualityScoreParser(attribute)
620
+ parser.ipqs_parser(json_response, enrich_type)
621
+ return parser.get_results()
622
+
623
+
624
+ def introspection():
625
+ """The function that returns a dict of the supported attributes (input and output) by your expansion module."""
626
+ return mispattributes
627
+
628
+
629
+ def version():
630
+ """The function that returns a dict with the version and the associated meta-data including potential
631
+ configurations required of the module. """
632
+ moduleinfo['config'] = moduleconfig
633
+ return moduleinfo