From c45c1335ae51996d28b21444c1fc02e2095ba85c Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Wed, 9 Feb 2022 15:23:04 -0800 Subject: [PATCH 01/30] Two changes. First, removed the separate splunkbase_apps and local_apps sections of the config file. They are now unified under a single section called apps. If you don't provide a splunkbase username and password, it will simply use the HTTP target/local_paths. If you do provide those, then it will donwload from splunkbase. However, local_path will override both of these. --- .../detection_testing_execution.py | 52 ++-- .../modules/container_manager.py | 17 +- .../modules/splunk_container.py | 62 ++--- .../modules/validate_args.py | 252 +++++------------- 4 files changed, 137 insertions(+), 246 deletions(-) diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py b/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py index ae4ae4ef2b..a648fc2e30 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py +++ b/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py @@ -30,7 +30,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" @@ -244,8 +244,8 @@ def finish_mock(settings: dict, detections: list[str], output_file_template: str 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") + # the app paths in settings['apps']['local_path'] if it exists + copy_local_apps_to_directory(settings['apps'], "prior_config/apps") for output_file_index in range(0, num_containers): fname = output_file_template % (output_file_index) @@ -331,23 +331,28 @@ 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 = ' and '.join(missing_credentials) + + + print(f"You have listed apps to install but have "\ + f"not provided {missing_credentials_string} via 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'] @@ -425,22 +430,22 @@ def main(args: list[str]): # 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) + settings['apps'], local_volume_absolute_path) # If this is a mock run, finish it now @@ -472,8 +477,7 @@ def main(args: list[str]): 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, diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/modules/container_manager.py b/bin/automated_detection_testing/ci/detection_testing_batch/modules/container_manager.py index 6b25b19b7f..5279cd91b4 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/modules/container_manager.py +++ b/bin/automated_detection_testing/ci/detection_testing_batch/modules/container_manager.py @@ -25,8 +25,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, @@ -47,8 +46,8 @@ class ContainerManager: 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,11 +92,10 @@ 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: @@ -170,8 +168,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, diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/modules/splunk_container.py b/bin/automated_detection_testing/ci/detection_testing_batch/modules/splunk_container.py index fea6d69bad..23b1a81063 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/modules/splunk_container.py +++ b/bin/automated_detection_testing/ci/detection_testing_batch/modules/splunk_container.py @@ -30,8 +30,7 @@ class SplunkContainer: 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] @@ -74,48 +73,51 @@ class SplunkContainer: 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 + 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 +126,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: diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/modules/validate_args.py b/bin/automated_detection_testing/ci/detection_testing_batch/modules/validate_args.py index a7398dd0d0..308c109e71 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/modules/validate_args.py +++ b/bin/automated_detection_testing/ci/detection_testing_batch/modules/validate_args.py @@ -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,113 @@ 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": "1.0.1", + "http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-sysmon_101.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" + #Note - end of life on July 15, 2022 - https://splunkbase.splunk.com/app/1274/ + "SPLUNK_APP_FOR_AWS": { + "app_number": 1274, + "app_version": "6.0.3", + "http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-app-for-aws_603.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" + "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_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_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_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" + "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_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": 3435, + "app_version": "3.4.0", + "http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-security-essentials_340.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 +208,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 From d0afcb458d1fe8228b7c8be2d44b294693d42a28 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Wed, 9 Feb 2022 15:37:34 -0800 Subject: [PATCH 02/30] Check to ensure that Splunkbase username and password are passed in the config or command line when one or more apps is lacking http_path or local_path. Before, this would be caught later on and not in the beginning. Also, the previous commit added new http_paths for all of the default apps. --- .../detection_testing_execution.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py b/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py index a648fc2e30..ffd9c1a3af 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py +++ b/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py @@ -335,17 +335,29 @@ def main(args: list[str]): 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 = ' and '.join(missing_credentials) + 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 {missing_credentials_string} via the command line or config file. " + 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: From 889f27064c3837a6b2dd38f07fdfb4196971b474 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Thu, 10 Feb 2022 15:50:25 -0800 Subject: [PATCH 03/30] Downloaded apps are now cached by default. This can save a huge amount of time on startup, especially for a large number of containers or for slower connections. Otherwise, each container that is set up needs over 600MB of throughput. --- .../detection_testing_execution.py | 145 ++++++++++-------- .../modules/splunk_container.py | 4 +- 2 files changed, 83 insertions(+), 66 deletions(-) diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py b/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py index ffd9c1a3af..5fd68a55ab 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py +++ b/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py @@ -1,6 +1,7 @@ import argparse import copy import csv +from ctypes.wintypes import tagRECT import json import os import queue @@ -45,27 +46,57 @@ 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 +108,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 +223,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 +256,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 +266,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['apps']['local_path'] if it exists - copy_local_apps_to_directory(settings['apps'], "prior_config/apps") - for output_file_index in range(0, num_containers): fname = output_file_template % (output_file_index) @@ -285,9 +308,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) @@ -422,23 +444,7 @@ 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 @@ -456,8 +462,17 @@ def main(args: list[str]): # Copy all the apps, to include ESCU (whether pregenerated or just generated) - copy_local_apps_to_directory( - settings['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 diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/modules/splunk_container.py b/bin/automated_detection_testing/ci/detection_testing_batch/modules/splunk_container.py index 23b1a81063..612e1d1056 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/modules/splunk_container.py +++ b/bin/automated_detection_testing/ci/detection_testing_batch/modules/splunk_container.py @@ -23,7 +23,7 @@ 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, @@ -96,6 +96,8 @@ class SplunkContainer: 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) From 4f0622a5aaf0cd30b3fae49c2381c6d9c5a2549d Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Thu, 10 Feb 2022 16:24:17 -0800 Subject: [PATCH 04/30] Update to latest Splunkbase Windows Sysmon --- .../ci/detection_testing_batch/modules/validate_args.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/modules/validate_args.py b/bin/automated_detection_testing/ci/detection_testing_batch/modules/validate_args.py index 308c109e71..0e31edc5db 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/modules/validate_args.py +++ b/bin/automated_detection_testing/ci/detection_testing_batch/modules/validate_args.py @@ -97,8 +97,8 @@ setup_schema = { }, "SPLUNK_ADD_ON_FOR_SYSMON": { "app_number": 5709, - "app_version": "1.0.1", - "http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-add-on-for-sysmon_101.tgz" + "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_COMMON_INFORMATION_MODEL": { "app_number": 1621, From 5b11f3daff3918e907246ccd1abd05b0f1b02cc3 Mon Sep 17 00:00:00 2001 From: tccontre Date: Fri, 11 Feb 2022 13:44:14 +0100 Subject: [PATCH 05/30] linux_lolbin_stage1 --- detections/endpoint/linux_at_application_execution.yml | 2 +- .../endpoint/linux_common_process_for_elevation_control.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/detections/endpoint/linux_at_application_execution.yml b/detections/endpoint/linux_at_application_execution.yml index 07a98f45d8..075783c9f9 100644 --- a/detections/endpoint/linux_at_application_execution.yml +++ b/detections/endpoint/linux_at_application_execution.yml @@ -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`' diff --git a/detections/endpoint/linux_common_process_for_elevation_control.yml b/detections/endpoint/linux_common_process_for_elevation_control.yml index 28eacca381..e0f2c65939 100644 --- a/detections/endpoint/linux_common_process_for_elevation_control.yml +++ b/detections/endpoint/linux_common_process_for_elevation_control.yml @@ -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`' From d96849c4499945d611072ef152ec6171945798fd Mon Sep 17 00:00:00 2001 From: tccontre Date: Mon, 14 Feb 2022 10:17:46 +0100 Subject: [PATCH 06/30] linux_lolbin_stage1 --- .../linux_system_network_discovery.yml | 63 +++++++++++++++++++ stories/network_discovery.yml | 23 +++++++ .../linux_system_network_discovery.test.yml | 12 ++++ 3 files changed, 98 insertions(+) create mode 100644 detections/endpoint/linux_system_network_discovery.yml create mode 100644 stories/network_discovery.yml create mode 100644 tests/endpoint/linux_system_network_discovery.test.yml diff --git a/detections/endpoint/linux_system_network_discovery.yml b/detections/endpoint/linux_system_network_discovery.yml new file mode 100644 index 0000000000..1f60ef85a0 --- /dev/null +++ b/detections/endpoint/linux_system_network_discovery.yml @@ -0,0 +1,63 @@ +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 + 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 \ No newline at end of file diff --git a/stories/network_discovery.yml b/stories/network_discovery.yml new file mode 100644 index 0000000000..9949ce6952 --- /dev/null +++ b/stories/network_discovery.yml @@ -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 \ No newline at end of file diff --git a/tests/endpoint/linux_system_network_discovery.test.yml b/tests/endpoint/linux_system_network_discovery.test.yml new file mode 100644 index 0000000000..ed79995680 --- /dev/null +++ b/tests/endpoint/linux_system_network_discovery.test.yml @@ -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 \ No newline at end of file From 7648c76c0bb839964a52ac6f77ea5b37c8efb432 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Mon, 14 Feb 2022 16:57:33 -0800 Subject: [PATCH 07/30] A handful of easy detection test fixes to get the tests to detections to work with the newest application baseline. --- .../cloud/aws_excessive_security_scanning.yml | 12 +++++++----- .../detect_regsvcs_with_network_connection.yml | 11 +++++++---- ..._session_on_remote_endpoint_with_powershell.yml | 10 +++++++--- detections/endpoint/net_profiler_uac_bypass.yml | 14 +++++++++----- .../process_deleting_its_process_file_path.yml | 13 ++++++++----- detections/endpoint/rundll32_dnsquery.yml | 8 +++++--- ..._execution_policy_to_unrestricted_or_bypass.yml | 13 ++++++++----- tests/endpoint/process_execution_via_wmi.test.yml | 2 +- 8 files changed, 52 insertions(+), 31 deletions(-) diff --git a/detections/cloud/aws_excessive_security_scanning.yml b/detections/cloud/aws_excessive_security_scanning.yml index 690360f6f2..97829915ef 100644 --- a/detections/cloud/aws_excessive_security_scanning.yml +++ b/detections/cloud/aws_excessive_security_scanning.yml @@ -8,11 +8,13 @@ datamodel: [] description: This search looks for AWS CloudTrail events and analyse the amount of eventNames which starts with Describe by a single user. This indicates that this user scans the configuration of your AWS cloud environment. -search: '`cloudtrail` eventName=Describe* OR eventName=List* OR eventName=Get* | - stats dc(eventName) as dc_events min(_time) as firstTime max(_time) as lastTime - values(eventName) as eventName values(src) as src values(userAgent) as userAgent - by user userIdentity.arn | where dc_events > 50 | `security_content_ctime(firstTime)` - | `security_content_ctime(lastTime)`|`aws_excessive_security_scanning_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 AWS add on and Splunk App for AWS. This search works with AWS CloudTrail logs. known_false_positives: While this search has no known false positives. diff --git a/detections/endpoint/detect_regsvcs_with_network_connection.yml b/detections/endpoint/detect_regsvcs_with_network_connection.yml index 7329369714..8bc454037e 100644 --- a/detections/endpoint/detect_regsvcs_with_network_connection.yml +++ b/detections/endpoint/detect_regsvcs_with_network_connection.yml @@ -15,10 +15,13 @@ 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 diff --git a/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml b/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml index eb2c580a34..1e4ead1eb5 100644 --- a/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml +++ b/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml @@ -10,9 +10,13 @@ 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` + | stats count + | where count > 0' 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. diff --git a/detections/endpoint/net_profiler_uac_bypass.yml b/detections/endpoint/net_profiler_uac_bypass.yml index 1bbe6e2656..895e3caf1d 100644 --- a/detections/endpoint/net_profiler_uac_bypass.yml +++ b/detections/endpoint/net_profiler_uac_bypass.yml @@ -12,11 +12,15 @@ 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 diff --git a/detections/endpoint/process_deleting_its_process_file_path.yml b/detections/endpoint/process_deleting_its_process_file_path.yml index 92a9f3a6d7..ca5987bc42 100644 --- a/detections/endpoint/process_deleting_its_process_file_path.yml +++ b/detections/endpoint/process_deleting_its_process_file_path.yml @@ -11,11 +11,14 @@ 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. diff --git a/detections/endpoint/rundll32_dnsquery.yml b/detections/endpoint/rundll32_dnsquery.yml index d542ef031f..2311ca8aca 100644 --- a/detections/endpoint/rundll32_dnsquery.yml +++ b/detections/endpoint/rundll32_dnsquery.yml @@ -11,9 +11,11 @@ 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. diff --git a/detections/endpoint/set_default_powershell_execution_policy_to_unrestricted_or_bypass.yml b/detections/endpoint/set_default_powershell_execution_policy_to_unrestricted_or_bypass.yml index 61f741ddab..4fbb59fa71 100644 --- a/detections/endpoint/set_default_powershell_execution_policy_to_unrestricted_or_bypass.yml +++ b/detections/endpoint/set_default_powershell_execution_policy_to_unrestricted_or_bypass.yml @@ -8,11 +8,14 @@ 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 diff --git a/tests/endpoint/process_execution_via_wmi.test.yml b/tests/endpoint/process_execution_via_wmi.test.yml index 3c1b9ee8b9..23f82aae83 100644 --- a/tests/endpoint/process_execution_via_wmi.test.yml +++ b/tests/endpoint/process_execution_via_wmi.test.yml @@ -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: From 2009364a6bf91a0d788ec9b38dcee94b4358b8c6 Mon Sep 17 00:00:00 2001 From: tccontre Date: Tue, 15 Feb 2022 10:55:01 +0100 Subject: [PATCH 08/30] linux_lolbin_stage1 --- .../endpoint/linux_dd_file_overwrite.yml | 70 +++++++++++++++++++ .../linux_system_network_discovery.yml | 7 ++ stories/data_destruction.yml | 21 ++++++ .../endpoint/linux_dd_file_overwrite.test.yml | 12 ++++ 4 files changed, 110 insertions(+) create mode 100644 detections/endpoint/linux_dd_file_overwrite.yml create mode 100644 stories/data_destruction.yml create mode 100644 tests/endpoint/linux_dd_file_overwrite.test.yml diff --git a/detections/endpoint/linux_dd_file_overwrite.yml b/detections/endpoint/linux_dd_file_overwrite.yml new file mode 100644 index 0000000000..5ec2767a4b --- /dev/null +++ b/detections/endpoint/linux_dd_file_overwrite.yml @@ -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 \ No newline at end of file diff --git a/detections/endpoint/linux_system_network_discovery.yml b/detections/endpoint/linux_system_network_discovery.yml index 1f60ef85a0..93f8d5c2f8 100644 --- a/detections/endpoint/linux_system_network_discovery.yml +++ b/detections/endpoint/linux_system_network_discovery.yml @@ -41,6 +41,13 @@ tags: - 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 diff --git a/stories/data_destruction.yml b/stories/data_destruction.yml new file mode 100644 index 0000000000..efeefb4568 --- /dev/null +++ b/stories/data_destruction.yml @@ -0,0 +1,21 @@ +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 + analytic_story: Data Destruction + category: + - Malware + product: + - Splunk Enterprise + - Splunk Enterprise Security + - Splunk Cloud + usecase: Advanced Threat Detection \ No newline at end of file diff --git a/tests/endpoint/linux_dd_file_overwrite.test.yml b/tests/endpoint/linux_dd_file_overwrite.test.yml new file mode 100644 index 0000000000..719a157be4 --- /dev/null +++ b/tests/endpoint/linux_dd_file_overwrite.test.yml @@ -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 \ No newline at end of file From 292d997eb5e25c02b8579fc6d043089d788967ec Mon Sep 17 00:00:00 2001 From: tccontre <26181693+tccontre@users.noreply.github.com> Date: Tue, 15 Feb 2022 11:04:28 +0100 Subject: [PATCH 09/30] Update data_destruction.yml --- stories/data_destruction.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stories/data_destruction.yml b/stories/data_destruction.yml index efeefb4568..45cd0ac30d 100644 --- a/stories/data_destruction.yml +++ b/stories/data_destruction.yml @@ -11,6 +11,7 @@ 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 @@ -18,4 +19,4 @@ references: - Splunk Enterprise - Splunk Enterprise Security - Splunk Cloud - usecase: Advanced Threat Detection \ No newline at end of file + usecase: Advanced Threat Detection From 77b300ab4e66b9dbd0d3cc3ed741411ae2f9bd2a Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Tue, 15 Feb 2022 09:37:52 -0800 Subject: [PATCH 10/30] Accidentally changed detection file detections/cloud/aws_excessive_security_scanning.yml. That file should not have been modified. Rolling back those changes. --- detections/cloud/aws_excessive_security_scanning.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/detections/cloud/aws_excessive_security_scanning.yml b/detections/cloud/aws_excessive_security_scanning.yml index 97829915ef..8d8d5bcc48 100644 --- a/detections/cloud/aws_excessive_security_scanning.yml +++ b/detections/cloud/aws_excessive_security_scanning.yml @@ -9,12 +9,11 @@ description: This search looks for AWS CloudTrail events and analyse the amount eventNames which starts with Describe by a single user. This indicates that this user scans the configuration of your AWS cloud environment. 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`' + `cloudtrail` eventName=Describe* OR eventName=List* OR eventName=Get* | + stats dc(eventName) as dc_events min(_time) as firstTime max(_time) as lastTime + values(eventName) as eventName values(src) as src values(userAgent) as userAgent + by user userIdentity.arn | where dc_events > 50 | `security_content_ctime(firstTime)` + | `security_content_ctime(lastTime)`|`aws_excessive_security_scanning_filter`' how_to_implement: You must install splunk AWS add on and Splunk App for AWS. This search works with AWS CloudTrail logs. known_false_positives: While this search has no known false positives. From a093ba7aaf3278d6eddcaf81c175028c2d368514 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Tue, 15 Feb 2022 09:47:36 -0800 Subject: [PATCH 11/30] Minor updates to fixed searches. Fixed some spacing issues and quotation mark issues that were causing search success problems. --- detections/cloud/aws_excessive_security_scanning.yml | 3 +-- .../o365_excessive_authentication_failures_alert.yml | 12 +++++++----- .../detect_regasm_with_network_connection.yml | 10 ++++++---- .../detect_regsvcs_with_network_connection.yml | 3 +-- ...ve_session_on_remote_endpoint_with_powershell.yml | 3 +-- detections/endpoint/net_profiler_uac_bypass.yml | 5 ++--- .../powershell_remove_windows_defender_directory.yml | 9 +++++---- .../process_deleting_its_process_file_path.yml | 3 +-- detections/endpoint/rundll32_dnsquery.yml | 3 +-- ...ll_execution_policy_to_unrestricted_or_bypass.yml | 3 +-- 10 files changed, 26 insertions(+), 28 deletions(-) diff --git a/detections/cloud/aws_excessive_security_scanning.yml b/detections/cloud/aws_excessive_security_scanning.yml index 8d8d5bcc48..690360f6f2 100644 --- a/detections/cloud/aws_excessive_security_scanning.yml +++ b/detections/cloud/aws_excessive_security_scanning.yml @@ -8,8 +8,7 @@ datamodel: [] description: This search looks for AWS CloudTrail events and analyse the amount of eventNames which starts with Describe by a single user. This indicates that this user scans the configuration of your AWS cloud environment. -search: ' - `cloudtrail` eventName=Describe* OR eventName=List* OR eventName=Get* | +search: '`cloudtrail` eventName=Describe* OR eventName=List* OR eventName=Get* | stats dc(eventName) as dc_events min(_time) as firstTime max(_time) as lastTime values(eventName) as eventName values(src) as src values(userAgent) as userAgent by user userIdentity.arn | where dc_events > 50 | `security_content_ctime(firstTime)` diff --git a/detections/cloud/o365_excessive_authentication_failures_alert.yml b/detections/cloud/o365_excessive_authentication_failures_alert.yml index 73c75b0e8c..47f6b3f960 100644 --- a/detections/cloud/o365_excessive_authentication_failures_alert.yml +++ b/detections/cloud/o365_excessive_authentication_failures_alert.yml @@ -7,11 +7,13 @@ 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 diff --git a/detections/endpoint/detect_regasm_with_network_connection.yml b/detections/endpoint/detect_regasm_with_network_connection.yml index bbcab20120..fb41117c3f 100644 --- a/detections/endpoint/detect_regasm_with_network_connection.yml +++ b/detections/endpoint/detect_regasm_with_network_connection.yml @@ -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 diff --git a/detections/endpoint/detect_regsvcs_with_network_connection.yml b/detections/endpoint/detect_regsvcs_with_network_connection.yml index 8bc454037e..7f1a44fea7 100644 --- a/detections/endpoint/detect_regsvcs_with_network_connection.yml +++ b/detections/endpoint/detect_regsvcs_with_network_connection.yml @@ -15,8 +15,7 @@ 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 +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)` diff --git a/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml b/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml index 1e4ead1eb5..d7c4fff9ad 100644 --- a/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml +++ b/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml @@ -10,8 +10,7 @@ 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*"") +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` diff --git a/detections/endpoint/net_profiler_uac_bypass.yml b/detections/endpoint/net_profiler_uac_bypass.yml index 895e3caf1d..146efd14ce 100644 --- a/detections/endpoint/net_profiler_uac_bypass.yml +++ b/detections/endpoint/net_profiler_uac_bypass.yml @@ -12,10 +12,9 @@ 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 +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"" + 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)` diff --git a/detections/endpoint/powershell_remove_windows_defender_directory.yml b/detections/endpoint/powershell_remove_windows_defender_directory.yml index fe08533824..63baf283fa 100644 --- a/detections/endpoint/powershell_remove_windows_defender_directory.yml +++ b/detections/endpoint/powershell_remove_windows_defender_directory.yml @@ -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. diff --git a/detections/endpoint/process_deleting_its_process_file_path.yml b/detections/endpoint/process_deleting_its_process_file_path.yml index ca5987bc42..7794f47d72 100644 --- a/detections/endpoint/process_deleting_its_process_file_path.yml +++ b/detections/endpoint/process_deleting_its_process_file_path.yml @@ -11,8 +11,7 @@ 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 CommandLine = "* /c *" CommandLine = "* del*" Image = "*\\cmd.exe" +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" diff --git a/detections/endpoint/rundll32_dnsquery.yml b/detections/endpoint/rundll32_dnsquery.yml index 2311ca8aca..c807ae877b 100644 --- a/detections/endpoint/rundll32_dnsquery.yml +++ b/detections/endpoint/rundll32_dnsquery.yml @@ -11,8 +11,7 @@ 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" +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)` diff --git a/detections/endpoint/set_default_powershell_execution_policy_to_unrestricted_or_bypass.yml b/detections/endpoint/set_default_powershell_execution_policy_to_unrestricted_or_bypass.yml index 4fbb59fa71..4faec0ffe0 100644 --- a/detections/endpoint/set_default_powershell_execution_policy_to_unrestricted_or_bypass.yml +++ b/detections/endpoint/set_default_powershell_execution_policy_to_unrestricted_or_bypass.yml @@ -8,8 +8,7 @@ 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 +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 From 580dba242f126c1c8ddb383b427abaca803841e2 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Tue, 15 Feb 2022 15:37:17 -0800 Subject: [PATCH 12/30] Added ability to Ctrl-C/SIGINT the testing process and have it gracefully finish. This stops the containers and writes out partial progress to the output file. --- .../detection_testing_execution.py | 53 ++++++++++++++++++- .../modules/container_manager.py | 46 ++++++++++------ .../modules/splunk_container.py | 33 ++++++++++-- .../modules/test_driver.py | 11 +--- 4 files changed, 114 insertions(+), 29 deletions(-) diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py b/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py index 5fd68a55ab..267f6af863 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py +++ b/bin/automated_detection_testing/ci/detection_testing_batch/detection_testing_execution.py @@ -20,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 @@ -51,6 +53,9 @@ CONTAINER_APP_DIRECTORY = "apps" MAX_RECOMMENDED_CONTAINERS_BEFORE_WARNING = 2 + + + 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") @@ -451,6 +456,7 @@ def main(args: list[str]): 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 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) @@ -499,6 +505,39 @@ 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, @@ -523,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: @@ -539,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__": diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/modules/container_manager.py b/bin/automated_detection_testing/ci/detection_testing_batch/modules/container_manager.py index 5279cd91b4..975bf5d9e6 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/modules/container_manager.py +++ b/bin/automated_detection_testing/ci/detection_testing_batch/modules/container_manager.py @@ -1,4 +1,5 @@ from collections import OrderedDict +from tabnanny import check import docker import datetime import docker.types @@ -42,6 +43,9 @@ 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) @@ -99,13 +103,22 @@ class ContainerManager: 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!") @@ -216,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: @@ -234,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() diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/modules/splunk_container.py b/bin/automated_detection_testing/ci/detection_testing_batch/modules/splunk_container.py index 612e1d1056..1c2c3c7f28 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/modules/splunk_container.py +++ b/bin/automated_detection_testing/ci/detection_testing_batch/modules/splunk_container.py @@ -71,6 +71,7 @@ class SplunkContainer: self.num_tests_completed = 0 + def prepare_apps_path( self, apps: OrderedDict, @@ -318,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( @@ -423,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 diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/modules/test_driver.py b/bin/automated_detection_testing/ci/detection_testing_batch/modules/test_driver.py index a55dc9edb2..3bbbbcb6e1 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/modules/test_driver.py +++ b/bin/automated_detection_testing/ci/detection_testing_batch/modules/test_driver.py @@ -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 From 05898d1c7285ca2ed06f132cdb77bcda8230d993 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Tue, 15 Feb 2022 15:43:36 -0800 Subject: [PATCH 13/30] Updated the test config used for github actions and the workflow to remove the use of the splunkbase username and password secrets. After this is merged, we can remove them from the repository. We will simply use the versions that we update in S3. --- .github/workflows/detection-testing.yml | 2 +- .../test_config_github_actions.json | 206 ++++++++---------- 2 files changed, 96 insertions(+), 112 deletions(-) diff --git a/.github/workflows/detection-testing.yml b/.github/workflows/detection-testing.yml index 0146f06aed..98ee2d736a 100644 --- a/.github/workflows/detection-testing.yml +++ b/.github/workflows/detection-testing.yml @@ -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 diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/test_config_github_actions.json b/bin/automated_detection_testing/ci/detection_testing_batch/test_config_github_actions.json index 70ba5473a2..fc72e21da8 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/test_config_github_actions.json +++ b/bin/automated_detection_testing/ci/detection_testing_batch/test_config_github_actions.json @@ -1,113 +1,103 @@ { + "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_AWS": { + "app_number": 1274, + "app_version": "6.0.3", + "http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-app-for-aws_603.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_SECURITY_ESSENTIALS": { + "app_number": 3435, + "app_version": "3.4.0", + "http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-security-essentials_340.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 +109,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" ] -} +} \ No newline at end of file From c8d88b6f93919dcf72ec96aec2b66b984567e229 Mon Sep 17 00:00:00 2001 From: Rod Soto Date: Wed, 16 Feb 2022 18:51:23 -0800 Subject: [PATCH 14/30] added run once search registry --- detections/endpoint/registry_keys_used_for_persistence.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/detections/endpoint/registry_keys_used_for_persistence.yml b/detections/endpoint/registry_keys_used_for_persistence.yml index b9a9021e9c..062acf9548 100644 --- a/detections/endpoint/registry_keys_used_for_persistence.yml +++ b/detections/endpoint/registry_keys_used_for_persistence.yml @@ -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* From e862edd743b1426b3469c7bb80bb898bb3d0d2b0 Mon Sep 17 00:00:00 2001 From: Rod Soto Date: Wed, 16 Feb 2022 19:02:09 -0800 Subject: [PATCH 15/30] added dataset link --- detections/endpoint/registry_keys_used_for_persistence.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/detections/endpoint/registry_keys_used_for_persistence.yml b/detections/endpoint/registry_keys_used_for_persistence.yml index 062acf9548..eb2addee58 100644 --- a/detections/endpoint/registry_keys_used_for_persistence.yml +++ b/detections/endpoint/registry_keys_used_for_persistence.yml @@ -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 From 9058ee478b15cd9e94ea4b3e2deb3138cde58ac5 Mon Sep 17 00:00:00 2001 From: Lou Stella Date: Thu, 17 Feb 2022 15:41:45 -0600 Subject: [PATCH 16/30] SSA conversion --- ...a___windows_diskshadow_proxy_execution.yml | 63 +++++++++++++++++++ ...indows_diskshadow_proxy_execution.test.yml | 9 +++ 2 files changed, 72 insertions(+) create mode 100644 detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml create mode 100644 tests/endpoint/ssa___windows_diskshadow_proxy_execution.test.yml diff --git a/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml b/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml new file mode 100644 index 0000000000..375718e0a8 --- /dev/null +++ b/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml @@ -0,0 +1,63 @@ +name: BA Windows Diskshadow Proxy Execution +id: aa502688-9037-11ec-842d-acde48001122 +version: 1 +date: '2022-02-17' +author: Lou Stella, Splunk +type: Anomaly +datamodel: +- Endpoint_Process +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=ucast(map_get(input_event, "process"), "string", null), process_name=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: +- +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 \ No newline at end of file diff --git a/tests/endpoint/ssa___windows_diskshadow_proxy_execution.test.yml b/tests/endpoint/ssa___windows_diskshadow_proxy_execution.test.yml new file mode 100644 index 0000000000..1b8c7d0d1b --- /dev/null +++ b/tests/endpoint/ssa___windows_diskshadow_proxy_execution.test.yml @@ -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 From e297fcd8c7a3a6ec04fca58813ad6ae576c87392 Mon Sep 17 00:00:00 2001 From: Lou Stella Date: Thu, 17 Feb 2022 15:45:26 -0600 Subject: [PATCH 17/30] Datamodel name correction --- .../endpoint/ssa___windows_diskshadow_proxy_execution.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml b/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml index 375718e0a8..98fac23192 100644 --- a/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml +++ b/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml @@ -5,7 +5,7 @@ date: '2022-02-17' author: Lou Stella, Splunk type: Anomaly datamodel: -- Endpoint_Process +- 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=ucast(map_get(input_event, "process"), "string", null), process_name=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. From 6e56931b2e3d5948c7d83a20d2766f16cc418ab8 Mon Sep 17 00:00:00 2001 From: Lou Stella Date: Thu, 17 Feb 2022 15:46:47 -0600 Subject: [PATCH 18/30] Missing quote --- .../endpoint/ssa___windows_diskshadow_proxy_execution.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml b/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml index 98fac23192..a57382c3b5 100644 --- a/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml +++ b/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml @@ -7,7 +7,7 @@ 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=ucast(map_get(input_event, "process"), "string", null), process_name=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();' +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=ucast(map_get(input_event, "process"), "string", null), process_name=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: From a47084d2e6e4c4fc1cd3e57915e976c75414fe96 Mon Sep 17 00:00:00 2001 From: Lou Stella Date: Thu, 17 Feb 2022 15:49:21 -0600 Subject: [PATCH 19/30] Missing reference --- .../endpoint/ssa___windows_diskshadow_proxy_execution.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml b/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml index a57382c3b5..c5c8181640 100644 --- a/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml +++ b/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml @@ -11,7 +11,7 @@ search: '| from read_ssa_enriched_events() | where "Endpoint_Processes" IN(_data 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 From b72643fe150b350c4e14ed02bf64f1a97ce83164 Mon Sep 17 00:00:00 2001 From: Lou Stella Date: Thu, 17 Feb 2022 16:39:39 -0600 Subject: [PATCH 20/30] Name tweak --- .../endpoint/ssa___windows_diskshadow_proxy_execution.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml b/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml index c5c8181640..ceea2b4278 100644 --- a/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml +++ b/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml @@ -1,4 +1,4 @@ -name: BA Windows Diskshadow Proxy Execution +name: Windows Diskshadow Proxy Execution id: aa502688-9037-11ec-842d-acde48001122 version: 1 date: '2022-02-17' From 33c389740f199ccb5163a12c8375808a9ab3460d Mon Sep 17 00:00:00 2001 From: David Dorsey Date: Thu, 17 Feb 2022 22:00:54 -0600 Subject: [PATCH 21/30] Changed syntax for special case for java --- .../endpoint/ssa___prohibited_apps_spawning_cmdprompt.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/detections/endpoint/ssa___prohibited_apps_spawning_cmdprompt.yml b/detections/endpoint/ssa___prohibited_apps_spawning_cmdprompt.yml index 0be577e79f..c4f2447379 100644 --- a/detections/endpoint/ssa___prohibited_apps_spawning_cmdprompt.yml +++ b/detections/endpoint/ssa___prohibited_apps_spawning_cmdprompt.yml @@ -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, From 033804472142ce91c52517115d37314bcc1ec9e6 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Fri, 18 Feb 2022 10:57:49 -0800 Subject: [PATCH 22/30] Updating the date to today and bumping the version for updated searches associated with this PR. --- .../cloud/o365_excessive_authentication_failures_alert.yml | 4 ++-- detections/endpoint/detect_regasm_with_network_connection.yml | 4 ++-- .../endpoint/detect_regsvcs_with_network_connection.yml | 4 ++-- ...interactive_session_on_remote_endpoint_with_powershell.yml | 4 ++-- detections/endpoint/net_profiler_uac_bypass.yml | 4 ++-- .../endpoint/powershell_remove_windows_defender_directory.yml | 4 ++-- .../endpoint/process_deleting_its_process_file_path.yml | 4 ++-- detections/endpoint/rundll32_dnsquery.yml | 4 ++-- ..._powershell_execution_policy_to_unrestricted_or_bypass.yml | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/detections/cloud/o365_excessive_authentication_failures_alert.yml b/detections/cloud/o365_excessive_authentication_failures_alert.yml index 47f6b3f960..65dece8106 100644 --- a/detections/cloud/o365_excessive_authentication_failures_alert.yml +++ b/detections/cloud/o365_excessive_authentication_failures_alert.yml @@ -1,7 +1,7 @@ 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: [] diff --git a/detections/endpoint/detect_regasm_with_network_connection.yml b/detections/endpoint/detect_regasm_with_network_connection.yml index fb41117c3f..69e22c6034 100644 --- a/detections/endpoint/detect_regasm_with_network_connection.yml +++ b/detections/endpoint/detect_regasm_with_network_connection.yml @@ -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: [] diff --git a/detections/endpoint/detect_regsvcs_with_network_connection.yml b/detections/endpoint/detect_regsvcs_with_network_connection.yml index 7f1a44fea7..6b7864e696 100644 --- a/detections/endpoint/detect_regsvcs_with_network_connection.yml +++ b/detections/endpoint/detect_regsvcs_with_network_connection.yml @@ -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: [] diff --git a/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml b/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml index d7c4fff9ad..243e5e4a99 100644 --- a/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml +++ b/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml @@ -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: [] diff --git a/detections/endpoint/net_profiler_uac_bypass.yml b/detections/endpoint/net_profiler_uac_bypass.yml index 6dfef1bea5..dc81093e1c 100644 --- a/detections/endpoint/net_profiler_uac_bypass.yml +++ b/detections/endpoint/net_profiler_uac_bypass.yml @@ -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: diff --git a/detections/endpoint/powershell_remove_windows_defender_directory.yml b/detections/endpoint/powershell_remove_windows_defender_directory.yml index 63baf283fa..4a76a32e9a 100644 --- a/detections/endpoint/powershell_remove_windows_defender_directory.yml +++ b/detections/endpoint/powershell_remove_windows_defender_directory.yml @@ -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: diff --git a/detections/endpoint/process_deleting_its_process_file_path.yml b/detections/endpoint/process_deleting_its_process_file_path.yml index 7794f47d72..99179ceec5 100644 --- a/detections/endpoint/process_deleting_its_process_file_path.yml +++ b/detections/endpoint/process_deleting_its_process_file_path.yml @@ -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: diff --git a/detections/endpoint/rundll32_dnsquery.yml b/detections/endpoint/rundll32_dnsquery.yml index c807ae877b..315305b782 100644 --- a/detections/endpoint/rundll32_dnsquery.yml +++ b/detections/endpoint/rundll32_dnsquery.yml @@ -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: diff --git a/detections/endpoint/set_default_powershell_execution_policy_to_unrestricted_or_bypass.yml b/detections/endpoint/set_default_powershell_execution_policy_to_unrestricted_or_bypass.yml index b01036caed..09fdaff5e6 100644 --- a/detections/endpoint/set_default_powershell_execution_policy_to_unrestricted_or_bypass.yml +++ b/detections/endpoint/set_default_powershell_execution_policy_to_unrestricted_or_bypass.yml @@ -1,7 +1,7 @@ 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: From 9fc009a44d82997023bdfc6e319e3224ceed330c Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Fri, 18 Feb 2022 11:17:21 -0800 Subject: [PATCH 23/30] Removed: SPLUNK_SECURITY_ESSENTIALS SPLUNK_APP_FOR_AWS from default app baseline. They are not required for testing as they just provide some dashboards. --- .../detection_testing_batch/modules/validate_args.py | 12 ------------ .../test_config_github_actions.json | 10 ---------- 2 files changed, 22 deletions(-) diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/modules/validate_args.py b/bin/automated_detection_testing/ci/detection_testing_batch/modules/validate_args.py index 0e31edc5db..bb74a7ee92 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/modules/validate_args.py +++ b/bin/automated_detection_testing/ci/detection_testing_batch/modules/validate_args.py @@ -105,12 +105,6 @@ setup_schema = { "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" }, - #Note - end of life on July 15, 2022 - https://splunkbase.splunk.com/app/1274/ - "SPLUNK_APP_FOR_AWS": { - "app_number": 1274, - "app_version": "6.0.3", - "http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-app-for-aws_603.tgz" - }, "PYTHON_FOR_SCIENTIFIC_COMPUTING_FOR_LINUX_64_BIT": { "app_number": 2882, "app_version": "3.0.2", @@ -136,12 +130,6 @@ setup_schema = { "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_SECURITY_ESSENTIALS": { - "app_number": 3435, - "app_version": "3.4.0", - "http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-security-essentials_340.tgz" - }, - "SPLUNK_ADD_ON_FOR_AMAZON_KINESIS_FIREHOSE": { "app_number": 3719, "app_version": "1.3.2", diff --git a/bin/automated_detection_testing/ci/detection_testing_batch/test_config_github_actions.json b/bin/automated_detection_testing/ci/detection_testing_batch/test_config_github_actions.json index fc72e21da8..647e4f808a 100644 --- a/bin/automated_detection_testing/ci/detection_testing_batch/test_config_github_actions.json +++ b/bin/automated_detection_testing/ci/detection_testing_batch/test_config_github_actions.json @@ -50,11 +50,6 @@ "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_AWS": { - "app_number": 1274, - "app_version": "6.0.3", - "http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-app-for-aws_603.tgz" - }, "SPLUNK_APP_FOR_STREAM": { "app_number": 1809, "app_version": "8.0.1", @@ -75,11 +70,6 @@ "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_SECURITY_ESSENTIALS": { - "app_number": 3435, - "app_version": "3.4.0", - "http_path": "https://attack-range-appbinaries.s3.us-west-2.amazonaws.com/Latest/splunk-security-essentials_340.tgz" - }, "SPLUNK_TA_FOR_ZEEK": { "app_number": 5466, "app_version": "1.0.5", From 9da1af7e8c082028e5a069cdc635f54c731c8321 Mon Sep 17 00:00:00 2001 From: patel-bhavin Date: Fri, 18 Feb 2022 11:51:32 -0800 Subject: [PATCH 24/30] minor changes for CIM fields and other metadata --- .../endpoint/detect_regasm_with_network_connection.yml | 6 +++--- .../endpoint/detect_regsvcs_with_network_connection.yml | 6 +++--- ...teractive_session_on_remote_endpoint_with_powershell.yml | 4 +--- .../endpoint/process_deleting_its_process_file_path.yml | 6 +++--- detections/endpoint/rundll32_dnsquery.yml | 1 - 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/detections/endpoint/detect_regasm_with_network_connection.yml b/detections/endpoint/detect_regasm_with_network_connection.yml index 69e22c6034..dc693a7c50 100644 --- a/detections/endpoint/detect_regasm_with_network_connection.yml +++ b/detections/endpoint/detect_regasm_with_network_connection.yml @@ -17,7 +17,7 @@ description: The following analytic identifies regasm.exe with a network connect 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_ip + | 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`' @@ -61,7 +61,7 @@ tags: type: User role: - Victim - - name: Computer + - name: dest type: Hostname role: - Victim @@ -79,7 +79,7 @@ tags: - dest_ip - process_name - Computer - - User + - user - src_ip - dest_host - dest_ip diff --git a/detections/endpoint/detect_regsvcs_with_network_connection.yml b/detections/endpoint/detect_regsvcs_with_network_connection.yml index 6b7864e696..dd41e27b88 100644 --- a/detections/endpoint/detect_regsvcs_with_network_connection.yml +++ b/detections/endpoint/detect_regsvcs_with_network_connection.yml @@ -17,7 +17,7 @@ description: The following analytic identifies Regsvcs.exe with a network connec 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_ip + | 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`' @@ -61,7 +61,7 @@ tags: type: User role: - Victim - - name: Computer + - name: dest type: Hostname role: - Victim @@ -79,7 +79,7 @@ tags: - dest_ip - process_name - Computer - - User + - user - src_ip - dest_host risk_score: 80 diff --git a/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml b/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml index 243e5e4a99..40bdb14ab8 100644 --- a/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml +++ b/detections/endpoint/interactive_session_on_remote_endpoint_with_powershell.yml @@ -13,9 +13,7 @@ description: The following analytic utilizes PowerShell Script Block Logging (Ev 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` - | stats count - | where count > 0' + | `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. diff --git a/detections/endpoint/process_deleting_its_process_file_path.yml b/detections/endpoint/process_deleting_its_process_file_path.yml index 99179ceec5..7a93edb934 100644 --- a/detections/endpoint/process_deleting_its_process_file_path.yml +++ b/detections/endpoint/process_deleting_its_process_file_path.yml @@ -36,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 @@ -53,7 +53,7 @@ tags: role: - Victim - name: user - type: user + type: User role: - Victim product: diff --git a/detections/endpoint/rundll32_dnsquery.yml b/detections/endpoint/rundll32_dnsquery.yml index 315305b782..72503d8052 100644 --- a/detections/endpoint/rundll32_dnsquery.yml +++ b/detections/endpoint/rundll32_dnsquery.yml @@ -60,7 +60,6 @@ tags: - QueryName - QueryStatus - ProcessId - - direction - Computer risk_score: 56 security_domain: endpoint From 09c54931af8fd4da5b9d07543f5462766597c211 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Fri, 18 Feb 2022 13:36:09 -0800 Subject: [PATCH 25/30] Memoizing/caching a call to CVESSEARCH_API_URL. Gives a SIGNIFICANT speedup in docgen. --- bin/doc_gen.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bin/doc_gen.py b/bin/doc_gen.py index 83c71a4cdb..b34e4bc165 100644 --- a/bin/doc_gen.py +++ b/bin/doc_gen.py @@ -1,3 +1,4 @@ +from functools import cache import glob import yaml import argparse @@ -94,6 +95,13 @@ 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. +@cache def get_cve_enrichment_new(cve_id): cve = CVESearch(CVESSEARCH_API_URL) result = cve.id(cve_id) From 46e0bb0a01b16bf1c93b596e238bbc1c8bd23b61 Mon Sep 17 00:00:00 2001 From: pyth0n1c <87383215+pyth0n1c@users.noreply.github.com> Date: Fri, 18 Feb 2022 14:19:57 -0800 Subject: [PATCH 26/30] Added a timeout to the CVESSEARCH_API enrichment in doc_gen. Sometimes, this was causing doc_gen to freeze. Now, when the API is not working or is extremely slow and a request takes longer than 10 seconds, a descriptive error will be thrown and the process will exit with 1 --- bin/doc_gen.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/bin/doc_gen.py b/bin/doc_gen.py index b34e4bc165..282436d5b5 100644 --- a/bin/doc_gen.py +++ b/bin/doc_gen.py @@ -1,3 +1,4 @@ +from threading import Thread from functools import cache import glob import yaml @@ -13,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): @@ -101,6 +103,9 @@ def parse_and_add_lookups(search_string, lookups): #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) @@ -111,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) @@ -335,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 From d4b0125a51863d14958d508a181cad5073a5e8dd Mon Sep 17 00:00:00 2001 From: Lou Stella Date: Tue, 22 Feb 2022 08:43:26 -0600 Subject: [PATCH 27/30] handling case sensitivity --- .../endpoint/ssa___windows_diskshadow_proxy_execution.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml b/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml index ceea2b4278..55a5e8feb3 100644 --- a/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml +++ b/detections/endpoint/ssa___windows_diskshadow_proxy_execution.yml @@ -7,7 +7,7 @@ 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=ucast(map_get(input_event, "process"), "string", null), process_name=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();' +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: @@ -60,4 +60,4 @@ tags: nist: - DE.CM cis20: - - CIS 8 \ No newline at end of file + - CIS 8 From 8f9561932d5368f82c04bb0b86150a13c8e58504 Mon Sep 17 00:00:00 2001 From: mhaag-spl <5632822+MHaggis@users.noreply.github.com> Date: Tue, 22 Feb 2022 10:56:24 -0700 Subject: [PATCH 28/30] Update scheduled_task_deleted_or_created_via_cmd.yml --- ...eduled_task_deleted_or_created_via_cmd.yml | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/detections/endpoint/scheduled_task_deleted_or_created_via_cmd.yml b/detections/endpoint/scheduled_task_deleted_or_created_via_cmd.yml index 12618ad7a7..cca35ab55c 100644 --- a/detections/endpoint/scheduled_task_deleted_or_created_via_cmd.yml +++ b/detections/endpoint/scheduled_task_deleted_or_created_via_cmd.yml @@ -1,13 +1,12 @@ 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. search: '| tstats `security_content_summariesonly` count values(Processes.process) as process values(Processes.parent_process) as parent_process min(_time) as firstTime @@ -15,17 +14,15 @@ search: '| tstats `security_content_summariesonly` count values(Processes.proces (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: From 1f3d15676b7221cb67b2925fa9485ca040de963d Mon Sep 17 00:00:00 2001 From: mhaag-spl <5632822+MHaggis@users.noreply.github.com> Date: Tue, 22 Feb 2022 10:57:07 -0700 Subject: [PATCH 29/30] Update scheduled_task_deleted_or_created_via_cmd.yml --- .../endpoint/scheduled_task_deleted_or_created_via_cmd.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/detections/endpoint/scheduled_task_deleted_or_created_via_cmd.yml b/detections/endpoint/scheduled_task_deleted_or_created_via_cmd.yml index cca35ab55c..04834ce99f 100644 --- a/detections/endpoint/scheduled_task_deleted_or_created_via_cmd.yml +++ b/detections/endpoint/scheduled_task_deleted_or_created_via_cmd.yml @@ -8,6 +8,7 @@ datamodel: - Endpoint 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 From ab11cd1c60abaa2e510633fb6ebe27f4108cff75 Mon Sep 17 00:00:00 2001 From: mhaag-spl <5632822+MHaggis@users.noreply.github.com> Date: Tue, 22 Feb 2022 10:59:25 -0700 Subject: [PATCH 30/30] Update pull_request_template.md --- .github/pull_request_template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 5f3e558c43..d61ff868b3 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -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 `__` +- [ ] Validate name matches `__` - [ ] Verify references match analytic. - [ ] Is there an Atomic Test? \ No newline at end of file