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,387 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import json
4
+ try:
5
+ from onyphe import Onyphe
6
+ except ImportError:
7
+ print("pyonyphe module not installed.")
8
+
9
+ misperrors = {'error': 'Error'}
10
+
11
+ mispattributes = {'input': ['ip-src', 'ip-dst', 'hostname', 'domain'],
12
+ 'output': ['hostname', 'domain', 'ip-src', 'ip-dst', 'url']}
13
+
14
+ # possible module-types: 'expansion', 'hover' or both
15
+ moduleinfo = {
16
+ 'version': '1',
17
+ 'author': 'Sebastien Larinier @sebdraven',
18
+ 'description': 'Module to process a full query on Onyphe.',
19
+ 'module-type': ['expansion', 'hover'],
20
+ 'name': 'Onyphe Full Lookup',
21
+ 'logo': 'onyphe.jpg',
22
+ 'requirements': ['onyphe python library', 'An access to the Onyphe API (apikey)'],
23
+ 'features': 'This module takes a domain, hostname, or IP address attribute as input in order to query the Onyphe API. Data fetched from the query is then parsed and MISP attributes are extracted.\n\nThe parsing is here more advanced than the one on onyphe module, and is returning more attributes, since more fields of the query result are watched and parsed.',
24
+ 'references': ['https://www.onyphe.io/', 'https://github.com/sebdraven/pyonyphe'],
25
+ 'input': 'A domain, hostname or IP address MISP attribute.',
26
+ 'output': 'MISP attributes fetched from the Onyphe query.',
27
+ }
28
+
29
+ # config fields that your code expects from the site admin
30
+ moduleconfig = ['apikey']
31
+
32
+
33
+ def handler(q=False):
34
+ if q:
35
+
36
+ request = json.loads(q)
37
+
38
+ if not request.get('config') or not request['config'].get('apikey'):
39
+ misperrors['error'] = 'Onyphe authentication is missing'
40
+ return misperrors
41
+
42
+ api = Onyphe(request['config'].get('apikey'))
43
+
44
+ if not api:
45
+ misperrors['error'] = 'Onyphe Error instance api'
46
+
47
+ ip = ''
48
+ if request.get('ip-src'):
49
+ ip = request['ip-src']
50
+ return handle_ip(api, ip, misperrors)
51
+ elif request.get('ip-dst'):
52
+ ip = request['ip-dst']
53
+ return handle_ip(api, ip, misperrors)
54
+ elif request.get('domain'):
55
+ domain = request['domain']
56
+ return handle_domain(api, domain, misperrors)
57
+ elif request.get('hostname'):
58
+ hostname = request['hostname']
59
+ return handle_domain(api, hostname, misperrors)
60
+ else:
61
+ misperrors['error'] = "Unsupported attributes type"
62
+ return misperrors
63
+ else:
64
+ return False
65
+
66
+
67
+ def handle_domain(api, domain, misperrors):
68
+ result_filtered = {"results": []}
69
+
70
+ r, status_ok = expand_pastries(api, misperrors, domain=domain)
71
+
72
+ if status_ok:
73
+ result_filtered['results'].extend(r)
74
+ else:
75
+ misperrors['error'] = 'Error pastries result'
76
+ return misperrors
77
+
78
+ r, status_ok = expand_datascan(api, misperrors, domain=domain)
79
+
80
+ if status_ok:
81
+ result_filtered['results'].extend(r)
82
+ else:
83
+ misperrors['error'] = 'Error datascan result '
84
+ return misperrors
85
+
86
+ r, status_ok = expand_threatlist(api, misperrors, domain=domain)
87
+
88
+ if status_ok:
89
+ result_filtered['results'].extend(r)
90
+ else:
91
+ misperrors['error'] = 'Error threat list'
92
+ return misperrors
93
+
94
+ return result_filtered
95
+
96
+
97
+ def handle_ip(api, ip, misperrors):
98
+ result_filtered = {"results": []}
99
+
100
+ r, status_ok = expand_syscan(api, ip, misperrors)
101
+
102
+ if status_ok:
103
+ result_filtered['results'].extend(r)
104
+ else:
105
+ misperrors['error'] = "Error syscan result"
106
+
107
+ r, status_ok = expand_pastries(api, misperrors, ip=ip)
108
+
109
+ if status_ok:
110
+ result_filtered['results'].extend(r)
111
+ else:
112
+ misperrors['error'] = 'Error pastries result'
113
+ return misperrors
114
+
115
+ r, status_ok = expand_datascan(api, misperrors, ip=ip)
116
+
117
+ if status_ok:
118
+ result_filtered['results'].extend(r)
119
+ else:
120
+ misperrors['error'] = 'Error datascan result '
121
+ return misperrors
122
+
123
+ r, status_ok = expand_forward(api, ip, misperrors)
124
+
125
+ if status_ok:
126
+ result_filtered['results'].extend(r)
127
+ else:
128
+ misperrors['error'] = 'Error forward result'
129
+ return misperrors
130
+
131
+ r, status_ok = expand_reverse(api, ip, misperrors)
132
+
133
+ if status_ok:
134
+ result_filtered['results'].extend(r)
135
+ else:
136
+ misperrors['error'] = 'Error reverse result'
137
+ return misperrors
138
+
139
+ r, status_ok = expand_threatlist(api, misperrors, ip=ip)
140
+
141
+ if status_ok:
142
+ result_filtered['results'].extend(r)
143
+ else:
144
+ misperrors['error'] = 'Error threat list'
145
+ return misperrors
146
+
147
+ return result_filtered
148
+
149
+
150
+ def expand_syscan(api, ip, misperror):
151
+ status_ok = False
152
+ r = []
153
+ asn_list = []
154
+ os_list = []
155
+ geoloc = []
156
+ orgs = []
157
+ results = api.synscan(ip)
158
+
159
+ if results['status'] == 'ok':
160
+ status_ok = True
161
+ for elem in results['results']:
162
+ asn_list.append(elem['asn'])
163
+ os_target = elem['os']
164
+ geoloc.append(elem['location'])
165
+ orgs.append(elem['organization'])
166
+ if os_target != 'Unknown' and os_target != 'Undefined':
167
+ os_list.append(elem['os'])
168
+
169
+ r.append({'types': ['target-machine'],
170
+ 'values': list(set(os_list)),
171
+ 'categories': ['Targeting data'],
172
+ 'comment': 'OS found on %s with synscan of Onyphe' % ip})
173
+
174
+ r.append({'types': ['target-location'],
175
+ 'values': list(set(geoloc)),
176
+ 'categories': ['Targeting data'],
177
+ 'comment': 'Geolocalisation of %s found with synscan of Onyphe'
178
+ % ip
179
+ })
180
+
181
+ r.append({'types': ['target-org'],
182
+ 'values': list(set(orgs)),
183
+ 'categories': ['Targeting data'],
184
+ 'comment': 'Organisations of %s found with synscan of Onyphe'
185
+ % ip
186
+ })
187
+
188
+ r.append({'types': ['AS'],
189
+ 'values': list(set(asn_list)),
190
+ 'categories': ['Network activity'],
191
+ 'comment': 'As number of %s found with synscan of Onyphe' % ip
192
+ })
193
+
194
+ return r, status_ok
195
+
196
+
197
+ def expand_datascan(api, misperror, **kwargs):
198
+ status_ok = False
199
+ r = []
200
+ # ip = ''
201
+ query = ''
202
+ asn_list = []
203
+ geoloc = []
204
+ orgs = []
205
+ ports = []
206
+
207
+ if 'ip' in kwargs:
208
+ query = kwargs.get('ip')
209
+ results = api.datascan(query)
210
+ else:
211
+ query = kwargs.get('domain')
212
+ results = api.search_datascan('domain:%s' % query)
213
+
214
+ if results['status'] == 'ok':
215
+ status_ok = True
216
+ for elem in results['results']:
217
+ asn_list.append(elem['asn'])
218
+ geoloc.append(elem['location'])
219
+ orgs.append(elem['organization'])
220
+ ports.append(elem['port'])
221
+
222
+ r.append({'types': ['port'],
223
+ 'values': list(set(ports)),
224
+ 'categories': ['Other'],
225
+ 'comment': 'Ports of %s found with datascan of Onyphe'
226
+ % query
227
+ })
228
+
229
+ r.append({'types': ['target-location'],
230
+ 'values': list(set(geoloc)),
231
+ 'categories': ['Targeting data'],
232
+ 'comment': 'Geolocalisation of %s found with synscan of Onyphe'
233
+ % query
234
+ })
235
+
236
+ r.append({'types': ['target-org'],
237
+ 'values': list(set(orgs)),
238
+ 'categories': ['Targeting data'],
239
+ 'comment': 'Organisations of %s found with synscan of Onyphe'
240
+ % query
241
+ })
242
+
243
+ r.append({'types': ['AS'],
244
+ 'values': list(set(asn_list)),
245
+ 'categories': ['Network activity'],
246
+ 'comment': 'As number of %s found with synscan of Onyphe' % query
247
+ })
248
+ return r, status_ok
249
+
250
+
251
+ def expand_reverse(api, ip, misperror):
252
+ status_ok = False
253
+ r = None
254
+ status_ok = False
255
+ r = []
256
+ results = api.reverse(ip)
257
+
258
+ domains_reverse = []
259
+
260
+ domains = []
261
+ if results['status'] == 'ok':
262
+ status_ok = True
263
+
264
+ for elem in results['results']:
265
+ domains_reverse.append(elem['reverse'])
266
+ domains.append(elem['domain'])
267
+
268
+ r.append({'types': ['domain'],
269
+ 'values': list(set(domains)),
270
+ 'categories': ['Network activity'],
271
+ 'comment': 'Domains of %s from forward service of Onyphe' % ip})
272
+
273
+ r.append({'types': ['domain'],
274
+ 'values': list(set(domains_reverse)),
275
+ 'categories': ['Network activity'],
276
+ 'comment': 'Reverse Domains of %s from forward service of Onyphe' % ip})
277
+ return r, status_ok
278
+
279
+
280
+ def expand_forward(api, ip, misperror):
281
+ status_ok = False
282
+ r = []
283
+ results = api.forward(ip)
284
+
285
+ domains_forward = []
286
+
287
+ domains = []
288
+ if results['status'] == 'ok':
289
+ status_ok = True
290
+
291
+ for elem in results['results']:
292
+ domains_forward.append(elem['forward'])
293
+ domains.append(elem['domain'])
294
+
295
+ r.append({'types': ['domain'],
296
+ 'values': list(set(domains)),
297
+ 'categories': ['Network activity'],
298
+ 'comment': 'Domains of %s from forward service of Onyphe' % ip})
299
+
300
+ r.append({'types': ['domain'],
301
+ 'values': list(set(domains_forward)),
302
+ 'categories': ['Network activity'],
303
+ 'comment': 'Forward Domains of %s from forward service of Onyphe' % ip})
304
+ return r, status_ok
305
+
306
+
307
+ def expand_pastries(api, misperror, **kwargs):
308
+ status_ok = False
309
+ r = []
310
+
311
+ query = None
312
+ result = None
313
+ urls_pasties = []
314
+ domains = []
315
+ ips = []
316
+ if 'ip' in kwargs:
317
+ query = kwargs.get('ip')
318
+ result = api.pastries(query)
319
+ if 'domain' in kwargs:
320
+ query = kwargs.get('domain')
321
+ result = api.search_pastries('domain:%s' % query)
322
+
323
+ if result['status'] == 'ok':
324
+ status_ok = True
325
+ for item in result['results']:
326
+ if item['@category'] == 'pastries':
327
+ if item['source'] == 'pastebin':
328
+ urls_pasties.append('https://pastebin.com/raw/%s' % item['key'])
329
+
330
+ if 'domain' in item:
331
+ domains.extend(item['domain'])
332
+ if 'ip' in item:
333
+ ips.extend(item['ip'])
334
+ if 'hostname' in item:
335
+ domains.extend(item['hostname'])
336
+
337
+ r.append({'types': ['url'],
338
+ 'values': urls_pasties,
339
+ 'categories': ['External analysis'],
340
+ 'comment': 'URLs of pasties where %s has found' % query})
341
+ r.append({'types': ['domain'], 'values': list(set(domains)),
342
+ 'categories': ['Network activity'],
343
+ 'comment': 'Domains found in pasties of Onyphe'})
344
+
345
+ r.append({'types': ['ip-dst'], 'values': list(set(ips)),
346
+ 'categories': ['Network activity'],
347
+ 'comment': 'IPs found in pasties of Onyphe'})
348
+
349
+ return r, status_ok
350
+
351
+
352
+ def expand_threatlist(api, misperror, **kwargs):
353
+ status_ok = False
354
+ r = []
355
+
356
+ query = None
357
+
358
+ threat_list = []
359
+
360
+ if 'ip' in kwargs:
361
+ query = kwargs.get('ip')
362
+ results = api.threatlist(query)
363
+ else:
364
+ query = kwargs.get('domain')
365
+ results = api.search_threatlist('domain:%s' % query)
366
+
367
+ if results['status'] == 'ok':
368
+ status_ok = True
369
+ threat_list = ['seen %s on %s ' % (item['seen_date'], item['threatlist'])
370
+ for item in results['results']]
371
+
372
+ r.append({'types': ['comment'],
373
+ 'categories': ['Other'],
374
+ 'values': threat_list,
375
+ 'comment': '%s is present in threatlist' % query
376
+ })
377
+
378
+ return r, status_ok
379
+
380
+
381
+ def introspection():
382
+ return mispattributes
383
+
384
+
385
+ def version():
386
+ moduleinfo['config'] = moduleconfig
387
+ return moduleinfo
@@ -0,0 +1,167 @@
1
+ import json
2
+ import requests
3
+ import re
4
+
5
+ misperrors = {'error': 'Error'}
6
+ mispattributes = {'input': ["hostname", "domain", "ip-src", "ip-dst", "md5", "sha1", "sha256", "sha512"],
7
+ 'output': ["domain", "ip-src", "ip-dst", "text", "md5", "sha1", "sha256", "sha512", "email"]
8
+ }
9
+
10
+ # possible module-types: 'expansion', 'hover' or both
11
+ moduleinfo = {
12
+ 'version': '1',
13
+ 'author': 'chrisdoman',
14
+ 'description': 'Module to get information from AlienVault OTX.',
15
+ 'module-type': ['expansion'],
16
+ 'name': 'AlienVault OTX Lookup',
17
+ 'logo': 'otx.png',
18
+ 'requirements': ['An access to the OTX API (apikey)'],
19
+ 'features': 'This module takes a MISP attribute as input to query the OTX Alienvault API. The API returns then the result of the query with some types we map into compatible types we add as MISP attributes.',
20
+ 'references': ['https://www.alienvault.com/open-threat-exchange'],
21
+ 'input': 'A MISP attribute included in the following list:\n- hostname\n- domain\n- ip-src\n- ip-dst\n- md5\n- sha1\n- sha256\n- sha512',
22
+ 'output': 'MISP attributes mapped from the result of the query on OTX, included in the following list:\n- domain\n- ip-src\n- ip-dst\n- text\n- md5\n- sha1\n- sha256\n- sha512\n- email',
23
+ }
24
+
25
+ # We're not actually using the API key yet
26
+ moduleconfig = ["apikey"]
27
+
28
+
29
+ # Avoid adding windows update to enrichment etc.
30
+ def isBlacklisted(value):
31
+ blacklist = ['0.0.0.0', '8.8.8.8', '255.255.255.255', '192.168.56.', 'time.windows.com']
32
+
33
+ for b in blacklist:
34
+ if value in b:
35
+ return False
36
+
37
+ return True
38
+
39
+
40
+ def valid_ip(ip):
41
+ m = re.match(r"^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$", ip)
42
+ return bool(m) and all(map(lambda n: 0 <= int(n) <= 255, m.groups()))
43
+
44
+
45
+ def findAll(data, keys):
46
+ a = []
47
+ if isinstance(data, dict):
48
+ for key, value in data.items():
49
+ if key == keys:
50
+ a.append(value)
51
+ else:
52
+ if isinstance(value, (dict, list)):
53
+ a.extend(findAll(value, keys))
54
+ if isinstance(data, list):
55
+ for i in data:
56
+ a.extend(findAll(i, keys))
57
+ return a
58
+
59
+
60
+ def valid_email(email):
61
+ return bool(re.search(r"[a-zA-Z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?", email))
62
+
63
+
64
+ def handler(q=False):
65
+ if q is False:
66
+ return False
67
+
68
+ q = json.loads(q)
69
+
70
+ key = q["config"]["apikey"]
71
+
72
+ r = {"results": []}
73
+
74
+ if "ip-src" in q:
75
+ r["results"] += getIP(q["ip-src"], key)
76
+ if "ip-dst" in q:
77
+ r["results"] += getIP(q["ip-dst"], key)
78
+ if "domain" in q:
79
+ r["results"] += getDomain(q["domain"], key)
80
+ if 'hostname' in q:
81
+ r["results"] += getDomain(q['hostname'], key)
82
+ if 'md5' in q:
83
+ r["results"] += getHash(q['md5'], key)
84
+ if 'sha1' in q:
85
+ r["results"] += getHash(q['sha1'], key)
86
+ if 'sha256' in q:
87
+ r["results"] += getHash(q['sha256'], key)
88
+ if 'sha512' in q:
89
+ r["results"] += getHash(q['sha512'], key)
90
+
91
+ uniq = []
92
+ for res in r["results"]:
93
+ if res not in uniq:
94
+ uniq.append(res)
95
+ r["results"] = uniq
96
+ return r
97
+
98
+
99
+ def getHash(_hash, key):
100
+
101
+ ret = []
102
+ req = json.loads(requests.get("https://otx.alienvault.com/otxapi/indicator/file/analysis/" + _hash).text)
103
+
104
+ for ip in findAll(req, "dst"):
105
+ if not isBlacklisted(ip) and valid_ip(ip):
106
+ ret.append({"types": ["ip-dst", "ip-src"], "values": [ip]})
107
+
108
+ for domain in findAll(req, "hostname"):
109
+ if "." in domain and not isBlacklisted(domain):
110
+ ret.append({"types": ["hostname"], "values": [domain]})
111
+
112
+ return ret
113
+
114
+
115
+ def getIP(ip, key):
116
+ ret = []
117
+ req = json.loads(requests.get("https://otx.alienvault.com/otxapi/indicator/ip/malware/" + ip + "?limit=1000").text)
118
+
119
+ for _hash in findAll(req, "hash"):
120
+ ret.append({"types": ["sha256"], "values": [_hash]})
121
+
122
+ req = json.loads(requests.get("https://otx.alienvault.com/otxapi/indicator/ip/passive_dns/" + ip).text)
123
+
124
+ for hostname in findAll(req, "hostname"):
125
+ if not isBlacklisted(hostname):
126
+ ret.append({"types": ["hostname"], "values": [hostname]})
127
+
128
+ return ret
129
+
130
+
131
+ def getDomain(domain, key):
132
+
133
+ ret = []
134
+
135
+ req = json.loads(requests.get("https://otx.alienvault.com/otxapi/indicator/domain/malware/" + domain + "?limit=1000").text)
136
+
137
+ for _hash in findAll(req, "hash"):
138
+ ret.append({"types": ["sha256"], "values": [_hash]})
139
+
140
+ req = json.loads(requests.get("https://otx.alienvault.com/otxapi/indicator/domain/whois/" + domain).text)
141
+
142
+ for _domain in findAll(req, "domain"):
143
+ ret.append({"types": ["hostname"], "values": [_domain]})
144
+
145
+ for email in findAll(req, "value"):
146
+ if valid_email(email):
147
+ ret.append({"types": ["email"], "values": [email]})
148
+
149
+ for _domain in findAll(req, "hostname"):
150
+ if "." in _domain and not isBlacklisted(_domain):
151
+ ret.append({"types": ["hostname"], "values": [_domain]})
152
+
153
+ req = json.loads(requests.get("https://otx.alienvault.com/otxapi/indicator/hostname/passive_dns/" + domain).text)
154
+ for ip in findAll(req, "address"):
155
+ if valid_ip(ip):
156
+ ret.append({"types": ["ip-dst"], "values": [ip]})
157
+
158
+ return ret
159
+
160
+
161
+ def introspection():
162
+ return mispattributes
163
+
164
+
165
+ def version():
166
+ moduleinfo['config'] = moduleconfig
167
+ return moduleinfo
@@ -0,0 +1,150 @@
1
+ import json
2
+ import requests
3
+ from . import check_input_attribute, standard_error_message
4
+ from collections import defaultdict
5
+ from pymisp import MISPEvent, MISPObject
6
+
7
+ misperrors = {'error': 'Error'}
8
+
9
+ mispattributes = {'input': ['ip-src', 'ip-dst', 'ssh-fingerprint'],
10
+ 'format': 'misp_standard'}
11
+
12
+ moduleinfo = {
13
+ 'version': '1',
14
+ 'author': 'Jean-Louis Huynen',
15
+ 'description': 'An expansion module to enrich, SSH key fingerprints and IP addresses with information collected by passive-ssh',
16
+ 'module-type': ['expansion', 'hover'],
17
+ 'name': 'Passive SSH Enrichment',
18
+ 'logo': '',
19
+ 'requirements': [],
20
+ 'features': '',
21
+ 'references': [],
22
+ 'input': '',
23
+ 'output': '',
24
+ }
25
+
26
+ moduleconfig = ["custom_api_url", "api_user", "api_key"]
27
+
28
+ passivessh_url = 'https://passivessh.circl.lu/'
29
+
30
+ host_query = '/host/ssh'
31
+ fingerprint_query = '/fingerprint/all'
32
+
33
+
34
+ class PassivesshParser():
35
+ def __init__(self, attribute, passivesshresult):
36
+ self.attribute = attribute
37
+ self.passivesshresult = passivesshresult
38
+ self.misp_event = MISPEvent()
39
+ self.misp_event.add_attribute(**attribute)
40
+ self.references = defaultdict(list)
41
+
42
+ def get_result(self):
43
+ if self.references:
44
+ self.__build_references()
45
+ event = json.loads(self.misp_event.to_json())
46
+ results = {key: event[key] for key in (
47
+ 'Attribute', 'Object') if (key in event and event[key])}
48
+ return {'results': results}
49
+
50
+ def parse_passivessh_information(self):
51
+ passivessh_object = MISPObject('passive-ssh')
52
+ if 'first_seen' in self.passivesshresult:
53
+ passivessh_object.add_attribute(
54
+ 'first_seen', **{'type': 'datetime', 'value': self.passivesshresult['first_seen']})
55
+ if 'last_seen' in self.passivesshresult:
56
+ passivessh_object.add_attribute(
57
+ 'last_seen', **{'type': 'datetime', 'value': self.passivesshresult['last_seen']})
58
+ if 'base64' in self.passivesshresult:
59
+ passivessh_object.add_attribute(
60
+ 'base64', **{'type': 'text', 'value': self.passivesshresult['base64']})
61
+ if 'keys' in self.passivesshresult:
62
+ for key in self.passivesshresult['keys']:
63
+ passivessh_object.add_attribute(
64
+ 'fingerprint', **{'type': 'ssh-fingerprint', 'value': key['fingerprint']})
65
+ if 'hosts' in self.passivesshresult:
66
+ for host in self.passivesshresult['hosts']:
67
+ passivessh_object.add_attribute(
68
+ 'host', **{'type': 'ip-dst', 'value': host})
69
+
70
+ passivessh_object.add_reference(self.attribute['uuid'], 'related-to')
71
+ self.misp_event.add_object(passivessh_object)
72
+
73
+ def __build_references(self):
74
+ for object_uuid, references in self.references.items():
75
+ for misp_object in self.misp_event.objects:
76
+ if misp_object.uuid == object_uuid:
77
+ for reference in references:
78
+ misp_object.add_reference(**reference)
79
+ break
80
+
81
+
82
+ def check_url(url):
83
+ return "{}/".format(url) if not url.endswith('/') else url
84
+
85
+
86
+ def handler(q=False):
87
+
88
+ if q is False:
89
+ return False
90
+ request = json.loads(q)
91
+
92
+ api_url = check_url(request['config']['custom_api_url']) if request['config'].get(
93
+ 'custom_api_url') else passivessh_url
94
+
95
+ if request['config'].get('api_user'):
96
+ api_user = request['config'].get('api_user')
97
+ else:
98
+ misperrors['error'] = 'passive-ssh user required'
99
+ return misperrors
100
+ if request['config'].get('api_key'):
101
+ api_key = request['config'].get('api_key')
102
+ else:
103
+ misperrors['error'] = 'passive-ssh password required'
104
+ return misperrors
105
+
106
+ if not request.get('attribute') or not check_input_attribute(request['attribute']):
107
+ return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'}
108
+ attribute = request['attribute']
109
+ if attribute.get('type') == 'ip-src':
110
+ type = host_query
111
+ pass
112
+ elif attribute.get('type') == 'ip-dst':
113
+ type = host_query
114
+ pass
115
+ elif attribute.get('type') == 'ssh-fingerprint':
116
+ type = fingerprint_query
117
+ pass
118
+ else:
119
+ misperrors['error'] = 'ip is missing.'
120
+ return misperrors
121
+
122
+ r = requests.get("{}{}/{}".format(api_url, type,
123
+ attribute['value']), auth=(api_user, api_key))
124
+
125
+ if r.status_code == 200:
126
+ passivesshresult = r.json()
127
+ if not passivesshresult:
128
+ misperrors['error'] = 'Empty result'
129
+ return misperrors
130
+ elif r.status_code == 404:
131
+ misperrors['error'] = 'Non existing hash'
132
+ return misperrors
133
+ else:
134
+ misperrors['error'] = 'API not accessible'
135
+ return misperrors
136
+
137
+ parser = PassivesshParser(attribute, passivesshresult)
138
+ parser.parse_passivessh_information()
139
+ result = parser.get_result()
140
+
141
+ return result
142
+
143
+
144
+ def introspection():
145
+ return mispattributes
146
+
147
+
148
+ def version():
149
+ moduleinfo['config'] = moduleconfig
150
+ return moduleinfo