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,552 @@
1
+ '''
2
+ import
3
+ define mandatory
4
+
5
+ '''
6
+ import json
7
+ import base64
8
+ import re
9
+ import zipfile
10
+ import ipaddress
11
+ import io
12
+ import logging
13
+
14
+ misperrors = {'error': 'Error'}
15
+ userConfig = {}
16
+ inputSource = ['file']
17
+
18
+ moduleinfo = {
19
+ 'version': '0.10',
20
+ 'author': 'Christophe Vandeplas',
21
+ 'description': 'Module to import ThreatAnalyzer archive.zip / analysis.json files.',
22
+ 'module-type': ['import'],
23
+ 'name': 'ThreadAnalyzer Sandbox Import',
24
+ 'logo': '',
25
+ 'requirements': [],
26
+ 'features': 'The module imports MISP Attributes from a ThreatAnalyzer format file. This file can be either ZIP, or JSON format.\nThere is by the way no special feature for users to make the module work.',
27
+ 'references': ['https://www.threattrack.com/malware-analysis.aspx'],
28
+ 'input': 'ThreatAnalyzer format file',
29
+ 'output': 'MISP Event attributes',
30
+ }
31
+
32
+ moduleconfig = []
33
+ log = logging.getLogger('misp-modules')
34
+
35
+ # FIXME - many hardcoded filters should be migrated to import regexes. See also https://github.com/MISP/MISP/issues/2712
36
+ # DISCLAIMER - This module is to be considered as experimental and needs much fine-tuning.
37
+ # more can be done with what's in the ThreatAnalyzer archive.zip
38
+
39
+
40
+ def handler(q=False):
41
+ if q is False:
42
+ return False
43
+ results = []
44
+ zip_starts = 'PK'
45
+ request = json.loads(q)
46
+ data = base64.b64decode(request['data'])
47
+
48
+ if data[:len(zip_starts)].decode() == zip_starts:
49
+ with zipfile.ZipFile(io.BytesIO(data), 'r') as zf:
50
+ # unzipped_files = []
51
+ modified_files_mapping = {}
52
+ # pre-process some of the files in the zip
53
+ for zip_file_name in zf.namelist(): # Get all files in the zip file
54
+ # find the filenames of the modified_files
55
+ if re.match(r"Analysis/proc_\d+/modified_files/mapping\.log", zip_file_name):
56
+ with zf.open(zip_file_name, mode='r', pwd=None) as fp:
57
+ file_data = fp.read()
58
+ for line in file_data.decode("utf-8", 'ignore').split('\n'):
59
+ if not line:
60
+ continue
61
+ if line.count('|') == 3:
62
+ l_fname, l_size, l_md5, l_created = line.split('|')
63
+ if line.count('|') == 4:
64
+ l_fname, l_size, l_md5, l_sha256, l_created = line.split('|')
65
+ l_fname = cleanup_filepath(l_fname)
66
+ if l_fname:
67
+ if l_size == 0:
68
+ results.append({'values': l_fname, 'type': 'filename', 'to_ids': True,
69
+ 'categories': ['Artifacts dropped', 'Payload delivery'], 'comment': ''})
70
+ else:
71
+ # file is a non empty sample, upload the sample later
72
+ modified_files_mapping[l_md5] = l_fname
73
+
74
+ # now really process the data
75
+ for zip_file_name in zf.namelist(): # Get all files in the zip file
76
+ # print('Processing file: {}'.format(zip_file_name))
77
+ if re.match(r"Analysis/proc_\d+/modified_files/.+\.", zip_file_name) and "mapping.log" not in zip_file_name:
78
+ sample_md5 = zip_file_name.split('/')[-1].split('.')[0]
79
+ if sample_md5 in modified_files_mapping:
80
+ current_sample_filename = modified_files_mapping[sample_md5]
81
+ # print("{} maps to {}".format(sample_md5, current_sample_filename))
82
+ with zf.open(zip_file_name, mode='r', pwd=None) as fp:
83
+ file_data = fp.read()
84
+ results.append({
85
+ 'values': current_sample_filename,
86
+ 'data': base64.b64encode(file_data).decode(),
87
+ 'type': 'malware-sample', 'categories': ['Artifacts dropped', 'Payload delivery'], 'to_ids': True, 'comment': ''})
88
+
89
+ if 'Analysis/analysis.json' in zip_file_name:
90
+ with zf.open(zip_file_name, mode='r', pwd=None) as fp:
91
+ file_data = fp.read()
92
+ analysis_json = json.loads(file_data.decode('utf-8'))
93
+ results += process_analysis_json(analysis_json)
94
+ try:
95
+ sample_filename = analysis_json.get('analysis').get('@filename')
96
+ if sample_filename:
97
+ with zf.open('sample', mode='r', pwd=None) as fp:
98
+ file_data = fp.read()
99
+ results.append({
100
+ 'values': sample_filename,
101
+ 'data': base64.b64encode(file_data).decode(),
102
+ 'type': 'malware-sample', 'categories': ['Payload delivery', 'Artifacts dropped'], 'to_ids': True, 'comment': ''})
103
+ except Exception:
104
+ # no 'sample' in archive, might be an url analysis, just ignore
105
+ pass
106
+
107
+ else:
108
+ try:
109
+ results = process_analysis_json(json.loads(data.decode('utf-8')))
110
+ except ValueError:
111
+ log.warning('MISP modules {0} failed: uploaded file is not a zip or json file.'.format(request['module']))
112
+ return {'error': 'Uploaded file is not a zip or json file.'}
113
+ pass
114
+ # keep only unique entries based on the value field
115
+ results = list({v['values']: v for v in results}.values())
116
+ r = {'results': results}
117
+ return r
118
+
119
+
120
+ def process_analysis_json(analysis_json):
121
+ if 'analysis' in analysis_json and 'processes' in analysis_json['analysis'] and 'process' in analysis_json['analysis']['processes']:
122
+ # if 'analysis' in analysis_json and '@filename' in analysis_json['analysis']:
123
+ # sample['values'] = analysis_json['analysis']['@filename']
124
+ for process in analysis_json['analysis']['processes']['process']:
125
+ # print_json(process)
126
+ if 'connection_section' in process and 'connection' in process['connection_section']:
127
+ # compensate for absurd behavior of the data format: if one entry = immediately the dict, if multiple entries = list containing dicts
128
+ # this will always create a list, even with only one item
129
+ if isinstance(process['connection_section']['connection'], dict):
130
+ process['connection_section']['connection'] = [process['connection_section']['connection']]
131
+
132
+ # iterate over each entry
133
+ for connection_section_connection in process['connection_section']['connection']:
134
+ # compensate for absurd behavior of the data format: if one entry = immediately the dict, if multiple entries = list containing dicts
135
+ # this will always create a list, even with only one item
136
+ for subsection in ['http_command', 'http_header']:
137
+ if isinstance(connection_section_connection[subsection], dict):
138
+ connection_section_connection[subsection] = [connection_section_connection[subsection]]
139
+
140
+ if 'name_to_ip' in connection_section_connection: # TA 6.1 data format
141
+ connection_section_connection['@remote_ip'] = connection_section_connection['name_to_ip']['@result_addresses']
142
+ connection_section_connection['@remote_hostname'] = connection_section_connection['name_to_ip']['@request_name']
143
+
144
+ connection_section_connection['@remote_ip'] = cleanup_ip(connection_section_connection['@remote_ip'])
145
+ connection_section_connection['@remote_hostname'] = cleanup_hostname(connection_section_connection['@remote_hostname'])
146
+ if connection_section_connection['@remote_ip'] and connection_section_connection['@remote_hostname']:
147
+ val = '{}|{}'.format(connection_section_connection['@remote_hostname'],
148
+ connection_section_connection['@remote_ip'])
149
+ # print("connection_section_connection hostname|ip: {}|{} IDS:yes".format(
150
+ # connection_section_connection['@remote_hostname'],
151
+ # connection_section_connection['@remote_ip'])
152
+ # )
153
+ yield({'values': val, 'type': 'domain|ip', 'categories': ['Network activity'], 'to_ids': True, 'comment': ''})
154
+ elif connection_section_connection['@remote_ip']:
155
+ # print("connection_section_connection ip-dst: {} IDS:yes".format(
156
+ # connection_section_connection['@remote_ip'])
157
+ # )
158
+ yield({'values': connection_section_connection['@remote_ip'], 'type': 'ip-dst', 'to_ids': True, 'comment': ''})
159
+ elif connection_section_connection['@remote_hostname']:
160
+ # print("connection_section_connection hostname: {} IDS:yes".format(
161
+ # connection_section_connection['@remote_hostname'])
162
+ # )
163
+ yield({'values': connection_section_connection['@remote_hostname'], 'type': 'hostname', 'to_ids': True, 'comment': ''})
164
+ if 'http_command' in connection_section_connection:
165
+ for http_command in connection_section_connection['http_command']:
166
+ # print('connection_section_connection HTTP COMMAND: {}\t{}'.format(
167
+ # connection_section_connection['http_command']['@method'], # comment
168
+ # connection_section_connection['http_command']['@url']) # url
169
+ # )
170
+ val = cleanup_url(http_command['@url'])
171
+ if val:
172
+ yield({'values': val, 'type': 'url', 'categories': ['Network activity'], 'to_ids': True, 'comment': http_command['@method']})
173
+
174
+ if 'http_header' in connection_section_connection:
175
+ for http_header in connection_section_connection['http_header']:
176
+ if 'User-Agent:' in http_header['@header']:
177
+ val = http_header['@header'][len('User-Agent: '):]
178
+ yield({'values': val, 'type': 'user-agent', 'categories': ['Network activity'], 'to_ids': False, 'comment': ''})
179
+ elif 'Host:' in http_header['@header']:
180
+ val = http_header['@header'][len('Host: '):]
181
+ if ':' in val:
182
+ try:
183
+ val_port = int(val.split(':')[1])
184
+ except ValueError:
185
+ val_port = False
186
+ val_hostname = cleanup_hostname(val.split(':')[0])
187
+ val_ip = cleanup_ip(val.split(':')[0])
188
+ if val_hostname and val_port:
189
+ val_combined = '{}|{}'.format(val_hostname, val_port)
190
+ # print({'values': val_combined, 'type': 'hostname|port', 'to_ids': True, 'comment': ''})
191
+ yield({'values': val_combined, 'type': 'hostname|port', 'categories': ['Network activity'], 'to_ids': True, 'comment': ''})
192
+ elif val_ip and val_port:
193
+ val_combined = '{}|{}'.format(val_ip, val_port)
194
+ # print({'values': val_combined, 'type': 'ip-dst|port', 'to_ids': True, 'comment': ''})
195
+ yield({'values': val_combined, 'type': 'ip-dst|port', 'to_ids': True, 'comment': ''})
196
+ else:
197
+ continue
198
+ val_hostname = cleanup_hostname(val)
199
+ if val_hostname:
200
+ # print({'values': val_hostname, 'type': 'hostname', 'to_ids': True, 'comment': ''})
201
+ yield({'values': val_hostname, 'type': 'hostname', 'to_ids': True, 'comment': ''})
202
+ else:
203
+ # LATER header not processed
204
+ pass
205
+ if 'filesystem_section' in process and 'create_file' in process['filesystem_section']:
206
+ for filesystem_section_create_file in process['filesystem_section']['create_file']:
207
+ # first skip some items
208
+ if filesystem_section_create_file['@create_disposition'] in {'FILE_OPEN_IF'}:
209
+ continue
210
+ # FIXME - this section is probably not needed considering the 'stored_files stored_created_file' section we process later.
211
+ # print('CREATE FILE: {}\t{}'.format(
212
+ # filesystem_section_create_file['@srcfile'], # filename
213
+ # filesystem_section_create_file['@create_disposition']) # comment - use this to filter out cases
214
+ # )
215
+
216
+ if 'networkoperation_section' in process and 'dns_request_by_addr' in process['networkoperation_section']:
217
+ for networkoperation_section_dns_request_by_addr in process['networkoperation_section']['dns_request_by_addr']:
218
+ # FIXME - it's unclear what this section is for.
219
+ # TODO filter this
220
+ # print('DNS REQUEST: {}\t{}'.format(
221
+ # networkoperation_section_dns_request_by_addr['@request_address'], # ip-dst
222
+ # networkoperation_section_dns_request_by_addr['@result_name']) # hostname
223
+ # ) # => NOT hostname|ip
224
+ pass
225
+ if 'networkoperation_section' in process and 'dns_request_by_name' in process['networkoperation_section']:
226
+ for networkoperation_section_dns_request_by_name in process['networkoperation_section']['dns_request_by_name']:
227
+ networkoperation_section_dns_request_by_name['@request_name'] = cleanup_hostname(networkoperation_section_dns_request_by_name['@request_name'].rstrip('.'))
228
+ networkoperation_section_dns_request_by_name['@result_addresses'] = cleanup_ip(networkoperation_section_dns_request_by_name['@result_addresses'])
229
+ if networkoperation_section_dns_request_by_name['@request_name'] and networkoperation_section_dns_request_by_name['@result_addresses']:
230
+ val = '{}|{}'.format(networkoperation_section_dns_request_by_name['@request_name'],
231
+ networkoperation_section_dns_request_by_name['@result_addresses'])
232
+ # print("networkoperation_section_dns_request_by_name hostname|ip: {}|{} IDS:yes".format(
233
+ # networkoperation_section_dns_request_by_name['@request_name'],
234
+ # networkoperation_section_dns_request_by_name['@result_addresses'])
235
+ # )
236
+ yield({'values': val, 'type': 'domain|ip', 'categories': ['Network activity'], 'to_ids': True, 'comment': ''})
237
+ elif networkoperation_section_dns_request_by_name['@request_name']:
238
+ # print("networkoperation_section_dns_request_by_name hostname: {} IDS:yes".format(
239
+ # networkoperation_section_dns_request_by_name['@request_name'])
240
+ # )
241
+ yield({'values': networkoperation_section_dns_request_by_name['@request_name'], 'type': 'hostname', 'to_ids': True, 'comment': ''})
242
+ elif networkoperation_section_dns_request_by_name['@result_addresses']:
243
+ # this happens when the IP is both in the request_name and result_address.
244
+ # print("networkoperation_section_dns_request_by_name hostname: {} IDS:yes".format(
245
+ # networkoperation_section_dns_request_by_name['@result_addresses'])
246
+ # )
247
+ yield({'values': networkoperation_section_dns_request_by_name['@result_addresses'], 'type': 'ip-dst', 'to_ids': True, 'comment': ''})
248
+
249
+ if 'networkpacket_section' in process and 'connect_to_computer' in process['networkpacket_section']:
250
+ for networkpacket_section_connect_to_computer in process['networkpacket_section']['connect_to_computer']:
251
+ networkpacket_section_connect_to_computer['@remote_hostname'] = cleanup_hostname(networkpacket_section_connect_to_computer['@remote_hostname'])
252
+ networkpacket_section_connect_to_computer['@remote_ip'] = cleanup_ip(networkpacket_section_connect_to_computer['@remote_ip'])
253
+ if networkpacket_section_connect_to_computer['@remote_hostname'] and networkpacket_section_connect_to_computer['@remote_ip']:
254
+ # print("networkpacket_section_connect_to_computer hostname|ip: {}|{} IDS:yes COMMENT:port {}".format(
255
+ # networkpacket_section_connect_to_computer['@remote_hostname'],
256
+ # networkpacket_section_connect_to_computer['@remote_ip'],
257
+ # networkpacket_section_connect_to_computer['@remote_port'])
258
+ # )
259
+ val_combined = "{}|{}".format(networkpacket_section_connect_to_computer['@remote_hostname'], networkpacket_section_connect_to_computer['@remote_ip'])
260
+ yield({'values': val_combined, 'type': 'domain|ip', 'to_ids': True, 'comment': ''})
261
+ elif networkpacket_section_connect_to_computer['@remote_hostname']:
262
+ # print("networkpacket_section_connect_to_computer hostname: {} IDS:yes COMMENT:port {}".format(
263
+ # networkpacket_section_connect_to_computer['@remote_hostname'],
264
+ # networkpacket_section_connect_to_computer['@remote_port'])
265
+ # )
266
+ val_combined = "{}|{}".format(networkpacket_section_connect_to_computer['@remote_hostname'], networkpacket_section_connect_to_computer['@remote_port'])
267
+ yield({'values': val_combined, 'type': 'hostname|port', 'categories': ['Network activity'], 'to_ids': True, 'comment': ''})
268
+ elif networkpacket_section_connect_to_computer['@remote_ip']:
269
+ # print("networkpacket_section_connect_to_computer ip-dst: {} IDS:yes COMMENT:port {}".format(
270
+ # networkpacket_section_connect_to_computer['@remote_ip'],
271
+ # networkpacket_section_connect_to_computer['@remote_port'])
272
+ # )
273
+ val_combined = "{}|{}".format(networkpacket_section_connect_to_computer['@remote_ip'], networkpacket_section_connect_to_computer['@remote_port'])
274
+ yield({'values': val_combined, 'type': 'ip-dst|port', 'to_ids': True, 'comment': ''})
275
+
276
+ if 'registry_section' in process and 'create_key' in process['registry_section']:
277
+ # FIXME this is a complicated section, together with the 'set_value'.
278
+ # it looks like this section is not ONLY about creating registry keys,
279
+ # more about accessing a handle to keys (with specific permissions)
280
+ # maybe we don't want to keep this, in favor of 'set_value'
281
+ for create_key in process['registry_section']['create_key']:
282
+ # print('REG CREATE: {}\t{}'.format(
283
+ # create_key['@desired_access'],
284
+ # create_key['@key_name']))
285
+ pass
286
+ if 'registry_section' in process and 'delete_key' in process['registry_section']:
287
+ # LATER we probably don't want to keep this. Much pollution.
288
+ # Maybe for later once we have filtered out this.
289
+ for delete_key in process['registry_section']['delete_key']:
290
+ # print('REG DELETE: {}'.format(
291
+ # delete_key['@key_name'])
292
+ # )
293
+ pass
294
+ if 'registry_section' in process and 'set_value' in process['registry_section']:
295
+ # FIXME this is a complicated section, together with the 'create_key'.
296
+ for set_value in process['registry_section']['set_value']:
297
+ # '@data_type' == 'REG_BINARY',
298
+ # '@data_type' == 'REG_DWORD',
299
+ # '@data_type' == 'REG_EXPAND_SZ',
300
+ # '@data_type' == 'REG_MULTI_SZ',
301
+ # '@data_type' == 'REG_NONE',
302
+ # '@data_type' == 'REG_QWORD',
303
+ # '@data_type' == 'REG_SZ',
304
+ regkey = cleanup_regkey("{}\\{}".format(set_value['@key_name'], set_value['@value_name']))
305
+ regdata = cleanup_regdata(set_value.get('@data'))
306
+ if not regkey:
307
+ continue
308
+ if set_value['@data_size'] == '0' or not regdata:
309
+ # print('registry_section set_value REG SET: {}\t{}\t{}'.format(
310
+ # set_value['@data_type'],
311
+ # set_value['@key_name'],
312
+ # set_value['@value_name'])
313
+ # )
314
+ yield({'values': regkey, 'type': 'regkey', 'to_ids': True,
315
+ 'categories': ['External analysis', 'Persistence mechanism', 'Artifacts dropped'], 'comment': set_value['@data_type']})
316
+ else:
317
+ try:
318
+ # unicode fun...
319
+ # print('registry_section set_value REG SET: {}\t{}\t{}\t{}'.format(
320
+ # set_value['@data_type'],
321
+ # set_value['@key_name'],
322
+ # set_value['@value_name'],
323
+ # set_value['@data'])
324
+ # )
325
+ val = "{}|{}".format(regkey, regdata)
326
+ yield({'values': val, 'type': 'regkey|value', 'to_ids': True,
327
+ 'categories': ['External analysis', 'Persistence mechanism', 'Artifacts dropped'], 'comment': set_value['@data_type']})
328
+ except Exception as e:
329
+ print("EXCEPTION registry_section {}".format(e))
330
+ # TODO - maybe we want to handle these later, or not...
331
+ pass
332
+ pass
333
+
334
+ if 'stored_files' in process and 'stored_created_file' in process['stored_files']:
335
+ for stored_created_file in process['stored_files']['stored_created_file']:
336
+ stored_created_file['@filename'] = cleanup_filepath(stored_created_file['@filename'])
337
+ if stored_created_file['@filename']:
338
+ if stored_created_file['@filesize'] != '0':
339
+ val = '{}|{}'.format(stored_created_file['@filename'], stored_created_file['@md5'])
340
+ # print("stored_created_file filename|md5: {}|{} IDS:yes".format(
341
+ # stored_created_file['@filename'], # filename
342
+ # stored_created_file['@md5']) # md5
343
+ # ) # => filename|md5
344
+ yield({'values': val, 'type': 'filename|md5', 'to_ids': True,
345
+ 'categories': ['Artifacts dropped', 'Payload delivery'], 'comment': ''})
346
+
347
+ else:
348
+ # print("stored_created_file filename: {} IDS:yes".format(
349
+ # stored_created_file['@filename']) # filename
350
+ # ) # => filename
351
+ yield({'values': stored_created_file['@filename'],
352
+ 'type': 'filename', 'to_ids': True,
353
+ 'categories': ['Artifacts dropped', 'Payload delivery'], 'comment': ''})
354
+
355
+ if 'stored_files' in process and 'stored_modified_file' in process['stored_files']:
356
+ for stored_modified_file in process['stored_files']['stored_modified_file']:
357
+ stored_modified_file['@filename'] = cleanup_filepath(stored_modified_file['@filename'])
358
+ if stored_modified_file['@filename']:
359
+ if stored_modified_file['@filesize'] != '0':
360
+ val = '{}|{}'.format(stored_modified_file['@filename'], stored_modified_file['@md5'])
361
+ # print("stored_modified_file MODIFY FILE: {}\t{}".format(
362
+ # stored_modified_file['@filename'], # filename
363
+ # stored_modified_file['@md5']) # md5
364
+ # ) # => filename|md5
365
+ yield({'values': val, 'type': 'filename|md5', 'to_ids': True,
366
+ 'categories': ['Artifacts dropped', 'Payload delivery'],
367
+ 'comment': 'modified'})
368
+ else:
369
+ # print("stored_modified_file MODIFY FILE: {}\t{}".format(
370
+ # stored_modified_file['@filename']) # filename
371
+ # ) # => filename
372
+ yield({'values': stored_modified_file['@filename'], 'type': 'filename', 'to_ids': True,
373
+ 'categories': ['Artifacts dropped', 'Payload delivery'],
374
+ 'comment': 'modified'})
375
+
376
+
377
+ def add_file(filename, results, hash, index, filedata=None):
378
+ pass
379
+ # results.append({'values': filename, 'data': "{}|{}".format(filename, filedata.decode()), 'type': 'malware-sample',
380
+ # 'categories': ['Artifacts dropped', 'Payload delivery']})
381
+
382
+
383
+ def add_file_zip():
384
+ # if 'malware-sample' in request:
385
+ # sample_filename = request.get("malware-sample").split("|", 1)[0]
386
+ # data = base64.b64decode(data)
387
+ # fl = io.BytesIO(data)
388
+ # zf = zipfile.ZipFile(fl)
389
+ # sample_hashname = zf.namelist()[0]
390
+ # data = zf.read(sample_hashname, b"infected")
391
+ # zf.close()
392
+ pass
393
+
394
+
395
+ def print_json(data):
396
+ print(json.dumps(data, sort_keys=True, indent=4, separators=(',', ': ')))
397
+
398
+
399
+ def list_in_string(lst, data, regex=False):
400
+ for item in lst:
401
+ if regex:
402
+ if re.search(item, data, flags=re.IGNORECASE):
403
+ return True
404
+ else:
405
+ if item in data:
406
+ return True
407
+
408
+
409
+ def cleanup_ip(item):
410
+ # you should exclude private IP ranges via import regexes
411
+ noise_substrings = {
412
+ '224.0.0.',
413
+ '127.0.0.',
414
+ '8.8.8.8',
415
+ '8.8.4.4',
416
+ '0.0.0.0',
417
+ 'NONE'
418
+ }
419
+ if list_in_string(noise_substrings, item):
420
+ return None
421
+ try:
422
+ ipaddress.ip_address(item)
423
+ return item
424
+ except ValueError:
425
+ return None
426
+
427
+
428
+ def cleanup_hostname(item):
429
+ noise_substrings = {
430
+ 'wpad',
431
+ 'teredo.ipv6.microsoft.com',
432
+ 'WIN7SP1-x64-UNP'
433
+ }
434
+ # take away common known bad
435
+ if list_in_string(noise_substrings, item):
436
+ return None
437
+ # eliminate IP addresses
438
+ try:
439
+ ipaddress.ip_address(item)
440
+ except ValueError:
441
+ # this is not an IP, so continue
442
+ return item
443
+ return None
444
+
445
+
446
+ def cleanup_url(item):
447
+ if item in ['/']:
448
+ return None
449
+ return item
450
+
451
+
452
+ def cleanup_filepath(item):
453
+ noise_substrings = {
454
+ '\\AppData\\Local\\GDIPFONTCACHEV1.DAT',
455
+ '\\AppData\\Local\\Microsoft\\Internet Explorer\\DOMStore\\',
456
+ '\\AppData\\Local\\Microsoft\\Internet Explorer\\Recovery\\High\\',
457
+ '\\AppData\\Local\\Microsoft\\Windows\\Caches\\',
458
+ '\\AppData\\Local\\Microsoft\\Windows\\Explorer\\thumbcache',
459
+ '\\AppData\\Local\\Microsoft\\Windows\\History\\History.',
460
+ '\\AppData\\Local\\Microsoft\\Windows\\Temporary Internet Files\\Content.',
461
+ '\\AppData\\Local\\Microsoft\\Windows\\WebCache\\',
462
+ '\\AppData\\Local\\Temp\\.*tmp$',
463
+ '\\AppData\\LocalLow\\Microsoft\\CryptnetUrlCache\\',
464
+ '\\AppData\\LocalLow\\Microsoft\\Internet Explorer\\Services\\search_',
465
+ '\\AppData\\Roaming\\Microsoft\\Office\\Recent\\',
466
+ '\\AppData\\Roaming\\Microsoft\\Windows\\Cookies\\',
467
+ '\\AppData\\Roaming\\Microsoft\\Windows\\Recent\\',
468
+ 'C:\\ProgramData\\Microsoft\\OfficeSoftwareProtectionPlatform\\Cache\\cache.dat',
469
+ 'C:\\Windows\\Prefetch\\',
470
+
471
+ '\\AppData\\Roaming\\Adobe\\Acrobat\\9.0\\SharedDataEvents-journal',
472
+ '\\AppData\\Roaming\\Adobe\\Acrobat\\9.0\\UserCache.bin',
473
+
474
+ '\\AppData\\Roaming\\Macromedia\\Flash Player\\macromedia.com\\support\\flashplayer\\sys\\settings.sol',
475
+ '\\AppData\\Roaming\\Adobe\\Flash Player\\NativeCache\\',
476
+ 'C:\\Windows\\AppCompat\\Programs\\',
477
+ 'C:\\~' # caused by temp file created by MS Office when opening malicious doc/xls/...
478
+ }
479
+ if list_in_string(noise_substrings, item):
480
+ return None
481
+ return item
482
+
483
+
484
+ def cleanup_regkey(item):
485
+ noise_substrings = {
486
+ r'\\CurrentVersion\\Explorer\\FileExts\\[a-z\.]+\\OpenWith',
487
+ r'\\CurrentVersion\\Explorer\\RecentDocs\\',
488
+ r'\\CurrentVersion\\Explorer\\UserAssist\\',
489
+ r'\\Local Settings\\Software\\Microsoft\\Windows\\Shell\\Bag',
490
+ r'\\Software\\Classes\\CLSID\\',
491
+ r'\\Software\\Classes\\Local Settings\\MuiCache\\',
492
+ r'\\Software\\Microsoft\\Internet Explorer\\Main\\WindowsSearch',
493
+ r'\\Software\\Microsoft\\Office\\[0-9\.]+\\',
494
+ r'\\Software\\Microsoft\\Office\\Common\\Smart Tag\\',
495
+ r'\\Software\\Microsoft\\OfficeSoftwareProtectionPlatform\\',
496
+ r'\\Software\\Microsoft\\Shared Tools\\Panose\\',
497
+ r'\\Software\\Microsoft\\Tracing\\',
498
+ r'\\Software\\Microsoft\\Tracing\\powershell_RASAPI32\\',
499
+ r'\\Software\\Microsoft\\Tracing\\powershell_RASMANCS\\',
500
+ r'\\Software\\Microsoft\\Windows\\CurrentVersion\\Action Center\\',
501
+ r'\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU\\',
502
+ r'\\Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\',
503
+ r'\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\',
504
+ r'\\System\\CurrentControlSet\\Services\\RdyBoost\\',
505
+ r'\\Usage\\SpellingAndGrammarFiles'
506
+ }
507
+ if list_in_string(noise_substrings, item, regex=True):
508
+ return None
509
+ return item
510
+
511
+
512
+ def cleanup_regdata(item):
513
+ if not item:
514
+ return None
515
+ item = item.replace('(UNICODE_0x00000000)', '')
516
+ return item
517
+
518
+
519
+ def get_zipped_contents(filename, data, password=None):
520
+ with zipfile.ZipFile(io.BytesIO(data), 'r') as zf:
521
+ unzipped_files = []
522
+ if password is not None:
523
+ password = str.encode(password) # Byte encoded password required
524
+ for zip_file_name in zf.namelist(): # Get all files in the zip file
525
+ # print(zip_file_name)
526
+ with zf.open(zip_file_name, mode='r', pwd=password) as fp:
527
+ file_data = fp.read()
528
+ unzipped_files.append({'values': zip_file_name,
529
+ 'data': file_data,
530
+ 'comment': 'Extracted from {0}'.format(filename)})
531
+ # print("{} : {}".format(zip_file_name, len(file_data)))
532
+ return unzipped_files
533
+
534
+
535
+ def introspection():
536
+ modulesetup = {}
537
+ try:
538
+ userConfig
539
+ modulesetup['userConfig'] = userConfig
540
+ except NameError:
541
+ pass
542
+ try:
543
+ inputSource
544
+ modulesetup['inputSource'] = inputSource
545
+ except NameError:
546
+ pass
547
+ return modulesetup
548
+
549
+
550
+ def version():
551
+ moduleinfo['config'] = moduleconfig
552
+ return moduleinfo
@@ -0,0 +1,97 @@
1
+ import json
2
+ import base64
3
+ from pymisp import MISPEvent, MISPObject, MISPAttribute
4
+ from pyfaup.faup import Faup
5
+
6
+ misperrors = {'error': 'Error'}
7
+ userConfig = {
8
+ 'include_scheme': {
9
+ 'type': 'Boolean',
10
+ 'message': 'Include scheme'
11
+ },
12
+ }
13
+
14
+ mispattributes = {
15
+ 'inputSource': ['file', 'paste'],
16
+ 'output': ['MISP Format'],
17
+ 'format': 'misp_standard'
18
+ }
19
+
20
+
21
+ moduleinfo = {
22
+ 'version': '0.1',
23
+ 'author': 'Sami Mokaddem',
24
+ 'description': 'Simple URL import tool with Faup',
25
+ 'module-type': ['import'],
26
+ 'name': 'URL Import',
27
+ 'logo': '',
28
+ 'requirements': [],
29
+ 'features': '',
30
+ 'references': [],
31
+ 'input': '',
32
+ 'output': '',
33
+ }
34
+
35
+ moduleconfig = []
36
+
37
+ fp = Faup()
38
+
39
+
40
+ def generateData(event, data, config):
41
+ for url in data.splitlines():
42
+ fp.decode(url)
43
+ parsed = fp.get()
44
+ obj = MISPObject('url')
45
+ obj.add_attribute('url', type='url', value=url)
46
+ if parsed['tld'] is not None:
47
+ obj.add_attribute('tld', type='text', value=parsed['tld'])
48
+ if parsed['subdomain'] is not None:
49
+ obj.add_attribute('subdomain', type='text', value=parsed['subdomain'])
50
+ if config['include_scheme'] is True:
51
+ obj.add_attribute('scheme', type='text', value=parsed['scheme'])
52
+ obj.add_attribute('resource_path', type='text', value=parsed['resource_path'])
53
+ obj.add_attribute('query_string', type='text', value=parsed['query_string'])
54
+ obj.add_attribute('port', type='port', value=parsed['port'])
55
+ obj.add_attribute('host', type='hostname', value=parsed['host'])
56
+ if parsed['fragment'] is not None:
57
+ obj.add_attribute('fragment', type='text', value=parsed['fragment'])
58
+ obj.add_attribute('domain_without_tld', type='text', value=parsed['domain_without_tld'])
59
+ obj.add_attribute('domain', type='domain', value=parsed['domain'])
60
+ event.objects.append(obj)
61
+
62
+
63
+ def handler(q=False):
64
+ if q is False:
65
+ return False
66
+ request = json.loads(q)
67
+ data = getUploadedData(request)
68
+ config = getPassedConfig(request)
69
+ event = MISPEvent()
70
+ generateData(event, data, config)
71
+ return {"results": json.loads(event.to_json())}
72
+
73
+
74
+ def getUploadedData(request):
75
+ return base64.b64decode(request['data']).decode('utf8')
76
+
77
+
78
+ def getPassedConfig(request):
79
+ for k, v in userConfig.items():
80
+ if v['type'] == 'Boolean':
81
+ request['config'][k] = True if request['config'][k] == '1' else False
82
+ return request['config']
83
+
84
+
85
+ def introspection():
86
+ modulesetup = mispattributes
87
+ try:
88
+ userConfig
89
+ modulesetup['userConfig'] = userConfig
90
+ except NameError:
91
+ pass
92
+ return modulesetup
93
+
94
+
95
+ def version():
96
+ moduleinfo['config'] = moduleconfig
97
+ return moduleinfo