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..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"]) @@ -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/modules/utils.py b/bin/ssa-end-to-end-testing/modules/utils.py index dea60d0715..0894f361b9 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()" @@ -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 = [] 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) 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/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 new file mode 100644 index 0000000000..1bf6365bc8 --- /dev/null +++ b/detections/endpoint/ssa___rare_parent_process_relationship_lolbas.yml @@ -0,0 +1,86 @@ +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 + - process_name + - parent_process_name + - _time + - dest_device_id + - dest_user_id + risk_severity: low + security_domain: endpoint \ No newline at end of file diff --git a/tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yml b/tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yml new file mode 100644 index 0000000000..057ea4115e --- /dev/null +++ b/tests/endpoint/ssa___rare_parent_process_relationship_lolbas.test.yml @@ -0,0 +1,11 @@ +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: 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 +