Repository navigation
Expand file tree
/
Copy pathtesting_service.py
More file actions
203 lines (132 loc) · 7.87 KB
/
Copy pathtesting_service.py
File metadata and controls
203 lines (132 loc) · 7.87 KB
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
import re
#import ansible_runner
import yaml
import uuid
import sys
import os
import time
import requests
from modules.DataManipulation import DataManipulation
from modules import utils
from modules import splunk_sdk
import timeit
from typing import Union, Tuple
from os.path import relpath
from tempfile import mkdtemp, mkstemp
import datetime
import http.client
def test_detection_wrapper(container_name:str, splunk_ip:str, splunk_password:str, splunk_port:int,
detection_file:str, attack_data_root_folder, wait_on_failure:bool=False, wait_on_completion:bool=False)->dict:
one_test_start = timeit.default_timer()
uuid_var = str(uuid.uuid4())
result_test, indices_to_delete = test_detection(splunk_ip, splunk_port, container_name, splunk_password, detection_file, uuid_var, attack_data_root_folder)
one_test_stop = timeit.default_timer()
if result_test is None:
#We failed so early in the process that we could not produce any meaningful result
raise(Exception("Test execution Error"))
#enter = input("Run some tests from [%s] on [%s] - we don't delete until you hit enter :)"%(container_name, test_file))
# delete test data
search_string = result_test['detection_result']['search_string']
#get pretty time info
elapsed_search_time_string = str(datetime.timedelta(seconds=round(one_test_stop - one_test_start)))
#search failed if there was an error or the detection failed to produce the expected result
#print("Elapsed search time: %s"%(elapsed_search_time_string))
if (wait_on_failure or wait_on_completion) and (result_test['detection_result']['error'] or not result_test['detection_result']['success']):
wait_on_delete = {'message':"\n\n\n****SEARCH FAILURE : Allowing time to debug search/data****"}
elif wait_on_completion:
wait_on_delete = {'message':"\n\n\n****SEARCH SUCCESS : Allowing time to examine search/data****"}
else:
wait_on_delete = None
splunk_sdk.delete_attack_data(splunk_ip, splunk_password, splunk_port, wait_on_delete, search_string, detection_file, indices = indices_to_delete)
return result_test
import splunklib.client as client
def get_service(splunk_ip:str, splunk_port:int, splunk_password:str):
try:
service = client.connect(
host=splunk_ip,
port=splunk_port,
username='admin',
password=splunk_password
)
except Exception as e:
raise(Exception("Unable to connect to Splunk instance: " + str(e)))
return service
def test_detection(splunk_ip:str, splunk_port:int, container_name:str, splunk_password:str, detection_file:str, uuid_var, attack_data_root_folder)->Tuple[Union[dict,None], set[str]]:
detection_file_obj = load_file(os.path.join("security_content/", detection_file))
if not detection_file_obj:
print("Not detection_file_obj!")
raise(Exception("No test file object found for [%s]"%detection_file))
#print(test_file_obj)
# write entry dynamodb
#aws_service.add_detection_results_in_dynamo_db('eu-central-1', uuid_var , uuid_test, test_file_obj['tests'][0]['name'], test_file_obj['tests'][0]['file'], str(int(time.time())))
#epoch_time = str(int(time.time()))
abs_folder_path = mkdtemp(prefix="DATA_", dir=attack_data_root_folder)
#We want the relative path, so we convert it as required
tests:dict = detection_file_obj.get("tests", {})
if len(tests) > 1:
print(f"****WARNING - THIS DETECTION CONTAINS {len(tests)} TESTS BUT WE WILL ONLY RUN 1")
test = tests[0]
indices_to_delete = set()
for attack_data in test['attack_data']:
url = attack_data['data']
if 'custom_index' in attack_data:
print(f"Found a custom index for {detection_file}: {attack_data['custom_index']}")
data_upload_index = attack_data['custom_index']
else:
data_upload_index = splunk_sdk.DEFAULT_DATA_INDEX
indices_to_delete.add(data_upload_index)
_, target_file = mkstemp(prefix="attack_data_", dir=abs_folder_path)
utils.download_file_from_http(url, target_file, overwrite_file=True)
# Update timestamps before replay
if 'update_timestamp' in attack_data:
if attack_data['update_timestamp'] == True:
data_manipulation = DataManipulation()
data_manipulation.manipulate_timestamp(target_file, attack_data['sourcetype'], attack_data['source'])
#replay_attack_dataset(container_name, splunk_password, folder_name, "test0", attack_data['sourcetype'], attack_data['source'], attack_data['file_name'])
try:
service = get_service(splunk_ip, splunk_port, splunk_password)
test_index = service.indexes[data_upload_index]
with open(target_file, 'rb') as target:
test_index.submit(target.read(), sourcetype=attack_data['sourcetype'], source=attack_data['source'], host=splunk_sdk.DEFAULT_EVENT_HOST)
except http.client.HTTPException as e:
raise(Exception(f"Failed to submit detection file {target_file} to Splunk Server: {str(e)}"))
except Exception as e:
raise(Exception(f"Failed to submit detection file {target_file} to Splunk Server: {str(e)}"))
if not splunk_sdk.wait_for_indexing_to_complete(splunk_ip, splunk_port, splunk_password, attack_data['sourcetype'], data_upload_index):
raise Exception("There was an error waiting for indexing to complete.")
#Allow some time for the data to be ingested and processed
#print("begin sleep 30")
#time.sleep(60)
#print("end sleep 30")
result_test = {}
if 'baselines' in test:
results_baselines = []
for baseline_obj in test['baselines']:
baseline_file_name = baseline_obj['file']
baseline = load_file(os.path.join(os.path.dirname(__file__), '../security_content', baseline_file_name))
result_obj = dict()
result_obj['baseline'] = baseline_obj['name']
result_obj['baseline_file'] = baseline_file_name
print("Making test_baseline_search request to: [%s:%d]"%(splunk_ip, splunk_port))
result = splunk_sdk.test_baseline_search(splunk_ip, splunk_port, splunk_password, baseline['search'], baseline_obj['pass_condition'], baseline['name'], baseline_file_name, baseline_obj['earliest_time'], baseline_obj['latest_time'])
#we don't seem to be doing anything with this loop... are we supposed to have the following line belwo?
results_baselines.append(result)
result_test['baselines_result'] = results_baselines
result_detection = splunk_sdk.test_detection_search(splunk_ip, splunk_port, splunk_password, detection_file_obj['search'], test.get('pass_condition', '| stats count | where count > 0'), detection_file_obj['name'], detection_file, test.get('earliest_time', '-24h'), test.get('latest_time', 'now'))
if result_detection['error']:
print("There was an error running the search: %s"%(result_detection['search_string']))
result_detection['detection_name'] = test['name']
result_detection['detection_file'] = detection_file
result_test['detection_result'] = result_detection
result_test['attack_data_directory'] = abs_folder_path
return result_test, indices_to_delete
def load_file(file_path):
try:
with open(file_path, 'r', encoding="utf-8") as stream:
try:
file = list(yaml.safe_load_all(stream))[0]
except yaml.YAMLError as exc:
raise(Exception("ERROR: parsing YAML for {0}:[{1}]".format(file_path, str(exc))))
except Exception as e:
raise(Exception("ERROR: opening {0}:[{1}]".format(file_path, str(e))))
return file