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,453 @@
1
+ #!/usr/local/bin/python
2
+ # Copyright © 2024 The Google Threat Intelligence authors. All Rights Reserved.
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Google Threat Intelligence MISP expansion module."""
16
+
17
+ from urllib import parse
18
+ import vt
19
+ import pymisp
20
+
21
+
22
+ mispattributes = {
23
+ 'input': [
24
+ 'hostname',
25
+ 'domain',
26
+ 'ip-src',
27
+ 'ip-dst',
28
+ 'md5',
29
+ 'sha1',
30
+ 'sha256',
31
+ 'url',
32
+ ],
33
+ 'format': 'misp_standard',
34
+ }
35
+
36
+ moduleinfo = {
37
+ 'version': '2',
38
+ 'author': 'Google Threat Intelligence team',
39
+ 'description': "An expansion module to have the observable's threat score assessed by Google Threat Intelligence.",
40
+ 'module-type': ['expansion'],
41
+ 'name': 'Google Threat Intelligence Lookup',
42
+ 'config': ['apikey', 'event_limit', 'proxy_host', 'proxy_port', 'proxy_username', 'proxy_password'],
43
+ 'logo': 'google_threat_intelligence.png',
44
+ 'requirements': ['An access to the Google Threat Intelligence API (apikey), with a high request rate limit.'],
45
+ 'features': 'GTI assessment for the given observable, this include information about level of severity, a clear verdict (malicious, suspicious, undetected and benign) and additional information provided by the Mandiant expertise combined with the VirusTotal database.\n\n[Output example screeshot](https://github.com/MISP/MISP/assets/4747608/e275db2f-bb1e-4413-8cc0-ec3cb05e0414)',
46
+ 'references': ['https://www.virustotal.com/', 'https://gtidocs.virustotal.com/reference'],
47
+ 'input': 'A domain, hash (md5, sha1, sha256 or sha512), hostname or IP address attribute.',
48
+ 'output': 'Text fields containing the threat score, the severity, the verdict and the threat label of the observable inspected.',
49
+ }
50
+
51
+ DEFAULT_RESULTS_LIMIT = 10
52
+
53
+
54
+ class GoogleThreatIntelligenceParser:
55
+ """Main parser class to create the MISP event."""
56
+ def __init__(self, client: vt.Client, limit: int) -> None:
57
+ self.client = client
58
+ self.limit = limit or DEFAULT_RESULTS_LIMIT
59
+ self.misp_event = pymisp.MISPEvent()
60
+ self.attribute = pymisp.MISPAttribute()
61
+ self.parsed_objects = {}
62
+ self.input_types_mapping = {
63
+ 'ip-src': self.parse_ip,
64
+ 'ip-dst': self.parse_ip,
65
+ 'domain': self.parse_domain,
66
+ 'hostname': self.parse_domain,
67
+ 'md5': self.parse_hash,
68
+ 'sha1': self.parse_hash,
69
+ 'sha256': self.parse_hash,
70
+ 'url': self.parse_url,
71
+ 'ip-src|port': self.parse_ip_port,
72
+ 'ip-dst|port': self.parse_ip_port,
73
+ }
74
+ self.proxies = None
75
+
76
+ @staticmethod
77
+ def get_total_analysis(analysis: dict,
78
+ known_distributors: dict = None) -> int:
79
+ """Get total """
80
+ if not analysis:
81
+ return 0
82
+ count = sum([analysis['undetected'],
83
+ analysis['suspicious'],
84
+ analysis['harmless']])
85
+ return count if known_distributors else count + analysis['malicious']
86
+
87
+ def query_api(self, attribute: dict) -> None:
88
+ """Get data from the API and parse it."""
89
+ self.attribute.from_dict(**attribute)
90
+ self.input_types_mapping[self.attribute.type](self.attribute.value)
91
+
92
+ def get_results(self) -> dict:
93
+ """Serialize the MISP event."""
94
+ event = self.misp_event.to_dict()
95
+ results = {
96
+ key: event[key] for key in ('Attribute', 'Object') \
97
+ if (key in event and event[key])
98
+ }
99
+ return {'results': results}
100
+
101
+
102
+ def add_gti_report(self, report: vt.Object) -> str:
103
+ analysis = report.get('last_analysis_stats')
104
+ total = self.get_total_analysis(analysis,
105
+ report.get('known_distributors'))
106
+ if report.type == 'ip_address':
107
+ rtype = 'ip-address'
108
+ else:
109
+ rtype = report.type
110
+ permalink = f'https://www.virustotal.com/gui/{rtype}/{report.id}'
111
+
112
+ gti_object = pymisp.MISPObject('google-threat-intelligence-report')
113
+ gti_object.add_attribute('permalink', type='link', value=permalink)
114
+ ratio = f"{analysis['malicious']}/{total}" if analysis else '-/-'
115
+ gti_object.add_attribute('detection-ratio',
116
+ type='text',
117
+ value=ratio,
118
+ disable_correlation=True)
119
+ report_dict = report.to_dict()
120
+ gti_object.add_attribute(
121
+ 'threat-score', type='text',
122
+ value=get_key(report_dict,
123
+ 'attributes.gti_assessment.threat_score.value'))
124
+ gti_object.add_attribute(
125
+ 'verdict', type='text',
126
+ value=get_key(report_dict,
127
+ 'attributes.gti_assessment.verdict.value').replace(
128
+ 'VERDICT_', ''))
129
+ gti_object.add_attribute(
130
+ 'severity', type='text',
131
+ value=get_key(report_dict,
132
+ 'attributes.gti_assessment.severity.value').replace(
133
+ 'SEVERITY_', ''))
134
+ self.misp_event.add_object(**gti_object)
135
+ return gti_object.uuid
136
+
137
+ def create_misp_object(self, report: vt.Object) -> pymisp.MISPObject:
138
+ misp_object = None
139
+ gti_uuid = self.add_gti_report(report)
140
+
141
+ if report.type == 'file':
142
+ misp_object = pymisp.MISPObject('file')
143
+ for hash_type in ('md5', 'sha1', 'sha256', 'tlsh',
144
+ 'vhash', 'ssdeep', 'imphash'):
145
+ misp_object.add_attribute(hash_type,
146
+ **{'type': hash_type,
147
+ 'value': report.get(hash_type)})
148
+ elif report.type == 'domain':
149
+ misp_object = pymisp.MISPObject('domain-ip')
150
+ misp_object.add_attribute('domain', type='domain', value=report.id)
151
+ elif report.type == 'ip_address':
152
+ misp_object = pymisp.MISPObject('domain-ip')
153
+ misp_object.add_attribute('ip', type='ip-dst', value=report.id)
154
+ elif report.type == 'url':
155
+ misp_object = pymisp.MISPObject('url')
156
+ misp_object.add_attribute('url', type='url', value=report.id)
157
+ misp_object.add_reference(gti_uuid, 'analyzed-with')
158
+ return misp_object
159
+
160
+ def parse_domain(self, domain: str) -> str:
161
+ domain_report = self.client.get_object(f'/domains/{domain}')
162
+
163
+ # DOMAIN
164
+ domain_object = self.create_misp_object(domain_report)
165
+
166
+ # WHOIS
167
+ if domain_report.whois:
168
+ whois_object = pymisp.MISPObject('whois')
169
+ whois_object.add_attribute('text', type='text',
170
+ value=domain_report.whois)
171
+ self.misp_event.add_object(**whois_object)
172
+
173
+ # SIBLINGS AND SUBDOMAINS
174
+ for relationship_name, misp_name in [
175
+ ('siblings', 'sibling-of'), ('subdomains', 'subdomain')]:
176
+ rel_iterator = self.client.iterator(
177
+ f'/domains/{domain_report.id}/{relationship_name}',
178
+ limit=self.limit)
179
+ for item in rel_iterator:
180
+ attr = pymisp.MISPAttribute()
181
+ attr.from_dict(**dict(type='domain', value=item.id))
182
+ self.misp_event.add_attribute(**attr)
183
+ domain_object.add_reference(attr.uuid, misp_name)
184
+
185
+ # RESOLUTIONS
186
+ resolutions_iterator = self.client.iterator(
187
+ f'/domains/{domain_report.id}/resolutions', limit=self.limit)
188
+ for resolution in resolutions_iterator:
189
+ domain_object.add_attribute('ip', type='ip-dst',
190
+ value=resolution.ip_address)
191
+
192
+ # COMMUNICATING, DOWNLOADED AND REFERRER FILES
193
+ for relationship_name, misp_name in [
194
+ ('communicating_files', 'communicates-with'),
195
+ ('downloaded_files', 'downloaded-from'),
196
+ ('referrer_files', 'referring')
197
+ ]:
198
+ files_iterator = self.client.iterator(
199
+ f'/domains/{domain_report.id}/{relationship_name}',
200
+ limit=self.limit)
201
+ for file in files_iterator:
202
+ file_object = self.create_misp_object(file)
203
+ file_object.add_reference(domain_object.uuid, misp_name)
204
+ self.misp_event.add_object(**file_object)
205
+
206
+ # URLS
207
+ urls_iterator = self.client.iterator(
208
+ f'/domains/{domain_report.id}/urls', limit=self.limit)
209
+ for url in urls_iterator:
210
+ url_object = self.create_misp_object(url)
211
+ url_object.add_reference(domain_object.uuid, 'hosted-in')
212
+ self.misp_event.add_object(**url_object)
213
+
214
+ self.misp_event.add_object(**domain_object)
215
+ return domain_object.uuid
216
+
217
+ def parse_hash(self, file_hash: str) -> str:
218
+ file_report = self.client.get_object(f'/files/{file_hash}')
219
+ file_object = self.create_misp_object(file_report)
220
+
221
+ # ITW URLS
222
+ urls_iterator = self.client.iterator(
223
+ f'/files/{file_report.id}/itw_urls', limit=self.limit)
224
+ for url in urls_iterator:
225
+ url_object = self.create_misp_object(url)
226
+ url_object.add_reference(file_object.uuid, 'downloaded')
227
+ self.misp_event.add_object(**url_object)
228
+
229
+ # COMMUNICATING, DOWNLOADED AND REFERRER FILES
230
+ for relationship_name, misp_name in [
231
+ ('contacted_urls', 'communicates-with'),
232
+ ('contacted_domains', 'communicates-with'),
233
+ ('contacted_ips', 'communicates-with')
234
+ ]:
235
+ related_files_iterator = self.client.iterator(
236
+ f'/files/{file_report.id}/{relationship_name}', limit=self.limit)
237
+ for related_file in related_files_iterator:
238
+ related_file_object = self.create_misp_object(related_file)
239
+ related_file_object.add_reference(file_object.uuid, misp_name)
240
+ self.misp_event.add_object(**related_file_object)
241
+
242
+ self.misp_event.add_object(**file_object)
243
+ return file_object.uuid
244
+
245
+ def parse_ip_port(self, ipport: str) -> str:
246
+ ip = ipport.split('|')[0]
247
+ self.parse_ip(ip)
248
+
249
+ def parse_ip(self, ip: str) -> str:
250
+ ip_report = self.client.get_object(f'/ip_addresses/{ip}')
251
+
252
+ # IP
253
+ ip_object = self.create_misp_object(ip_report)
254
+
255
+ # ASN
256
+ asn_object = pymisp.MISPObject('asn')
257
+ asn_object.add_attribute('asn', type='AS', value=ip_report.asn)
258
+ asn_object.add_attribute('subnet-announced', type='ip-src',
259
+ value=ip_report.network)
260
+ asn_object.add_attribute('country', type='text',
261
+ value=ip_report.country)
262
+ self.misp_event.add_object(**asn_object)
263
+
264
+ # RESOLUTIONS
265
+ resolutions_iterator = self.client.iterator(
266
+ f'/ip_addresses/{ip_report.id}/resolutions', limit=self.limit)
267
+ for resolution in resolutions_iterator:
268
+ ip_object.add_attribute('domain', type='domain',
269
+ value=resolution.host_name)
270
+
271
+ # URLS
272
+ urls_iterator = self.client.iterator(
273
+ f'/ip_addresses/{ip_report.id}/urls', limit=self.limit)
274
+ for url in urls_iterator:
275
+ url_object = self.create_misp_object(url)
276
+ url_object.add_reference(ip_object.uuid, 'hosted-in')
277
+ self.misp_event.add_object(**url_object)
278
+
279
+ self.misp_event.add_object(**ip_object)
280
+ return ip_object.uuid
281
+
282
+ def parse_url(self, url: str) -> str:
283
+ url_id = vt.url_id(url)
284
+ url_report = self.client.get_object(f'/urls/{url_id}')
285
+ url_object = self.create_misp_object(url_report)
286
+
287
+ # COMMUNICATING, DOWNLOADED AND REFERRER FILES
288
+ for relationship_name, misp_name in [
289
+ ('communicating_files', 'communicates-with'),
290
+ ('downloaded_files', 'downloaded-from'),
291
+ ('referrer_files', 'referring')
292
+ ]:
293
+ files_iterator = self.client.iterator(
294
+ f'/urls/{url_report.id}/{relationship_name}', limit=self.limit)
295
+ for file in files_iterator:
296
+ file_object = self.create_misp_object(file)
297
+ file_object.add_reference(url_object.uuid, misp_name)
298
+ self.misp_event.add_object(**file_object)
299
+
300
+ self.misp_event.add_object(**url_object)
301
+ return url_object.uuid
302
+
303
+
304
+ def get_key(dictionary, key, default_value=''):
305
+ """Get value from nested dictionaries."""
306
+ dictionary = dictionary or {}
307
+ keys = key.split('.')
308
+ field_name = keys.pop()
309
+ for k in keys:
310
+ if k not in dictionary:
311
+ return default_value
312
+ dictionary = dictionary[k]
313
+ return dictionary.get(field_name, default_value)
314
+
315
+
316
+ def get_proxy_settings(config: dict) -> dict:
317
+ """Returns proxy settings in the requests format or None if not set up."""
318
+ proxies = None
319
+ host = config.get('proxy_host')
320
+ port = config.get('proxy_port')
321
+ username = config.get('proxy_username')
322
+ password = config.get('proxy_password')
323
+
324
+ if host:
325
+ if not port:
326
+ raise KeyError(
327
+ ('The google_threat_intelligence_proxy_host config is set, '
328
+ 'please also set the virustotal_proxy_port.'))
329
+ parsed = parse.urlparse(host)
330
+ if 'http' in parsed.scheme:
331
+ scheme = 'http'
332
+ else:
333
+ scheme = parsed.scheme
334
+ netloc = parsed.netloc
335
+ host = f'{netloc}:{port}'
336
+
337
+ if username:
338
+ if not password:
339
+ raise KeyError(('The google_threat_intelligence_'
340
+ ' proxy_host config is set, please also'
341
+ ' set the virustotal_proxy_password.'))
342
+ auth = f'{username}:{password}'
343
+ host = auth + '@' + host
344
+
345
+ proxies = {
346
+ 'http': f'{scheme}://{host}',
347
+ 'https': f'{scheme}://{host}'
348
+ }
349
+ return proxies
350
+
351
+
352
+ def dict_handler(request: dict):
353
+ """MISP entry point fo the module."""
354
+ if not request.get('config') or not request['config'].get('apikey'):
355
+ return {
356
+ 'error': ('A Google Threat Intelligence api '
357
+ 'key is required for this module.')
358
+ }
359
+
360
+ if not request.get('attribute'):
361
+ return {
362
+ 'error': ('This module requires an "attribute" field as input,'
363
+ ' which should contain at least a type, a value and an'
364
+ ' uuid.')
365
+ }
366
+
367
+ if request['attribute']['type'] not in mispattributes['input']:
368
+ return {'error': 'Unsupported attribute type.'}
369
+
370
+ event_limit = request['config'].get('event_limit')
371
+ attribute = request['attribute']
372
+
373
+ try:
374
+ proxy_settings = get_proxy_settings(request.get('config'))
375
+ client = vt.Client(
376
+ request['config']['apikey'],
377
+ headers={
378
+ 'x-tool': 'MISPModuleGTIExpansion',
379
+ },
380
+ proxy=proxy_settings['http'] if proxy_settings else None)
381
+ parser = GoogleThreatIntelligenceParser(
382
+ client, int(event_limit) if event_limit else None)
383
+ parser.query_api(attribute)
384
+ except vt.APIError as ex:
385
+ return {'error': ex.message}
386
+ except KeyError as ex:
387
+ return {'error': str(ex)}
388
+
389
+ return parser.get_results()
390
+
391
+
392
+ def introspection():
393
+ """Returns the module input attributes required."""
394
+ return mispattributes
395
+
396
+
397
+ def version():
398
+ """Returns the module metadata."""
399
+ return moduleinfo
400
+
401
+
402
+ if __name__ == '__main__':
403
+ # Testing/debug calls.
404
+ import os
405
+ api_key = os.getenv('GTI_API_KEY')
406
+ # File
407
+ request_data = {
408
+ 'config': {'apikey': api_key},
409
+ 'attribute': {
410
+ 'type': 'sha256',
411
+ 'value': ('ed01ebfbc9eb5bbea545af4d01bf5f10'
412
+ '71661840480439c6e5babe8e080e41aa')
413
+ }
414
+ }
415
+ response = dict_handler(request_data)
416
+ report_obj = response['results']['Object'][0]
417
+ print(report_obj.to_dict())
418
+
419
+ # URL
420
+ request_data = {
421
+ 'config': {'apikey': api_key},
422
+ 'attribute': {
423
+ 'type': 'url',
424
+ 'value': 'http://47.21.48.182:60813/Mozi.a'
425
+ }
426
+ }
427
+ response = dict_handler(request_data)
428
+ report_obj = response['results']['Object'][0]
429
+ print(report_obj.to_dict())
430
+
431
+ # Ip
432
+ request_data = {
433
+ 'config': {'apikey': api_key},
434
+ 'attribute': {
435
+ 'type': 'ip-src',
436
+ 'value': '180.72.148.38'
437
+ }
438
+ }
439
+ response = dict_handler(request_data)
440
+ report_obj = response['results']['Object'][0]
441
+ print(report_obj.to_dict())
442
+
443
+ # Domain
444
+ request_data = {
445
+ 'config': {'apikey': api_key},
446
+ 'attribute': {
447
+ 'type': 'domain',
448
+ 'value': 'qexyhuv.com'
449
+ }
450
+ }
451
+ response = dict_handler(request_data)
452
+ report_obj = response['results']['Object'][0]
453
+ print(report_obj.to_dict())