forked from phantomcyber/playbooks
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmcafee_phishing_attachment_investigate.py
382 lines (288 loc) · 18.3 KB
/
mcafee_phishing_attachment_investigate.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
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
"""
Originally written as an example playbook for BlackHat 2017, this playbook investigates a potential phishing email with a suspicious attachment. Execution starts when a suspicious email with an attachment is forwarded to an inbox which Phantom is polling. McAfee Advanced Threat Defense is used to do static and dynamic analysis on any attachments to the email. If the attachment(s) appear malicious, any IP addresses identified during the ATD detonation are used to do geolocation and IP address reputation, further enriching the data in the original container. The ATD detonation also records the hashes of files that are created or touched. McAfee Active Response is used to search within the enterprise for those hashes, and if there are any matches they are shared on the OpenDXL message fabric.
"""
import phantom.rules as phantom
import json
from datetime import datetime, timedelta
def on_start(container):
phantom.debug('on_start() called')
# call 'decision_1' block
decision_1(container=container)
return
"""
Only proceed with the playbook if the ingested email has an artifact with a vaultId, which will be present if the email has one or more file attachments.
"""
def decision_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('decision_1() called')
# check for 'if' condition 1
matched_artifacts_1, matched_results_1 = phantom.condition(
container=container,
conditions=[
["artifact:*.cef.vaultId", "!=", ""],
])
# call connected blocks if condition 1 matched
if matched_artifacts_1 or matched_results_1:
atd_detonate_file(action=action, success=success, container=container, results=results, handle=handle)
return
return
"""
Enrich the container with the geolocation of any IP addresses found in the detonation to inform any further analysis.
"""
def geolocate_ip(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('geolocate_ip() called')
#phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
# collect data for 'geolocate_ip' call
filtered_results_data_1 = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_1:atd_detonate_file:action_result.data.*.Summary.Ips.*.Ipv4", "filtered-data:filter_1:condition_1:atd_detonate_file:action_result.parameter.context.artifact_id"])
parameters = []
# build parameters list for 'geolocate_ip' call
for filtered_results_item_1 in filtered_results_data_1:
if filtered_results_item_1[0]:
parameters.append({
'ip': filtered_results_item_1[0],
# context (artifact id) is added to associate results with the artifact
'context': {'artifact_id': filtered_results_item_1[1]},
})
phantom.act("geolocate ip", parameters=parameters, assets=['maxmind'], name="geolocate_ip")
return
"""
Only proceed with investigation if the final severity verdict returned by ATD is greater than or equal to 3, indicating the file is probably malicious.
"""
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,
action_results=results,
conditions=[
["atd_detonate_file:action_result.data.*.Summary.Verdict.Severity", ">=", "3"],
],
name="filter_1:condition_1")
# call connected blocks if filtered artifacts or results
if matched_artifacts_1 or matched_results_1:
geolocate_ip(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
opendxl_push_ip(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
mar_lookup_hash(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
return
"""
Investigate further for any hashes that were identified in the MAR lookup.
"""
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=[
["mar_lookup_hash:action_result.data.*.items.*.count", ">", 0],
],
name="filter_2:condition_1")
# call connected blocks if filtered artifacts or results
if matched_artifacts_1 or matched_results_1:
opendxl_push_hash(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
format_1(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
return
"""
Summarize the key findings from the investigation to populate a new ticket and an email to an analyst.
"""
def format_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('format_1() called')
template = """Phantom received a potential phishing email from {0} with source_id {1}
Since the email contained an attachment, Phantom used McAfee ATD to detonate the file. The detonation returned a severity verdict of {2}
Phantom proceeded to use McAfee Active Response to hunt internally for endpoints associated with any of the hashes identified during ATD detonation. The following internal hostnames were identified:
{3}
Please continue to investigate these endpoints as soon as possible."""
# parameter list for template variable replacement
parameters = [
"artifact:*.cef.fromEmail",
"container:source_data_identifier",
"atd_detonate_file:action_result.data.*.Summary.Verdict.Severity",
"filtered-data:filter_2:condition_1:mar_lookup_hash:action_result.data.*.items.*.output.HostInfo|hostname",
]
phantom.format(container=container, template=template, parameters=parameters, name="format_1")
create_ticket_2(container=container)
send_email_to_analyst(container=container)
return
"""
Create a new ticket with a summary of the information needed to start remediation.
"""
def create_ticket_2(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('create_ticket_2() called')
#phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
# collect data for 'create_ticket_2' call
container_data = phantom.collect2(container=container, datapath=['artifact:*.cef.vaultId', 'artifact:*.id'])
formatted_data_1 = phantom.get_format_data(name='format_1')
parameters = []
# build parameters list for 'create_ticket_2' call
for container_item in container_data:
parameters.append({
'short_description': "phishing attachment present on endpoints",
'table': "",
'vault_id': container_item[0],
'description': formatted_data_1,
'fields': "",
# context (artifact id) is added to associate results with the artifact
'context': {'artifact_id': container_item[1]},
})
phantom.act("create ticket", parameters=parameters, assets=['servicenow'], name="create_ticket_2")
return
"""
Email an analyst with a summary of the information needed to start remediation.
"""
def send_email_to_analyst(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('send_email_to_analyst() called')
#phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
# collect data for 'send_email_to_analyst' call
container_data = phantom.collect2(container=container, datapath=['artifact:*.cef.vaultId', 'artifact:*.id'])
formatted_data_1 = phantom.get_format_data(name='format_1')
parameters = []
# build parameters list for 'send_email_to_analyst' call
for container_item in container_data:
parameters.append({
'body': formatted_data_1,
'to': "[email protected]",
'from': "[email protected]",
'attachments': container_item[0],
'subject': "phishing attachment present on endpoints",
# context (artifact id) is added to associate results with the artifact
'context': {'artifact_id': container_item[1]},
})
phantom.act("send email", parameters=parameters, assets=['smtp'], name="send_email_to_analyst")
return
"""
Submit the file(s) to ATD for both static and dynamic analysis. The combined analysis will provide a security verdict about how dangerous the file looks as well as one or more hashes from different stages of its execution and one or more IP addresses hard-coded within it (static analysis) or detected during execution (dynamic analysis).
"""
def atd_detonate_file(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('atd_detonate_file() called')
# collect data for 'atd_detonate_file' call
container_data = phantom.collect2(container=container, datapath=['artifact:*.cef.vaultId', 'artifact:*.id'])
parameters = []
# build parameters list for 'atd_detonate_file' call
for container_item in container_data:
if container_item[0]:
parameters.append({
'vault_id': container_item[0],
# context (artifact id) is added to associate results with the artifact
'context': {'artifact_id': container_item[1]},
})
phantom.act("detonate file", parameters=parameters, assets=['atd'], callback=atd_detonate_file_callback, name="atd_detonate_file")
return
def atd_detonate_file_callback(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('atd_detonate_file_callback() called')
send_email_to_admin(action=action, success=success, container=container, results=results, handle=handle)
filter_1(action=action, success=success, container=container, results=results, handle=handle)
return
"""
Push any IP addresses identified in the ATD detonation to the OpenDXL message fabric. These can be received by any number of other tools listening on OpenDXL.
"""
def opendxl_push_ip(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('opendxl_push_ip() called')
#phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
# collect data for 'opendxl_push_ip' call
filtered_results_data_1 = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_1:atd_detonate_file:action_result.data.*.Summary.Ips.*.Ipv4", "filtered-data:filter_1:condition_1:atd_detonate_file:action_result.parameter.context.artifact_id"])
parameters = []
# build parameters list for 'opendxl_push_ip' call
for filtered_results_item_1 in filtered_results_data_1:
if filtered_results_item_1[0]:
parameters.append({
'dxl_ip': filtered_results_item_1[0],
# context (artifact id) is added to associate results with the artifact
'context': {'artifact_id': filtered_results_item_1[1]},
})
phantom.act("post ip", parameters=parameters, assets=['opendxl'], callback=ip_reputation_1, name="opendxl_push_ip")
return
"""
Enrich the container with the reputation of any IP addresses found in the detonation to inform any further analysis.
"""
def ip_reputation_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('ip_reputation_1() called')
#phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
# collect data for 'ip_reputation_1' call
results_data_1 = phantom.collect2(container=container, datapath=['opendxl_push_ip:action_result.parameter.dxl_ip', 'opendxl_push_ip:action_result.parameter.context.artifact_id'], action_results=results)
parameters = []
# build parameters list for 'ip_reputation_1' call
for results_item_1 in results_data_1:
if results_item_1[0]:
parameters.append({
'ip': results_item_1[0],
'ph': "",
'from': "",
'to': "",
# context (artifact id) is added to associate results with the artifact
'context': {'artifact_id': results_item_1[1]},
})
phantom.act("ip reputation", parameters=parameters, assets=['passivetotal'], name="ip_reputation_1", parent_action=action)
return
"""
Use OpenDXL to connect to McAfee Active Response, querying for the existence of the detected hashes on any endpoints within the enterprise. MAR searches for the hash on disk, in netflow, or in processes and registry keys.
"""
def mar_lookup_hash(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('mar_lookup_hash() called')
#phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
# collect data for 'mar_lookup_hash' call
filtered_results_data_1 = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_1:atd_detonate_file:action_result.data.*.Summary.Files.*.Md5", "filtered-data:filter_1:condition_1:atd_detonate_file:action_result.parameter.context.artifact_id"])
parameters = []
# build parameters list for 'mar_lookup_hash' call
for filtered_results_item_1 in filtered_results_data_1:
if filtered_results_item_1[0]:
parameters.append({
'mar_md5': filtered_results_item_1[0],
# context (artifact id) is added to associate results with the artifact
'context': {'artifact_id': filtered_results_item_1[1]},
})
phantom.act("lookup hash", parameters=parameters, assets=['opendxl'], callback=filter_2, name="mar_lookup_hash")
return
"""
Always send the high-level ATD verdict description to the admin to keep them in the loop.
"""
def send_email_to_admin(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('send_email_to_admin() called')
#phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
# collect data for 'send_email_to_admin' call
results_data_1 = phantom.collect2(container=container, datapath=['atd_detonate_file:action_result.data.*.Summary.Verdict.Description', 'atd_detonate_file:action_result.summary.verdict', 'atd_detonate_file:action_result.parameter.context.artifact_id'], action_results=results)
parameters = []
# build parameters list for 'send_email_to_admin' call
for results_item_1 in results_data_1:
if results_item_1[0]:
parameters.append({
'body': results_item_1[0],
'to': "[email protected]",
'from': "[email protected]",
'attachments': "",
'subject': results_item_1[1],
# context (artifact id) is added to associate results with the artifact
'context': {'artifact_id': results_item_1[2]},
})
phantom.act("send email", parameters=parameters, assets=['smtp'], name="send_email_to_admin", parent_action=action)
return
"""
Push out the identified hashes for use by McAfee Threat Intelligence Exchange (TIE). From there the hash can be blocked depending on the reputation threshold configured in TIE policy and whether or not files with that hash are signed by a trusted source.
"""
def opendxl_push_hash(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None):
phantom.debug('opendxl_push_hash() called')
#phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
# collect data for 'opendxl_push_hash' call
filtered_results_data_1 = phantom.collect2(container=container, datapath=["filtered-data:filter_2:condition_1:mar_lookup_hash:action_result.data.*.items.*.output.Files|md5", "filtered-data:filter_2:condition_1:mar_lookup_hash:action_result.parameter.context.artifact_id"])
parameters = []
# build parameters list for 'opendxl_push_hash' call
for filtered_results_item_1 in filtered_results_data_1:
if filtered_results_item_1[0]:
parameters.append({
'tie_md5': filtered_results_item_1[0],
'dxl_rep': "KNOWN_MALICIOUS",
# context (artifact id) is added to associate results with the artifact
'context': {'artifact_id': filtered_results_item_1[1]},
})
phantom.act("post hash", parameters=parameters, assets=['opendxl'], name="opendxl_push_hash")
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