forked from phantomcyber/playbooks
-
Notifications
You must be signed in to change notification settings - Fork 0
/
email_notification_for_malware.py
339 lines (258 loc) · 14.1 KB
/
email_notification_for_malware.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
"""
This playbook tries to determine if a file is malware and whether or not the file is present on any managed machines. VirusTotal "file reputation" and PAN WildFire "detonate file" are used to determine if a file is malware, and CarbonBlack Response "hunt file" is used to search managed machines for the file. The results of these investigations are summarized in an email to the incident response team.
"""
import phantom.rules as phantom
import json
from datetime import datetime, timedelta
def on_start(container):
phantom.debug('on_start() called')
# call 'filter_1' block
filter_1(container=container)
return
"""
Run a reputation lookup on the fileHash to determine how many antivirus engines recognize it as malware.
"""
def file_reputation_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('file_reputation_1() called')
# collect data for 'file_reputation_1' call
filtered_artifacts_data_1 = phantom.collect2(container=container, datapath=['filtered-data:filter_1:condition_1:artifact:*.cef.fileHash', 'filtered-data:filter_1:condition_1:artifact:*.id'])
parameters = []
# build parameters list for 'file_reputation_1' call
for filtered_artifacts_item_1 in filtered_artifacts_data_1:
if filtered_artifacts_item_1[0]:
parameters.append({
'hash': filtered_artifacts_item_1[0],
# context (artifact id) is added to associate results with the artifact
'context': {'artifact_id': filtered_artifacts_item_1[1]},
})
phantom.act("file reputation", parameters=parameters, assets=['virustotal'], callback=filter_2, name="file_reputation_1")
return
"""
Send the formatted string as an email.
"""
def send_email_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('send_email_1() called')
#phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
# collect data for 'send_email_1' call
formatted_data_1 = phantom.get_format_data(name='format_for_emailer')
parameters = []
# build parameters list for 'send_email_1' call
parameters.append({
'body': formatted_data_1,
'to': "[email protected]",
'from': "[email protected]",
'attachments': "",
'subject': "Malware event confirmed",
})
phantom.act("send email", parameters=parameters, assets=['smtp'], name="send_email_1")
return
"""
Format all results for an email.
"""
def format_for_emailer(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('format_for_emailer() called')
template = """Malware in a security event has been confirmed using file reputation and/or file detonation services.
Reputation Results:
Using file reputation services, the following file hashes have been identified as malware:
{0}
EDR tool detects that the above file hashes are present on the following endpoints:
{1}
Detonation Results:
Using sandboxing services, the following file hashes have been identified as malware:
{2}
The Phantom Vault ID for the malicious files, as determined by the sandbox service, are as follows:
{3}
EDR tool detects that the file hashes indicated as positives per the sandbox service are present on the following endpoints:
{4}
Container id: {5}
[EOM]"""
# parameter list for template variable replacement
parameters = [
"hunt_file_1:action_result.parameter.hash",
"hunt_file_1:action_result.data.*.process.results.*.hostname",
"filtered-data:filter_5:condition_1:detonate_file_1:action_result.data.*.file_info.md5",
"filtered-data:filter_5:condition_1:detonate_file_1:action_result.parameter.vault_id",
"hunt_file_2:action_result.data.*.process.results.*.hostname",
"container:id",
]
phantom.format(container=container, template=template, parameters=parameters, name="format_for_emailer")
send_email_1(container=container)
return
def join_format_for_emailer(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('join_format_for_emailer() called')
# if the joined function has already been called, do nothing
if phantom.get_run_data(key='join_format_for_emailer_called'):
return
# no callbacks to check, call connected block "format_for_emailer"
phantom.save_run_data(key='join_format_for_emailer_called', value='format_for_emailer', auto=True)
format_for_emailer(container=container, handle=handle)
return
"""
Hunt for binaries with the malicious fileHash across endpoints.
"""
def hunt_file_2(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('hunt_file_2() called')
#phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
# collect data for 'hunt_file_2' call
filtered_results_data_1 = phantom.collect2(container=container, datapath=["filtered-data:filter_5:condition_1:detonate_file_1:action_result.data.*.file_info.md5", "filtered-data:filter_5:condition_1:detonate_file_1:action_result.parameter.context.artifact_id"])
parameters = []
# build parameters list for 'hunt_file_2' call
for filtered_results_item_1 in filtered_results_data_1:
if filtered_results_item_1[0]:
parameters.append({
'hash': filtered_results_item_1[0],
'range': "",
'type': "",
# context (artifact id) is added to associate results with the artifact
'context': {'artifact_id': filtered_results_item_1[1]},
})
phantom.act("hunt file", parameters=parameters, assets=['carbonblack'], callback=join_format_for_emailer, name="hunt_file_2")
return
"""
Detonate file requires a Vault file, so only proceed if vaultId is not null.
"""
def filter_4(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('filter_4() called')
# collect filtered artifact ids for 'if' condition 1
matched_artifacts_1, matched_results_1 = phantom.condition(
container=container,
action_results=results,
conditions=[
["filtered-data:filter_3:condition_1:artifact:*.cef.vaultId", "!=", ""],
],
name="filter_4:condition_1")
# call connected blocks if filtered artifacts or results
if matched_artifacts_1 or matched_results_1:
detonate_file_1(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
return
"""
Only hunt files that are considered malware per the sandbox (malware == yes).
"""
def filter_5(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('filter_5() called')
# collect filtered artifact ids for 'if' condition 1
matched_artifacts_1, matched_results_1 = phantom.condition(
container=container,
action_results=results,
conditions=[
["detonate_file_1:action_result.summary.malware", "==", "yes"],
],
name="filter_5:condition_1")
# call connected blocks if filtered artifacts or results
if matched_artifacts_1 or matched_results_1:
hunt_file_2(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
return
"""
Match hashes with less than 10 positives to artifacts to identify filtered_artifacts.
"""
def filter_3(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('filter_3() called')
# collect filtered artifact ids for 'if' condition 1
matched_artifacts_1, matched_results_1 = phantom.condition(
container=container,
action_results=results,
conditions=[
["filtered-data:filter_2:condition_2:file_reputation_1:action_result.parameter.hash", "==", "artifact:*.cef.fileHash"],
],
name="filter_3:condition_1")
# call connected blocks if filtered artifacts or results
if matched_artifacts_1 or matched_results_1:
filter_4(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
return
"""
If 10 or more antivirus engines flagged the hash, proceed directly to hunt for the file. Else, use a sandbox to detonate the executable first.
"""
def filter_2(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('filter_2() called')
# collect filtered artifact ids for 'if' condition 1
matched_artifacts_1, matched_results_1 = phantom.condition(
container=container,
action_results=results,
conditions=[
["file_reputation_1:action_result.summary.positives", ">=", 10],
],
name="filter_2:condition_1")
# call connected blocks if filtered artifacts or results
if matched_artifacts_1 or matched_results_1:
hunt_file_1(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
# collect filtered artifact ids for 'if' condition 2
matched_artifacts_2, matched_results_2 = phantom.condition(
container=container,
action_results=results,
conditions=[
["file_reputation_1:action_result.summary.positives", "<", 10],
],
name="filter_2:condition_2")
# call connected blocks if filtered artifacts or results
if matched_artifacts_2 or matched_results_2:
filter_3(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_2, filtered_results=matched_results_2)
return
"""
Hunt for binaries with the malicious fileHash across endpoints.
"""
def hunt_file_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('hunt_file_1() called')
#phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
# collect data for 'hunt_file_1' call
filtered_results_data_1 = phantom.collect2(container=container, datapath=["filtered-data:filter_2:condition_1:file_reputation_1:action_result.parameter.hash", "filtered-data:filter_2:condition_1:file_reputation_1:action_result.parameter.context.artifact_id"])
parameters = []
# build parameters list for 'hunt_file_1' call
for filtered_results_item_1 in filtered_results_data_1:
if filtered_results_item_1[0]:
parameters.append({
'hash': filtered_results_item_1[0],
'range': "",
'type': "",
# context (artifact id) is added to associate results with the artifact
'context': {'artifact_id': filtered_results_item_1[1]},
})
phantom.act("hunt file", parameters=parameters, assets=['carbonblack'], callback=join_format_for_emailer, name="hunt_file_1")
return
"""
Only process artifacts that have a CEF fileHash.
"""
def filter_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('filter_1() called')
# collect filtered artifact ids for 'if' condition 1
matched_artifacts_1, matched_results_1 = phantom.condition(
container=container,
conditions=[
["artifact:*.cef.fileHash", "!=", ""],
],
name="filter_1:condition_1")
# call connected blocks if filtered artifacts or results
if matched_artifacts_1 or matched_results_1:
file_reputation_1(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
return
"""
Detonate the file(s) in the vault.
"""
def detonate_file_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('detonate_file_1() called')
#phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
# collect data for 'detonate_file_1' call
filtered_artifacts_data_1 = phantom.collect2(container=container, datapath=['filtered-data:filter_4:condition_1:artifact:*.cef.vaultId', 'filtered-data:filter_4:condition_1:artifact:*.id'])
parameters = []
# build parameters list for 'detonate_file_1' call
for filtered_artifacts_item_1 in filtered_artifacts_data_1:
if filtered_artifacts_item_1[0]:
parameters.append({
'file_name': "",
'vault_id': filtered_artifacts_item_1[0],
# context (artifact id) is added to associate results with the artifact
'context': {'artifact_id': filtered_artifacts_item_1[1]},
})
phantom.act("detonate file", parameters=parameters, assets=['wildfire'], callback=filter_5, name="detonate_file_1")
return
def on_finish(container, summary):
phantom.debug('on_finish() called')
# This function is called after all actions are completed.
# summary of all the action and/or all detals of actions
# can be collected here.
# summary_json = phantom.get_summary()
# if 'result' in summary_json:
# for action_result in summary_json['result']:
# if 'action_run_id' in action_result:
# action_results = phantom.get_action_results(action_run_id=action_result['action_run_id'], result_data=False, flatten=False)
# phantom.debug(action_results)
return