Skip to content

Commit 39cdbb6

Browse files
authored
Merge pull request #3576 from ccl0utier/develop
Added Playbooks from the CrowdStrike EDR Pack - now with IDs
2 parents de55408 + 4faaa1d commit 39cdbb6

File tree

36 files changed

+8702
-0
lines changed

36 files changed

+8702
-0
lines changed

playbooks/CrowdStrike_OAuth_API_Endpoint_Analysis.json

Lines changed: 795 additions & 0 deletions
Large diffs are not rendered by default.
42.8 KB
Loading

playbooks/CrowdStrike_OAuth_API_Endpoint_Analysis.py

Lines changed: 830 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: CrowdStrike OAuth API Endpoint Analysis
2+
id: 1356baeb-9ad4-4d2c-b6ae-55dda6bd9db5
3+
version: 1
4+
date: '2025-06-09'
5+
author: Christian Cloutier, Splunk
6+
type: Investigation
7+
description: "Accepts a hostname or device id as input and collects running processes, network connections and various system information from the device via Crowdstrike. We then generate an observable report for each. This can be customized based on user preference."
8+
playbook: CrowdStrike_OAuth_API_Endpoint_Analysis
9+
how_to_implement: This input playbook requires the CrowdStrike OAuth API connector to be configured. It is designed to work with an endpoint hostname or device id and collect key information about the system, network connections and running processes for use in automation playbooks.
10+
references: []
11+
app_list:
12+
- CrowdStrike OAuth API
13+
tags:
14+
platform_tags:
15+
- "host name"
16+
- "device id"
17+
- "enrichment"
18+
- "D3-NTA"
19+
- "D3-PA"
20+
- "D3-AI"
21+
- "CrowdStrike_OAuth_API"
22+
playbook_type: Input
23+
vpe_type: Modern
24+
playbook_fields: [device]
25+
product:
26+
- Splunk SOAR
27+
use_cases:
28+
- Enrichment
29+
- Malware
30+
- Endpoint
31+
defend_technique_id:
32+
- D3-NTA
33+
- D3-PA
34+
- D3-AI

playbooks/CrowdStrike_OAuth_API_Executable_Denylisting.json

Lines changed: 407 additions & 0 deletions
Large diffs are not rendered by default.
26.8 KB
Loading
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
"""
2+
Accepts a hostname or device id as well as a file hash as input and add an indicator (IOC) for a device in Crowdstrike. We then generate an observable report as well as a Markdown formatted report. Both reports can be customized based on user preference.
3+
"""
4+
5+
6+
import phantom.rules as phantom
7+
import json
8+
from datetime import datetime, timedelta
9+
10+
11+
@phantom.playbook_block()
12+
def on_start(container):
13+
phantom.debug('on_start() called')
14+
15+
# call 'input_filter' block
16+
input_filter(container=container)
17+
18+
return
19+
20+
@phantom.playbook_block()
21+
def input_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, loop_state_json=None, **kwargs):
22+
phantom.debug("input_filter() called")
23+
24+
################################################################################
25+
# Determines if the provided inputs are present in the dataset.
26+
################################################################################
27+
28+
# collect filtered artifact ids and results for 'if' condition 1
29+
matched_artifacts_1, matched_results_1 = phantom.condition(
30+
container=container,
31+
logical_operator="and",
32+
conditions=[
33+
["playbook_input:device", "!=", ""],
34+
["playbook_input:hash", "!=", ""]
35+
],
36+
name="input_filter:condition_1",
37+
delimiter=None)
38+
39+
# call connected blocks if filtered artifacts or results
40+
if matched_artifacts_1 or matched_results_1:
41+
format_fql(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1)
42+
43+
return
44+
45+
46+
@phantom.playbook_block()
47+
def file_observables(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, loop_state_json=None, **kwargs):
48+
phantom.debug("file_observables() called")
49+
50+
################################################################################
51+
# Format a normalized output for each host
52+
################################################################################
53+
54+
query_device_result_data = phantom.collect2(container=container, datapath=["query_device:action_result.data.*.device_id","query_device:action_result.data.*.hostname"], action_results=results)
55+
filtered_input_0_hash = phantom.collect2(container=container, datapath=["filtered-data:input_filter:condition_1:playbook_input:hash"])
56+
upload_indicator_result_data = phantom.collect2(container=container, datapath=["upload_indicator:action_result.status","upload_indicator:action_result.message"], action_results=results)
57+
58+
query_device_result_item_0 = [item[0] for item in query_device_result_data]
59+
query_device_result_item_1 = [item[1] for item in query_device_result_data]
60+
filtered_input_0_hash_values = [item[0] for item in filtered_input_0_hash]
61+
upload_indicator_result_item_0 = [item[0] for item in upload_indicator_result_data]
62+
upload_indicator_result_message = [item[1] for item in upload_indicator_result_data]
63+
64+
file_observables__observable_array = None
65+
66+
################################################################################
67+
## Custom Code Start
68+
################################################################################
69+
70+
file_observables__observable_array = []
71+
72+
for device_id, hostname, file_hash, status, status_message in zip(query_device_result_item_0, query_device_result_item_1, filtered_input_0_hash_values, upload_indicator_result_item_0, upload_indicator_result_message):
73+
# Initialize the observable dictionary
74+
observable = {
75+
"source": "Crowdstrike OAuth API",
76+
"type": "Endpoint",
77+
"activity_name": "File Execution Prevention",
78+
"uid": device_id,
79+
"hostname": hostname,
80+
"status": status,
81+
"status_detail": status_message,
82+
"file": {
83+
"hashes": [
84+
{
85+
"algorithm": "SHA-256",
86+
"algorithm_id": 3,
87+
"value": file_hash
88+
}
89+
]
90+
},
91+
"d3fend": {
92+
"d3f_tactic": "Isolate",
93+
"d3f_technique": "D3-EDL",
94+
"version": "1.0.0"
95+
}
96+
}
97+
98+
# Add the observable to the array
99+
file_observables__observable_array.append(observable)
100+
101+
# Debug output for verification
102+
phantom.debug(file_observables__observable_array)
103+
104+
################################################################################
105+
## Custom Code End
106+
################################################################################
107+
108+
phantom.save_run_data(key="file_observables:observable_array", value=json.dumps(file_observables__observable_array))
109+
110+
return
111+
112+
113+
@phantom.playbook_block()
114+
def format_fql(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, loop_state_json=None, **kwargs):
115+
phantom.debug("format_fql() called")
116+
117+
################################################################################
118+
# Format the FQL query to get the input device information using its ID or hostname.
119+
################################################################################
120+
121+
template = """%%\nhostname:['{0}'],device_id:['{0}']\n%%"""
122+
123+
# parameter list for template variable replacement
124+
parameters = [
125+
"playbook_input:device"
126+
]
127+
128+
################################################################################
129+
## Custom Code Start
130+
################################################################################
131+
132+
# Write your custom code here...
133+
134+
################################################################################
135+
## Custom Code End
136+
################################################################################
137+
138+
phantom.format(container=container, template=template, parameters=parameters, name="format_fql")
139+
140+
query_device(container=container)
141+
142+
return
143+
144+
145+
@phantom.playbook_block()
146+
def query_device(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, loop_state_json=None, **kwargs):
147+
phantom.debug("query_device() called")
148+
149+
# phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
150+
151+
################################################################################
152+
# Get information about the device to unquarantine using its hostname or device
153+
# id.
154+
################################################################################
155+
156+
format_fql__as_list = phantom.get_format_data(name="format_fql__as_list")
157+
158+
parameters = []
159+
160+
# build parameters list for 'query_device' call
161+
for format_fql__item in format_fql__as_list:
162+
parameters.append({
163+
"limit": 50,
164+
"filter": format_fql__item,
165+
})
166+
167+
################################################################################
168+
## Custom Code Start
169+
################################################################################
170+
171+
# Write your custom code here...
172+
173+
################################################################################
174+
## Custom Code End
175+
################################################################################
176+
177+
phantom.act("query device", parameters=parameters, name="query_device", assets=["crowdstrike_oauth_api"], callback=upload_indicator)
178+
179+
return
180+
181+
182+
@phantom.playbook_block()
183+
def format_executable_denylisting_report(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, loop_state_json=None, **kwargs):
184+
phantom.debug("format_executable_denylisting_report() called")
185+
186+
################################################################################
187+
# Format a summary table with the information gathered from the playbook.
188+
################################################################################
189+
190+
template = """Endpoint Files were denylisted by Splunk SOAR. The table below summarizes the information gathered.\n\n| Device ID | Executable Hash | Action | Denylisting Status | Message |\n| --- | --- | --- | --- | --- |\n%%\n| {0} | {1} | {2} | {3} | {4} |\n%%"""
191+
192+
# parameter list for template variable replacement
193+
parameters = [
194+
"query_device:action_result.data.*.device_id",
195+
"filtered-data:input_filter:condition_1:playbook_input:hash",
196+
"upload_indicator:action_result.parameter.action",
197+
"upload_indicator:action_result.status",
198+
"upload_indicator:action_result.message"
199+
]
200+
201+
################################################################################
202+
## Custom Code Start
203+
################################################################################
204+
205+
# Write your custom code here...
206+
207+
################################################################################
208+
## Custom Code End
209+
################################################################################
210+
211+
phantom.format(container=container, template=template, parameters=parameters, name="format_executable_denylisting_report")
212+
213+
file_observables(container=container)
214+
215+
return
216+
217+
218+
@phantom.playbook_block()
219+
def upload_indicator(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, loop_state_json=None, **kwargs):
220+
phantom.debug("upload_indicator() called")
221+
222+
# phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED')))
223+
224+
################################################################################
225+
# Upload indicator that we want CrowdStrike to prevent and watch for all platforms.
226+
################################################################################
227+
228+
filtered_input_0_hash = phantom.collect2(container=container, datapath=["filtered-data:input_filter:condition_1:playbook_input:hash"])
229+
230+
parameters = []
231+
232+
# build parameters list for 'upload_indicator' call
233+
for filtered_input_0_hash_item in filtered_input_0_hash:
234+
if filtered_input_0_hash_item[0] is not None:
235+
parameters.append({
236+
"ioc": filtered_input_0_hash_item[0],
237+
"action": "prevent",
238+
"source": "IOC uploaded via Splunk SOAR",
239+
"severity": "MEDIUM",
240+
"platforms": "linux,mac,windows",
241+
"description": "File Indicator blocked from Splunk SOAR",
242+
})
243+
244+
################################################################################
245+
## Custom Code Start
246+
################################################################################
247+
248+
# Write your custom code here...
249+
250+
################################################################################
251+
## Custom Code End
252+
################################################################################
253+
254+
phantom.act("upload indicator", parameters=parameters, name="upload_indicator", assets=["crowdstrike_oauth_api"], callback=format_executable_denylisting_report)
255+
256+
return
257+
258+
259+
@phantom.playbook_block()
260+
def on_finish(container, summary):
261+
phantom.debug("on_finish() called")
262+
263+
format_executable_denylisting_report = phantom.get_format_data(name="format_executable_denylisting_report")
264+
file_observables__observable_array = json.loads(_ if (_ := phantom.get_run_data(key="file_observables:observable_array")) != "" else "null") # pylint: disable=used-before-assignment
265+
266+
output = {
267+
"observable": file_observables__observable_array,
268+
"markdown_report": format_executable_denylisting_report,
269+
}
270+
271+
################################################################################
272+
## Custom Code Start
273+
################################################################################
274+
275+
# Write your custom code here...
276+
277+
################################################################################
278+
## Custom Code End
279+
################################################################################
280+
281+
phantom.save_playbook_output_data(output=output)
282+
283+
return
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: CrowdStrike OAuth API Executable Denylisting
2+
id: 9d87f2d5-2578-4f39-9eee-c1a88af658bb
3+
version: 1
4+
date: '2025-06-09'
5+
author: Christian Cloutier, Splunk
6+
type: Response
7+
description: "Accepts a hostname or device id as well as a file hash as input and add an indicator (IOC) for a device in Crowdstrike. We then generate an observable report as well as a Markdown formatted report. Both reports can be customized based on user preference."
8+
playbook: CrowdStrike_OAuth_API_Executable_Denylisting
9+
how_to_implement: This input playbook requires the CrowdStrike OAuth API connector to be configured. It is designed to work with an endpoint hostname or device id and create an indicator in CrowdStrike Falcon based on the malicious process hash value (preventing it from running on other endpoints) for use in automation playbooks.
10+
references: []
11+
app_list:
12+
- CrowdStrike OAuth API
13+
tags:
14+
platform_tags:
15+
- "host name"
16+
- "device id"
17+
- "file_hash"
18+
- "Executable Denylisting"
19+
- "D3-EDL"
20+
- "CrowdStrike_OAuth_API"
21+
playbook_type: Input
22+
vpe_type: Modern
23+
playbook_fields: [device,file_hash]
24+
product:
25+
- Splunk SOAR
26+
use_cases:
27+
- Response
28+
- Malware
29+
- Endpoint
30+
defend_technique_id:
31+
- D3-EDL

0 commit comments

Comments
 (0)