Branch was auto-updated.

This commit is contained in:
pyth0n1c
2022-02-24 11:32:31 -08:00
committed by GitHub
32 changed files with 832 additions and 540 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
# PR Template for new Detections
For Authors:
1. Make sure that CI/CD [detection-testing and build-and-validate](https://github.com/splunk/security_content/actions) jobs passed ✔️.
- [ ] Make sure that CI/CD [detection-testing and build-and-validate](https://github.com/splunk/security_content/actions) jobs passed ✔️.
For Reviewers:
- [ ] Verify CI/CD jobs have passed without errors.
- [ ] Validate SPL logic.
- [ ] Validate tags, description, and how to implement.
- [ ] Validate name patches `<platform>_<mitre att&ck technique>_<short description>`
- [ ] Validate name matches `<platform>_<mitre att&ck technique>_<short description>`
- [ ] Verify references match analytic.
- [ ] Is there an Atomic Test?
+1 -1
View File
@@ -163,7 +163,7 @@ jobs:
cd bin/automated_detection_testing/ci/detection_testing_batch
source .venv/bin/activate
python3 detection_testing_execution.py run -c prior_config/${{ matrix.manifest_filename}} --splunkbase_username ${{ secrets.SPLUNKBASE_TESTING_USERNAME }} --splunkbase_password ${{ secrets.SPLUNKBASE_TESTING_KEY }}
python3 detection_testing_execution.py run -c prior_config/${{ matrix.manifest_filename}}
- name: Upload Test Results Files
@@ -1,6 +1,7 @@
import argparse
import copy
import csv
from ctypes.wintypes import tagRECT
import json
import os
import queue
@@ -19,6 +20,8 @@ from tempfile import mkdtemp
from timeit import default_timer as timer
from typing import Union
from urllib.parse import urlparse
import signal
import docker
import requests
@@ -30,7 +33,7 @@ import modules.new_arguments2
from modules import (container_manager, new_arguments2,
testing_service, validate_args)
from modules.github_service import GithubService
from modules.validate_args import validate, validate_and_write
from modules.validate_args import validate, validate_and_write, ES_APP_NAME
SPLUNK_CONTAINER_APPS_DIR = "/opt/splunk/etc/apps"
index_file_local_path = "indexes.conf.tar"
@@ -45,27 +48,60 @@ datamodel_file_container_path = os.path.join(
authorizations_file_local_path = "authorize.conf.tar"
authorizations_file_container_path = "/opt/splunk/etc/system/local"
CONTAINER_APP_DIRECTORY = "apps"
MAX_RECOMMENDED_CONTAINERS_BEFORE_WARNING = 2
def download_file_from_http(url:str, target:str)->None:
#Will just overwrite an existing file
def download_file_from_http(url:str, destination_file:str, overwrite_file:bool=False)->None:
if os.path.exists(destination_file) and overwrite_file is False:
print(f"[{destination_file}] already exists...using cached version")
return
print(f"downloading to [{destination_file}]")
file_to_download = requests.get(url, stream=True)
with open(target, "wb") as output:
with open(destination_file, "wb") as output:
for piece in file_to_download.iter_content(chunk_size=(1024*1024)):
output.write(piece)
def copy_local_apps_to_directory(apps: dict[str, dict], target_directory) -> None:
for key, item in apps.items():
def copy_local_apps_to_directory(apps: dict[str, dict], splunkbase_username:tuple[str,None] = None, splunkbase_password:tuple[str,None] = None, mock:bool = False, target_directory:str = "apps") -> str:
if mock is True:
target_directory = os.path.join("prior_config", target_directory)
# Remove the apps directory or the prior config directory. If it's just an apps directory, then we don't want
#to remove that.
shutil.rmtree(target_directory, ignore_errors=True)
try:
# Make sure the directory exists. If it already did, that's okay. Don't delete anything from it
# We want to re-use previously downloaded apps
os.makedirs(target_directory, exist_ok = True)
except Exception as e:
raise(Exception(f"Some error occured when trying to make the {target_directory}: [{str(e)}]"))
for key, item in apps.items():
# These apps are URLs that will be passed. The apps will be downloaded and installed by the container
# # Get the file from an http source
splunkbase_info = True if ('app_number' in item and item['app_number'] is not None and
'app_version' in item and item['app_version'] is not None) else False
splunkbase_creds = True if (splunkbase_username is not None and
splunkbase_password is not None) else False
can_download_from_splunkbase = splunkbase_info and splunkbase_creds
#local apps can either have a local_path or an http_path
if 'local_path' in item:
source_path = os.path.abspath(os.path.expanduser(item['local_path']))
base_name = os.path.basename(source_path)
dest_path = os.path.join(target_directory, base_name)
try:
print(f"copying {os.path.relpath(source_path)} to {os.path.relpath(dest_path)}")
shutil.copy(source_path, dest_path)
item['local_path'] = dest_path
except shutil.SameFileError as e:
@@ -77,25 +113,34 @@ def copy_local_apps_to_directory(apps: dict[str, dict], target_directory) -> Non
source_path, dest_path, str(e)), file=sys.stderr)
sys.exit(1)
# These apps are URLs that will be passed. The apps will be downloaded and installed by the container
# # Get the file from an http source
# elif 'http_path' in item:
# http_path = item['http_path']
# try:
# url_parse_obj = urlparse(http_path)
# path_after_host = url_parse_obj[2].rstrip('/') #removes / at the end, if applicable
# base_name = path_after_host.rpartition('/')[-1] #just get the file name
# dest_path = os.path.join(target_directory, base_name) #write the whole path
# download_file_from_http(http_path, dest_path)
# #we need to updat the local path because this is used to copy it into the container later
# item['local_path'] = dest_path
# except Exception as e:
# print("Error trying to download %s @ %s: [%s]. This app is required.\n\tQuitting..."%(key, http_path, str(e)),file=sys.stderr)
# sys.exit(1)
# else:
# print("Error - trying to install a local app that does not have 'local_path' or 'http_path'.\n\tQuitting...")
# sys.exit(1)
elif can_download_from_splunkbase is True:
#Don't do anything, this will be downloaded from splunkbase
pass
elif splunkbase_info is True and splunkbase_creds is False and mock is True:
#Don't need to do anything, when this actually runs the apps will be downloaded from Splunkbase
#There is another opportunity to provide the creds then
pass
elif 'http_path' in item and can_download_from_splunkbase is False:
http_path = item['http_path']
try:
url_parse_obj = urlparse(http_path)
path_after_host = url_parse_obj[2].rstrip('/') #removes / at the end, if applicable
base_name = path_after_host.rpartition('/')[-1] #just get the file name
dest_path = os.path.join(target_directory, base_name) #write the whole path
download_file_from_http(http_path, dest_path)
#we need to update the local path because this is used to copy it into the container later
item['local_path'] = dest_path
#Remove the HTTP Path, we will use the local_path instead
except Exception as e:
print("Error trying to download %s @ %s: [%s]. This app is required.\n\tQuitting..."%(key, http_path, str(e)),file=sys.stderr)
sys.exit(1)
elif splunkbase_info is False:
print(f"Error - trying to install an app [{key}] that does not have 'local_path', 'http_path', "
"or 'app_version' and 'app_number' for installing from Splunkbase.\n\tQuitting...")
sys.exit(1)
return target_directory
def ensure_security_content(branch: str, commit_hash: Union[str,None], pr_number: Union[int, None], persist_security_content: bool) -> tuple[GithubService, bool]:
@@ -183,7 +228,7 @@ def generate_escu_app(persist_security_content: bool = False) -> str:
else:
os.mkdir("slim_packaging")
os.mkdir("apps")
try:
SPLUNK_PACKAGING_TOOLKIT_URL = "https://download.splunk.com/misc/packaging-toolkit/splunk-packaging-toolkit-0.9.0.tar.gz"
SPLUNK_PACKAGING_TOOLKIT_FILENAME = 'splunk-packaging-toolkit-latest.tar.gz'
@@ -216,7 +261,7 @@ def generate_escu_app(persist_security_content: bool = False) -> str:
ret = subprocess.run("; ".join(commands),
shell=True, capture_output=True)
if ret.returncode != 0:
if ret.returncode != 0:
print("Command List:\n%s" % (commands))
print("Error generating new ESCU Package.\n\tQuitting and dumping error...\n[%s]" % (
ret.stderr.decode('utf-8')), file=sys.stderr)
@@ -226,27 +271,10 @@ def generate_escu_app(persist_security_content: bool = False) -> str:
return output_file_path_from_root
def finish_mock(settings: dict, detections: list[str], output_file_template: str = "prior_config/config_tests_%d.json")->bool:
num_containers = settings['num_containers']
try:
# Remove the prior config directory if it exists. If not, continue
shutil.rmtree("prior_config", ignore_errors=True)
# We want to make the prior_config directory and the prior_config/apps directory
os.makedirs("prior_config/apps")
except FileExistsError as e:
print("Directory priorconfig/apps exists, but we just deleted it!\n\tQuitting...", file=sys.stderr)
return False
except Exception as e:
print("Some error occured when trying to make the configs folder: [%s]\n\tQuitting..." % (
str(e)), file=sys.stderr)
return False
# Copy the apps to the appropriate local. This will also update
# the app paths in settings['local_apps']
copy_local_apps_to_directory(settings['local_apps'], "prior_config/apps")
for output_file_index in range(0, num_containers):
fname = output_file_template % (output_file_index)
@@ -285,9 +313,8 @@ def finish_mock(settings: dict, detections: list[str], output_file_template: str
# Make sure that it still validates after all of the changes
try:
with open(fname, 'w') as cfg:
validated_settings, b = validate_and_write(
mock_settings, cfg)
with open(fname, 'w') as outfile:
validated_settings, b = validate_and_write(configuration=mock_settings, output_file = outfile, strip_credentials=True)
if validated_settings is None:
print(
"There was an error validating the updated mock settings.\n\tQuitting...", file=sys.stderr)
@@ -331,23 +358,40 @@ def main(args: list[str]):
credentials_needed = False
credential_error = False
if len(settings['splunkbase_apps']) > 0:
credentials_needed = True
if settings['splunkbase_username'] == None and credentials_needed:
print("Error - you have listed apps to download from Splunkbase but have "\
"not provided --splunkbase_username via the command line or config file.",file=sys.stderr)
credential_error = True
if settings['splunkbase_username'] == None or settings['splunkbase_password'] == None:
missing_credentials = []
if settings['splunkbase_username'] == None:
missing_credentials.append("--splunkbase_username")
if settings['splunkbase_password'] == None:
missing_credentials.append("--splunkbase_password")
missing_credentials_string = '\n\t'.join(missing_credentials)
splunkbase_only_apps = []
for app,content in settings['apps'].items():
if 'local_path' not in content and 'http_path' not in content:
splunkbase_only_apps.append(app)
if len(splunkbase_only_apps) != 0:
print(f"Error - you have attempted to install the following apps: {splunkbase_only_apps}, "
"but you have not provided a local_path or an http_path in the config file. Normally, "
"we would download these from Splunkbase, but the following credentials are "
f"missing:\n\t{missing_credentials_string}\n Please provide them on the command line "
"or in the config file.\n\tQuitting...")
sys.exit(1)
print(f"You have listed apps to install but have "\
f"not provided\n\t{missing_credentials_string} \nvia the command line or config file. "
f"We will download these files from S3 rather than Splunkbase.")
else:
print(f"You have listed apps to install and provided Splunkbase credentials. "\
f"These apps will be downloaded and installed from Splunkbase!")
if settings['splunkbase_password'] == None and credentials_needed:
print("Error - you have listed apps to download from Splunkbase but have "\
"not provided --splunkbase_password via the command line or config file.",file=sys.stderr)
credential_error = True
if credential_error:
print("Please supply the required credentials to continue.\n\tQuitting...",file=sys.stderr)
sys.exit(1)
FULL_DOCKER_HUB_CONTAINER_NAME = "splunk/splunk:%s" % settings['container_tag']
@@ -405,42 +449,36 @@ def main(args: list[str]):
print("***This run will test [%d] detections!***"%(len(all_test_files)))
#Set up the directory that will be used to store the local apps/apps we build
local_volume_absolute_path = os.path.abspath(
os.path.join(os.getcwd(), "apps"))
try:
# remove the directory first
shutil.rmtree(local_volume_absolute_path, ignore_errors=True)
os.mkdir(local_volume_absolute_path)
except FileExistsError as e:
# Directory already exists, do nothing
pass
except Exception as e:
print("Error creating the apps folder [%s]: [%s]\n\tQuitting..."
% (local_volume_absolute_path, str(e)), file=sys.stderr)
sys.exit(1)
#Add the info about the mount
mounts = [{"local_path": local_volume_absolute_path,
"container_path": "/tmp/apps", "type": "bind", "read_only": True}]
# Check to see if we want to install ESCU and whether it was preeviously generated and we should use that file
if 'SPLUNK_ES_CONTENT_UPDATE' in settings['local_apps'] and settings['local_apps']['SPLUNK_ES_CONTENT_UPDATE']['local_path'] is not None:
if ES_APP_NAME in settings['apps'] and settings['apps'][ES_APP_NAME]['local_path'] is not None:
# Using a pregenerated ESCU, no need to build it
pass
elif 'SPLUNK_ES_CONTENT_UPDATE' not in settings['local_apps']:
print("%s was not found in %s. We assume this is an error and shut down.\n\t"
"Quitting..." % ('SPLUNK_ES_CONTENT_UPDATE', "settings['local_apps']"), file=sys.stderr)
elif ES_APP_NAME not in settings['apps']:
print(f"{ES_APP_NAME} was not found in {settings['apps'].keys()}. We assume this is an error and shut down.\n\t"
"Quitting...", file=sys.stderr)
sys.exit(1)
else:
# Generate the ESCU package from this branch.
source_path = generate_escu_app(settings['persist_security_content'])
settings['local_apps']['SPLUNK_ES_CONTENT_UPDATE']['local_path'] = source_path
settings['apps']['SPLUNK_ES_CONTENT_UPDATE']['local_path'] = source_path
# Copy all the apps, to include ESCU (whether pregenerated or just generated)
copy_local_apps_to_directory(
settings['local_apps'], local_volume_absolute_path)
try:
relative_app_path = copy_local_apps_to_directory(settings['apps'],
splunkbase_username = settings['splunkbase_username'],
splunkbase_password = settings['splunkbase_password'],
mock=settings['mock'], target_directory = CONTAINER_APP_DIRECTORY)
mounts = [{"local_path": os.path.abspath(relative_app_path),
"container_path": "/tmp/apps", "type": "bind", "read_only": True}]
except Exception as e:
print(f"Error occurred when copying apps to app folder: [{str(e)}]\n\tQuitting...", file=sys.stderr)
sys.exit(1)
# If this is a mock run, finish it now
@@ -467,13 +505,45 @@ def main(args: list[str]):
def shutdown_signal_handler_setup(sig, frame):
print(f"Signal {sig} received... stopping all [{settings['num_containers']}] containers and shutting down...")
shutdown_client = docker.client.from_env()
errorCount = 0
for container_number in range(settings['num_containers']):
container_name = settings['local_base_container_name']%container_number
print(f"Shutting down {container_name}...", file=sys.stderr, end='')
sys.stdout.flush()
try:
container = shutdown_client.containers.get(container_name)
#Note that stopping does not remove any of the volumes or logs,
#so stopping can be useful if we want to debug any container failure
container.stop(timeout=10)
print("done", file=sys.stderr)
except Exception as e:
print(f"Error trying to shut down {container_name}. It may have already shut down. Stop it youself with 'docker containter stop {container_name}", sys.stderr)
errorCount += 1
if errorCount == 0:
print("All containers shut down successfully", file=sys.stderr)
else:
print(f"{errorCount} containers may still be running. Find out what is running with:\n\t'docker container ls'\nand shut them down with\n\t'docker container stop CONTAINER_NAME' ", file=sys.stderr)
print("Quitting...",file=sys.stderr)
#We must use os._exit(1) because sys.exit(1) actually generates an exception which can be caught! And then we don't Quit!
os._exit(1)
#Setup requires a different teardown handler than during execution
signal.signal(signal.SIGINT, shutdown_signal_handler_setup)
try:
cm = container_manager.ContainerManager(all_test_files,
FULL_DOCKER_HUB_CONTAINER_NAME,
settings['local_base_container_name'],
settings['num_containers'],
settings['local_apps'],
settings['splunkbase_apps'],
settings['apps'],
settings['branch'],
settings['commit_hash'],
reproduce_test_config,
@@ -492,6 +562,15 @@ def main(args: list[str]):
print("Error - unrecoverable error trying to set up the containers: [%s].\n\tQuitting..."%(str(e)),file=sys.stderr)
sys.exit(1)
def shutdown_signal_handler_execution(sig, frame):
#Set that a container has failed which will gracefully stop the other containers.
#This way we get our full cleanup routine, too!
print("Got a signal to shut down. Shutting down all containers, please wait...", file=sys.stderr)
cm.synchronization_object.containerFailure()
#Update the signal handler
signal.signal(signal.SIGINT, shutdown_signal_handler_execution)
try:
result = cm.run_test()
except Exception as e:
@@ -508,7 +587,10 @@ def main(args: list[str]):
sys.exit(0)
else:
print("Test Execution Failed - review the logs for more details")
sys.exit(1)
#Because one or more of the threads could be stuck in a certain setup loop, like
#trying to copy files to a containers (which igonores errors), we must os._exit
#instead of sys.exit
os._exit(1)
if __name__ == "__main__":
@@ -1,4 +1,5 @@
from collections import OrderedDict
from tabnanny import check
import docker
import datetime
import docker.types
@@ -25,8 +26,7 @@ class ContainerManager:
full_docker_hub_name: str,
container_name_template: str,
num_containers: int,
local_apps: OrderedDict,
splunkbase_apps:OrderedDict,
apps: OrderedDict,
branch:str,
commit_hash:str,
summarization_reproduce_failure_config:dict,
@@ -43,12 +43,15 @@ class ContainerManager:
interactive:bool=False
):
#Used to determine whether or not we should wait for container threads to finish when summarizing
self.all_tests_completed = False
self.synchronization_object = test_driver.TestDriver(
test_list, num_containers, summarization_reproduce_failure_config)
self.mounts = self.create_mounts(mounts)
self.local_apps = local_apps
self.splunkbase_apps = splunkbase_apps
self.apps = apps
if container_password is None:
self.container_password = self.get_random_password()
@@ -93,21 +96,29 @@ class ContainerManager:
self.baseline['TEST_FINISH_TIME'] = "TO BE UPDATED"
self.baseline['TEST_DURATION'] = "TO BE UPDATED"
for key in self.local_apps:
self.baseline[key] = self.local_apps[key]
for key in self.apps:
self.baseline[key] = self.apps[key]
for key in self.splunkbase_apps:
self.baseline[key] = self.splunkbase_apps[key]
def run_test(self)->bool:
self.run_containers()
self.run_status_thread()
for container in self.containers:
container.thread.join()
print(container.get_container_summary())
print("Waiting for next summary thread printout to finish...")
self.run_containers()
self.summary_thread.join()
for container in self.containers:
if self.all_tests_completed == True:
container.thread.join()
elif self.all_tests_completed == False:
#For some reason, we stopped early. So don't wait on the child threads to finish. Don't join,
#these threads may be stuck in their setup loops. Continue on.
pass
print(container.get_container_summary())
print("All containers completed testing!")
@@ -170,8 +181,7 @@ class ContainerManager:
self.synchronization_object,
full_docker_hub_name,
container_name,
self.local_apps,
self.splunkbase_apps,
self.apps,
web_port_tuple,
management_port_tuple,
self.container_password,
@@ -219,16 +229,19 @@ class ContainerManager:
password = "".join(password_list)
return password
def queue_status_thread(self, status_interval:int=60)->None:
def queue_status_thread(self, status_interval:int=60, num_steps:int=10)->None:
while True:
if self.synchronization_object.checkContainerFailure():
print("One of the containers has shut down prematurely and generated an exception. Shut down the rest of the containers.")
for container in self.containers:
container.stopContainer()
print("All containers stopped")
return None
#This for loop lets us run the summarize print less often, but check for failure more often
for chunk in range(0, status_interval, int(status_interval/num_steps)):
if self.synchronization_object.checkContainerFailure():
print("One of the containers has shut down prematurely or the test was halted. Ensuring all containers are stopped.")
for container in self.containers:
container.stopContainer()
print("All containers stopped")
self.all_tests_completed = False
return None
time.sleep(status_interval/num_steps)
at_least_one_container_has_started_running_tests = False
for container in self.containers:
@@ -237,9 +250,9 @@ class ContainerManager:
break
if self.synchronization_object.summarize(testing_currently_active = at_least_one_container_has_started_running_tests) == False:
#There are no more tests to run, so we can return from this thread
self.all_tests_completed = True
return None
time.sleep(status_interval)
def setup_image(self, reuse_images: bool, container_name: str) -> None:
client = docker.client.from_env()
@@ -23,15 +23,14 @@ SPLUNKBASE_URL = "https://splunkbase.splunk.com/app/%d/release/%s/download"
SPLUNK_START_ARGS = "--accept-license"
#Give ten minutes to start - this is probably enough time
MAX_CONTAINER_START_TIME_SECONDS = 60*10
MAX_CONTAINER_START_TIME_SECONDS = 60*20
class SplunkContainer:
def __init__(
self,
synchronization_object: test_driver.TestDriver,
full_docker_hub_path,
container_name: str,
local_apps: OrderedDict,
splunkbase_apps: OrderedDict,
apps: OrderedDict,
web_port_tuple: tuple[str, int],
management_port_tuple: tuple[str, int],
container_password: str,
@@ -49,15 +48,15 @@ class SplunkContainer:
self.client = docker.client.from_env()
self.full_docker_hub_path = full_docker_hub_path
self.container_password = container_password
self.local_apps = local_apps
self.splunkbase_apps = splunkbase_apps
self.apps = apps
self.files_to_copy_to_container = files_to_copy_to_container
self.splunk_ip = splunk_ip
self.container_name = container_name
self.mounts = mounts
self.environment = self.make_environment(
local_apps, splunkbase_apps, container_password, splunkbase_username, splunkbase_password
apps, container_password, splunkbase_username, splunkbase_password
)
self.ports = self.make_ports(web_port_tuple, management_port_tuple)
self.web_port = web_port_tuple[1]
@@ -72,50 +71,56 @@ class SplunkContainer:
self.num_tests_completed = 0
def prepare_apps_path(
self,
local_apps: OrderedDict,
splunkbase_apps: OrderedDict,
apps: OrderedDict,
splunkbase_username: Union[str, None] = None,
splunkbase_password: Union[str, None] = None,
) -> tuple[str, bool]:
apps_to_install = []
#We don't require credentials unless we install at least one splunkbase app
require_credentials = False
for app_name, app_info in self.local_apps.items():
if 'local_path' in app_info:
#If the username and password are supplied, then we will use splunkbase...
#assuming that the app_name and app_number are supplied. Note that if a
#local_path is supplied, then it should override this option!
if splunkbase_username is not None and splunkbase_password is not None:
use_splunkbase = True
else:
use_splunkbase = False
for app_name, app_info in self.apps.items():
if use_splunkbase is True and 'local_path' not in app_info:
target = SPLUNKBASE_URL % (app_info["app_number"], app_info["app_version"])
apps_to_install.append(target)
#We will require credentials since we are installing at least one splunkbase app
require_credentials = True
#Some paths may have a local_path and an HTTP path defined. Default to the local_path first,
#mostly because we may have copied it before into the cache to speed up start time.
elif 'local_path' in app_info:
app_file_name = os.path.basename(app_info['local_path'])
app_file_container_path = os.path.join("/tmp/apps", app_file_name)
apps_to_install.append(app_file_container_path)
apps_to_install.append(app_file_container_path)
elif 'http_path' in app_info:
apps_to_install.append(app_info['http_path'])
else:
print("Error, the app %s: %s has no http_path or local_path.\n\tQuitting..."%(app_name,app_info), file=sys.stderr)
if use_splunkbase is True:
print("Error, the app %s: %s could not be installed from Splunkbase because "
"--splunkbase_username and.or --splunkbase_password were not provided."
"\n\tQuitting..."%(app_name,app_info), file=sys.stderr)
else:
print("Error, the app %s: %s has no http_path or local_path.\n\tQuitting..."%(app_name,app_info), file=sys.stderr)
sys.exit(1)
for app_name, app_info in self.splunkbase_apps.items():
if splunkbase_username is None or splunkbase_password is None:
raise Exception(
"Error: Requested app from Splunkbase but Splunkbase username and/or password were not supplied."
)
target = SPLUNKBASE_URL % (
app_info["app_number"], app_info["app_version"])
apps_to_install.append(target)
require_credentials = True
# elif app["location"] == "local":
# apps_to_install.append(app["container_path"])
#for printing out all the app paths we will install
#for num, name in zip(range(len(apps_to_install)),apps_to_install):
# print("%d: %s"%(num,name))
return ",".join(apps_to_install), require_credentials
def make_environment(
self,
local_apps: OrderedDict,
splunkbase_apps: OrderedDict,
apps: OrderedDict,
container_password: str,
splunkbase_username: Union[str, None] = None,
splunkbase_password: Union[str, None] = None,
@@ -124,7 +129,7 @@ class SplunkContainer:
env["SPLUNK_START_ARGS"] = SPLUNK_START_ARGS
env["SPLUNK_PASSWORD"] = container_password
splunk_apps_url, require_credentials = self.prepare_apps_path(
local_apps, splunkbase_apps, splunkbase_username, splunkbase_password
apps, splunkbase_username, splunkbase_password
)
if require_credentials:
@@ -314,10 +319,36 @@ class SplunkContainer:
time.sleep(seconds_between_attempts)
@wrapt_timeout_decorator.timeout(MAX_CONTAINER_START_TIME_SECONDS, timeout_exception=RuntimeError)
#@wrapt_timeout_decorator.timeout(MAX_CONTAINER_START_TIME_SECONDS, timeout_exception=RuntimeError)
def setup_container(self):
self.container.start()
# def shutdown_signal_handler(sig, frame):
# shutdown_client = docker.client.from_env()
# errorCount = 0
# print(f"Shutting down {self.container_name}...", file=sys.stderr)
# try:
# container = shutdown_client.containers.get(self.container_name)
# #Note that stopping does not remove any of the volumes or logs,
# #so stopping can be useful if we want to debug any container failure
# container.stop(timeout=10)
# print(f"{self.container_name} shut down successfully", file=sys.stderr)
# except Exception as e:
# print(f"Error trying to shut down {self.container_name}. It may have already shut down. Stop it youself with 'docker containter stop {self.container_name}", sys.stderr)
# #We must use os._exit(1) because sys.exit(1) actually generates an exception which can be caught! And then we don't Quit!
# import os
# os._exit(1)
# import signal
# signal.signal(signal.SIGINT, shutdown_signal_handler)
# By default, first copy the index file then the datamodel file
for file_description, file_dict in self.files_to_copy_to_container.items():
self.extract_tar_file_to_container(
@@ -419,7 +450,7 @@ class SplunkContainer:
% (detection_to_test, str(e))
)
traceback.print_exc()
#traceback.print_exc()
#import pdb
#pdb.set_trace()
# Fill in all the "Empty" fields with default values. Otherwise, we will not be able to
@@ -235,7 +235,7 @@ class TestDriver:
if self.checkContainerFailure():
print("One or more containers crashed, so testing did not complete successfully. We wrote out all the results that we could")
print("One or more containers crashed or the test was HALTED early, so testing did not complete successfully. We wrote out all the results that we could")
return False
else:
return success
@@ -269,16 +269,7 @@ class TestDriver:
def summarize(self,testing_currently_active:bool=False)->bool:
if self.checkContainerFailure() == True:
print("Error running containers... shutting down", file=sys.stderr)
return False
self.lock.acquire()
try:
#Get a summary of some system stats
@@ -8,7 +8,7 @@ from typing import Union
# If we want, we can easily add a description field to any of the objects here!
ES_APP_NAME = "SPLUNK_ES_CONTENT_UPDATE"
setup_schema = {
"type": "object",
"properties": {
@@ -50,7 +50,7 @@ setup_schema = {
},
"local_apps": {
"apps": {
"type": "object",
"additionalProperties": False,
"patternProperties": {
@@ -59,131 +59,101 @@ setup_schema = {
"additionalProperties": False,
"properties": {
"app_number": {
"type": [
"integer",
"null"
]
"type": ["integer","null"]
},
"app_version": {
"type": [
"string",
"null"
]
"type": ["string","null"]
},
"local_path": {
"type": [
"string",
"null"
]
"type": ["string","null"]
},
"http_path": {
"type": [
"string"
]
"type": ["string", "null"]
}
},
"oneOf": [
"anyOf": [
{"required": ["local_path"]},
{"required": ["http_path"]}
{"required": ["http_path"] },
{"required": ["app_number", "app_version"] },
]
}
},
"default": {
"SPLUNK_ES_CONTENT_UPDATE": {
ES_APP_NAME : {
"app_number": 3449,
"app_version": None,
"local_path": None
},
#The default apps below were taken from the attack_range loadout: https://github.com/splunk/attack_range/blob/develop/attack_range.conf.template
"SPLUNK_WINDOWS_TA": {
"app_number": 0,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-microsoft-windows_812.tgz"
"SPLUNK_ADD_ON_FOR_MICROSOFT_WINDOWS": {
"app_number": 742,
"app_version": "8.4.0",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-windows_840.tgz"
},
"SPLUNK_ADD_ON_FOR_SYSMON_OLD": {
"app_number": 1,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-microsoft-sysmon_1062.tgz"
"ADD_ON_FOR_LINUX_SYSMON": {
"app_number": 6176,
"app_version": "1.0.4",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/add-on-for-linux-sysmon_104.tgz"
},
"SPLUNK_SYSMON_LINUX_TA_PATCHED": {
"app_number": 2,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/add-on-for-linux-sysmon_103_PATCHED.tgz"
"SPLUNK_ADD_ON_FOR_SYSMON": {
"app_number": 5709,
"app_version": "2.0.0",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-sysmon_200.tgz"
},
"SPLUNK_CIM_APP": {
"app_number": 3,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-common-information-model-cim_4200.tgz"
"SPLUNK_COMMON_INFORMATION_MODEL": {
"app_number": 1621,
"app_version": "5.0.0",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-common-information-model-cim_500.tgz"
},
"SPLUNK_AWS_TA": {
"app_number": 4,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-amazon-web-services_510.tgz"
"PYTHON_FOR_SCIENTIFIC_COMPUTING_FOR_LINUX_64_BIT": {
"app_number": 2882,
"app_version": "3.0.2",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/python-for-scientific-computing-for-linux-64-bit_302.tgz"
},
"SPLUNK_PYTHON_APP": {
"app_number": 5,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/python-for-scientific-computing-for-linux-64-bit_202.tgz"
"SPLUNK_MACHINE_LEARNING_TOOLKIT": {
"app_number": 2890,
"app_version": "5.3.1",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-machine-learning-toolkit_531.tgz"
},
"SPLUNK_ASX_APP": {
"app_number": 17,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/Splunk_ASX-latest.tar.gz"
},
"SPLUNK_MLTK_APP": {
"app_number": 6,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-machine-learning-toolkit_521.tgz"
},
"SPLUNK_STREAM_APP": {
"app_number": 7,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-app-for-stream_730.tgz"
"SPLUNK_APP_FOR_STREAM": {
"app_number": 1809,
"app_version": "8.0.1",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-app-for-stream_801.tgz"
},
"SPLUNK_ADD_ON_FOR_STREAM_WIRE_DATA": {
"app_number": 8,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-stream-wire-data_730.tgz"
"app_number": 5234,
"app_version": "8.0.1",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-stream-wire-data_801.tgz"
},
"SPLUNK_ADD_ON_FOR_STREAM_FORWARDERS": {
"app_number": 9,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-stream-forwarders_730.tgz"
},
"SPLUNK_SECURITY_ESSENTIALS": {
"app_number": 10,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-security-essentials_333.tgz"
},
"SPLUNK_ADD_ON_FOR_ZEEK_AKA_BRO": {
"app_number": 11,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-zeek-aka-bro_400.tgz"
"app_number": 5238,
"app_version": "8.0.1",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-stream-forwarders_801.tgz"
},
"SPLUNK_ADD_ON_FOR_AMAZON_KINESIS_FIREHOSE": {
"app_number": 12,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-amazon-kinesis-firehose_131r7d1d093.tgz"
"app_number": 3719,
"app_version": "1.3.2",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-amazon-kinesis-firehose_132.tgz"
},
"SPLUNK_ADD_ON_FOR_MICROSOFT_OFFICE_365": {
"app_number": 13,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-microsoft-office-365_202.tgz"
"app_number": 4055,
"app_version": "2.2.0",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-office-365_220.tgz"
},
"SPLUNK_LINUX_TA": {
"app_number": 14,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-unix-and-linux_820.tgz"
"SPLUNK_ADD_ON_FOR_UNIX_AND_LINUX": {
"app_number": 833,
"app_version": "8.4.0",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-unix-and-linux_840.tgz"
},
"SPLUNK_NGINX_TA": {
"app_number": 15,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-nginx_310.tgz"
"SPLUNK_ADD_ON_FOR_NGINX": {
"app_number": 3258,
"app_version": "3.1.0",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-nginx_310.tgz"
},
"SPLUNK_TA_FOR_ZEEK": {
"app_number": 16,
"app_version": None,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/ta-for-zeek_105.tgz"
"app_number": 5466,
"app_version": "1.0.5",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/ta-for-zeek_105.tgz"
},
}
@@ -226,100 +196,6 @@ setup_schema = {
},
"splunkbase_apps": {
"type": "object",
"patternProperties": {
"^.*$": {
"type": "object",
"additionalProperties": False,
"properties": {
"app_number": {
"type": "integer"
},
"app_version": {
"type": "string"
}
}
}
},
"default": {
"SPLUNK_ADD_ON_FOR_SYSMON": {
"app_number": 5709,
"app_version": "1.0.1"
}
},
# "default": {
# "ADD-ON_FOR_LINUX_SYSMON": {
# "app_number": 6176,
# "app_version": "1.0.3"
# },
# "SPLUNK_ADD_ON_FOR_AMAZON_WEB_SERVICES": {
# "app_number": 1876,
# "app_version": "5.2.1"
# },
# "SPLUNK_ADD_ON_FOR_MICROSOFT_OFFICE_365":
# {
# "app_number": 4055,
# "app_version": "2.2.0"
# },
# "SPLUNK_ADD_ON_FOR_AMAZON_KINESIS_FIREHOSE": {
# "app_number": 3719,
# "app_version": "1.3.2"
# },
# "SPLUNK_ANALYTIC_STORY_EXECUTION_APP": {
# "app_number": 4971,
# "app_version": "2.0.3"
# },
# "PYTHON_FOR_SCIENTIC_COMPUTING_LINUX_64_BIT": {
# "app_number": 2882,
# "app_version": "3.0.1"
# },
# "SPLUNK_MACHINE_LEARNING_TOOLKIT": {
# "app_number": 2890,
# "app_version": "5.3.0"
# },
# "SPLUNK_APP_FOR_STREAM": {
# "app_number": 1809,
# "app_version": "8.0.1"
# },
# "SPLUNK_ADD_ON_FOR_STREAM_WIRE_DATA": {
# "app_number": 5234,
# "app_version": "8.0.1"
# },
# "SPLUNK_ADD_ON_FOR_STREAM_FORWARDERS": {
# "app_number": 5238,
# "app_version": "8.0.1"
# },
# "SPLUNK_ADD_ON_FOR_ZEEK_AKA_BRO": {
# "app_number": 1617,
# "app_version": "4.0.0"
# },
# "SPLUNK_ADD_ON_FOR_UNIX_AND_LINUX": {
# "app_number": 833,
# "app_version": "8.4.0"
# },
# "SPLUNK_ADD_ON_FOR_SYSMON": {
# "app_number": 5709,
# "app_version": "1.0.1"
# },
# # According to https://docs.splunk.com/Documentation/ES/6.6.2/Install/Datamodels, these are included in ES. Don't install separately.
# "SPLUNK_COMMON_INFORMATION_MODEL": {
# "app_number": 1621,
# "app_version": "5.0.0"
# }
# }
},
"splunkbase_username": {
"type": ["string", "null"],
"default": None
@@ -1,113 +1,93 @@
{
"apps": {
"ADD_ON_FOR_LINUX_SYSMON": {
"app_number": 6176,
"app_version": "1.0.4",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/add-on-for-linux-sysmon_104.tgz"
},
"PYTHON_FOR_SCIENTIFIC_COMPUTING_FOR_LINUX_64_BIT": {
"app_number": 2882,
"app_version": "3.0.2",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/python-for-scientific-computing-for-linux-64-bit_302.tgz"
},
"SPLUNK_ADD_ON_FOR_AMAZON_KINESIS_FIREHOSE": {
"app_number": 3719,
"app_version": "1.3.2",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-amazon-kinesis-firehose_132.tgz"
},
"SPLUNK_ADD_ON_FOR_MICROSOFT_OFFICE_365": {
"app_number": 4055,
"app_version": "2.2.0",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-office-365_220.tgz"
},
"SPLUNK_ADD_ON_FOR_MICROSOFT_WINDOWS": {
"app_number": 742,
"app_version": "8.4.0",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-microsoft-windows_840.tgz"
},
"SPLUNK_ADD_ON_FOR_NGINX": {
"app_number": 3258,
"app_version": "3.1.0",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-nginx_310.tgz"
},
"SPLUNK_ADD_ON_FOR_STREAM_FORWARDERS": {
"app_number": 5238,
"app_version": "8.0.1",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-stream-forwarders_801.tgz"
},
"SPLUNK_ADD_ON_FOR_STREAM_WIRE_DATA": {
"app_number": 5234,
"app_version": "8.0.1",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-stream-wire-data_801.tgz"
},
"SPLUNK_ADD_ON_FOR_SYSMON": {
"app_number": 5709,
"app_version": "2.0.0",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-sysmon_200.tgz"
},
"SPLUNK_ADD_ON_FOR_UNIX_AND_LINUX": {
"app_number": 833,
"app_version": "8.4.0",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-unix-and-linux_840.tgz"
},
"SPLUNK_APP_FOR_STREAM": {
"app_number": 1809,
"app_version": "8.0.1",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-app-for-stream_801.tgz"
},
"SPLUNK_COMMON_INFORMATION_MODEL": {
"app_number": 1621,
"app_version": "5.0.0",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-common-information-model-cim_500.tgz"
},
"SPLUNK_ES_CONTENT_UPDATE": {
"app_number": 3449,
"app_version": null,
"local_path": null
},
"SPLUNK_MACHINE_LEARNING_TOOLKIT": {
"app_number": 2890,
"app_version": "5.3.1",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-machine-learning-toolkit_531.tgz"
},
"SPLUNK_TA_FOR_ZEEK": {
"app_number": 5466,
"app_version": "1.0.5",
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/ta-for-zeek_105.tgz"
}
},
"branch": "BRANCH_DOES_NOT_EXIST_USE_CLI_ARGUMENT",
"commit_hash": null,
"container_tag": "latest",
"detections_file": null,
"detections_list": null,
"folders": [
"endpoint",
"cloud",
"network",
"web"
"endpoint",
"cloud",
"network",
"web"
],
"interactive": false,
"local_apps": {
"SPLUNK_ADD_ON_FOR_AMAZON_KINESIS_FIREHOSE": {
"app_number": 12,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-amazon-kinesis-firehose_131r7d1d093.tgz"
},
"SPLUNK_ADD_ON_FOR_MICROSOFT_OFFICE_365": {
"app_number": 13,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-microsoft-office-365_202.tgz"
},
"SPLUNK_ADD_ON_FOR_STREAM_FORWARDERS": {
"app_number": 9,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-stream-forwarders_730.tgz"
},
"SPLUNK_ADD_ON_FOR_STREAM_WIRE_DATA": {
"app_number": 8,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-stream-wire-data_730.tgz"
},
"SPLUNK_ADD_ON_FOR_SYSMON_OLD": {
"app_number": 1,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-microsoft-sysmon_1062.tgz"
},
"SPLUNK_ADD_ON_FOR_ZEEK_AKA_BRO": {
"app_number": 11,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-zeek-aka-bro_400.tgz"
},
"SPLUNK_ASX_APP": {
"app_number": 17,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/Splunk_ASX-latest.tar.gz"
},
"SPLUNK_AWS_TA": {
"app_number": 4,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-amazon-web-services_510.tgz"
},
"SPLUNK_CIM_APP": {
"app_number": 3,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-common-information-model-cim_4200.tgz"
},
"SPLUNK_ES_CONTENT_UPDATE": {
"app_number": 3449,
"app_version": null,
"local_path": null
},
"SPLUNK_LINUX_TA": {
"app_number": 14,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-unix-and-linux_820.tgz"
},
"SPLUNK_MLTK_APP": {
"app_number": 6,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/splunk-machine-learning-toolkit_531.tgz"
},
"SPLUNK_NGINX_TA": {
"app_number": 15,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-nginx_310.tgz"
},
"SPLUNK_PYTHON_APP": {
"app_number": 5,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/python-for-scientific-computing-for-linux-64-bit_302.tgz"
},
"SPLUNK_SECURITY_ESSENTIALS": {
"app_number": 10,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-security-essentials_333.tgz"
},
"SPLUNK_STREAM_APP": {
"app_number": 7,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-app-for-stream_730.tgz"
},
"SPLUNK_SYSMON_LINUX_TA_PATCHED": {
"app_number": 2,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/add-on-for-linux-sysmon_103_PATCHED.tgz"
},
"SPLUNK_TA_FOR_ZEEK": {
"app_number": 16,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/ta-for-zeek_105.tgz"
},
"SPLUNK_WINDOWS_TA": {
"app_number": 0,
"app_version": null,
"http_path": "https://attack-range-appbinaries.s3-us-west-2.amazonaws.com/splunk-add-on-for-microsoft-windows_812.tgz"
}
},
"local_base_container_name": "splunk_test_%d",
"mock": false,
"mode": "changes",
@@ -119,17 +99,11 @@
"show_splunk_app_password": false,
"splunk_app_password": null,
"splunk_container_apps_directory": "/opt/splunk/etc/apps",
"splunkbase_apps": {
"SPLUNK_ADD_ON_FOR_SYSMON": {
"app_number": 5709,
"app_version": "1.0.1"
}
},
"splunkbase_password": null,
"splunkbase_username": null,
"types": [
"Anomaly",
"Hunting",
"TTP"
"Anomaly",
"Hunting",
"TTP"
]
}
}
+33 -2
View File
@@ -1,3 +1,5 @@
from threading import Thread
from functools import cache
import glob
import yaml
import argparse
@@ -12,6 +14,7 @@ from stix2 import Filter
from pycvesearch import CVESearch
CVESSEARCH_API_URL = 'https://cve.circl.lu'
CVESSEARCH_API_TIMEOUT = 10
def load_objects(REPO_PATH, TYPE):
@@ -94,6 +97,16 @@ def parse_and_add_lookups(search_string, lookups):
return lookup_objects
#This function hits an API, which can be slow, especially if the API
#or network connection is slow. This has sometimes caused issues
#in our CI/CD taking a very long time. Here, we memoize/cache
#the calls to this function - there will be many duplicates which
#should all return the same results. This will gives us a significant
#speedup.
#Hackish way of keeping the original functionality and allowing it
#to easily return a value when in a thread... making an optional
#default argument with a type that is mutable!
@cache
def get_cve_enrichment_new(cve_id):
cve = CVESearch(CVESSEARCH_API_URL)
result = cve.id(cve_id)
@@ -103,6 +116,15 @@ def get_cve_enrichment_new(cve_id):
cve_enriched['summary'] = result['summary']
return cve_enriched
#helper function to easily return a value from a thread that is
#running a memoized/@cached function
def get_cve_enrichment_new_wrapper(cve_id,mutable_list):
mutable_list.append(get_cve_enrichment_new(cve_id))
def get_all_techniques(projects_path):
path_cti = path.join(projects_path,'cti/enterprise-attack')
fs = FileSystemSource(path_cti)
@@ -327,8 +349,17 @@ def generate_doc_detections(REPO_PATH, OUTPUT_DIR, TEMPLATE_PATH, attack, messag
cves = []
if 'cve' in detection_yaml['tags']:
for cve_id in detection_yaml['tags']['cve']:
cve = get_cve_enrichment_new(cve_id)
cves.append(cve)
mutable_list = []
try:
cve_thread = Thread(target=get_cve_enrichment_new_wrapper, args=(cve_id, mutable_list))
cve_thread.start()
cve_thread.join(timeout=CVESSEARCH_API_TIMEOUT)
if cve_thread.is_alive():
raise(Exception(f"Timed out getting CVE Enrichment from {CVESSEARCH_API_URL} after {CVESSEARCH_API_TIMEOUT} seconds."))
cves.append(mutable_list[0])
except Exception as e:
print(f"Error - {str(e)}\nQuitting...",file=sys.stderr)
sys.exit(1)
detection_yaml['cve'] = cves
# enrich with macros
@@ -1,17 +1,19 @@
name: O365 Excessive Authentication Failures Alert
id: d441364c-349c-453b-b55f-12eccab67cf9
version: 1
date: '2020-12-16'
version: 2
date: '2022-02-18'
author: Rod Soto, Splunk
type: Anomaly
datamodel: []
description: This search detects when an excessive number of authentication failures
occur this search also includes attempts against MFA prompt codes
search: '`o365_management_activity` Workload=AzureActiveDirectory UserAuthenticationMethod=*
status=Failed | stats count earliest(_time) as firstTime latest(_time) values(UserAuthenticationMethod)
AS UserAuthenticationMethod values(UserAgent) AS UserAgent values(status) AS status
values(src_ip) AS src_ip by user | where count > 10 |`security_content_ctime(firstTime)`
|`security_content_ctime(lastTime)` | `o365_excessive_authentication_failures_alert_filter`'
search: '`o365_management_activity` Workload=AzureActiveDirectory UserAuthenticationMethod=* status=failure
| stats count earliest(_time) as firstTime latest(_time) values(UserAuthenticationMethod) AS UserAuthenticationMethod
values(UserAgent) AS UserAgent values(status) AS status values(src_ip) AS src_ip by user
| where count > 10
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `o365_excessive_authentication_failures_alert_filter`'
how_to_implement: You must install splunk Microsoft Office 365 add-on. This search
works with o365:management:activity
known_false_positives: The threshold for alert is above 10 attempts and this should
@@ -1,7 +1,7 @@
name: Detect Regasm with Network Connection
id: 07921114-6db4-4e2e-ae58-3ea8a52ae93f
version: 1
date: '2021-02-16'
version: 2
date: '2022-02-18'
author: Michael Haag, Splunk
type: TTP
datamodel: []
@@ -15,10 +15,12 @@ description: The following analytic identifies regasm.exe with a network connect
and review accordingly. Review the reputation of the remote IP or domain and block
as needed. regsvcs.exe and regasm.exe are natively found in C:\Windows\Microsoft.NET\Framework\v*\regasm|regsvcs.exe
and C:\Windows\Microsoft.NET\Framework64\v*\regasm|regsvcs.exe.
search: '`sysmon` EventID=3 dest_ip!=10.0.0.0/12 dest_ip!=172.16.0.0/12 dest_ip!=192.168.0.0/16
process_name=regasm.exe | rename Computer as dest | stats count min(_time) as firstTime
max(_time) as lastTime by dest, User, process_name, src_ip, dest_host, dest_ip |
`security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `detect_regasm_with_network_connection_filter`'
search: '`sysmon` EventID=3 dest_ip!=10.0.0.0/12 dest_ip!=172.16.0.0/12 dest_ip!=192.168.0.0/16 process_name=regasm.exe
| rename Computer as dest
| stats count min(_time) as firstTime max(_time) as lastTime by dest, user, process_name, src_ip, dest_ip
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `detect_regasm_with_network_connection_filter`'
how_to_implement: To successfully implement this search, you need to be ingesting
logs with the process name, parent process, and command-line executions from your
endpoints. If you are using Sysmon, you must have at least version 6.0.4 of the
@@ -59,7 +61,7 @@ tags:
type: User
role:
- Victim
- name: Computer
- name: dest
type: Hostname
role:
- Victim
@@ -77,7 +79,7 @@ tags:
- dest_ip
- process_name
- Computer
- User
- user
- src_ip
- dest_host
- dest_ip
@@ -1,7 +1,7 @@
name: Detect Regsvcs with Network Connection
id: e3e7a1c0-f2b9-445c-8493-f30a63522d1a
version: 1
date: '2021-02-16'
version: 2
date: '2022-02-18'
author: Michael Haag, Splunk
type: TTP
datamodel: []
@@ -15,10 +15,12 @@ description: The following analytic identifies Regsvcs.exe with a network connec
and review accordingly. Review the reputation of the remote IP or domain and block
as needed. regsvcs.exe and regasm.exe are natively found in C:\Windows\Microsoft.NET\Framework\v*\regasm|regsvcs.exe
and C:\Windows\Microsoft.NET\Framework64\v*\regasm|regsvcs.exe.
search: '`sysmon` EventID=3 dest_ip!=10.0.0.0/12 dest_ip!=172.16.0.0/12 dest_ip!=192.168.0.0/16
process_name=regsvcs.exe | rename Computer as dest | stats count min(_time) as firstTime
max(_time) as lastTime by dest, User, process_name, src_ip, dest_host, dest_ip |
`security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `detect_regsvcs_with_network_connection_filter`'
search: '`sysmon` EventID=3 dest_ip!=10.0.0.0/12 dest_ip!=172.16.0.0/12 dest_ip!=192.168.0.0/16 process_name=regsvcs.exe
| rename Computer as dest
| stats count min(_time) as firstTime max(_time) as lastTime by dest, user, process_name, src_ip, dest_ip
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `detect_regsvcs_with_network_connection_filter`'
how_to_implement: To successfully implement this search, you need to be ingesting
logs with the process name, parent process, and command-line executions from your
endpoints. If you are using Sysmon, you must have at least version 6.0.4 of the
@@ -59,7 +61,7 @@ tags:
type: User
role:
- Victim
- name: Computer
- name: dest
type: Hostname
role:
- Victim
@@ -77,7 +79,7 @@ tags:
- dest_ip
- process_name
- Computer
- User
- user
- src_ip
- dest_host
risk_score: 80
@@ -1,7 +1,7 @@
name: Interactive Session on Remote Endpoint with PowerShell
id: a4e8f3a4-48b2-11ec-bcfc-3e22fbd008af
version: 1
date: '2021-11-18'
version: 2
date: '2022-02-18'
author: Mauricio Velazco, Splunk
type: TTP
datamodel: []
@@ -10,9 +10,10 @@ description: The following analytic utilizes PowerShell Script Block Logging (Ev
an interactive session on a remote endpoint leveraging the WinRM protocol. Red Teams
and adversaries alike may abuse WinRM and `Enter-PSSession` for lateral movement
and remote code execution.
search: powershell` EventCode=4104 (Message="*Enter-PSSession*" AND Message="*-ComputerName*")
| stats count min(_time) as firstTime max(_time) as lastTime by EventCode Message
ComputerName User | `security_content_ctime(firstTime)` | `interactive_session_on_remote_endpoint_with_powershell_filter`
search: '`powershell` EventCode=4104 (Message="*Enter-PSSession*" AND Message="*-ComputerName*")
| stats count min(_time) as firstTime max(_time) as lastTime by EventCode Message ComputerName User
| `security_content_ctime(firstTime)`
| `interactive_session_on_remote_endpoint_with_powershell_filter`'
how_to_implement: To successfully implement this analytic, you will need to enable
PowerShell Script Block Logging on some or all endpoints. Additional setup instructions
can be found https://docs.splunk.com/Documentation/UBA/5.0.4.1/GetDataIn/AddPowerShell#Configure_module_logging_for_PowerShell.
@@ -13,7 +13,7 @@ description: The following analytic identifies a suspicious process creation of
and after this process execution, when it was executed and what schedule task it
will execute.
search: '| tstats `security_content_summariesonly` count from datamodel=Endpoint.Processes
where Processes.process_name = at OR Processes.parent_process_name = at by Processes.dest
where Processes.process_name IN ("at", "atd") OR Processes.parent_process_name IN ("at", "atd") by Processes.dest
Processes.user Processes.parent_process_name Processes.process_name Processes.process
Processes.process_id Processes.parent_process_id Processes.process_guid | `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `linux_at_application_execution_filter`'
@@ -16,10 +16,10 @@ search: '| tstats `security_content_summariesonly` count min(_time) as firstTime
as lastTime from datamodel=Endpoint.Processes where Processes.process_name IN ("chmod",
"chown", "fchmod", "fchmodat", "fchown", "fchownat", "fremovexattr", "fsetxattr",
"lchown", "lremovexattr", "lsetxattr", "removexattr", "setuid", "setgid", "setreuid",
"setregid") OR Processes.process IN ("*chmod *", "*chown *", "*fchmod *", "*fchmodat
"setregid", "chattr") OR Processes.process IN ("*chmod *", "*chown *", "*fchmod *", "*fchmodat
*", "*fchown *", "*fchownat *", "*fremovexattr *", "*fsetxattr *", "*lchown *",
"*lremovexattr *", "*lsetxattr *", "*removexattr *", "*setuid *", "*setgid *", "*setreuid
*", "*setregid *", "*setcap *") by Processes.dest Processes.user Processes.parent_process_name
*", "*setregid *", "*setcap *", "*chattr *") by Processes.dest Processes.user Processes.parent_process_name
Processes.process_name Processes.process Processes.process_id Processes.parent_process_id
Processes.process_guid | `drop_dm_object_name(Processes)` | `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)` | `linux_common_process_for_elevation_control_filter`'
@@ -0,0 +1,70 @@
name: Linux DD File Overwrite
id: 9b6aae5e-8d85-11ec-b2ae-acde48001122
version: 1
date: '2022-02-14'
author: Teoderick Contreras, Splunk
type: TTP
datamodel:
- Endpoint
description: This analytic is to look for dd command to overwrite file. This technique was abused by adversaries or
threat actor to destroy files or data on specific system or in a large number of host within network to interrupt host avilability,
services and many more. This is also used to destroy data where it make the file irrecoverable by forensic techniques through overwriting files,
data or local and remote drives.
search: '| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) as lastTime from datamodel=Endpoint.Processes
where Processes.process_name = "dd" AND Processes.process = "*of=*"
by Processes.dest Processes.user Processes.parent_process_name Processes.process_name Processes.process Processes.process_id Processes.parent_process_id Processes.process_guid
| `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `linux_dd_file_overwrite_filter`'
how_to_implement: To successfully implement this search, you need to be ingesting
logs with the process name, parent process, and command-line executions from your
endpoints. If you are using Sysmon, you can use the Add-on for Linux Sysmon from
Splunkbase.
known_false_positives: Administrator or network operator can execute this command.
Please update the filter macros to remove false positives.
references:
- https://gtfobins.github.io/gtfobins/dd/
- https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1485/T1485.md
tags:
analytic_story:
- Data Destruction
dataset:
- https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1485/linux_dd_file_overwrite/sysmon_linux.log
kill_chain_phases:
- Exploitation
mitre_attack_id:
- T1485
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
required_fields:
- _time
- Processes.dest
- Processes.user
- Processes.parent_process_name
- Processes.process_name
- Processes.process
- Processes.process_id
- Processes.parent_process_id
security_domain: endpoint
impact: 80
confidence: 80
# (impact * confidence)/100
risk_score: 64
context:
- Source:Endpoint
- Stage:Impact
message: A commandline $process$ executed on $dest$
observable:
- name: dest
type: Hostname
role:
- Victim
nist:
- DE.CM
cis20:
- CIS 3
- CIS 5
- CIS 16
@@ -0,0 +1,70 @@
name: Linux System Network Discovery
id: 535cb214-8b47-11ec-a2c7-acde48001122
version: 1
date: '2022-02-11'
author: Teoderick Contreras, Splunk
type: Anomaly
datamodel:
- Endpoint
description: This analytic is to look for possible enumeration of local network configuration.
This technique is commonly used as part of recon of adversaries or threat actor to know some network information for its next or further
attack. This anomaly detections may capture normal event made by administrator during auditing or testing network connection of specific
host or network to network.
search: '| tstats `security_content_summariesonly`
count values(Processes.process_name) as process_name_list values(Processes.process) as process_list
values(Processes.process_id) as process_id_list values(Processes.parent_process_id) as parent_process_id_list
values(Processes.process_guid) as process_guid_list dc(Processes.process_name) as process_name_count from datamodel=Endpoint.Processes
where Processes.process_name IN ("arp", "ifconfig", "ip", "netstat", "firewall-cmd", "ufw", "iptables", "ss", "route")
by _time span=30m Processes.dest Processes.user
| where process_name_count >=4
| `drop_dm_object_name(Processes)`| `linux_system_network_discovery_filter`'
how_to_implement: To successfully implement this search, you need to be ingesting
logs with the process name, parent process, and command-line executions from your
endpoints. If you are using Sysmon, you can use the Add-on for Linux Sysmon from
Splunkbase.
known_false_positives: Administrator or network operator can execute this command.
Please update the filter macros to remove false positives.
references:
- https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1016/T1016.md
tags:
analytic_story:
- Network Discovery
dataset:
- https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1016/atomic_red_team/linux_net_discovery/sysmon_linux.log
kill_chain_phases:
- Reconnaissance
mitre_attack_id:
- T1016
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
required_fields:
- _time
- Processes.dest
- Processes.user
- Processes.parent_process_name
- Processes.process_name
- Processes.process
- Processes.process_id
- Processes.parent_process_id
security_domain: endpoint
impact: 30
confidence: 30
# (impact * confidence)/100
risk_score: 9
context:
- Source:Endpoint
- Stage:Reconaissance
message: A commandline $process$ executed on $dest$
observable:
- name: dest
type: Hostname
role:
- Victim
nist:
- DE.CM
cis20:
- CIS 3
- CIS 5
- CIS 16
@@ -1,7 +1,7 @@
name: NET Profiler UAC bypass
id: 0252ca80-e30d-11eb-8aa3-acde48001122
version: 1
date: '2021-07-12'
version: 2
date: '2022-02-18'
author: Teoderick Contreras, Splunk
type: TTP
datamodel:
@@ -12,11 +12,14 @@ description: This search is to detect modification of registry to bypass UAC win
the registry key and values in the detection area. It may happened that windows
update some dll related to mmc.exe and add dll path in this registry. In this case
filtering is needed.
search: '| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time)
as lastTime from datamodel=Endpoint.Registry where Registry.registry_path= "*\\Environment\\COR_PROFILER_PATH"
Registry.registry_value_name = "*.dll" by Registry.registry_path Registry.registry_key_name
Registry.registry_value_name Registry.dest | `drop_dm_object_name(Registry)` | `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)` | `net_profiler_uac_bypass_filter`'
search: '| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Registry where
Registry.registry_path= "*\\Environment\\COR_PROFILER_PATH" Registry.registry_value_data = "*.dll"
by Registry.registry_path Registry.registry_key_name Registry.registry_value_data Registry.dest
| `drop_dm_object_name(Registry)`
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `net_profiler_uac_bypass_filter`'
how_to_implement: To successfully implement this search you need to be ingesting information
on process that include the name of the process responsible for the changes from
your endpoints into the `Endpoint` datamodel in the `Registry` node. Also make sure
@@ -1,7 +1,7 @@
name: Powershell Remove Windows Defender Directory
id: adf47620-79fa-11ec-b248-acde48001122
version: 1
date: '2022-01-20'
version: 2
date: '2022-01-18'
author: Teoderick Contreras, Splunk
type: TTP
datamodel:
@@ -11,10 +11,11 @@ description: This analytic will identify a suspicious PowerShell command used to
campaign where it used Nirsofts advancedrun.exe to gain administrative privileges
to then execute a PowerShell command to delete the Windows Defender folder. This
is a good indicator the offending process is trying corrupt a Windows Defender installation.
search: '`powershell` EventCode=4104 Message = "* rmdir *" AND Message = "*\\Microsoft\\Windows
Defender*" | stats count min(_time) as firstTime max(_time) as lastTime by EventCode
Message ComputerName User | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
| `powershell_remove_windows_defender_directory_filter`'
search: '`powershell` EventCode=4104 Message = "*rmdir *" AND Message = "*\\Microsoft\\Windows Defender*"
| stats count min(_time) as firstTime max(_time) as lastTime by EventCode Message ComputerName User
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `powershell_remove_windows_defender_directory_filter` '
how_to_implement: To successfully implement this analytic, you will need to enable
PowerShell Script Block Logging on some or all endpoints. Additional setup here
https://docs.splunk.com/Documentation/UBA/5.0.4.1/GetDataIn/AddPowerShell#Configure_module_logging_for_PowerShell.
@@ -1,7 +1,7 @@
name: Process Deleting Its Process File Path
id: f7eda4bc-871c-11eb-b110-acde48001122
version: 1
date: '2021-03-17'
version: 2
date: '2022-02-18'
author: Teoderick Contreras
type: TTP
datamodel:
@@ -11,11 +11,13 @@ description: This detection is to identify a suspicious process that tries to de
evasion once a certain condition of malware is satisfied or not. Clop ransomware
use this technique where it will try to delete its process file path using a .bat
command if the keyboard layout is not the layout it tries to infect.
search: '`sysmon` EventCode=1 cmdline = "* /c *" cmdline = "* del*" Image = "*\\cmd.exe"
|eval result = if(like(process,"%".parent_process."%"), "Found", "Not Found") |
stats min(_time) as firstTime max(_time) as lastTime count by Computer user ParentImage
ParentCommandLine Image cmdline EventCode ProcessID result | where result = "Found"
| `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)` | `process_deleting_its_process_file_path_filter`'
search: '`sysmon` EventCode=1 CommandLine = "* /c *" CommandLine = "* del*" Image = "*\\cmd.exe"
| eval result = if(like(process,"%".parent_process."%"), "Found", "Not Found")
| stats min(_time) as firstTime max(_time) as lastTime count by Computer user ParentImage ParentCommandLine Image CommandLine EventCode ProcessID result
| where result = "Found"
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `process_deleting_its_process_file_path_filter`'
how_to_implement: You must be ingesting data that records process activity from your
hosts to populate the Endpoint data model in the Processes node. You must also be
ingesting logs with both the process name and command line from your endpoints.
@@ -34,8 +36,8 @@ tags:
automated_detection_testing: passed
confidence: 100
context:
- source:endpoint
- stage: Credential Access
- Source:Endpoint
- Stage:Credential Access
dataset:
- https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/malware/clop/clop_a/windows-sysmon.log
impact: 60
@@ -51,7 +53,7 @@ tags:
role:
- Victim
- name: user
type: user
type: User
role:
- Victim
product:
@@ -2,14 +2,14 @@ name: Registry Keys Used For Persistence
id: f5f6af30-7aa7-4295-bfe9-07fe87c01a4b
version: 7
date: '2022-01-26'
author: Jose Hernandez, David Dorsey, Teoderick Contreras, Splunk
author: Jose Hernandez, David Dorsey, Teoderick Contreras, Rod Soto, Splunk
type: TTP
datamodel:
- Endpoint
description: The search looks for modifications to registry keys that can be used
to launch an application or service at system startup.
search: '| tstats `security_content_summariesonly` count FROM datamodel=Endpoint.Registry
where (Registry.registry_path=*\\currentversion\\run* OR Registry.registry_path=*\\currentVersion\\Windows\\Appinit_Dlls*
where (Registry.registry_path=*\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce OR Registry.registry_path=*\\currentversion\\run* OR Registry.registry_path=*\\currentVersion\\Windows\\Appinit_Dlls*
OR Registry.registry_path=*\\CurrentVersion\\Winlogon\\Shell* OR Registry.registry_path=*\\CurrentVersion\\Winlogon\\Notify*
OR Registry.registry_path=*\\CurrentVersion\\Winlogon\\Userinit* OR Registry.registry_path=*\\CurrentVersion\\Winlogon\\VmApplet*
OR Registry.registry_path=*\\currentversion\\policies\\explorer\\run* OR Registry.registry_path=*\\currentversion\\runservices*
@@ -65,6 +65,7 @@ tags:
- Privilege Escalation
dataset:
- https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1547.001/atomic_red_team/windows-sysmon.log
- https://raw.githubusercontent.com/splunk/attack_data/master/datasets/attack_techniques/T1547.001/atomic_red_team/t1547001-runonce.log
impact: 80
kill_chain_phases:
- Actions on Objectives
+6 -6
View File
@@ -1,7 +1,7 @@
name: Rundll32 DNSQuery
id: f1483f5e-ee29-11eb-9d23-acde48001122
version: 1
date: '2021-07-26'
version: 2
date: '2022-02-18'
author: Teoderick Contreras, Splunk
type: TTP
datamodel:
@@ -11,9 +11,10 @@ description: This search is to detect a suspicious rundll32.exe process having a
malware where the rundll32 that execute its payload will contact amazon.com to check
internet connect and to communicate to its C&C server to download config and other
file component.
search: '`sysmon` EventCode=22 process_name="rundll32.exe" | stats count min(_time)
as firstTime max(_time) as lastTime by Image QueryName QueryStatus ProcessId direction
Computer | `security_content_ctime(firstTime)` | `security_content_ctime(lastTime)`
search: '`sysmon` EventCode=22 process_name="rundll32.exe"
| stats count min(_time) as firstTime max(_time) as lastTime by Image QueryName QueryStatus ProcessId Computer
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `rundll32_dnsquery_filter`'
how_to_implement: To successfully implement this search, you need to be ingesting
logs with the process name and eventcode = 22 dnsquery executions from your endpoints.
@@ -59,7 +60,6 @@ tags:
- QueryName
- QueryStatus
- ProcessId
- direction
- Computer
risk_score: 56
security_domain: endpoint
@@ -1,31 +1,29 @@
name: Scheduled Task Deleted Or Created via CMD
id: d5af132c-7c17-439c-9d31-13d55340f36c
version: 5
date: '2020-12-17'
version: 6
date: '2022-02-22'
author: Bhavin Patel, Splunk
type: TTP
datamodel:
- Endpoint
description: This search looks for flags passed to schtasks.exe on the command-line
that indicate a task was created via command like. This has been associated with
description: The following analytic identifies the creation or deletion of a scheduled task using schtasks.exe with flags - create or delete being passed on the command-line. This has been associated with
the Dragonfly threat actor, and the SUNBURST attack against Solarwinds.
This analytic replaces "Scheduled Task used in BadRabbit Ransomware".
search: '| tstats `security_content_summariesonly` count values(Processes.process)
as process values(Processes.parent_process) as parent_process min(_time) as firstTime
max(_time) as lastTime from datamodel=Endpoint.Processes where Processes.process_name=schtasks.exe
(Processes.process=*delete* OR Processes.process=*create*) by Processes.user Processes.process_name
Processes.parent_process_name Processes.dest | `drop_dm_object_name(Processes)`
| `security_content_ctime(firstTime)`| `security_content_ctime(lastTime)` | `scheduled_task_deleted_or_created_via_cmd_filter` '
how_to_implement: You must be ingesting endpoint data that tracks process activity,
including parent-child relationships from your endpoints to populate the Endpoint
data model in the Processes node. The command-line arguments are mapped to the "process"
field in the Endpoint data model.
known_false_positives: Tasks should not be manually created via CLI, this is rarely
done by admins as well
references: []
how_to_implement: To successfully implement this search you need to be ingesting information on process that include the name of the process responsible for the changes from your endpoints into the `Endpoint` datamodel in the `Processes` node. In addition, confirm the latest CIM App 4.20 or higher is installed and the latest TA for the endpoint product.
known_false_positives: It is possible scripts or administrators may trigger this analytic. Filter as needed based on parent process, application.
references:
- https://thedfirreport.com/2022/02/21/qbot-and-zerologon-lead-to-full-domain-compromise/
tags:
analytic_story:
- DHS Report TA18-074A
- NOBELIUM Group
- Windows Persistence Techniques
asset_type: Endpoint
automated_detection_testing: passed
cis20:
@@ -1,18 +1,20 @@
name: Set Default PowerShell Execution Policy To Unrestricted or Bypass
id: c2590137-0b08-4985-9ec5-6ae23d92f63d
version: 6
date: '2020-11-06'
version: 7
date: '2022-02-18'
author: Patrick Bareiss, Splunk
type: TTP
datamodel:
- Endpoint
description: Monitor for changes of the ExecutionPolicy in the registry to the values
"unrestricted" or "bypass," which allows the execution of malicious scripts.
search: '| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time)
as lastTime from datamodel=Endpoint.Registry where Registry.registry_path=*Software\\Microsoft\\Powershell\\1\\ShellIds\\Microsoft.PowerShell*
Registry.registry_key_name=ExecutionPolicy (Registry.registry_value_name=Unrestricted
OR Registry.registry_value_name=Bypass) by Registry.registry_path Registry.registry_key_name
Registry.registry_value_name Registry.dest | `drop_dm_object_name(Registry)` | `security_content_ctime(firstTime)`|`security_content_ctime(lastTime)`
search: '| tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) as lastTime from
datamodel=Endpoint.Registry where Registry.registry_path=*Software\\Microsoft\\Powershell\\1\\ShellIds\\Microsoft.PowerShell*
Registry.registry_value_name=ExecutionPolicy (Registry.registry_value_data=Unrestricted OR Registry.registry_value_data=Bypass)
by Registry.registry_path Registry.registry_value_name Registry.registry_value_data Registry.dest
| `drop_dm_object_name(Registry)`
| `security_content_ctime(firstTime)`
| `security_content_ctime(lastTime)`
| `set_default_powershell_execution_policy_to_unrestricted_or_bypass_filter`'
how_to_implement: You must be ingesting data that records process activity from your
hosts to populate the Endpoint data model in the Registry node. You must also be
@@ -11,7 +11,6 @@ description: The following analytic identifies parent processes, browsers, Windo
many applications spawn cmd.exe natively or built into macros. Much of this will
need to be tuned to further enhance the risk.
search: '| from read_ssa_enriched_events()
| eval timestamp=parse_long(ucast(map_get(input_event, "_time"), "string", null))
| eval process_name=ucast(map_get(input_event, "process_name"), "string", null),
parent_process=lower(ucast(map_get(input_event, "parent_process_name"), "string",
@@ -23,7 +22,7 @@ search: '| from read_ssa_enriched_events()
OR ParentBaseFileName="powerpnt.exe" OR ParentBaseFileName="visio.exe" OR ParentBaseFileName="mspub.exe"
OR ParentBaseFileName="acrobat.exe" OR ParentBaseFileName="acrord32.exe" OR ParentBaseFileName="iexplore.exe"
OR ParentBaseFileName="opera.exe" OR ParentBaseFileName="firefox.exe" OR (ParentBaseFileName="java.exe"
AND (cmd_line IS NULL OR (cmd_line IS NOT NULL AND NOT like(cmd_line, "%patch1-Hotfix1a%"))))
AND (cmd_line IS NULL OR (cmd_line IS NOT NULL AND match_regex(cmd_line, /(?i)patch1-Hotfix1a/)=false)))
OR ParentBaseFileName="powershell.exe" OR (ParentBaseFileName="chrome.exe" AND (cmd_line
IS NULL OR (cmd_line IS NOT NULL AND NOT like(cmd_line, "%chrome-extension%"))))
| eval start_time=timestamp, end_time=timestamp, entities=mvappend(dest_device_id,
@@ -0,0 +1,63 @@
name: Windows Diskshadow Proxy Execution
id: aa502688-9037-11ec-842d-acde48001122
version: 1
date: '2022-02-17'
author: Lou Stella, Splunk
type: Anomaly
datamodel:
- Endpoint_Processes
description: DiskShadow.exe is a Microsoft Signed binary present on Windows Server. It has a scripting mode intended for complex scripted backup operations. This feature also allows for execution of arbitrary unsigned code. This analytic looks for the usage of the scripting mode flags in executions of DiskShadow. During triage, compare to known backup behavior in your environment and then review the scripts called by diskshadow.
search: '| from read_ssa_enriched_events() | where "Endpoint_Processes" IN(_datamodels) | eval timestamp=parse_long(ucast(map_get(input_event, "_time"), "string", null)), cmd_line=lower(ucast(map_get(input_event, "process"), "string", null)), process_name=lower(ucast(map_get(input_event, "process_name"), "string", null)), process_path=ucast(map_get(input_event, "process_path"), "string", null), parent_process_name=ucast(map_get(input_event, "parent_process_name"), "string", null), event_id=ucast(map_get(input_event, "event_id"), "string", null) | where cmd_line IS NOT NULL AND process_name IS NOT NULL AND process_name="diskshadow.exe" AND (like (cmd_line, "%-s%") OR like (cmd_line, "%/s%")) | eval start_time=timestamp, end_time=timestamp, entities=mvappend(ucast(map_get(input_event, "dest_user_id"), "string", null), ucast(map_get(input_event, "dest_device_id"), "string", null)) | eval body=create_map(["event_id", event_id, "cmd_line", cmd_line, "process_name", process_name, "parent_process_name", parent_process_name, "process_path", process_path]) | into write_ssa_detected_events();'
how_to_implement: To successfully implement this search you need to be ingesting information on processes that include the name of the process responsible for the changes from your endpoints into the `Endpoint_Processess` datamodel.
known_false_positives: Administrators using the DiskShadow tool in their infrastructure as a main backup tool with scripts will cause false positives
references:
- https://bohops.com/2018/03/26/diskshadow-the-return-of-vss-evasion-persistence-and-active-directory-database-extraction/
tags:
analytic_story:
- Living Off The Land
dataset:
- https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1218/diskshadow/windows-security.log
kill_chain_phases:
- Exploitation
mitre_attack_id:
- T1218
product:
- Splunk Behavioral Analytics
required_fields:
- _time
- dest_device_id
- process_name
- parent_process_name
- process_path
- dest_user_id
- process
- cmd_line
security_domain: endpoint
impact: 70
confidence: 70
risk_score: 49
context:
- Source:Endpoint
- Stage:Execution
message: An instance of $parent_process_name$ spawning $process_name$ was identified on endpoint $dest_device_id$ by user $dest_user_id$ attempting to run a script.
observable:
- name: dest_device_id
type: Hostname
role:
- Victim
- name: dest_user_id
type: User
role:
- Victim
- name: parent_process_name
type: Parent Process
role:
- Parent Process
- name: process_name
type: Process
role:
- Child Process
nist:
- DE.CM
cis20:
- CIS 8
+22
View File
@@ -0,0 +1,22 @@
name: Data Destruction
id: 4ae5c0d1-cebd-47d1-bfce-71bf096e38aa
version: 1
date: '2022-02-14'
author: Teoderick Contreras, Splunk
description: Leverage searches that allow you to detect and investigate unusual activities
that might relate to the data destruction, including deleting files, overwriting files, wiping disk and encrypting files.
narrative: Adversaries may use this technique to maximize the impact on the target organization in operations where network wide availability interruption
is the goal.
references:
- https://attack.mitre.org/techniques/T1485/
- https://researchcenter.paloaltonetworks.com/2018/09/unit42-xbash-combines-botnet-ransomware-coinmining-worm-targets-linux-windows/
- https://www.picussecurity.com/blog/a-brief-history-and-further-technical-analysis-of-sodinokibi-ransomware
tags:
analytic_story: Data Destruction
category:
- Malware
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
usecase: Advanced Threat Detection
+23
View File
@@ -0,0 +1,23 @@
name: Network Discovery
id: af228995-f182-49d7-90b3-2a732944f00f
version: 1
date: '2022-02-14'
author: Teoderick Contreras, Splunk
description: Leverage searches that allow you to detect and investigate unusual activities
that might relate to the network discovery, including looking for network configuration, settings such as IP, MAC address,
firewall settings and many more.
narrative: Adversaries may use the information from System Network Configuration Discovery during automated discovery to shape follow-on behaviors,
including determining certain access within the target network and what actions to do next.
references:
- https://attack.mitre.org/techniques/T1016/
- https://www.welivesecurity.com/wp-content/uploads/2021/01/ESET_Kobalos.pdf
- https://researchcenter.paloaltonetworks.com/2018/09/unit42-xbash-combines-botnet-ransomware-coinmining-worm-targets-linux-windows/
tags:
analytic_story: Network Discovery
category:
- Malware
product:
- Splunk Enterprise
- Splunk Enterprise Security
- Splunk Cloud
usecase: Advanced Threat Detection
@@ -0,0 +1,12 @@
name: Linux DD File Overwrite Unit Test
tests:
- name: Linux DD File Overwrite
file: endpoint/linux_dd_file_overwrite.yml
pass_condition: '| stats count | where count > 0'
earliest_time: '-24h'
latest_time: 'now'
attack_data:
- file_name: sysmon_linux.log
data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1485/linux_dd_file_overwrite/sysmon_linux.log
source: Syslog:Linux-Sysmon/Operational
sourcetype: sysmon_linux
@@ -0,0 +1,12 @@
name: Linux System Network Discovery Unit Test
tests:
- name: Linux System Network Discovery
file: endpoint/linux_system_network_discovery.yml
pass_condition: '| stats count | where count > 0'
earliest_time: '-24h'
latest_time: 'now'
attack_data:
- file_name: sysmon_linux.log
data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1016/atomic_red_team/linux_net_discovery/sysmon_linux.log
source: Syslog:Linux-Sysmon/Operational
sourcetype: sysmon_linux
@@ -2,7 +2,7 @@ name: Process execution via wmi Unit Test
tests:
- name: Process execution via wmi
file: endpoint/process_execution_via_wmi.yml
pass_condition: '| stats count | where count = 1'
pass_condition: '| stats count | where count > 0'
earliest_time: '-24h'
latest_time: 'now'
attack_data:
@@ -0,0 +1,9 @@
name: BA Windows Diskshadow Proxy Execution Unit Test
tests:
- name: BA Windows Diskshadow Proxy Execution
file: endpoint/ssa___windows_diskshadow_proxy_execution.yml
pass_condition: '@count_gt(0)'
attack_data:
- file_name: windows-security.log
data: https://media.githubusercontent.com/media/splunk/attack_data/master/datasets/attack_techniques/T1218/diskshadow/windows-security.log
source: WinEventLog:Security