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,57 @@
1
+ import json
2
+ import requests
3
+
4
+ misperrors = {'error': 'Error'}
5
+ mispattributes = {'input': ['vulnerability'], 'output': ['text']}
6
+ moduleinfo = {
7
+ 'version': '0.4',
8
+ 'author': 'Alexandre Dulaunoy',
9
+ 'description': 'An expansion hover module to expand information about CVE id.',
10
+ 'module-type': ['hover'],
11
+ 'name': 'CVE Lookup',
12
+ 'logo': 'cve.png',
13
+ 'requirements': [],
14
+ 'features': 'The module takes a vulnerability attribute as input and queries the CIRCL CVE search API to get information about the vulnerability as it is described in the list of CVEs.',
15
+ 'references': ['https://vulnerability.circl.lu/', 'https://cve.mitre.org/'],
16
+ 'input': 'Vulnerability attribute.',
17
+ 'output': 'Text giving information about the CVE related to the Vulnerability.',
18
+ }
19
+ moduleconfig = ["custom_API"]
20
+ cveapi_url = 'https://vulnerability.circl.lu/api/cve/'
21
+
22
+
23
+ def check_url(url):
24
+ return "{}/".format(url) if not url.endswith('/') else url
25
+
26
+
27
+ def handler(q=False):
28
+ if q is False:
29
+ return False
30
+ request = json.loads(q)
31
+ if not request.get('vulnerability'):
32
+ misperrors['error'] = 'Vulnerability id missing'
33
+ return misperrors
34
+
35
+ api_url = check_url(request['config']['custom_API']) if request.get('config') and request['config'].get('custom_API') else cveapi_url
36
+ r = requests.get("{}{}".format(api_url, request.get('vulnerability')))
37
+ if r.status_code == 200:
38
+ vulnerability = json.loads(r.text)
39
+ try:
40
+ summary = vulnerability['containers']['cna']['descriptions'][0]['value']
41
+ except Exception:
42
+ summary = 'Non existing CVE'
43
+ else:
44
+ misperrors['error'] = 'API not accessible'
45
+ return misperrors['error']
46
+
47
+ r = {'results': [{'types': mispattributes['output'], 'values': summary}]}
48
+ return r
49
+
50
+
51
+ def introspection():
52
+ return mispattributes
53
+
54
+
55
+ def version():
56
+ moduleinfo['config'] = moduleconfig
57
+ return moduleinfo
@@ -0,0 +1,185 @@
1
+ import json
2
+ import requests
3
+ from . import check_input_attribute, standard_error_message
4
+ from collections import defaultdict
5
+ from pymisp import MISPAttribute, MISPEvent, MISPObject
6
+
7
+ misperrors = {'error': 'Error'}
8
+ mispattributes = {'input': ['vulnerability'], 'format': 'misp_standard'}
9
+ moduleinfo = {
10
+ 'version': '2',
11
+ 'author': 'Christian Studer',
12
+ 'description': 'An expansion module to query the CIRCL CVE search API for more information about a vulnerability (CVE).',
13
+ 'module-type': ['expansion', 'hover'],
14
+ 'name': 'CVE Advanced Lookup',
15
+ 'logo': 'cve.png',
16
+ 'requirements': [],
17
+ 'features': 'The module takes a vulnerability attribute as input and queries the CIRCL CVE search API to gather additional information.\n\nThe result of the query is then parsed to return additional information about the vulnerability, like its cvss score or some references, as well as the potential related weaknesses and attack patterns.\n\nThe vulnerability additional data is returned in a vulnerability MISP object, and the related additional information are put into weakness and attack-pattern MISP objects.',
18
+ 'references': ['https://vulnerability.circl.lu', 'https://cve/mitre.org/'],
19
+ 'input': 'Vulnerability attribute.',
20
+ 'output': 'Additional information about the vulnerability, such as its cvss score, some references, or the related weaknesses and attack patterns.',
21
+ }
22
+ moduleconfig = ["custom_API"]
23
+ cveapi_url = 'https://cvepremium.circl.lu/api/'
24
+
25
+
26
+ class VulnerabilityParser():
27
+ def __init__(self, attribute, api_url):
28
+ misp_attribute = MISPAttribute()
29
+ misp_attribute.from_dict(**attribute)
30
+ misp_event = MISPEvent()
31
+ misp_event.add_attribute(**misp_attribute)
32
+ self.__misp_attribute = misp_attribute
33
+ self.__misp_event = misp_event
34
+ self.__api_url = api_url
35
+ self.references = defaultdict(list)
36
+ self.__capec_features = ('id', 'name', 'summary', 'prerequisites', 'solutions')
37
+ self.__vulnerability_mapping = {
38
+ 'id': 'id', 'summary': 'summary',
39
+ 'Modified': 'modified', 'cvss3': 'cvss-score',
40
+ 'cvss3-vector': 'cvss-string'
41
+ }
42
+ self.__vulnerability_multiple_mapping = {
43
+ 'vulnerable_configuration': 'vulnerable-configuration',
44
+ 'vulnerable_configuration_cpe_2_2': 'vulnerable-configuration',
45
+ 'references': 'references'
46
+ }
47
+ self.__weakness_mapping = {
48
+ 'name': 'name', 'description_summary': 'description',
49
+ 'status': 'status', 'weaknessabs': 'weakness-abs'
50
+ }
51
+
52
+ @property
53
+ def api_url(self) -> str:
54
+ return self.__api_url
55
+
56
+ @property
57
+ def capec_features(self) -> tuple:
58
+ return self.__capec_features
59
+
60
+ @property
61
+ def misp_attribute(self) -> MISPAttribute:
62
+ return self.__misp_attribute
63
+
64
+ @property
65
+ def misp_event(self) -> MISPEvent:
66
+ return self.__misp_event
67
+
68
+ @property
69
+ def vulnerability_mapping(self) -> dict:
70
+ return self.__vulnerability_mapping
71
+
72
+ @property
73
+ def vulnerability_multiple_mapping(self) -> dict:
74
+ return self.__vulnerability_multiple_mapping
75
+
76
+ @property
77
+ def weakness_mapping(self) -> dict:
78
+ return self.__weakness_mapping
79
+
80
+ def get_result(self):
81
+ if self.references:
82
+ self.__build_references()
83
+ event = json.loads(self.misp_event.to_json())
84
+ results = {key: event[key] for key in ('Attribute', 'Object') if (key in event and event[key])}
85
+ return {'results': results}
86
+
87
+ def parse_vulnerability_information(self, vulnerability):
88
+ vulnerability_object = MISPObject('vulnerability')
89
+ for feature, relation in self.vulnerability_mapping.items():
90
+ if vulnerability.get(feature):
91
+ vulnerability_object.add_attribute(relation, vulnerability[feature])
92
+ if 'Published' in vulnerability:
93
+ vulnerability_object.add_attribute('published', vulnerability['Published'])
94
+ vulnerability_object.add_attribute('state', 'Published')
95
+ for feature, relation in self.vulnerability_multiple_mapping.items():
96
+ if feature in vulnerability:
97
+ for value in vulnerability[feature]:
98
+ if isinstance(value, dict):
99
+ value = value['title']
100
+ vulnerability_object.add_attribute(relation, value)
101
+ vulnerability_object.add_reference(self.misp_attribute.uuid, 'related-to')
102
+ self.misp_event.add_object(vulnerability_object)
103
+ if 'cwe' in vulnerability and vulnerability['cwe'] not in ('Unknown', 'NVD-CWE-noinfo'):
104
+ self.__parse_weakness(vulnerability['cwe'], vulnerability_object.uuid)
105
+ if 'capec' in vulnerability:
106
+ self.__parse_capec(vulnerability['capec'], vulnerability_object.uuid)
107
+
108
+ def __build_references(self):
109
+ for object_uuid, references in self.references.items():
110
+ for misp_object in self.misp_event.objects:
111
+ if misp_object.uuid == object_uuid:
112
+ for reference in references:
113
+ misp_object.add_reference(**reference)
114
+ break
115
+
116
+ def __parse_capec(self, capec_values, vulnerability_uuid):
117
+ for capec in capec_values:
118
+ capec_object = MISPObject('attack-pattern')
119
+ for feature in self.capec_features:
120
+ capec_object.add_attribute(feature, capec[feature])
121
+ for related_weakness in capec['related_weakness']:
122
+ capec_object.add_attribute('related-weakness', f"CWE-{related_weakness}")
123
+ self.misp_event.add_object(capec_object)
124
+ self.references[vulnerability_uuid].append(
125
+ {
126
+ 'referenced_uuid': capec_object.uuid,
127
+ 'relationship_type': 'targeted-by'
128
+ }
129
+ )
130
+
131
+ def __parse_weakness(self, cwe_value, vulnerability_uuid):
132
+ cwe_string, cwe_id = cwe_value.split('-')[:2]
133
+ cwe = requests.get(f'{self.api_url}cwe/{cwe_id}')
134
+ if cwe.status_code == 200:
135
+ cwe = cwe.json()
136
+ weakness_object = MISPObject('weakness')
137
+ weakness_object.add_attribute('id', f'{cwe_string}-{cwe_id}')
138
+ for feature, relation in self.weakness_mapping.items():
139
+ if cwe.get(feature):
140
+ weakness_object.add_attribute(relation, cwe[feature])
141
+ self.misp_event.add_object(weakness_object)
142
+ self.references[vulnerability_uuid].append(
143
+ {
144
+ 'referenced_uuid': weakness_object.uuid,
145
+ 'relationship_type': 'weakened-by'
146
+ }
147
+ )
148
+
149
+
150
+ def check_url(url):
151
+ return f"{url}/" if not url.endswith('/') else url
152
+
153
+
154
+ def handler(q=False):
155
+ if q is False:
156
+ return False
157
+ request = json.loads(q)
158
+ if not request.get('attribute') or not check_input_attribute(request['attribute']):
159
+ return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'}
160
+ attribute = request['attribute']
161
+ if attribute.get('type') != 'vulnerability':
162
+ misperrors['error'] = 'Vulnerability id missing.'
163
+ return misperrors
164
+ api_url = check_url(request['config']['custom_API']) if request.get('config', {}).get('custom_API') else cveapi_url
165
+ r = requests.get(f"{api_url}cve/{attribute['value']}")
166
+ if r.status_code == 200:
167
+ vulnerability = r.json()
168
+ if not vulnerability:
169
+ misperrors['error'] = 'Non existing CVE'
170
+ return misperrors['error']
171
+ else:
172
+ misperrors['error'] = 'API not accessible'
173
+ return misperrors['error']
174
+ parser = VulnerabilityParser(attribute, api_url)
175
+ parser.parse_vulnerability_information(vulnerability)
176
+ return parser.get_result()
177
+
178
+
179
+ def introspection():
180
+ return mispattributes
181
+
182
+
183
+ def version():
184
+ moduleinfo['config'] = moduleconfig
185
+ return moduleinfo
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env python3
2
+
3
+ '''
4
+ Cytomic Orion MISP Module
5
+ An expansion module to enrich attributes in MISP and share indicators of compromise with Cytomic Orion
6
+
7
+
8
+ '''
9
+
10
+ from . import check_input_attribute, standard_error_message
11
+ from pymisp import MISPAttribute, MISPEvent, MISPObject
12
+ import json
13
+ import requests
14
+ import sys
15
+
16
+ misperrors = {'error': 'Error'}
17
+ mispattributes = {'input': ['md5'], 'format': 'misp_standard'}
18
+ moduleinfo = {
19
+ 'version': '0.3',
20
+ 'author': 'Koen Van Impe',
21
+ 'description': 'An expansion module to enrich attributes in MISP by quering the Cytomic Orion API',
22
+ 'module-type': ['expansion'],
23
+ 'name': 'Cytomic Orion Lookup',
24
+ 'logo': 'cytomic_orion.png',
25
+ 'requirements': ['Access (license) to Cytomic Orion'],
26
+ 'features': 'This module takes an MD5 hash and searches for occurrences of this hash in the Cytomic Orion database. Returns observed files and machines.',
27
+ 'references': ['https://www.vanimpe.eu/2020/03/10/integrating-misp-and-cytomic-orion/', 'https://www.cytomicmodel.com/solutions/'],
28
+ 'input': 'MD5, hash of the sample / malware to search for.',
29
+ 'output': 'MISP objects with sightings of the hash in Cytomic Orion. Includes files and machines.',
30
+ }
31
+ moduleconfig = ['api_url', 'token_url', 'clientid', 'clientsecret', 'clientsecret', 'username', 'password', 'upload_timeframe', 'upload_tag', 'delete_tag', 'upload_ttlDays', 'upload_threat_level_id', 'limit_upload_events', 'limit_upload_attributes']
32
+ # There are more config settings in this module than used by the enrichment
33
+ # There is also a PyMISP module which reuses the module config, and requires additional configuration, for example used for pushing indicators to the API
34
+
35
+
36
+ class CytomicParser():
37
+ def __init__(self, attribute, config_object):
38
+ self.misp_event = MISPEvent()
39
+ self.attribute = MISPAttribute()
40
+ self.attribute.from_dict(**attribute)
41
+ self.misp_event.add_attribute(**self.attribute)
42
+
43
+ self.config_object = config_object
44
+
45
+ if self.config_object:
46
+ self.token = self.get_token()
47
+ else:
48
+ sys.exit('Missing configuration')
49
+
50
+ def get_token(self):
51
+ try:
52
+ scope = self.config_object['scope']
53
+ grant_type = self.config_object['grant_type']
54
+ username = self.config_object['username']
55
+ password = self.config_object['password']
56
+ token_url = self.config_object['token_url']
57
+ clientid = self.config_object['clientid']
58
+ clientsecret = self.config_object['clientsecret']
59
+
60
+ if scope and grant_type and username and password:
61
+ data = {'scope': scope, 'grant_type': grant_type, 'username': username, 'password': password}
62
+
63
+ if token_url and clientid and clientsecret:
64
+ access_token_response = requests.post(token_url, data=data, verify=False, allow_redirects=False, auth=(clientid, clientsecret))
65
+ tokens = json.loads(access_token_response.text)
66
+ if 'access_token' in tokens:
67
+ return tokens['access_token']
68
+ else:
69
+ self.result = {'error': 'No token received.'}
70
+ return
71
+ else:
72
+ self.result = {'error': 'No token_url, clientid or clientsecret supplied.'}
73
+ return
74
+ else:
75
+ self.result = {'error': 'No scope, grant_type, username or password supplied.'}
76
+ return
77
+ except Exception:
78
+ self.result = {'error': 'Unable to connect to token_url.'}
79
+ return
80
+
81
+ def get_results(self):
82
+ if hasattr(self, 'result'):
83
+ return self.result
84
+ event = json.loads(self.misp_event.to_json())
85
+ results = {key: event[key] for key in ('Attribute', 'Object')}
86
+ return {'results': results}
87
+
88
+ def parse(self, searchkey):
89
+
90
+ if self.token:
91
+
92
+ endpoint_fileinformation = self.config_object['endpoint_fileinformation']
93
+ endpoint_machines = self.config_object['endpoint_machines']
94
+ endpoint_machines_client = self.config_object['endpoint_machines_client']
95
+ query_machines = self.config_object['query_machines']
96
+ query_machine_info = self.config_object['query_machine_info']
97
+
98
+ # Update endpoint URLs
99
+ query_endpoint_fileinformation = endpoint_fileinformation.format(md5=searchkey)
100
+ query_endpoint_machines = endpoint_machines.format(md5=searchkey)
101
+
102
+ # API calls
103
+ api_call_headers = {'Authorization': 'Bearer ' + self.token}
104
+ result_query_endpoint_fileinformation = requests.get(query_endpoint_fileinformation, headers=api_call_headers, verify=False)
105
+ json_result_query_endpoint_fileinformation = json.loads(result_query_endpoint_fileinformation.text)
106
+
107
+ if json_result_query_endpoint_fileinformation:
108
+
109
+ cytomic_object = MISPObject('cytomic-orion-file')
110
+
111
+ cytomic_object.add_attribute('fileName', type='text', value=json_result_query_endpoint_fileinformation['fileName'])
112
+ cytomic_object.add_attribute('fileSize', type='text', value=json_result_query_endpoint_fileinformation['fileSize'])
113
+ cytomic_object.add_attribute('last-seen', type='datetime', value=json_result_query_endpoint_fileinformation['lastSeen'])
114
+ cytomic_object.add_attribute('first-seen', type='datetime', value=json_result_query_endpoint_fileinformation['firstSeen'])
115
+ cytomic_object.add_attribute('classification', type='text', value=json_result_query_endpoint_fileinformation['classification'])
116
+ cytomic_object.add_attribute('classificationName', type='text', value=json_result_query_endpoint_fileinformation['classificationName'])
117
+ self.misp_event.add_object(**cytomic_object)
118
+
119
+ result_query_endpoint_machines = requests.get(query_endpoint_machines, headers=api_call_headers, verify=False)
120
+ json_result_query_endpoint_machines = json.loads(result_query_endpoint_machines.text)
121
+
122
+ if query_machines and json_result_query_endpoint_machines and len(json_result_query_endpoint_machines) > 0:
123
+ for machine in json_result_query_endpoint_machines:
124
+
125
+ if query_machine_info and machine['muid']:
126
+ query_endpoint_machines_client = endpoint_machines_client.format(muid=machine['muid'])
127
+ result_endpoint_machines_client = requests.get(query_endpoint_machines_client, headers=api_call_headers, verify=False)
128
+ json_result_endpoint_machines_client = json.loads(result_endpoint_machines_client.text)
129
+
130
+ if json_result_endpoint_machines_client:
131
+
132
+ cytomic_machine_object = MISPObject('cytomic-orion-machine')
133
+
134
+ clienttag = [{'name': json_result_endpoint_machines_client['clientName']}]
135
+
136
+ cytomic_machine_object.add_attribute('machineName', type='target-machine', value=json_result_endpoint_machines_client['machineName'], Tag=clienttag)
137
+ cytomic_machine_object.add_attribute('machineMuid', type='text', value=machine['muid'])
138
+ cytomic_machine_object.add_attribute('clientName', type='target-org', value=json_result_endpoint_machines_client['clientName'], Tag=clienttag)
139
+ cytomic_machine_object.add_attribute('clientId', type='text', value=machine['clientId'])
140
+ cytomic_machine_object.add_attribute('machinePath', type='text', value=machine['lastPath'])
141
+ cytomic_machine_object.add_attribute('first-seen', type='datetime', value=machine['firstSeen'])
142
+ cytomic_machine_object.add_attribute('last-seen', type='datetime', value=machine['lastSeen'])
143
+ cytomic_machine_object.add_attribute('creationDate', type='datetime', value=json_result_endpoint_machines_client['creationDate'])
144
+ cytomic_machine_object.add_attribute('clientCreationDateUTC', type='datetime', value=json_result_endpoint_machines_client['clientCreationDateUTC'])
145
+ cytomic_machine_object.add_attribute('lastSeenUtc', type='datetime', value=json_result_endpoint_machines_client['lastSeenUtc'])
146
+ self.misp_event.add_object(**cytomic_machine_object)
147
+ else:
148
+ self.result = {'error': 'No (valid) token.'}
149
+ return
150
+
151
+
152
+ def handler(q=False):
153
+ if q is False:
154
+ return False
155
+ request = json.loads(q)
156
+
157
+ if not request.get('attribute'):
158
+ return {'error': 'Unsupported input.'}
159
+
160
+ if not request.get('attribute') or not check_input_attribute(request['attribute']):
161
+ return {'error': f'{standard_error_message}, which should contain at least a type, a value and an uuid.'}
162
+ attribute = request['attribute']
163
+ if not any(input_type == attribute['type'] for input_type in mispattributes['input']):
164
+ return {'error': 'Unsupported attribute type.'}
165
+
166
+ if not request.get('config'):
167
+ return {'error': 'Missing configuration'}
168
+
169
+ config_object = {
170
+ 'clientid': request["config"].get("clientid"),
171
+ 'clientsecret': request["config"].get("clientsecret"),
172
+ 'scope': 'orion.api',
173
+ 'password': request["config"].get("password"),
174
+ 'username': request["config"].get("username"),
175
+ 'grant_type': 'password',
176
+ 'token_url': request["config"].get("token_url"),
177
+ 'endpoint_fileinformation': '{api_url}{endpoint}'.format(api_url=request["config"].get("api_url"), endpoint='/forensics/md5/{md5}/info'),
178
+ 'endpoint_machines': '{api_url}{endpoint}'.format(api_url=request["config"].get("api_url"), endpoint='/forensics/md5/{md5}/muids'),
179
+ 'endpoint_machines_client': '{api_url}{endpoint}'.format(api_url=request["config"].get("api_url"), endpoint='/forensics/muid/{muid}/info'),
180
+ 'query_machines': True,
181
+ 'query_machine_info': True
182
+ }
183
+
184
+ cytomic_parser = CytomicParser(attribute, config_object)
185
+ cytomic_parser.parse(attribute['value'])
186
+
187
+ return cytomic_parser.get_results()
188
+
189
+
190
+ def introspection():
191
+ return mispattributes
192
+
193
+
194
+ def version():
195
+ moduleinfo['config'] = moduleconfig
196
+ return moduleinfo
@@ -0,0 +1,78 @@
1
+ import json
2
+ import sys
3
+
4
+ try:
5
+ original_path = sys.path
6
+ sys.path = original_path[1:]
7
+ import dns.resolver
8
+ sys.path = original_path
9
+ resolver = dns.resolver.Resolver()
10
+ resolver.timeout = 0.2
11
+ resolver.lifetime = 0.2
12
+ except ImportError:
13
+ print("dnspython3 is missing, use 'pip install dnspython3' to install it.")
14
+ sys.exit(0)
15
+
16
+ misperrors = {'error': 'Error'}
17
+ mispattributes = {'input': ['domain', 'domain|ip', 'hostname', 'hostname|port'], 'output': ['text']}
18
+ moduleinfo = {
19
+ 'version': '0.1',
20
+ 'author': 'Christian Studer',
21
+ 'description': 'Checks Spamhaus DBL for a domain name.',
22
+ 'module-type': ['expansion', 'hover'],
23
+ 'name': 'DBL Spamhaus Lookup',
24
+ 'logo': 'spamhaus.jpg',
25
+ 'requirements': ['dnspython3: DNS python3 library'],
26
+ 'features': 'This modules takes a domain or a hostname in input and queries the Domain Block List provided by Spamhaus to determine what kind of domain it is.\n\nDBL then returns a response code corresponding to a certain classification of the domain we display. If the queried domain is not in the list, it is also mentionned.\n\nPlease note that composite MISP attributes containing domain or hostname are supported as well.',
27
+ 'references': ['https://www.spamhaus.org/faq/section/Spamhaus%20DBL'],
28
+ 'input': 'Domain or hostname attribute.',
29
+ 'output': 'Information about the nature of the input.',
30
+ }
31
+ moduleconfig = []
32
+
33
+ dbl = 'dbl.spamhaus.org'
34
+ dbl_mapping = {'127.0.1.2': 'spam domain',
35
+ '127.0.1.4': 'phish domain',
36
+ '127.0.1.5': 'malware domain',
37
+ '127.0.1.6': 'botnet C&C domain',
38
+ '127.0.1.102': 'abused legit spam',
39
+ '127.0.1.103': 'abused spammed redirector domain',
40
+ '127.0.1.104': 'abused legit phish',
41
+ '127.0.1.105': 'abused legit malware',
42
+ '127.0.1.106': 'abused legit botnet C&C',
43
+ '127.0.1.255': 'IP queries prohibited!'}
44
+
45
+
46
+ def fetch_requested_value(request):
47
+ for attribute_type in mispattributes['input']:
48
+ if request.get(attribute_type):
49
+ return request[attribute_type].split('|')[0]
50
+ return None
51
+
52
+
53
+ def handler(q=False):
54
+ if q is False:
55
+ return False
56
+ request = json.loads(q)
57
+ requested_value = fetch_requested_value(request)
58
+ if requested_value is None:
59
+ misperrors['error'] = "Unsupported attributes type"
60
+ return misperrors
61
+ query = "{}.{}".format(requested_value, dbl)
62
+ try:
63
+ query_result = resolver.resolve(query, 'A')[0]
64
+ result = "{} - {}".format(requested_value, dbl_mapping[str(query_result)])
65
+ except dns.resolver.NXDOMAIN as e:
66
+ result = e.msg
67
+ except Exception:
68
+ return {'error': 'Not able to reach dbl.spamhaus.org or something went wrong'}
69
+ return {'results': [{'types': mispattributes.get('output'), 'values': result}]}
70
+
71
+
72
+ def introspection():
73
+ return mispattributes
74
+
75
+
76
+ def version():
77
+ moduleinfo['config'] = moduleconfig
78
+ return moduleinfo
@@ -0,0 +1,71 @@
1
+ import json
2
+ import dns.resolver
3
+
4
+ misperrors = {'error': 'Error'}
5
+ mispattributes = {'input': ['hostname', 'domain', 'domain|ip'], 'output': ['ip-src',
6
+ 'ip-dst']}
7
+ moduleinfo = {
8
+ 'version': '0.3',
9
+ 'author': 'Alexandre Dulaunoy',
10
+ 'description': 'jj',
11
+ 'module-type': ['expansion', 'hover'],
12
+ 'name': 'DNS Resolver',
13
+ 'logo': '',
14
+ 'requirements': ['dnspython3: DNS python3 library'],
15
+ 'features': 'The module takes a domain of hostname attribute as input, and tries to resolve it. If no error is encountered, the IP address that resolves the domain is returned, otherwise the origin of the error is displayed.\n\nThe address of the DNS resolver to use is also configurable, but if no configuration is set, we use the Google public DNS address (8.8.8.8).\n\nPlease note that composite MISP attributes containing domain or hostname are supported as well.',
16
+ 'references': [],
17
+ 'input': 'Domain or hostname attribute.',
18
+ 'output': 'IP address resolving the input.',
19
+ }
20
+
21
+ moduleconfig = ['nameserver']
22
+
23
+
24
+ def handler(q=False):
25
+ if q is False:
26
+ return False
27
+ request = json.loads(q)
28
+ if request.get('hostname'):
29
+ toquery = request['hostname']
30
+ elif request.get('domain'):
31
+ toquery = request['domain']
32
+ elif request.get('domain|ip'):
33
+ toquery = request['domain|ip'].split('|')[0]
34
+ else:
35
+ return False
36
+ r = dns.resolver.Resolver()
37
+ r.timeout = 2
38
+ r.lifetime = 2
39
+
40
+ if request.get('config'):
41
+ if request['config'].get('nameserver'):
42
+ nameservers = []
43
+ nameservers.append(request['config'].get('nameserver'))
44
+ r.nameservers = nameservers
45
+ else:
46
+ r.nameservers = ['8.8.8.8']
47
+
48
+ try:
49
+ answer = r.resolve(toquery, 'A')
50
+ except dns.resolver.NXDOMAIN:
51
+ misperrors['error'] = "NXDOMAIN"
52
+ return misperrors
53
+ except dns.exception.Timeout:
54
+ misperrors['error'] = "Timeout"
55
+ return misperrors
56
+ except Exception as e:
57
+ misperrors['error'] = f'DNS resolving error {e}'
58
+ return misperrors
59
+
60
+ r = {'results': [{'types': mispattributes['output'],
61
+ 'values':[str(answer[0])]}]}
62
+ return r
63
+
64
+
65
+ def introspection():
66
+ return mispattributes
67
+
68
+
69
+ def version():
70
+ moduleinfo['config'] = moduleconfig
71
+ return moduleinfo
@@ -0,0 +1,71 @@
1
+ import json
2
+ import binascii
3
+ import np
4
+ import docx
5
+ import io
6
+
7
+ misperrors = {'error': 'Error'}
8
+ mispattributes = {'input': ['attachment'],
9
+ 'output': ['freetext', 'text']}
10
+ moduleinfo = {
11
+ 'version': '0.1',
12
+ 'author': 'Sascha Rommelfangen',
13
+ 'description': 'Module to extract freetext from a .docx document.',
14
+ 'module-type': ['expansion'],
15
+ 'name': 'DOCX Enrich',
16
+ 'logo': 'docx.png',
17
+ 'requirements': ['docx python library'],
18
+ 'features': 'The module reads the text contained in a .docx document. The result is passed to the freetext import parser so IoCs can be extracted out of it.',
19
+ 'references': [],
20
+ 'input': 'Attachment attribute containing a .docx document.',
21
+ 'output': 'Text and freetext parsed from the document.',
22
+ }
23
+
24
+ moduleconfig = []
25
+
26
+
27
+ def handler(q=False):
28
+ if q is False:
29
+ return False
30
+ q = json.loads(q)
31
+ filename = q['attachment']
32
+ try:
33
+ docx_array = np.frombuffer(binascii.a2b_base64(q['data']), np.uint8)
34
+ except Exception as e:
35
+ print(e)
36
+ err = "Couldn't fetch attachment (JSON 'data' is empty). Are you using the 'Query enrichment' action?"
37
+ misperrors['error'] = err
38
+ print(err)
39
+ return misperrors
40
+
41
+ doc_content = ""
42
+ doc_file = io.BytesIO(docx_array)
43
+ try:
44
+ doc = docx.Document(doc_file)
45
+ for para in doc.paragraphs:
46
+ print(para.text)
47
+ doc_content = doc_content + "\n" + para.text
48
+ tables = doc.tables
49
+ for table in tables:
50
+ for row in table.rows:
51
+ for cell in row.cells:
52
+ for para in cell.paragraphs:
53
+ print(para.text)
54
+ doc_content = doc_content + "\n" + para.text
55
+ print(doc_content)
56
+ return {'results': [{'types': ['freetext'], 'values': doc_content, 'comment': ".docx-to-text from file " + filename},
57
+ {'types': ['text'], 'values': doc_content, 'comment': ".docx-to-text from file " + filename}]}
58
+ except Exception as e:
59
+ print(e)
60
+ err = "Couldn't analyze file as .docx. Error was: " + str(e)
61
+ misperrors['error'] = err
62
+ return misperrors
63
+
64
+
65
+ def introspection():
66
+ return mispattributes
67
+
68
+
69
+ def version():
70
+ moduleinfo['config'] = moduleconfig
71
+ return moduleinfo