diff --git a/bin/docker_detection_tester/authorize.conf.tar b/bin/docker_detection_tester/authorize.conf.tar index 3a624a3b27..a525572e5b 100644 Binary files a/bin/docker_detection_tester/authorize.conf.tar and b/bin/docker_detection_tester/authorize.conf.tar differ diff --git a/bin/docker_detection_tester/modules/splunk_sdk.py b/bin/docker_detection_tester/modules/splunk_sdk.py index 2dc6c36556..f930e7531b 100644 --- a/bin/docker_detection_tester/modules/splunk_sdk.py +++ b/bin/docker_detection_tester/modules/splunk_sdk.py @@ -192,15 +192,15 @@ def test_baseline_search(splunk_host, splunk_port, splunk_password, search, pass # search = search.replace('\\','\\\\') if search.startswith('|'): - search = search + updated_search = search else: - search = 'search ' + search + updated_search = 'search ' + search kwargs = {"exec_mode": "blocking", "dispatch.earliest_time": earliest_time, "dispatch.latest_time": latest_time} - splunk_search = search + ' ' + pass_condition + splunk_search = updated_search + ' ' + pass_condition try: job = service.jobs.create(splunk_search, **kwargs) @@ -233,15 +233,15 @@ def test_detection_search(splunk_host:str, splunk_port:int, splunk_password:str, attempts_remaining -= 1 if search.startswith('|'): - search = search + updated_search = search else: - search = 'search ' + search + updated_search = 'search ' + search kwargs = {"exec_mode": "blocking", "dispatch.earliest_time": "-1d", "dispatch.latest_time": "now"} - splunk_search = search + ' ' + pass_condition + splunk_search = updated_search + ' ' + pass_condition test_results = dict() #These will always be present. By default, we will say that the @@ -317,6 +317,12 @@ def test_detection_search(splunk_host:str, splunk_port:int, splunk_password:str, def delete_attack_data(splunk_host:str, splunk_password:str, splunk_port:int, wait_on_delete:Union[dict,None], search_string:str, detection_filename:str, indices:list[str]=[DEFAULT_DATA_INDEX], host:str=DEFAULT_EVENT_HOST)->bool: + if wait_on_delete: + print(wait_on_delete['message']) + print("FILENAME : [%s]"%(detection_filename)) + print("SEARCH :\n%s"%(search_string)) + _ = input("****************Press ENTER to Complete Test and DELETE data****************\n\n\n") + try: service = client.connect( host=splunk_host, @@ -329,21 +335,11 @@ def delete_attack_data(splunk_host:str, splunk_password:str, splunk_port:int, wa raise(Exception("Unable to connect to Splunk instance: " + str(e))) - #splunk_search = 'search index=test* | delete' - if wait_on_delete: - print(wait_on_delete['message']) - print("FILENAME : [%s]"%(detection_filename)) - print("SEARCH :\n%s"%(search_string)) - _ = input("****************Press ENTER to Complete Test and DELETE data****************\n\n\n") - - data_exists = True - #print(f"Deleting data for {detection_filename}: {indices}") for index in indices: while (get_number_of_indexed_events(splunk_host, splunk_port, splunk_password, index=index, event_host=host) != 0) : splunk_search = f'search index="{index}" host="{host}" | delete' - kwargs = { "exec_mode": "blocking", "dispatch.earliest_time": "-1d", @@ -353,34 +349,9 @@ def delete_attack_data(splunk_host:str, splunk_password:str, splunk_port:int, wa job = service.jobs.create(splunk_search, **kwargs) reader = results.ResultsReader(job) - - ''' - error_in_results = False - for result in reader: - if hasattr(result,"message") and hasattr(result,"type") and ("You have insufficient privileges to delete events" in result.message or result.type == "FATAL"): - print("Delete is not enabled for admin: [%s] - enabling delete and trying to delete again..."%(result.message), file=sys.stderr) - if already_enabled_delete is True: - print("We already enabled delete, but the setting did not take effect.") - raise(Exception("Enabling delete command failed to take effect")) - if enable_delete_for_admin(splunk_host, splunk_port,splunk_password) != True: - raise(Exception("Failure enabling delete for admin. We cannot continue")) - # We enabled delete, so now we will try to delete again - already_enabled_delete = True - break - else: - #This is not one of the error messages, do nothing - pass - ''' - #No need to issue Delete command again, we will now break out of the loop - #if error_in_results is False: - # data_exists = False - - #Otherwise, we will loop again except Exception as e: - print(f"Trouble deleting data from a run.... we will try again: {str(e)}") - time.sleep(5) - #raise(Exception("Unable to delete data from a run: " + str(e))) + raise(Exception(f"Trouble deleting data using the search {splunk_search}: {str(e)}")) return True diff --git a/bin/docker_detection_tester/modules/testing_service.py b/bin/docker_detection_tester/modules/testing_service.py index 72b39cfc69..23a526c5fa 100644 --- a/bin/docker_detection_tester/modules/testing_service.py +++ b/bin/docker_detection_tester/modules/testing_service.py @@ -122,7 +122,7 @@ def test_detection(splunk_ip:str, splunk_port:int, container_name:str, splunk_pa test_index = service.indexes[data_upload_index] with open(target_file, 'rb') as target: - test_index.submit(target.read(), sourcetype=attack_data['sourcetype'], source=attack_data['source']) + test_index.submit(target.read(), sourcetype=attack_data['sourcetype'], source=attack_data['source'], host=splunk_sdk.DEFAULT_EVENT_HOST) except http.client.HTTPException as e: raise(Exception(f"Failed to submit detection file {target_file} to Splunk Server: {str(e)}")) diff --git a/detections/endpoint/office_product_writing_cab_or_inf.yml b/detections/endpoint/office_product_writing_cab_or_inf.yml index 1affe30763..b906212777 100644 --- a/detections/endpoint/office_product_writing_cab_or_inf.yml +++ b/detections/endpoint/office_product_writing_cab_or_inf.yml @@ -1,7 +1,7 @@ name: Office Product Writing cab or inf id: f48cd1d4-125a-11ec-a447-acde48001122 -version: 1 -date: '2021-09-10' +version: 2 +date: '2022-07-07' author: Michael Haag, Splunk type: TTP datamodel: @@ -10,16 +10,21 @@ description: The following analytic identifies behavior related to CVE-2021-4044 Whereas the malicious document will load ActiveX and download the remote payload (.inf, .cab). During triage, review parallel processes and further activity on endpoint to identify additional patterns. Retrieve the file modifications and analyze further. -search: '| tstats `security_content_summariesonly` count FROM datamodel=Endpoint.Processes - where Processes.process_name IN ("winword.exe","excel.exe","powerpnt.exe","mspub.exe","visio.exe","wordpad.exe","wordview.exe") - by _time span=1h Processes.process_id Processes.process_name Processes.process Processes.dest - | `drop_dm_object_name(Processes)` | join process_guid, _time [| tstats `security_content_summariesonly` - count min(_time) as firstTime max(_time) as lastTime FROM datamodel=Endpoint.Filesystem - where Filesystem.file_name IN ("*.inf","*.cab") by _time span=1h Filesystem.dest - Filesystem.file_create_time Filesystem.file_name Filesystem.file_path | `drop_dm_object_name(Filesystem)` - | fields _time dest file_create_time file_name file_path process_name process_path - process] | dedup file_create_time | table dest, process_name, process, file_create_time, - file_name, file_path | `office_product_writing_cab_or_inf_filter`' +search: '| tstats `security_content_summariesonly` count FROM datamodel=Endpoint.Processes + where Processes.process_name IN ("winword.exe","excel.exe","powerpnt.exe","mspub.exe","visio.exe","wordpad.exe","wordview.exe") + by _time span=1h Processes.process_id Processes.process_name Processes.process Processes.dest Processes.process_guid + | `drop_dm_object_name(Processes)` + |rename process_guid as proc_guid + | join proc_guid, _time [ + | tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) as lastTime FROM datamodel=Endpoint.Filesystem + where Filesystem.file_name IN ("*.inf","*.cab") + by _time span=1h Filesystem.dest Filesystem.file_create_time Filesystem.file_name Filesystem.file_path Filesystem.process_guid + | `drop_dm_object_name(Filesystem)` + |rename process_guid as proc_guid + | fields _time dest file_create_time file_name file_path process_name process_path process proc_guid] + | dedup file_create_time + | table dest, process_name, process, file_create_time, file_name, file_path, proc_guid + | `office_product_writing_cab_or_inf_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 `Processes` node and `Filesystem` diff --git a/detections/endpoint/suspicious_wav_file_in_appdata_folder.yml b/detections/endpoint/suspicious_wav_file_in_appdata_folder.yml index ff532958aa..fa79a58594 100644 --- a/detections/endpoint/suspicious_wav_file_in_appdata_folder.yml +++ b/detections/endpoint/suspicious_wav_file_in_appdata_folder.yml @@ -1,7 +1,7 @@ name: Suspicious WAV file in Appdata Folder id: 5be109e6-1ac5-11ec-b421-acde48001122 -version: 1 -date: '2021-09-21' +version: 2 +date: '2022-07-07' author: Teoderick Contreras, Splunk type: TTP datamodel: @@ -12,15 +12,19 @@ description: This analytic is to detect a suspicious creation of .wav file in ap to its C2 server as part of its exfiltration to the compromised machine. creation of wav files in this folder path is not a ussual disk place used by user to save audio format file. -search: '| tstats `security_content_summariesonly` count FROM datamodel=Endpoint.Processes - where Processes.process_name=*.exe Processes.process_path="*\\appdata\\Roaming\\*" - by _time span=1h Processes.process_id Processes.process_name Processes.process Processes.dest - | `drop_dm_object_name(Processes)` | join process_guid, _time [| tstats `security_content_summariesonly` - count min(_time) as firstTime max(_time) as lastTime FROM datamodel=Endpoint.Filesystem - where Filesystem.file_name IN ("*.wav") Filesystem.file_path = "*\\appdata\\Roaming\\*" - by _time span=1h Filesystem.dest Filesystem.file_create_time Filesystem.file_name - Filesystem.file_path | `drop_dm_object_name(Filesystem)` | fields file_name file_path - process_name process_path process dest file_create_time _time ] | `suspicious_wav_file_in_appdata_folder_filter`' +search: '| tstats `security_content_summariesonly` count FROM datamodel=Endpoint.Processes + where Processes.process_name=*.exe Processes.process_path="*\\appdata\\Roaming\\*" + by _time span=1h Processes.process_id Processes.process_name Processes.process Processes.dest Processes.process_guid + | `drop_dm_object_name(Processes)` + |rename process_guid as proc_guid + | join proc_guid, _time [ + | tstats `security_content_summariesonly` count min(_time) as firstTime max(_time) as lastTime FROM datamodel=Endpoint.Filesystem + where Filesystem.file_name IN ("*.wav") Filesystem.file_path = "*\\appdata\\Roaming\\*" + by _time span=1h Filesystem.dest Filesystem.file_create_time Filesystem.file_name Filesystem.file_path Filesystem.process_guid + | `drop_dm_object_name(Filesystem)` + |rename process_guid as proc_guid + | fields file_name file_path process_name process_path process dest file_create_time _time proc_guid] + | `suspicious_wav_file_in_appdata_folder_filter`' how_to_implement: To successfully implement this search, you need to be ingesting logs with the process name, parent process, file_name, file_path and command-line executions from your endpoints. If you are using Sysmon, you must have at least