Skip to content

Commit 7bf8726

Browse files
authored
Merge pull request #1095 from splunk/total_replay_tool
total_replay_tool
2 parents 5ef004f + de1d695 commit 7bf8726

File tree

12 files changed

+2014
-3
lines changed

12 files changed

+2014
-3
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ See a quick demo 📺 of the process to dump a dataset [here](https://www.youtub
126126

127127
To contribute a dataset simply create a PR on this repository, for general instructions on creating a PR [see this guide](https://gist.github.com/Chaser324/ce0505fbed06b947d962).
128128

129+
# TOTAL-REPLAY
130+
A lightweight tool helps you make the most of Splunk’s [Security Content](https://github.com/splunk/security_content) metadata, such as detection names, analytic stories, and more, by replaying relevant test event logs or attack data from either the [Splunk Attack Data](https://github.com/splunk/attack_data) or [Splunk Attack Range](https://github.com/splunk/attack_range) projects.
131+
132+
for more information of this tool, please refer to [TOTAL-REPLAY Guide](total_replay/readme.md)
133+
129134
# Automatically generated Datasets ⚙️
130135

131136
This project takes advantage of automation to generate datasets using the attack_range. You can see details about this service on this [sub-project folder attack_data_service](https://github.com/splunk/attack_data/tree/master/attack_data_service).

bin/replay.py

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from urllib3 import disable_warnings
1111
import yaml
1212
from pathlib import Path
13-
13+
from urllib.parse import urlparse, urlunparse, unquote
1414

1515
def load_environment_variables():
1616
"""Load required environment variables for Splunk connection."""
@@ -114,6 +114,70 @@ def send_data_to_splunk(file_path, splunk_host, hec_token, event_host_uuid,
114114
except Exception as e:
115115
print(f":x: Error sending {file_path} to Splunk HEC: {e}")
116116

117+
def parse_old_attack_yml_data_file(yml_file_path,
118+
index_override,
119+
source_override,
120+
sourcetype_override,
121+
host_uuid):
122+
### handling possible empty inputs
123+
print("Processing old attack data yml file")
124+
if source_override == "" or sourcetype_override == "" or index_override == "":
125+
return None, [], {}
126+
127+
try:
128+
with open(yml_file_path, 'r') as file:
129+
data = yaml.safe_load(file)
130+
131+
# Extract required fields
132+
file_id = host_uuid
133+
d = data.get('dataset')
134+
### if the instance is list
135+
if isinstance(d, list):
136+
dataset_val = d[0]
137+
if isinstance(d, str):
138+
dataset_val = d
139+
140+
name_value = os.path.basename(dataset_val).split(".")[0]
141+
p = urlparse(dataset_val)
142+
if not p.scheme or not p.netloc:
143+
raise ValueError(f"Unsupported GitHub URL format: {dataset_val}")
144+
145+
m, path_value = str(p.path).split("master")
146+
### generate our own datasets data
147+
### "datasets": [
148+
### {
149+
### "name": "windows-sysmon_creddump",
150+
### "path": "/datasets/attack_techniques/T1003.001/atomic_red_team/windows-sysmon_creddump.log",
151+
### "sourcetype": "XmlWinEventLog",
152+
### "source": "XmlWinEventLog:Microsoft-Windows-Sysmon/Operational"
153+
### }
154+
### ]
155+
# Extract required fields
156+
157+
datasets = [
158+
{
159+
"name": name_value,
160+
"path": path_value,
161+
"sourcetype":sourcetype_override,
162+
"source":source_override
163+
}
164+
]
165+
#print(datasets)
166+
# Extract default metadata from YAML file
167+
default_index = index_override
168+
default_source = source_override
169+
default_sourcetype = sourcetype_override
170+
171+
# Return tuple of (id, datasets_list, default_metadata)
172+
return file_id, datasets, {
173+
'index': default_index,
174+
'source': default_source,
175+
'sourcetype': default_sourcetype
176+
}
177+
178+
except Exception as e:
179+
print(f"Error parsing {yml_file_path}: {e}")
180+
return None, [], {}
117181

118182
def main():
119183
parser = argparse.ArgumentParser(
@@ -205,8 +269,12 @@ def main():
205269
file_id, datasets, defaults = parse_data_yml(yml_file)
206270

207271
if not file_id or not datasets:
208-
print(f"Skipping {yml_file} - no valid data found")
209-
continue
272+
273+
file_id, datasets, defaults = parse_old_attack_yml_data_file(yml_file, args.index_override, args.source_override, args.sourcetype_override, args.host_uuid)
274+
275+
if not file_id or not datasets:
276+
print(f"Skipping {yml_file} - no valid data found")
277+
continue
210278

211279
# Use the id from YAML file as host field (unless user provided one)
212280
event_host_uuid = args.host_uuid or file_id

total_replay/assets/banner.png

107 KB
Loading

total_replay/assets/usage.png

417 KB
Loading
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
settings:
2+
security_content_detection_path: ~/path/to/your/security_content/detections
3+
attack_data_dir_path: ~/path/to/your/attack_data
4+
output_dir_name : output
5+
cache_replay_yaml_name : cache_replay_data.yml
6+
replayed_yaml_cache_dir_name: replayed_yaml_cache
7+
debug_print: False

0 commit comments

Comments
 (0)