From 2110e8ddd35f23762d87c5fbd0af1a3ed1acae73 Mon Sep 17 00:00:00 2001 From: peter-cg <682716+peter-cg@users.noreply.github.com> Date: Wed, 26 May 2021 16:39:15 -0500 Subject: [PATCH 1/8] Adding v2 of ssa___rare_..._lolbas --- bin/ssa_test.py | 2 +- ...are_parent_process_relationship_lolbas.yml | 85 +++++++++++++++++++ ...rent_process_relationship_lolbas.test.yaml | 10 +++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 detections/endpoint/ssa___rare_parent_process_relationship_lolbas.yml create mode 100644 tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yaml diff --git a/bin/ssa_test.py b/bin/ssa_test.py index bd5028a0be..d9d58a6b88 100644 --- a/bin/ssa_test.py +++ b/bin/ssa_test.py @@ -101,7 +101,7 @@ def test_detection(test, args): # Download data to temporal folder for unit in test_desc['tests']: detection = get_detection(unit) - if detection['type'] == "SSA": + if detection['type'] == 'streaming': log(logging.INFO, "Testing %s" % name) # Prepare data data_dir = tempfile.TemporaryDirectory(prefix="data", dir=get_path("%s" % SSML_CWD)) diff --git a/detections/endpoint/ssa___rare_parent_process_relationship_lolbas.yml b/detections/endpoint/ssa___rare_parent_process_relationship_lolbas.yml new file mode 100644 index 0000000000..f9da69febf --- /dev/null +++ b/detections/endpoint/ssa___rare_parent_process_relationship_lolbas.yml @@ -0,0 +1,85 @@ +name: Rare Parent-Child Process Relationship +id: cf090c78-bcc6-11eb-8529-0242ac130003 +version: 1 +date: '2021-05-20' +author: Peter Gael, Splunk; Ignacio Bermudez Corrales, Splunk +type: streaming +datamodel: [] +description: An attacker may use LOLBAS tools spawned from vulnerable applications + not typically used by system administrators. This search leverages the Splunk Streaming + ML DSP plugin to find rare parent/child relationships. The list of application has + been extracted from https://github.com/LOLBAS-Project/LOLBAS/tree/master/yml/OSBinaries +search: '| from read_ssa_enriched_events() | eval timestamp=parse_long(ucast(map_get(input_event, + "_time"), "string", null)) | eval parent_process=lower(ucast(map_get(input_event, + "parent_process_name"), "string", null)), parent_process_name=mvindex(split(parent_process, + "\\"), -1), process_name=lower(ucast(map_get(input_event, "process_name"), "string", + null)), cmd_line=ucast(map_get(input_event, "process"), "string", + null), dest_user_id=ucast(map_get(input_event, "dest_user_id"), "string", null), + dest_device_id=ucast(map_get(input_event, "dest_device_id"), "string", null) + | where parent_process_name!=null + | select parent_process_name, process_name, cmd_line, timestamp, dest_device_id, dest_user_id + | conditional_anomaly conditional="parent_process_name" target="process_name" + | where (process_name="powershell.exe" OR process_name="regsvcs.exe" + OR process_name="ftp.exe" OR process_name="dfsvc.exe" OR process_name="rasautou.exe" + OR process_name="schtasks.exe" OR process_name="xwizard.exe" OR process_name="findstr.exe" + OR process_name="esentutl.exe" OR process_name="cscript.exe" OR process_name="reg.exe" + OR process_name="csc.exe" OR process_name="atbroker.exe" OR process_name="print.exe" + OR process_name="pcwrun.exe" OR process_name="vbc.exe" OR process_name="rpcping.exe" + OR process_name="wsreset.exe" OR process_name="ilasm.exe" OR process_name="certutil.exe" + OR process_name="replace.exe" OR process_name="mshta.exe" OR process_name="bitsadmin.exe" + OR process_name="wscript.exe" OR process_name="ieexec.exe" OR process_name="cmd.exe" + OR process_name="microsoft.workflow.compiler.exe" OR process_name="runscripthelper.exe" + OR process_name="makecab.exe" OR process_name="forfiles.exe" OR process_name="desktopimgdownldr.exe" + OR process_name="control.exe" OR process_name="msbuild.exe" OR process_name="register-cimprovider.exe" + OR process_name="tttracer.exe" OR process_name="ie4uinit.exe" OR process_name="sc.exe" + OR process_name="bash.exe" OR process_name="hh.exe" OR process_name="cmstp.exe" + OR process_name="mmc.exe" OR process_name="jsc.exe" OR process_name="scriptrunner.exe" + OR process_name="odbcconf.exe" OR process_name="extexport.exe" OR process_name="msdt.exe" + OR process_name="diskshadow.exe" OR process_name="extrac32.exe" OR process_name="eventvwr.exe" + OR process_name="mavinject.exe" OR process_name="regasm.exe" OR process_name="gpscript.exe" + OR process_name="rundll32.exe" OR process_name="regsvr32.exe" OR process_name="regedit.exe" + OR process_name="msiexec.exe" OR process_name="gfxdownloadwrapper.exe" OR process_name="presentationhost.exe" + OR process_name="regini.exe" OR process_name="wmic.exe" OR process_name="runonce.exe" + OR process_name="syncappvpublishingserver.exe" OR process_name="verclsid.exe" OR + process_name="psr.exe" OR process_name="infdefaultinstall.exe" OR process_name="explorer.exe" + OR process_name="expand.exe" OR process_name="installutil.exe" OR process_name="netsh.exe" + OR process_name="wab.exe" OR process_name="dnscmd.exe" OR process_name="at.exe" + OR process_name="pcalua.exe" OR process_name="cmdkey.exe" OR process_name="msconfig.exe") + | eval input = (-1)*log(output) + | adaptive_threshold algorithm="gaussian" threshold=0.001 window=604800000L + | where label AND input > mean + | eval start_time = timestamp, end_time = timestamp, entities = mvappend(dest_device_id, + dest_user_id), body = create_map(["process_name", process_name, "parent_process_name", parent_process_name, "input", input, "mean", mean, "variance", variance, "output", output, "cmd_line", cmd_line]) + | into write_ssa_detected_events();' +how_to_implement: Collect endpoint data such as sysmon or 4688 events. +known_false_positives: 'Some custom tools used by admins could be used rarely to launch + remotely applications. This might trigger false positives at the beginning when + it hasn''t collected yet enough data to construct the baseline. + + ' +references: [] +tags: + analytic_story: + - Unusual Processes + cis20: + - CIS 8 + kill_chain_phases: + - Exploitation + mitre_attack_id: + - T1203 + - T1059 + - T1053 + - T1072 + nist: + - PR.PT + - DE.CM + product: + - Splunk Behavioral Analytics + required_fields: + - process_name + - parent_process_name + - _time + - dest_device_id + - dest_user_id + risk_severity: low + security_domain: endpoint diff --git a/tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yaml b/tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yaml new file mode 100644 index 0000000000..175b85e4fb --- /dev/null +++ b/tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yaml @@ -0,0 +1,10 @@ +name: Rare Parent/Child Process Relationship with LOLBAS - SSA Unit Test +tests: + - name: Rare Parent/Child Process Relationship with LOLBAS + file: endpoint/ssa___rare_parent_process_relationship_lolbas.yml + pass_condition: '@count_gt(0)' + description: Test detection looking for LOLBAS processes spawned by other processes that are rarely seen together + attack_data: + - file_name: T1059.all.labeled.lolbas-test.json + data: https://ssa-test-dataset.s3-us-west-2.amazonaws.com/T1059.all.labeled.lolbas-test2.json + From 059beca313261d5c9bd53258a962ca1d0528dc1e Mon Sep 17 00:00:00 2001 From: peter-cg <682716+peter-cg@users.noreply.github.com> Date: Thu, 27 May 2021 11:17:03 -0500 Subject: [PATCH 2/8] fix ssa_validate and add required field to lolbas detection --- bin/ssa_validate.py | 7 ++++--- .../ssa___rare_parent_process_relationship_lolbas.yml | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/bin/ssa_validate.py b/bin/ssa_validate.py index 100c3bfd69..a9f9b166b1 100644 --- a/bin/ssa_validate.py +++ b/bin/ssa_validate.py @@ -7,7 +7,7 @@ from modules.ssa_utils import * from modules.testing_utils import * -DUMB_PIPELINE_INPUT = '| from read_text("/")' \ +DUMB_PIPELINE_INPUT = '| from read_text("test.spl2")' \ '| select from_json_object(value) as input_event' \ '| eval timestamp=parse_long(ucast(map_get(input_event, "_time"), "string", null))' @@ -71,12 +71,13 @@ def extract_ssa_fields(spl2): fields_file = os.path.join(data_dir.name, "fields.out") write_validation_pipeline(spl2, pipeline_file) subprocess.run(["/usr/bin/java", - "-jar", get_path("%s/humvee.jar" % SSML_CWD), + "-jar", "humvee.jar", 'cli', '-i', pipeline_file, '-o', fields_file, '-f'], - stderr=subprocess.DEVNULL, + #stderr=subprocess.DEVNULL, + cwd=get_path(SSML_CWD), check=True) spl2_ssa_fields = set() with open(fields_file, 'r') as test_out_fh: diff --git a/detections/endpoint/ssa___rare_parent_process_relationship_lolbas.yml b/detections/endpoint/ssa___rare_parent_process_relationship_lolbas.yml index f9da69febf..7c2ebec3a4 100644 --- a/detections/endpoint/ssa___rare_parent_process_relationship_lolbas.yml +++ b/detections/endpoint/ssa___rare_parent_process_relationship_lolbas.yml @@ -76,6 +76,7 @@ tags: product: - Splunk Behavioral Analytics required_fields: + - process - process_name - parent_process_name - _time From 1b3d5d27b344bae08553d46eb8d577d5f9c55f00 Mon Sep 17 00:00:00 2001 From: peter-cg <682716+peter-cg@users.noreply.github.com> Date: Fri, 28 May 2021 16:15:31 -0500 Subject: [PATCH 3/8] Clear out old CI pipelines --- .../modules/github_service.py | 10 +++++-- .../modules/streams_service_api_helper.py | 28 +++++++++++++++---- .../modules/test_ssa_detections.py | 22 +++++++++++++-- .../run_ssa_smoketest.py | 3 +- 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/bin/ssa-end-to-end-testing/modules/github_service.py b/bin/ssa-end-to-end-testing/modules/github_service.py index f71c8c2ec8..bec2b63469 100644 --- a/bin/ssa-end-to-end-testing/modules/github_service.py +++ b/bin/ssa-end-to-end-testing/modules/github_service.py @@ -16,8 +16,14 @@ class GithubService: def __init__(self, security_content_branch): self.security_content_branch = security_content_branch - self.security_content_repo_obj = self.clone_project(SECURITY_CONTENT_URL, f"security_content", f"develop") - self.security_content_repo_obj.git.checkout(security_content_branch) + if os.path.exists('security_content'): + LOGGER.warning(f"Found Existing Security Content Project") + self.created_repo = False + else: + self.security_content_repo_obj = self.clone_project(SECURITY_CONTENT_URL, f"security_content", f"develop") + self.security_content_repo_obj.git.checkout(security_content_branch) + self.created_repo = True + def clone_project(self, url, project, branch): LOGGER.info(f"Clone Security Content Project") diff --git a/bin/ssa-end-to-end-testing/modules/streams_service_api_helper.py b/bin/ssa-end-to-end-testing/modules/streams_service_api_helper.py index c7183f4ac3..ae57996d4c 100644 --- a/bin/ssa-end-to-end-testing/modules/streams_service_api_helper.py +++ b/bin/ssa-end-to-end-testing/modules/streams_service_api_helper.py @@ -102,6 +102,23 @@ class DSPApi: return response_body + def get_pipelines(self): + """ + Returns the list of pipelines + + @return: + list of pipelines + """ + headers = {"Content-Type": "application/json", "Authorization": self.header_token} + + response = requests.get(self.return_api_endpoint(PIPELINES_ENDPOINT), headers=headers) + response_body = response.json() + if response.status_code == HTTPStatus.OK: + return response_body.get('items') + else: + LOGGER.error(f"Failed to get pipelines: %s", response.text) + + def create_pipeline(self, upl): """ POST pipelines endpoint to create a pipeline based on the valid upl @@ -176,12 +193,12 @@ class DSPApi: response_body = response.json() if response.status_code == HTTPStatus.OK: - pipeline_id = response_body.get("id") - LOGGER.info(f"Pipeline {pipeline_id} successfully created") - return response_body + pipeline_id = response_body.get("activated") + LOGGER.info(f"Pipeline {pipeline_id} successfully activated") else: - LOGGER.error(f"Failed to activate pipeline {pipeline_id}") - return + LOGGER.error(f"Failed to activate pipeline {pipeline_id}: {response.text}") + + return response_body def deactivate_pipeline(self, pipeline_id): @@ -501,4 +518,3 @@ class DSPApi: delete_url = f"{datasets_endpoint_api}/{index_id}" response = requests.delete(delete_url, headers=request_headers(self.header_token)) return response.status_code - diff --git a/bin/ssa-end-to-end-testing/modules/test_ssa_detections.py b/bin/ssa-end-to-end-testing/modules/test_ssa_detections.py index 8d5306c359..e861973123 100644 --- a/bin/ssa-end-to-end-testing/modules/test_ssa_detections.py +++ b/bin/ssa-end-to-end-testing/modules/test_ssa_detections.py @@ -85,12 +85,31 @@ class SSADetectionTesting: return self.update_execution_time(time_in_s) def ssa_detection_test_init(self): + self.cleanup_old_pipelines() self.test_results["result"] = True self.test_results["msg"] = "" self.results_index = self.api.create_temp_index("mc") self.created_pipelines = [] self.activated_pipelines = [] + def cleanup_old_pipelines(self): + pipelines = self.api.get_pipelines() + yesterday = (time.time() - 24*3600) * 1000 # milliseconds + for pipeline in pipelines: + if pipeline['name'].startswith("ssa_smoke_test_pipeline_helper") and pipeline['createDate'] < yesterday: + if pipeline['status'] == 'ACTIVATED': + # deactivate pipeline + resp, _ = self.api.deactivate_pipeline(pipeline['id']) + if resp.status_code != HTTPStatus.OK: + LOGGER.error("Error deactivating old pipeline %s: %s", pipeline['name'], resp.text) + + # delete pipeline + resp = self.api.delete_pipeline(pipeline['id']) + if resp.status_code != HTTPStatus.NO_CONTENT: + LOGGER.error("Error deleting old pipeline %s: %s", pipeline['name'], resp.text) + else: + LOGGER.warning("Found and deleted an old pipeline: %s", pipeline['name']) + def ssa_detection_test_main(self, spl, source, test_name): self.execution_passed = True @@ -192,8 +211,7 @@ class SSADetectionTesting: "msg": f"Detection test failure for {test_name}"} except Exception as e: self.ssa_detection_test_teardown() - LOGGER.error(e) - LOGGER.error(f"Detection test failure for {test_name} (perhaps SCS problems)") + LOGGER.exception(f"Detection test failure for {test_name} (perhaps SCS problems)") return {"result": False, "msg": f"Detection test failure for {test_name} (perhaps SCS problems)"} diff --git a/bin/ssa-end-to-end-testing/run_ssa_smoketest.py b/bin/ssa-end-to-end-testing/run_ssa_smoketest.py index 804871326b..0a04350170 100644 --- a/bin/ssa-end-to-end-testing/run_ssa_smoketest.py +++ b/bin/ssa-end-to-end-testing/run_ssa_smoketest.py @@ -80,7 +80,8 @@ def main(args): LOGGER.info(test_result['msg']) LOGGER.info('-----------------------------------') - remove_security_content() + if github_service.created_repo: + remove_security_content() exit_code = not test_passed sys.exit(exit_code) From 7b985cc728aaaa0c7b6158fa2902cbf985d1f299 Mon Sep 17 00:00:00 2001 From: P4T12ICK Date: Mon, 31 May 2021 11:21:38 +0200 Subject: [PATCH 4/8] rename .yaml to .yml --- ...aml => ssa___rare_parent_process_relationship_lolbas.test.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/endpoint/{ssa___rare_parent_process_relationship_lolbas.test.yaml => ssa___rare_parent_process_relationship_lolbas.test.yml} (100%) diff --git a/tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yaml b/tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yml similarity index 100% rename from tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yaml rename to tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yml From 404cfec0451aad4945fd42a69d1d0b6da918abd0 Mon Sep 17 00:00:00 2001 From: peter-cg <682716+peter-cg@users.noreply.github.com> Date: Thu, 3 Jun 2021 00:06:05 -0500 Subject: [PATCH 5/8] Updating pulsar connection info --- bin/ssa-end-to-end-testing/modules/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/ssa-end-to-end-testing/modules/utils.py b/bin/ssa-end-to-end-testing/modules/utils.py index dea60d0715..13541496a1 100644 --- a/bin/ssa-end-to-end-testing/modules/utils.py +++ b/bin/ssa-end-to-end-testing/modules/utils.py @@ -14,7 +14,7 @@ LOGGER = logging.getLogger(__name__) # Macros PULSAR_SOURCE_CONNECTION_ID_PLAYGROUND = f"29fb61f1-9342-48f5-9793-1afa008c377b" PULSAR_SOURCE_TOPIC_PLAYGROUND = f"persistent://ssa/egress/decorated-events-research2" -PULSAR_SOURCE_CONNECTION_ID_STAGING = f"fd92bf9f-5d40-4c2e-bb75-bf0c3fc13980" +PULSAR_SOURCE_CONNECTION_ID_STAGING = f"b8c81601-a7e0-4501-802c-cb2831c72b6f" PULSAR_SOURCE_TOPIC_STAGING = f"persistent://ssa/egress/decorated-events-research" READ_SSA_ENRICHED_EVENTS = f"| from read_ssa_enriched_events()" From 0599a3027f78ce6fae20fb592fa321eaea0cbe9d Mon Sep 17 00:00:00 2001 From: peter-cg <682716+peter-cg@users.noreply.github.com> Date: Mon, 7 Jun 2021 22:37:23 -0500 Subject: [PATCH 6/8] Fixing test data pointer --- .../ssa___rare_parent_process_relationship_lolbas.test.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yml b/tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yml index 175b85e4fb..057ea4115e 100644 --- a/tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yml +++ b/tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yml @@ -5,6 +5,7 @@ tests: pass_condition: '@count_gt(0)' description: Test detection looking for LOLBAS processes spawned by other processes that are rarely seen together attack_data: - - file_name: T1059.all.labeled.lolbas-test.json - data: https://ssa-test-dataset.s3-us-west-2.amazonaws.com/T1059.all.labeled.lolbas-test2.json + - file_name: windows-sec-events.out + data: https://raw.githubusercontent.com/splunk/attack_data/master/datasets/attack_techniques/T1003.003/atomic_red_team/windows-sec-events.out + source: WinEventLog:Security From 99c756e4762b234fa7b2fde6b8e87ef5b460e3a8 Mon Sep 17 00:00:00 2001 From: peter-cg <682716+peter-cg@users.noreply.github.com> Date: Fri, 11 Jun 2021 16:37:29 -0500 Subject: [PATCH 7/8] Fixing some relative paths --- bin/ssa-end-to-end-testing/modules/test_ssa_detections.py | 2 +- bin/ssa-end-to-end-testing/modules/utils.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/bin/ssa-end-to-end-testing/modules/test_ssa_detections.py b/bin/ssa-end-to-end-testing/modules/test_ssa_detections.py index e861973123..fde01d0534 100644 --- a/bin/ssa-end-to-end-testing/modules/test_ssa_detections.py +++ b/bin/ssa-end-to-end-testing/modules/test_ssa_detections.py @@ -64,7 +64,7 @@ class SSADetectionTesting: def test_ssa_detections(self, test_obj): LOGGER.info('Test SSA Detection: ' + test_obj["detection_obj"]["name"]) self.max_execution_time = MAX_EXECUTION_TIME_LIMIT - file_path_attack_data = os.path.join(os.path.dirname(__file__), "../", test_obj["attack_data_file_path"]) + file_path_attack_data = test_obj["attack_data_file_path"] test_results = self.ssa_detection_test(test_obj["detection_obj"]["search"], file_path_attack_data, "SSA Smoke Test " + test_obj["test_obj"]["name"]) diff --git a/bin/ssa-end-to-end-testing/modules/utils.py b/bin/ssa-end-to-end-testing/modules/utils.py index 13541496a1..0894f361b9 100644 --- a/bin/ssa-end-to-end-testing/modules/utils.py +++ b/bin/ssa-end-to-end-testing/modules/utils.py @@ -110,8 +110,7 @@ def replace_ssa_macros(source, sink, spl): return spl -def read_data(file_name): - file_path = os.path.join(os.path.dirname(__file__), 'data', file_name) +def read_data(file_path): data_manipulation = DataManipulation() modified_file = data_manipulation.manipulate_timestamp(file_path, 'xmlwineventlog', 'WinEventLog:Security') data = [] From be3519aeac25649bd9a0cc829270bd6a559723f7 Mon Sep 17 00:00:00 2001 From: P4T12ICK Date: Fri, 18 Jun 2021 09:19:02 +0200 Subject: [PATCH 8/8] small change to triiger ci --- .../endpoint/ssa___rare_parent_process_relationship_lolbas.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detections/endpoint/ssa___rare_parent_process_relationship_lolbas.yml b/detections/endpoint/ssa___rare_parent_process_relationship_lolbas.yml index 7c2ebec3a4..1bf6365bc8 100644 --- a/detections/endpoint/ssa___rare_parent_process_relationship_lolbas.yml +++ b/detections/endpoint/ssa___rare_parent_process_relationship_lolbas.yml @@ -83,4 +83,4 @@ tags: - dest_device_id - dest_user_id risk_severity: low - security_domain: endpoint + security_domain: endpoint \ No newline at end of file