From 88ab5e6948d42402736c21569cc1c6eec6471d2a Mon Sep 17 00:00:00 2001 From: frack113 <62423083+frack113@users.noreply.github.com> Date: Sat, 25 Nov 2023 08:31:50 +0100 Subject: [PATCH 1/8] =?UTF-8?q?ci:=20=F0=9F=A4=96=20Use=20pySigma=200.10.9?= =?UTF-8?q?=20validators?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/sigma_cli_conf.yml | 7 + tests/test_rules.py | 1269 +++++++++++++++++++------------------- 2 files changed, 646 insertions(+), 630 deletions(-) diff --git a/tests/sigma_cli_conf.yml b/tests/sigma_cli_conf.yml index 0ad3f69537c..33f145f4055 100644 --- a/tests/sigma_cli_conf.yml +++ b/tests/sigma_cli_conf.yml @@ -3,6 +3,10 @@ validators: - attacktag - cartag - cvetag + - custom_attributes + - date_existence + - description_existence + - description_length - detection_tag - duplicate_filename - duplicate_references @@ -12,9 +16,12 @@ validators: - filename_sigmahq - identifier_existence - identifier_uniqueness + - level_existence - status_existence - status_unsupported + - stptag - tlptag + exclusions: # escaped_wildcard 021310d9-30a6-480a-84b7-eaa69aeb92bb: escaped_wildcard diff --git a/tests/test_rules.py b/tests/test_rules.py index f46dfe3f8ce..2962d747834 100755 --- a/tests/test_rules.py +++ b/tests/test_rules.py @@ -11,16 +11,18 @@ import yaml import re import string -#from attackcti import attack_client + +# from attackcti import attack_client from colorama import init from colorama import Fore import collections -# Old Tests cover by pySigma 0.10.6 and simgma-cli 0.7.8 +# Old Tests cover by pySigma 0.10.9 and simgma-cli 0.7.10 # Use sigma check --fail-on-error --fail-on-issues --validation-config tests/sigma_cli_conf.yml rules* # + class TestRules(unittest.TestCase): # @classmethod # def setUpClass(cls): @@ -87,19 +89,6 @@ def get_rule_yaml(self, file_path: str) -> dict: return data # Tests - # def test_confirm_extension_is_yml(self): - # files_with_incorrect_extensions = [] - - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # file_name_and_extension = os.path.splitext(file) - # if len(file_name_and_extension) == 2: - # extension = file_name_and_extension[1] - # if extension != ".yml": - # files_with_incorrect_extensions.append(file) - - # self.assertEqual(files_with_incorrect_extensions, [], Fore.RED + - # "There are rule files with extensions other than .yml") - def test_legal_trademark_violations(self): # See Issue # https://github.com/SigmaHQ/sigma/issues/1028 files_with_legal_issues = [] @@ -118,101 +107,28 @@ def test_legal_trademark_violations(self): + "There are rule files which contains a trademark or reference that doesn't comply with the respective trademark requirements - please remove the trademark to avoid legal issues", ) - def test_optional_tags(self): - files_with_incorrect_tags = [] - tags_pattern = re.compile( - r"cve\.\d+\.\d+|attack\.(t\d{4}\.\d{3}|[gts]\d{4})$|attack\.[a-z_]+|car\.\d{4}-\d{2}-\d{3}|detection\.\w+" - ) - for file in self.yield_next_rule_file_path(self.path_to_rules): - tags = self.get_rule_part(file_path=file, part_name="tags") - if tags: - for tag in tags: - if tags_pattern.match(tag) == None: - print( - Fore.RED - + "Rule {} has the invalid tag <{}>".format(file, tag) - ) - files_with_incorrect_tags.append(file) - - self.assertEqual( - files_with_incorrect_tags, - [], - Fore.RED - + "There are rules with incorrect/unknown Tags. (please inform us about new tags that are not yet supported in our tests) and check the correct tags here: https://github.com/SigmaHQ/sigma-specification/blob/main/Tags_specification.md ", - ) - - # sigma-cli validators attacktag - # def test_confirm_correct_mitre_tags(self): - # files_with_incorrect_mitre_tags = [] - - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # tags = self.get_rule_part(file_path=file, part_name="tags") - # if tags: - # for tag in tags: - # if tag.startswith("attack.") and tag not in self.MITRE_ALL: - # print( - # Fore.RED - # + "Rule {} has the following incorrect MITRE tag {}".format( - # file, tag - # ) - # ) - # files_with_incorrect_mitre_tags.append(file) - - # self.assertEqual( - # files_with_incorrect_mitre_tags, - # [], - # Fore.RED - # + "There are rules with incorrect/unknown MITRE Tags. (please inform us about new tags that are not yet supported in our tests) and check the correct tags here: https://attack.mitre.org/ ", + # sigma error and validator attacktag,cartag,cvetag,detection_tag,stptag,tlptag + # def test_optional_tags(self): + # files_with_incorrect_tags = [] + # tags_pattern = re.compile( + # r"cve\.\d+\.\d+|attack\.(t\d{4}\.\d{3}|[gts]\d{4})$|attack\.[a-z_]+|car\.\d{4}-\d{2}-\d{3}|detection\.\w+" # ) - - # sigma validators duplicate_tag - # def test_duplicate_tags(self): - # files_with_incorrect_mitre_tags = [] - # for file in self.yield_next_rule_file_path(self.path_to_rules): # tags = self.get_rule_part(file_path=file, part_name="tags") # if tags: - # known_tags = [] # for tag in tags: - # if tag in known_tags: - # print( - # Fore.RED - # + "Rule {} has the duplicate tag {}".format(file, tag) - # ) - # files_with_incorrect_mitre_tags.append(file) - # else: - # known_tags.append(tag) - - # self.assertEqual( - # files_with_incorrect_mitre_tags, - # [], - # Fore.RED + "There are rules with duplicate tags", - # ) - - # sigma validators duplicate_references - # def test_duplicate_references(self): - # files_with_duplicate_references = [] - - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # references = self.get_rule_part(file_path=file, part_name="references") - # if references: - # known_references = [] - # for reference in references: - # if reference in known_references: + # if tags_pattern.match(tag) == None: # print( # Fore.RED - # + "Rule {} has the duplicate reference {}".format( - # file, reference - # ) + # + "Rule {} has the invalid tag <{}>".format(file, tag) # ) - # files_with_duplicate_references.append(file) - # else: - # known_references.append(reference) + # files_with_incorrect_tags.append(file) # self.assertEqual( - # files_with_duplicate_references, + # files_with_incorrect_tags, # [], - # Fore.RED + "There are rules with duplicate references", + # Fore.RED + # + "There are rules with incorrect/unknown Tags. (please inform us about new tags that are not yet supported in our tests) and check the correct tags here: https://github.com/SigmaHQ/sigma-specification/blob/main/Tags_specification.md ", # ) def test_look_for_duplicate_filters(self): @@ -320,23 +236,6 @@ def test_single_named_condition_with_x_of_them(self): + "There are rules using '1/all of them' style conditions but only have one condition", ) - # Sigma validator all_of_them_condition - # def test_all_of_them_condition(self): - # faulty_detections = [] - - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # detection = self.get_rule_part(file_path=file, part_name="detection") - - # if "all of them" in detection["condition"]: - # faulty_detections.append(file) - - # self.assertEqual( - # faulty_detections, - # [], - # Fore.RED - # + "There are rules using 'all of them'. Better use e.g. 'all of selection*' instead (and use the 'selection_' prefix as search-identifier).", - # ) - def test_duplicate_detections(self): def compare_detections(detection1: dict, detection2: dict) -> bool: # If they have different log sources. They can't be the same @@ -496,105 +395,73 @@ def test_event_id_instead_of_process_creation(self): + "There are rules still using Sysmon 1 or Event ID 4688. Please migrate to the process_creation category.", ) - # sigma validator identifier_existence identifier_uniqueness - # def test_missing_id(self): + # sigma error and validator custom_attributes + # def test_optional_related(self): # faulty_rules = [] - # dict_id = {} + # valid_type = ["derived", "obsoletes", "merged", "renamed", "similar"] # for file in self.yield_next_rule_file_path(self.path_to_rules): - # id = self.get_rule_part(file_path=file, part_name="id") - # if not id: - # print(Fore.YELLOW + "Rule {} has no field 'id'.".format(file)) - # faulty_rules.append(file) - # elif len(id) != 36: - # print( - # Fore.YELLOW - # + "Rule {} has a malformed 'id' (not 36 chars).".format(file) - # ) - # faulty_rules.append(file) - # elif id.lower() in dict_id.keys(): - # print( - # Fore.YELLOW - # + "Rule {} has the same 'id' as {}. Ids have to be unique.".format( - # file, dict_id[id] + # related_lst = self.get_rule_part(file_path=file, part_name="related") + # if related_lst: + # # it exists but isn't a list + # if not isinstance(related_lst, list): + # print( + # Fore.YELLOW + # + "Rule {} has a 'related' field that isn't a list.".format( + # file + # ) # ) - # ) - # faulty_rules.append(file) + # faulty_rules.append(file) + # else: + # type_ok = True + # for ref in related_lst: + # try: + # id_str = ref["id"] + # type_str = ref["type"] + # except KeyError: + # print( + # Fore.YELLOW + # + "Rule {} has an invalid form of 'related/type' value.".format( + # file + # ) + # ) + # faulty_rules.append(file) + # continue + # if not type_str in valid_type: + # type_ok = False + # # Only add one time if many bad type in the same file + # if type_ok == False: + # print( + # Fore.YELLOW + # + "Rule {} has a 'related/type' invalid value.".format(file) + # ) + # faulty_rules.append(file) # else: - # dict_id[id.lower()] = file + # typo_list = [] + # # Add more typos + # typo_list.append( + # self.get_rule_part(file_path=file, part_name="realted") + # ) + # typo_list.append( + # self.get_rule_part(file_path=file, part_name="relatde") + # ) + # typo_list.append(self.get_rule_part(file_path=file, part_name="relted")) + # typo_list.append(self.get_rule_part(file_path=file, part_name="rlated")) + + # for i in typo_list: + # if i != None: + # print( + # Fore.YELLOW + # + "Rule {} has a typo in it's 'related' field.".format(file) + # ) + # faulty_rules.append(file) # self.assertEqual( # faulty_rules, # [], # Fore.RED - # + "There are rules with missing or malformed 'id' fields. Generate an id (e.g. here: https://www.uuidgenerator.net/version4) and add it to the reported rule(s).", + # + "There are rules with malformed optional 'related' fields. (check https://github.com/SigmaHQ/sigma-specification)", # ) - def test_optional_related(self): - faulty_rules = [] - valid_type = ["derived", "obsoletes", "merged", "renamed", "similar"] - for file in self.yield_next_rule_file_path(self.path_to_rules): - related_lst = self.get_rule_part(file_path=file, part_name="related") - if related_lst: - # it exists but isn't a list - if not isinstance(related_lst, list): - print( - Fore.YELLOW - + "Rule {} has a 'related' field that isn't a list.".format( - file - ) - ) - faulty_rules.append(file) - else: - type_ok = True - for ref in related_lst: - try: - id_str = ref["id"] - type_str = ref["type"] - except KeyError: - print( - Fore.YELLOW - + "Rule {} has an invalid form of 'related/type' value.".format( - file - ) - ) - faulty_rules.append(file) - continue - if not type_str in valid_type: - type_ok = False - # Only add one time if many bad type in the same file - if type_ok == False: - print( - Fore.YELLOW - + "Rule {} has a 'related/type' invalid value.".format(file) - ) - faulty_rules.append(file) - else: - typo_list = [] - # Add more typos - typo_list.append( - self.get_rule_part(file_path=file, part_name="realted") - ) - typo_list.append( - self.get_rule_part(file_path=file, part_name="relatde") - ) - typo_list.append(self.get_rule_part(file_path=file, part_name="relted")) - typo_list.append(self.get_rule_part(file_path=file, part_name="rlated")) - - for i in typo_list: - if i != None: - print( - Fore.YELLOW - + "Rule {} has a typo in it's 'related' field.".format(file) - ) - faulty_rules.append(file) - - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with malformed optional 'related' fields. (check https://github.com/SigmaHQ/sigma-specification)", - ) - def test_sysmon_rule_without_eventid(self): faulty_rules = [] for file in self.yield_next_rule_file_path(self.path_to_rules): @@ -619,222 +486,155 @@ def test_sysmon_rule_without_eventid(self): + "There are rules using sysmon events but with no EventID specified", ) - def test_missing_date(self): - faulty_rules = [] - for file in self.yield_next_rule_file_path(self.path_to_rules): - datefield = self.get_rule_part(file_path=file, part_name="date") - if not datefield: - print(Fore.YELLOW + "Rule {} has no field 'date'.".format(file)) - faulty_rules.append(file) - elif not isinstance(datefield, str): - print( - Fore.YELLOW - + "Rule {} has a malformed 'date' (should be YYYY/MM/DD).".format( - file - ) - ) - faulty_rules.append(file) - elif len(datefield) != 10: - print( - Fore.YELLOW - + "Rule {} has a malformed 'date' (not 10 chars, should be YYYY/MM/DD).".format( - file - ) - ) - faulty_rules.append(file) - elif datefield[4] != "/" or datefield[7] != "/": - print( - Fore.YELLOW - + "Rule {} has a malformed 'date' (should be YYYY/MM/DD).".format( - file - ) - ) - faulty_rules.append(file) - - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with missing or malformed 'date' fields. (create one, e.g. date: 2019/01/14)", - ) - - def test_missing_description(self): - faulty_rules = [] - for file in self.yield_next_rule_file_path(self.path_to_rules): - descriptionfield = self.get_rule_part( - file_path=file, part_name="description" - ) - if not descriptionfield: - print(Fore.YELLOW + "Rule {} has no field 'description'.".format(file)) - faulty_rules.append(file) - elif not isinstance(descriptionfield, str): - print( - Fore.YELLOW - + "Rule {} has a 'description' field that isn't a string.".format( - file - ) - ) - faulty_rules.append(file) - elif len(descriptionfield) < 16: - print( - Fore.YELLOW - + "Rule {} has a really short description. Please elaborate.".format( - file - ) - ) - faulty_rules.append(file) - - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with missing or malformed 'description' field. (create one, e.g. description: Detects the suspicious behaviour of process XY doing YZ)", - ) - - # sigma-cli error - # def test_optional_date_modified(self): + # sigma error validators date_existence + # def test_missing_date(self): # faulty_rules = [] # for file in self.yield_next_rule_file_path(self.path_to_rules): - # modifiedfield = self.get_rule_part(file_path=file, part_name="modified") - # if modifiedfield: - # if not isinstance(modifiedfield, str): - # print( - # Fore.YELLOW - # + "Rule {} has a malformed 'modified' (should be YYYY/MM/DD).".format( - # file - # ) - # ) - # faulty_rules.append(file) - # elif len(modifiedfield) != 10: - # print( - # Fore.YELLOW - # + "Rule {} has a malformed 'modified' (not 10 chars, should be YYYY/MM/DD).".format( - # file - # ) + # datefield = self.get_rule_part(file_path=file, part_name="date") + # if not datefield: + # print(Fore.YELLOW + "Rule {} has no field 'date'.".format(file)) + # faulty_rules.append(file) + # elif not isinstance(datefield, str): + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'date' (should be YYYY/MM/DD).".format( + # file # ) - # faulty_rules.append(file) - # elif modifiedfield[4] != "/" or modifiedfield[7] != "/": - # print( - # Fore.YELLOW - # + "Rule {} has a malformed 'modified' (should be YYYY/MM/DD).".format( - # file - # ) + # ) + # faulty_rules.append(file) + # elif len(datefield) != 10: + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'date' (not 10 chars, should be YYYY/MM/DD).".format( + # file # ) - # faulty_rules.append(file) + # ) + # faulty_rules.append(file) + # elif datefield[4] != "/" or datefield[7] != "/": + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'date' (should be YYYY/MM/DD).".format( + # file + # ) + # ) + # faulty_rules.append(file) # self.assertEqual( # faulty_rules, # [], # Fore.RED - # + "There are rules with malformed 'modified' fields. (create one, e.g. date: 2019/01/14)", + # + "There are rules with missing or malformed 'date' fields. (create one, e.g. date: 2019/01/14)", + # ) +^ + # sigma validators description_existence description_length + # def test_missing_description(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # descriptionfield = self.get_rule_part( + # file_path=file, part_name="description" + # ) + # if not descriptionfield: + # print(Fore.YELLOW + "Rule {} has no field 'description'.".format(file)) + # faulty_rules.append(file) + # elif not isinstance(descriptionfield, str): + # print( + # Fore.YELLOW + # + "Rule {} has a 'description' field that isn't a string.".format( + # file + # ) + # ) + # faulty_rules.append(file) + # elif len(descriptionfield) < 16: + # print( + # Fore.YELLOW + # + "Rule {} has a really short description. Please elaborate.".format( + # file + # ) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with missing or malformed 'description' field. (create one, e.g. description: Detects the suspicious behaviour of process XY doing YZ)", # ) - # sigma-cli error and validator status_existence status_unsupported - # def test_optional_status(self): + # sigma error validators level_existence + # def test_level(self): # faulty_rules = [] - # valid_status = ["stable", "test", "experimental", "deprecated", "unsupported"] + # valid_level = [ + # "informational", + # "low", + # "medium", + # "high", + # "critical", + # ] # for file in self.yield_next_rule_file_path(self.path_to_rules): - # status_str = self.get_rule_part(file_path=file, part_name="status") - # if status_str: - # if not status_str in valid_status: + # level_str = self.get_rule_part(file_path=file, part_name="level") + # if not level_str: + # print(Fore.YELLOW + "Rule {} has no field 'level'.".format(file)) + # faulty_rules.append(file) + # elif not level_str in valid_level: + # print( + # Fore.YELLOW + # + "Rule {} has a invalid 'level' (check wiki).".format(file) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with missing or malformed 'level' fields. (check https://github.com/SigmaHQ/sigma-specification)", + # ) + + # sigma error + # def test_optional_fields(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # fields_str = self.get_rule_part(file_path=file, part_name="fields") + # if fields_str: + # # it exists but isn't a list + # if not isinstance(fields_str, list): # print( # Fore.YELLOW - # + "Rule {} has a invalid 'status' (check wiki).".format(file) + # + "Rule {} has a 'fields' field that isn't a list.".format(file) # ) # faulty_rules.append(file) - # elif status_str == "unsupported": + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed optional 'fields' fields. (has to be a list of values even if it contains only a single value)", + # ) + + # sigma error + # def test_optional_falsepositives_listtype(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # falsepositives_str = self.get_rule_part( + # file_path=file, part_name="falsepositives" + # ) + # if falsepositives_str: + # # it exists but isn't a list + # if not isinstance(falsepositives_str, list): # print( # Fore.YELLOW - # + "Rule {} has the unsupported 'status', can not be in rules directory".format( + # + "Rule {} has a 'falsepositives' field that isn't a list.".format( # file # ) # ) # faulty_rules.append(file) - # else: - # print( - # Fore.YELLOW + "Rule {} is missing the 'status' field".format(file) - # ) - # faulty_rules.append(file) # self.assertEqual( # faulty_rules, # [], # Fore.RED - # + "There are rules with malformed or missing 'status' fields. (check https://github.com/SigmaHQ/sigma-specification)", + # + "There are rules with malformed optional 'falsepositives' fields. (has to be a list of values even if it contains only a single value)", # ) - def test_level(self): - faulty_rules = [] - valid_level = [ - "informational", - "low", - "medium", - "high", - "critical", - ] - for file in self.yield_next_rule_file_path(self.path_to_rules): - level_str = self.get_rule_part(file_path=file, part_name="level") - if not level_str: - print(Fore.YELLOW + "Rule {} has no field 'level'.".format(file)) - faulty_rules.append(file) - elif not level_str in valid_level: - print( - Fore.YELLOW - + "Rule {} has a invalid 'level' (check wiki).".format(file) - ) - faulty_rules.append(file) - - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with missing or malformed 'level' fields. (check https://github.com/SigmaHQ/sigma-specification)", - ) - - def test_optional_fields(self): - faulty_rules = [] - for file in self.yield_next_rule_file_path(self.path_to_rules): - fields_str = self.get_rule_part(file_path=file, part_name="fields") - if fields_str: - # it exists but isn't a list - if not isinstance(fields_str, list): - print( - Fore.YELLOW - + "Rule {} has a 'fields' field that isn't a list.".format(file) - ) - faulty_rules.append(file) - - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with malformed optional 'fields' fields. (has to be a list of values even if it contains only a single value)", - ) - - def test_optional_falsepositives_listtype(self): - faulty_rules = [] - for file in self.yield_next_rule_file_path(self.path_to_rules): - falsepositives_str = self.get_rule_part( - file_path=file, part_name="falsepositives" - ) - if falsepositives_str: - # it exists but isn't a list - if not isinstance(falsepositives_str, list): - print( - Fore.YELLOW - + "Rule {} has a 'falsepositives' field that isn't a list.".format( - file - ) - ) - faulty_rules.append(file) - - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with malformed optional 'falsepositives' fields. (has to be a list of values even if it contains only a single value)", - ) - def test_optional_falsepositives_capital(self): faulty_rules = [] for file in self.yield_next_rule_file_path(self.path_to_rules): @@ -897,28 +697,29 @@ def test_optional_falsepositives_blocked_content(self): + "There are rules with invalid false positive definitions (e.g. Pentest, None or common typos)", ) - # Upgrade Detection Rule License 1.1 - def test_optional_author(self): - faulty_rules = [] - for file in self.yield_next_rule_file_path(self.path_to_rules): - author_str = self.get_rule_part(file_path=file, part_name="author") - if author_str: - # it exists but isn't a string - if not isinstance(author_str, str): - print( - Fore.YELLOW - + "Rule {} has a 'author' field that isn't a string.".format( - file - ) - ) - faulty_rules.append(file) + # sigma error + # # Upgrade Detection Rule License 1.1 + # def test_optional_author(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # author_str = self.get_rule_part(file_path=file, part_name="author") + # if author_str: + # # it exists but isn't a string + # if not isinstance(author_str, str): + # print( + # Fore.YELLOW + # + "Rule {} has a 'author' field that isn't a string.".format( + # file + # ) + # ) + # faulty_rules.append(file) - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with malformed 'author' fields. (has to be a string even if it contains many author)", - ) + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed 'author' fields. (has to be a string even if it contains many author)", + # ) def test_optional_license(self): faulty_rules = [] @@ -941,60 +742,6 @@ def test_optional_license(self): + "There are rules with malformed 'license' fields. (has to be a string )", ) - # sigma-cli validators tlptag - # def test_optional_tlp(self): - # faulty_rules = [] - # valid_tlp = [ - # "WHITE", - # "GREEN", - # "AMBER", - # "RED", - # ] - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # tlp_str = self.get_rule_part(file_path=file, part_name="tlp") - # if tlp_str: - # # it exists but isn't a string - # if not isinstance(tlp_str, str): - # print( - # Fore.YELLOW - # + "Rule {} has a 'tlp' field that isn't a string.".format(file) - # ) - # faulty_rules.append(file) - # elif not tlp_str.upper() in valid_tlp: - # print( - # Fore.YELLOW - # + "Rule {} has a 'tlp' field with not valid value.".format(file) - # ) - # faulty_rules.append(file) - - # self.assertEqual( - # faulty_rules, - # [], - # Fore.RED - # + "There are rules with malformed optional 'tlp' fields. (https://www.cisa.gov/tlp)", - # ) - - # Not in the specification - # def test_optional_target(self): - # faulty_rules = [] - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # target = self.get_rule_part(file_path=file, part_name="target") - # if target: - # # it exists but isn't a list - # if not isinstance(target, list): - # print( - # Fore.YELLOW - # + "Rule {} has a 'target' field that isn't a list.".format(file) - # ) - # faulty_rules.append(file) - - # self.assertEqual( - # faulty_rules, - # [], - # Fore.RED - # + "There are rules with malformed 'target' fields. (has to be a list of values even if it contains only a single value)", - # ) - def test_references(self): faulty_rules = [] for file in self.yield_next_rule_file_path(self.path_to_rules): @@ -1053,20 +800,21 @@ def test_references_in_description(self): + "There are rules with malformed 'description' fields. (links and external references have to be in a seperate field named 'references'. see specification https://github.com/SigmaHQ/sigma-specification)", ) - def test_references_plural(self): - faulty_rules = [] - for file in self.yield_next_rule_file_path(self.path_to_rules): - reference = self.get_rule_part(file_path=file, part_name="reference") - if reference: - # it exists but in singular form - faulty_rules.append(file) + # sigma validator custom_attributes + # def test_references_plural(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # reference = self.get_rule_part(file_path=file, part_name="reference") + # if reference: + # # it exists but in singular form + # faulty_rules.append(file) - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with malformed 'references' fields. (has to be 'references' in plural form, not singular)", - ) + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed 'references' fields. (has to be 'references' in plural form, not singular)", + # ) def test_file_names(self): faulty_rules = [] @@ -1362,80 +1110,20 @@ def test_title_in_first_line(self): + "There are rules without the 'title' attribute in their first line.", ) - # sigma validators duplicate_title - # def test_duplicate_titles(self): - # # This test ensure that every rule has a unique title - # faulty_rules = [] - # titles_dict = {} - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # title = ( - # self.get_rule_part(file_path=file, part_name="title").lower().rstrip() - # ) - # duplicate = False - # for rule, title_ in titles_dict.items(): - # if title == title_: - # print( - # Fore.RED - # + "Rule {} has an already used title in {}.".format(file, rule) - # ) - # duplicate = True - # faulty_rules.append(file) - # continue - # if not duplicate: - # titles_dict[file] = title - - # self.assertEqual( - # faulty_rules, - # [], - # Fore.RED - # + "There are rules that share the same 'title'. Please check: https://github.com/SigmaHQ/sigma/wiki/Rule-Creation-Guide#title", - # ) - - # def test_invalid_logsource_attributes(self): - # faulty_rules = [] - # valid_logsource = [ - # 'category', - # 'product', - # 'service', - # 'definition', - # ] - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # logsource = self.get_rule_part( - # file_path=file, part_name="logsource") - # if not logsource: - # print(Fore.RED + "Rule {} has no 'logsource'.".format(file)) - # faulty_rules.append(file) - # continue - # valid = True - # for key in logsource: - # if key.lower() not in valid_logsource: - # print( - # Fore.RED + "Rule {} has a logsource with an invalid field ({})".format(file, key)) - # valid = False - # elif not isinstance(logsource[key], str): - # print( - # Fore.RED + "Rule {} has a logsource with an invalid field type ({})".format(file, key)) - # valid = False - # if not valid: - # faulty_rules.append(file) - - # self.assertEqual(faulty_rules, [], Fore.RED + - # "There are rules with non-conform 'logsource' fields. Please check: https://github.com/SigmaHQ/sigma/wiki/Rule-Creation-Guide#log-source") - - def test_selection_list_one_value(self): - def treat_list(file, values, valid_, selection_name): - # rule with only list of Keywords term - if len(values) == 1 and not isinstance(values[0], str): - print( - Fore.RED - + "Rule {} has the selection ({}) with a list of only 1 element in detection".format( - file, key - ) - ) - valid_ = False - elif isinstance(values[0], dict): - valid_ = treat_dict(file, values, valid_, selection_name) - return valid_ + def test_selection_list_one_value(self): + def treat_list(file, values, valid_, selection_name): + # rule with only list of Keywords term + if len(values) == 1 and not isinstance(values[0], str): + print( + Fore.RED + + "Rule {} has the selection ({}) with a list of only 1 element in detection".format( + file, key + ) + ) + valid_ = False + elif isinstance(values[0], dict): + valid_ = treat_dict(file, values, valid_, selection_name) + return valid_ def treat_dict(file, values, valid_, selection_name): if isinstance(values, list): @@ -1580,84 +1268,6 @@ def test_unused_selection(self): faulty_rules, [], Fore.RED + "There are rules with unused selections" ) - # def test_field_name_typo(self): - # # add "OriginalFilename" after Aurora switched to SourceFilename - # # add "ProviderName" after special case powershell classic is resolved - # faulty_rules = [] - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # # typos is a list of tuples where each tuple contains ("The typo", "The correct version") - # typos = [("ServiceFilename", "ServiceFileName"), ("TargetFileName", "TargetFilename"), ("SourceFileName", "OriginalFileName"), ("Commandline", "CommandLine"), ("Targetobject", "TargetObject"), ("OriginalName", "OriginalFileName"), ("ImageFileName", "OriginalFileName"), ("details", "Details")] - # # Some fields exists in certain log sources in different forms than other log sources. We need to handle these as special cases - # # We check first the logsource to handle special cases - # logsource = self.get_rule_part(file_path=file, part_name="logsource").values() - # # add more typos in specific logsources below - # if "windefend" in logsource: - # typos += [("New_Value", "NewValue"), ("Old_Value", "OldValue"), ('Source_Name', 'SourceName'), ("Newvalue", "NewValue"), ("Oldvalue", "OldValue"), ('Sourcename', 'SourceName')] - # elif "registry_set" in logsource or "registry_add" in logsource or "registry_event" in logsource: - # typos += [("Targetobject", "TargetObject"), ("Eventtype", "EventType"), ("Newname", "NewName")] - # elif "process_creation" in logsource: - # typos += [("Parentimage", "ParentImage"), ("Integritylevel", "IntegrityLevel"), ("IntegritiLevel", "IntegrityLevel")] - # elif "file_access" in logsource: - # del(typos[typos.index(("TargetFileName", "TargetFilename"))]) # We remove the entry to "TargetFileName" to avoid confusion - # typos += [("TargetFileName", "FileName"), ("TargetFilename","FileName")] - # detection = self.get_rule_part(file_path=file, part_name="detection") - # if detection: - # for search_identifier in detection: - # if isinstance(detection[search_identifier], dict): - # for field in detection[search_identifier]: - # for typo in typos: - # if typo[0] in field: - # print(Fore.RED + "Rule {} has a common typo ({}) which should be ({}) in selection ({}/{})".format(file, typo[0], typo[1], search_identifier, field)) - # faulty_rules.append(file) - - # self.assertEqual(faulty_rules, [], Fore.RED + "There are rules with common typos in field names.") - - # Sigma error SigmaModifierError - # def test_unknown_value_modifier(self): - # known_modifiers = [ - # "contains", - # "startswith", - # "endswith", - # "all", - # "base64offset", - # "base64", - # "utf16le", - # "utf16be", - # "wide", - # "utf16", - # "windash", - # "re", - # "cidr", - # ] - # faulty_rules = [] - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # detection = self.get_rule_part(file_path=file, part_name="detection") - # if detection: - # for search_identifier in detection: - # if isinstance(detection[search_identifier], dict): - # for field in detection[search_identifier]: - # if "|" in field: - # for current_modifier in field.split("|")[1:]: - # found = False - # for target_modifier in known_modifiers: - # if current_modifier == target_modifier: - # found = True - # if not found: - # print( - # Fore.RED - # + "Rule {} uses an unknown field modifier ({}/{})".format( - # file, search_identifier, field - # ) - # ) - # faulty_rules.append(file) - - # self.assertEqual( - # faulty_rules, - # [], - # Fore.RED - # + "There are rules with unknown value modifiers. Most often it is just a typo.", - # ) - def test_all_value_modifier_single_item(self): faulty_rules = [] for file in self.yield_next_rule_file_path(self.path_to_rules): @@ -1919,6 +1529,405 @@ def check_item_for_bad_escapes(item): faulty_rules, [], Fore.RED + "There are rules using illegal re-escapes" ) + + # def test_confirm_extension_is_yml(self): + # files_with_incorrect_extensions = [] + + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # file_name_and_extension = os.path.splitext(file) + # if len(file_name_and_extension) == 2: + # extension = file_name_and_extension[1] + # if extension != ".yml": + # files_with_incorrect_extensions.append(file) + + # self.assertEqual(files_with_incorrect_extensions, [], Fore.RED + + # "There are rule files with extensions other than .yml") + # sigma-cli validators attacktag + # def test_confirm_correct_mitre_tags(self): + # files_with_incorrect_mitre_tags = [] + + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # tags = self.get_rule_part(file_path=file, part_name="tags") + # if tags: + # for tag in tags: + # if tag.startswith("attack.") and tag not in self.MITRE_ALL: + # print( + # Fore.RED + # + "Rule {} has the following incorrect MITRE tag {}".format( + # file, tag + # ) + # ) + # files_with_incorrect_mitre_tags.append(file) + + # self.assertEqual( + # files_with_incorrect_mitre_tags, + # [], + # Fore.RED + # + "There are rules with incorrect/unknown MITRE Tags. (please inform us about new tags that are not yet supported in our tests) and check the correct tags here: https://attack.mitre.org/ ", + # ) + + # sigma validators duplicate_tag + # def test_duplicate_tags(self): + # files_with_incorrect_mitre_tags = [] + + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # tags = self.get_rule_part(file_path=file, part_name="tags") + # if tags: + # known_tags = [] + # for tag in tags: + # if tag in known_tags: + # print( + # Fore.RED + # + "Rule {} has the duplicate tag {}".format(file, tag) + # ) + # files_with_incorrect_mitre_tags.append(file) + # else: + # known_tags.append(tag) + + # self.assertEqual( + # files_with_incorrect_mitre_tags, + # [], + # Fore.RED + "There are rules with duplicate tags", + # ) + + # sigma validators duplicate_references + # def test_duplicate_references(self): + # files_with_duplicate_references = [] + + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # references = self.get_rule_part(file_path=file, part_name="references") + # if references: + # known_references = [] + # for reference in references: + # if reference in known_references: + # print( + # Fore.RED + # + "Rule {} has the duplicate reference {}".format( + # file, reference + # ) + # ) + # files_with_duplicate_references.append(file) + # else: + # known_references.append(reference) + + # self.assertEqual( + # files_with_duplicate_references, + # [], + # Fore.RED + "There are rules with duplicate references", + # ) + + # sigma validator identifier_existence identifier_uniqueness + # def test_missing_id(self): + # faulty_rules = [] + # dict_id = {} + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # id = self.get_rule_part(file_path=file, part_name="id") + # if not id: + # print(Fore.YELLOW + "Rule {} has no field 'id'.".format(file)) + # faulty_rules.append(file) + # elif len(id) != 36: + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'id' (not 36 chars).".format(file) + # ) + # faulty_rules.append(file) + # elif id.lower() in dict_id.keys(): + # print( + # Fore.YELLOW + # + "Rule {} has the same 'id' as {}. Ids have to be unique.".format( + # file, dict_id[id] + # ) + # ) + # faulty_rules.append(file) + # else: + # dict_id[id.lower()] = file + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with missing or malformed 'id' fields. Generate an id (e.g. here: https://www.uuidgenerator.net/version4) and add it to the reported rule(s).", + # ) + # sigma-cli error + # def test_optional_date_modified(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # modifiedfield = self.get_rule_part(file_path=file, part_name="modified") + # if modifiedfield: + # if not isinstance(modifiedfield, str): + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'modified' (should be YYYY/MM/DD).".format( + # file + # ) + # ) + # faulty_rules.append(file) + # elif len(modifiedfield) != 10: + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'modified' (not 10 chars, should be YYYY/MM/DD).".format( + # file + # ) + # ) + # faulty_rules.append(file) + # elif modifiedfield[4] != "/" or modifiedfield[7] != "/": + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'modified' (should be YYYY/MM/DD).".format( + # file + # ) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed 'modified' fields. (create one, e.g. date: 2019/01/14)", + # ) + + # sigma-cli error and validator status_existence status_unsupported + # def test_optional_status(self): + # faulty_rules = [] + # valid_status = ["stable", "test", "experimental", "deprecated", "unsupported"] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # status_str = self.get_rule_part(file_path=file, part_name="status") + # if status_str: + # if not status_str in valid_status: + # print( + # Fore.YELLOW + # + "Rule {} has a invalid 'status' (check wiki).".format(file) + # ) + # faulty_rules.append(file) + # elif status_str == "unsupported": + # print( + # Fore.YELLOW + # + "Rule {} has the unsupported 'status', can not be in rules directory".format( + # file + # ) + # ) + # faulty_rules.append(file) + # else: + # print( + # Fore.YELLOW + "Rule {} is missing the 'status' field".format(file) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed or missing 'status' fields. (check https://github.com/SigmaHQ/sigma-specification)", + # ) + + # Sigma validator all_of_them_condition + # def test_all_of_them_condition(self): + # faulty_detections = [] + + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # detection = self.get_rule_part(file_path=file, part_name="detection") + + # if "all of them" in detection["condition"]: + # faulty_detections.append(file) + + # self.assertEqual( + # faulty_detections, + # [], + # Fore.RED + # + "There are rules using 'all of them'. Better use e.g. 'all of selection*' instead (and use the 'selection_' prefix as search-identifier).", + # ) + + # sigma-cli validators tlptag + # def test_optional_tlp(self): + # faulty_rules = [] + # valid_tlp = [ + # "WHITE", + # "GREEN", + # "AMBER", + # "RED", + # ] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # tlp_str = self.get_rule_part(file_path=file, part_name="tlp") + # if tlp_str: + # # it exists but isn't a string + # if not isinstance(tlp_str, str): + # print( + # Fore.YELLOW + # + "Rule {} has a 'tlp' field that isn't a string.".format(file) + # ) + # faulty_rules.append(file) + # elif not tlp_str.upper() in valid_tlp: + # print( + # Fore.YELLOW + # + "Rule {} has a 'tlp' field with not valid value.".format(file) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed optional 'tlp' fields. (https://www.cisa.gov/tlp)", + # ) + + # Not in the specification + # def test_optional_target(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # target = self.get_rule_part(file_path=file, part_name="target") + # if target: + # # it exists but isn't a list + # if not isinstance(target, list): + # print( + # Fore.YELLOW + # + "Rule {} has a 'target' field that isn't a list.".format(file) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed 'target' fields. (has to be a list of values even if it contains only a single value)", + # ) + # sigma validators duplicate_title + # def test_duplicate_titles(self): + # # This test ensure that every rule has a unique title + # faulty_rules = [] + # titles_dict = {} + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # title = ( + # self.get_rule_part(file_path=file, part_name="title").lower().rstrip() + # ) + # duplicate = False + # for rule, title_ in titles_dict.items(): + # if title == title_: + # print( + # Fore.RED + # + "Rule {} has an already used title in {}.".format(file, rule) + # ) + # duplicate = True + # faulty_rules.append(file) + # continue + # if not duplicate: + # titles_dict[file] = title + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules that share the same 'title'. Please check: https://github.com/SigmaHQ/sigma/wiki/Rule-Creation-Guide#title", + # ) + + # def test_invalid_logsource_attributes(self): + # faulty_rules = [] + # valid_logsource = [ + # 'category', + # 'product', + # 'service', + # 'definition', + # ] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # logsource = self.get_rule_part( + # file_path=file, part_name="logsource") + # if not logsource: + # print(Fore.RED + "Rule {} has no 'logsource'.".format(file)) + # faulty_rules.append(file) + # continue + # valid = True + # for key in logsource: + # if key.lower() not in valid_logsource: + # print( + # Fore.RED + "Rule {} has a logsource with an invalid field ({})".format(file, key)) + # valid = False + # elif not isinstance(logsource[key], str): + # print( + # Fore.RED + "Rule {} has a logsource with an invalid field type ({})".format(file, key)) + # valid = False + # if not valid: + # faulty_rules.append(file) + + # self.assertEqual(faulty_rules, [], Fore.RED + + # "There are rules with non-conform 'logsource' fields. Please check: https://github.com/SigmaHQ/sigma/wiki/Rule-Creation-Guide#log-source") + # def test_field_name_typo(self): + # # add "OriginalFilename" after Aurora switched to SourceFilename + # # add "ProviderName" after special case powershell classic is resolved + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # # typos is a list of tuples where each tuple contains ("The typo", "The correct version") + # typos = [("ServiceFilename", "ServiceFileName"), ("TargetFileName", "TargetFilename"), ("SourceFileName", "OriginalFileName"), ("Commandline", "CommandLine"), ("Targetobject", "TargetObject"), ("OriginalName", "OriginalFileName"), ("ImageFileName", "OriginalFileName"), ("details", "Details")] + # # Some fields exists in certain log sources in different forms than other log sources. We need to handle these as special cases + # # We check first the logsource to handle special cases + # logsource = self.get_rule_part(file_path=file, part_name="logsource").values() + # # add more typos in specific logsources below + # if "windefend" in logsource: + # typos += [("New_Value", "NewValue"), ("Old_Value", "OldValue"), ('Source_Name', 'SourceName'), ("Newvalue", "NewValue"), ("Oldvalue", "OldValue"), ('Sourcename', 'SourceName')] + # elif "registry_set" in logsource or "registry_add" in logsource or "registry_event" in logsource: + # typos += [("Targetobject", "TargetObject"), ("Eventtype", "EventType"), ("Newname", "NewName")] + # elif "process_creation" in logsource: + # typos += [("Parentimage", "ParentImage"), ("Integritylevel", "IntegrityLevel"), ("IntegritiLevel", "IntegrityLevel")] + # elif "file_access" in logsource: + # del(typos[typos.index(("TargetFileName", "TargetFilename"))]) # We remove the entry to "TargetFileName" to avoid confusion + # typos += [("TargetFileName", "FileName"), ("TargetFilename","FileName")] + # detection = self.get_rule_part(file_path=file, part_name="detection") + # if detection: + # for search_identifier in detection: + # if isinstance(detection[search_identifier], dict): + # for field in detection[search_identifier]: + # for typo in typos: + # if typo[0] in field: + # print(Fore.RED + "Rule {} has a common typo ({}) which should be ({}) in selection ({}/{})".format(file, typo[0], typo[1], search_identifier, field)) + # faulty_rules.append(file) + + # self.assertEqual(faulty_rules, [], Fore.RED + "There are rules with common typos in field names.") + + # Sigma error SigmaModifierError + # def test_unknown_value_modifier(self): + # known_modifiers = [ + # "contains", + # "startswith", + # "endswith", + # "all", + # "base64offset", + # "base64", + # "utf16le", + # "utf16be", + # "wide", + # "utf16", + # "windash", + # "re", + # "cidr", + # ] + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # detection = self.get_rule_part(file_path=file, part_name="detection") + # if detection: + # for search_identifier in detection: + # if isinstance(detection[search_identifier], dict): + # for field in detection[search_identifier]: + # if "|" in field: + # for current_modifier in field.split("|")[1:]: + # found = False + # for target_modifier in known_modifiers: + # if current_modifier == target_modifier: + # found = True + # if not found: + # print( + # Fore.RED + # + "Rule {} uses an unknown field modifier ({}/{})".format( + # file, search_identifier, field + # ) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with unknown value modifiers. Most often it is just a typo.", + # ) + + # sigma-cli validators attacktag # def get_mitre_data(): # """ From 40216ca247c7df0b04a2af6d546a0cfda0e56fcb Mon Sep 17 00:00:00 2001 From: frack113 <62423083+frack113@users.noreply.github.com> Date: Sat, 25 Nov 2023 08:32:42 +0100 Subject: [PATCH 2/8] =?UTF-8?q?docs:=20=F0=9F=93=9A=20Add=20Summiting=20th?= =?UTF-8?q?e=20Pyramid=20tags?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account_management/win_security_access_token_abuse.yml | 1 + .../pipe_created_hktl_cobaltstrike_susp_pipe_patterns.yml | 1 + .../powershell/powershell_script/posh_ps_get_acl_service.yml | 1 + .../proc_creation_win_hktl_cobaltstrike_bloopers_cmd.yml | 1 + .../process_creation/proc_creation_win_pua_adfind_susp_usage.yml | 1 + .../process_creation/proc_creation_win_schtasks_creation.yml | 1 + 6 files changed, 6 insertions(+) diff --git a/rules/windows/builtin/security/account_management/win_security_access_token_abuse.yml b/rules/windows/builtin/security/account_management/win_security_access_token_abuse.yml index 588b3189f38..b0f532c5294 100644 --- a/rules/windows/builtin/security/account_management/win_security_access_token_abuse.yml +++ b/rules/windows/builtin/security/account_management/win_security_access_token_abuse.yml @@ -12,6 +12,7 @@ tags: - attack.defense_evasion - attack.privilege_escalation - attack.t1134.001 + - stp.4u logsource: product: windows service: security diff --git a/rules/windows/pipe_created/pipe_created_hktl_cobaltstrike_susp_pipe_patterns.yml b/rules/windows/pipe_created/pipe_created_hktl_cobaltstrike_susp_pipe_patterns.yml index 7a8334821b7..b881a6e61fc 100644 --- a/rules/windows/pipe_created/pipe_created_hktl_cobaltstrike_susp_pipe_patterns.yml +++ b/rules/windows/pipe_created/pipe_created_hktl_cobaltstrike_susp_pipe_patterns.yml @@ -17,6 +17,7 @@ tags: - attack.defense_evasion - attack.privilege_escalation - attack.t1055 + - stp.1k logsource: product: windows category: pipe_created diff --git a/rules/windows/powershell/powershell_script/posh_ps_get_acl_service.yml b/rules/windows/powershell/powershell_script/posh_ps_get_acl_service.yml index 66a861bb0fd..3a916a38434 100644 --- a/rules/windows/powershell/powershell_script/posh_ps_get_acl_service.yml +++ b/rules/windows/powershell/powershell_script/posh_ps_get_acl_service.yml @@ -13,6 +13,7 @@ date: 2021/12/30 tags: - attack.persistence - attack.t1574.011 + - stp.2a logsource: product: windows category: ps_script diff --git a/rules/windows/process_creation/proc_creation_win_hktl_cobaltstrike_bloopers_cmd.yml b/rules/windows/process_creation/proc_creation_win_hktl_cobaltstrike_bloopers_cmd.yml index 93d713b1d99..35ac828df1d 100644 --- a/rules/windows/process_creation/proc_creation_win_hktl_cobaltstrike_bloopers_cmd.yml +++ b/rules/windows/process_creation/proc_creation_win_hktl_cobaltstrike_bloopers_cmd.yml @@ -15,6 +15,7 @@ modified: 2023/01/30 tags: - attack.execution - attack.t1059.003 + - stp.1u logsource: category: process_creation product: windows diff --git a/rules/windows/process_creation/proc_creation_win_pua_adfind_susp_usage.yml b/rules/windows/process_creation/proc_creation_win_pua_adfind_susp_usage.yml index 606e4b6806a..0464748680d 100644 --- a/rules/windows/process_creation/proc_creation_win_pua_adfind_susp_usage.yml +++ b/rules/windows/process_creation/proc_creation_win_pua_adfind_susp_usage.yml @@ -24,6 +24,7 @@ tags: - attack.t1087.002 - attack.t1482 - attack.t1069.002 + - stp.1u logsource: category: process_creation product: windows diff --git a/rules/windows/process_creation/proc_creation_win_schtasks_creation.yml b/rules/windows/process_creation/proc_creation_win_schtasks_creation.yml index 3e10b8fcae5..14c91908c3e 100644 --- a/rules/windows/process_creation/proc_creation_win_schtasks_creation.yml +++ b/rules/windows/process_creation/proc_creation_win_schtasks_creation.yml @@ -12,6 +12,7 @@ tags: - attack.t1053.005 - attack.s0111 - car.2013-08-001 + - stp.1u logsource: category: process_creation product: windows From 96e0b996725d1b7165bb95effcd94a7caa5470b7 Mon Sep 17 00:00:00 2001 From: frack113 <62423083+frack113@users.noreply.github.com> Date: Sat, 25 Nov 2023 08:38:05 +0100 Subject: [PATCH 3/8] =?UTF-8?q?refactor:=20=F0=9F=94=A8=20Remove=20useless?= =?UTF-8?q?=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_rules.py b/tests/test_rules.py index 2962d747834..cec4df221b6 100755 --- a/tests/test_rules.py +++ b/tests/test_rules.py @@ -525,7 +525,7 @@ def test_sysmon_rule_without_eventid(self): # Fore.RED # + "There are rules with missing or malformed 'date' fields. (create one, e.g. date: 2019/01/14)", # ) -^ + # sigma validators description_existence description_length # def test_missing_description(self): # faulty_rules = [] From 7b2406e60767c5a395dac1f12e8bdee91cc5a445 Mon Sep 17 00:00:00 2001 From: Nasreddine Bencherchali <8741929+nasbench@users.noreply.github.com> Date: Mon, 27 Nov 2023 00:50:05 +0100 Subject: [PATCH 4/8] Merge PR #4595 from @nasbench - Disable Greetings Workflow chore: temporarily disable greetings workflow --- .github/workflows/greetings.yml | 68 ++++++++++++++++----------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml index 92c9e151030..1dbfb288b44 100644 --- a/.github/workflows/greetings.yml +++ b/.github/workflows/greetings.yml @@ -1,34 +1,34 @@ -name: Auto message for PR's and Issues - -on: [pull_request_target, issues] - -jobs: - build: - name: Hello new contributor - runs-on: ubuntu-latest - steps: - - uses: actions/first-interaction@v1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - issue-message: |- - Welcome @${{github.actor}} :wave: - - It looks like this is your first issue on the Sigma rules repository! - - The following repository accepts issues related to `false positives` or 'rule ideas'. - - If you're reporting an issue related to the pySigma library please consider submitting it [here](https://github.com/SigmaHQ/pySigma) - - If you're reporting an issue related to the deprecated sigmac library please consider submitting it [here](https://github.com/SigmaHQ/legacy-sigmatools) - - Thanks for taking the time to open this issue, and welcome to the Sigma community! :smiley: - - - pr-message: |- - Welcome @${{github.actor}} :wave: - - It looks like this is your first pull request on the Sigma rules repository! - - Please make sure to read the [SigmaHQ conventions](https://github.com/SigmaHQ/sigma-specification/blob/main/sigmahq/sigmahq_conventions.md) document to make sure your contribution is adhering to best practices and has all the necessary elements in place for a successful approval. - - Thanks again, and welcome to the Sigma community! :smiley: \ No newline at end of file +#name: Auto message for PR's and Issues +# +#on: [pull_request_target, issues] +# +#jobs: +# build: +# name: Hello new contributor +# runs-on: ubuntu-latest +# steps: +# - uses: actions/first-interaction@v1 +# with: +# repo-token: ${{ secrets.GITHUB_TOKEN }} +# issue-message: |- +# Welcome @${{github.actor}} :wave: +# +# It looks like this is your first issue on the Sigma rules repository! +# +# The following repository accepts issues related to `false positives` or 'rule ideas'. +# +# If you're reporting an issue related to the pySigma library please consider submitting it [here](https://github.com/SigmaHQ/pySigma) +# +# If you're reporting an issue related to the deprecated sigmac library please consider submitting it [here](https://github.com/SigmaHQ/legacy-sigmatools) +# +# Thanks for taking the time to open this issue, and welcome to the Sigma community! :smiley: +# +# +# pr-message: |- +# Welcome @${{github.actor}} :wave: +# +# It looks like this is your first pull request on the Sigma rules repository! +# +# Please make sure to read the [SigmaHQ conventions](https://github.com/SigmaHQ/sigma-specification/blob/main/sigmahq/sigmahq_conventions.md) document to make sure your contribution is adhering to best practices and has all the necessary elements in place for a successful approval. +# +# Thanks again, and welcome to the Sigma community! :smiley: From 79247a3ede711d3ba529f1b89232a2a2065b33c4 Mon Sep 17 00:00:00 2001 From: frack113 <62423083+frack113@users.noreply.github.com> Date: Sat, 25 Nov 2023 08:31:50 +0100 Subject: [PATCH 5/8] =?UTF-8?q?ci:=20=F0=9F=A4=96=20Use=20pySigma=200.10.9?= =?UTF-8?q?=20validators?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/sigma_cli_conf.yml | 7 + tests/test_rules.py | 1269 +++++++++++++++++++------------------- 2 files changed, 646 insertions(+), 630 deletions(-) diff --git a/tests/sigma_cli_conf.yml b/tests/sigma_cli_conf.yml index 0ad3f69537c..33f145f4055 100644 --- a/tests/sigma_cli_conf.yml +++ b/tests/sigma_cli_conf.yml @@ -3,6 +3,10 @@ validators: - attacktag - cartag - cvetag + - custom_attributes + - date_existence + - description_existence + - description_length - detection_tag - duplicate_filename - duplicate_references @@ -12,9 +16,12 @@ validators: - filename_sigmahq - identifier_existence - identifier_uniqueness + - level_existence - status_existence - status_unsupported + - stptag - tlptag + exclusions: # escaped_wildcard 021310d9-30a6-480a-84b7-eaa69aeb92bb: escaped_wildcard diff --git a/tests/test_rules.py b/tests/test_rules.py index f46dfe3f8ce..2962d747834 100755 --- a/tests/test_rules.py +++ b/tests/test_rules.py @@ -11,16 +11,18 @@ import yaml import re import string -#from attackcti import attack_client + +# from attackcti import attack_client from colorama import init from colorama import Fore import collections -# Old Tests cover by pySigma 0.10.6 and simgma-cli 0.7.8 +# Old Tests cover by pySigma 0.10.9 and simgma-cli 0.7.10 # Use sigma check --fail-on-error --fail-on-issues --validation-config tests/sigma_cli_conf.yml rules* # + class TestRules(unittest.TestCase): # @classmethod # def setUpClass(cls): @@ -87,19 +89,6 @@ def get_rule_yaml(self, file_path: str) -> dict: return data # Tests - # def test_confirm_extension_is_yml(self): - # files_with_incorrect_extensions = [] - - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # file_name_and_extension = os.path.splitext(file) - # if len(file_name_and_extension) == 2: - # extension = file_name_and_extension[1] - # if extension != ".yml": - # files_with_incorrect_extensions.append(file) - - # self.assertEqual(files_with_incorrect_extensions, [], Fore.RED + - # "There are rule files with extensions other than .yml") - def test_legal_trademark_violations(self): # See Issue # https://github.com/SigmaHQ/sigma/issues/1028 files_with_legal_issues = [] @@ -118,101 +107,28 @@ def test_legal_trademark_violations(self): + "There are rule files which contains a trademark or reference that doesn't comply with the respective trademark requirements - please remove the trademark to avoid legal issues", ) - def test_optional_tags(self): - files_with_incorrect_tags = [] - tags_pattern = re.compile( - r"cve\.\d+\.\d+|attack\.(t\d{4}\.\d{3}|[gts]\d{4})$|attack\.[a-z_]+|car\.\d{4}-\d{2}-\d{3}|detection\.\w+" - ) - for file in self.yield_next_rule_file_path(self.path_to_rules): - tags = self.get_rule_part(file_path=file, part_name="tags") - if tags: - for tag in tags: - if tags_pattern.match(tag) == None: - print( - Fore.RED - + "Rule {} has the invalid tag <{}>".format(file, tag) - ) - files_with_incorrect_tags.append(file) - - self.assertEqual( - files_with_incorrect_tags, - [], - Fore.RED - + "There are rules with incorrect/unknown Tags. (please inform us about new tags that are not yet supported in our tests) and check the correct tags here: https://github.com/SigmaHQ/sigma-specification/blob/main/Tags_specification.md ", - ) - - # sigma-cli validators attacktag - # def test_confirm_correct_mitre_tags(self): - # files_with_incorrect_mitre_tags = [] - - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # tags = self.get_rule_part(file_path=file, part_name="tags") - # if tags: - # for tag in tags: - # if tag.startswith("attack.") and tag not in self.MITRE_ALL: - # print( - # Fore.RED - # + "Rule {} has the following incorrect MITRE tag {}".format( - # file, tag - # ) - # ) - # files_with_incorrect_mitre_tags.append(file) - - # self.assertEqual( - # files_with_incorrect_mitre_tags, - # [], - # Fore.RED - # + "There are rules with incorrect/unknown MITRE Tags. (please inform us about new tags that are not yet supported in our tests) and check the correct tags here: https://attack.mitre.org/ ", + # sigma error and validator attacktag,cartag,cvetag,detection_tag,stptag,tlptag + # def test_optional_tags(self): + # files_with_incorrect_tags = [] + # tags_pattern = re.compile( + # r"cve\.\d+\.\d+|attack\.(t\d{4}\.\d{3}|[gts]\d{4})$|attack\.[a-z_]+|car\.\d{4}-\d{2}-\d{3}|detection\.\w+" # ) - - # sigma validators duplicate_tag - # def test_duplicate_tags(self): - # files_with_incorrect_mitre_tags = [] - # for file in self.yield_next_rule_file_path(self.path_to_rules): # tags = self.get_rule_part(file_path=file, part_name="tags") # if tags: - # known_tags = [] # for tag in tags: - # if tag in known_tags: - # print( - # Fore.RED - # + "Rule {} has the duplicate tag {}".format(file, tag) - # ) - # files_with_incorrect_mitre_tags.append(file) - # else: - # known_tags.append(tag) - - # self.assertEqual( - # files_with_incorrect_mitre_tags, - # [], - # Fore.RED + "There are rules with duplicate tags", - # ) - - # sigma validators duplicate_references - # def test_duplicate_references(self): - # files_with_duplicate_references = [] - - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # references = self.get_rule_part(file_path=file, part_name="references") - # if references: - # known_references = [] - # for reference in references: - # if reference in known_references: + # if tags_pattern.match(tag) == None: # print( # Fore.RED - # + "Rule {} has the duplicate reference {}".format( - # file, reference - # ) + # + "Rule {} has the invalid tag <{}>".format(file, tag) # ) - # files_with_duplicate_references.append(file) - # else: - # known_references.append(reference) + # files_with_incorrect_tags.append(file) # self.assertEqual( - # files_with_duplicate_references, + # files_with_incorrect_tags, # [], - # Fore.RED + "There are rules with duplicate references", + # Fore.RED + # + "There are rules with incorrect/unknown Tags. (please inform us about new tags that are not yet supported in our tests) and check the correct tags here: https://github.com/SigmaHQ/sigma-specification/blob/main/Tags_specification.md ", # ) def test_look_for_duplicate_filters(self): @@ -320,23 +236,6 @@ def test_single_named_condition_with_x_of_them(self): + "There are rules using '1/all of them' style conditions but only have one condition", ) - # Sigma validator all_of_them_condition - # def test_all_of_them_condition(self): - # faulty_detections = [] - - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # detection = self.get_rule_part(file_path=file, part_name="detection") - - # if "all of them" in detection["condition"]: - # faulty_detections.append(file) - - # self.assertEqual( - # faulty_detections, - # [], - # Fore.RED - # + "There are rules using 'all of them'. Better use e.g. 'all of selection*' instead (and use the 'selection_' prefix as search-identifier).", - # ) - def test_duplicate_detections(self): def compare_detections(detection1: dict, detection2: dict) -> bool: # If they have different log sources. They can't be the same @@ -496,105 +395,73 @@ def test_event_id_instead_of_process_creation(self): + "There are rules still using Sysmon 1 or Event ID 4688. Please migrate to the process_creation category.", ) - # sigma validator identifier_existence identifier_uniqueness - # def test_missing_id(self): + # sigma error and validator custom_attributes + # def test_optional_related(self): # faulty_rules = [] - # dict_id = {} + # valid_type = ["derived", "obsoletes", "merged", "renamed", "similar"] # for file in self.yield_next_rule_file_path(self.path_to_rules): - # id = self.get_rule_part(file_path=file, part_name="id") - # if not id: - # print(Fore.YELLOW + "Rule {} has no field 'id'.".format(file)) - # faulty_rules.append(file) - # elif len(id) != 36: - # print( - # Fore.YELLOW - # + "Rule {} has a malformed 'id' (not 36 chars).".format(file) - # ) - # faulty_rules.append(file) - # elif id.lower() in dict_id.keys(): - # print( - # Fore.YELLOW - # + "Rule {} has the same 'id' as {}. Ids have to be unique.".format( - # file, dict_id[id] + # related_lst = self.get_rule_part(file_path=file, part_name="related") + # if related_lst: + # # it exists but isn't a list + # if not isinstance(related_lst, list): + # print( + # Fore.YELLOW + # + "Rule {} has a 'related' field that isn't a list.".format( + # file + # ) # ) - # ) - # faulty_rules.append(file) + # faulty_rules.append(file) + # else: + # type_ok = True + # for ref in related_lst: + # try: + # id_str = ref["id"] + # type_str = ref["type"] + # except KeyError: + # print( + # Fore.YELLOW + # + "Rule {} has an invalid form of 'related/type' value.".format( + # file + # ) + # ) + # faulty_rules.append(file) + # continue + # if not type_str in valid_type: + # type_ok = False + # # Only add one time if many bad type in the same file + # if type_ok == False: + # print( + # Fore.YELLOW + # + "Rule {} has a 'related/type' invalid value.".format(file) + # ) + # faulty_rules.append(file) # else: - # dict_id[id.lower()] = file + # typo_list = [] + # # Add more typos + # typo_list.append( + # self.get_rule_part(file_path=file, part_name="realted") + # ) + # typo_list.append( + # self.get_rule_part(file_path=file, part_name="relatde") + # ) + # typo_list.append(self.get_rule_part(file_path=file, part_name="relted")) + # typo_list.append(self.get_rule_part(file_path=file, part_name="rlated")) + + # for i in typo_list: + # if i != None: + # print( + # Fore.YELLOW + # + "Rule {} has a typo in it's 'related' field.".format(file) + # ) + # faulty_rules.append(file) # self.assertEqual( # faulty_rules, # [], # Fore.RED - # + "There are rules with missing or malformed 'id' fields. Generate an id (e.g. here: https://www.uuidgenerator.net/version4) and add it to the reported rule(s).", + # + "There are rules with malformed optional 'related' fields. (check https://github.com/SigmaHQ/sigma-specification)", # ) - def test_optional_related(self): - faulty_rules = [] - valid_type = ["derived", "obsoletes", "merged", "renamed", "similar"] - for file in self.yield_next_rule_file_path(self.path_to_rules): - related_lst = self.get_rule_part(file_path=file, part_name="related") - if related_lst: - # it exists but isn't a list - if not isinstance(related_lst, list): - print( - Fore.YELLOW - + "Rule {} has a 'related' field that isn't a list.".format( - file - ) - ) - faulty_rules.append(file) - else: - type_ok = True - for ref in related_lst: - try: - id_str = ref["id"] - type_str = ref["type"] - except KeyError: - print( - Fore.YELLOW - + "Rule {} has an invalid form of 'related/type' value.".format( - file - ) - ) - faulty_rules.append(file) - continue - if not type_str in valid_type: - type_ok = False - # Only add one time if many bad type in the same file - if type_ok == False: - print( - Fore.YELLOW - + "Rule {} has a 'related/type' invalid value.".format(file) - ) - faulty_rules.append(file) - else: - typo_list = [] - # Add more typos - typo_list.append( - self.get_rule_part(file_path=file, part_name="realted") - ) - typo_list.append( - self.get_rule_part(file_path=file, part_name="relatde") - ) - typo_list.append(self.get_rule_part(file_path=file, part_name="relted")) - typo_list.append(self.get_rule_part(file_path=file, part_name="rlated")) - - for i in typo_list: - if i != None: - print( - Fore.YELLOW - + "Rule {} has a typo in it's 'related' field.".format(file) - ) - faulty_rules.append(file) - - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with malformed optional 'related' fields. (check https://github.com/SigmaHQ/sigma-specification)", - ) - def test_sysmon_rule_without_eventid(self): faulty_rules = [] for file in self.yield_next_rule_file_path(self.path_to_rules): @@ -619,222 +486,155 @@ def test_sysmon_rule_without_eventid(self): + "There are rules using sysmon events but with no EventID specified", ) - def test_missing_date(self): - faulty_rules = [] - for file in self.yield_next_rule_file_path(self.path_to_rules): - datefield = self.get_rule_part(file_path=file, part_name="date") - if not datefield: - print(Fore.YELLOW + "Rule {} has no field 'date'.".format(file)) - faulty_rules.append(file) - elif not isinstance(datefield, str): - print( - Fore.YELLOW - + "Rule {} has a malformed 'date' (should be YYYY/MM/DD).".format( - file - ) - ) - faulty_rules.append(file) - elif len(datefield) != 10: - print( - Fore.YELLOW - + "Rule {} has a malformed 'date' (not 10 chars, should be YYYY/MM/DD).".format( - file - ) - ) - faulty_rules.append(file) - elif datefield[4] != "/" or datefield[7] != "/": - print( - Fore.YELLOW - + "Rule {} has a malformed 'date' (should be YYYY/MM/DD).".format( - file - ) - ) - faulty_rules.append(file) - - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with missing or malformed 'date' fields. (create one, e.g. date: 2019/01/14)", - ) - - def test_missing_description(self): - faulty_rules = [] - for file in self.yield_next_rule_file_path(self.path_to_rules): - descriptionfield = self.get_rule_part( - file_path=file, part_name="description" - ) - if not descriptionfield: - print(Fore.YELLOW + "Rule {} has no field 'description'.".format(file)) - faulty_rules.append(file) - elif not isinstance(descriptionfield, str): - print( - Fore.YELLOW - + "Rule {} has a 'description' field that isn't a string.".format( - file - ) - ) - faulty_rules.append(file) - elif len(descriptionfield) < 16: - print( - Fore.YELLOW - + "Rule {} has a really short description. Please elaborate.".format( - file - ) - ) - faulty_rules.append(file) - - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with missing or malformed 'description' field. (create one, e.g. description: Detects the suspicious behaviour of process XY doing YZ)", - ) - - # sigma-cli error - # def test_optional_date_modified(self): + # sigma error validators date_existence + # def test_missing_date(self): # faulty_rules = [] # for file in self.yield_next_rule_file_path(self.path_to_rules): - # modifiedfield = self.get_rule_part(file_path=file, part_name="modified") - # if modifiedfield: - # if not isinstance(modifiedfield, str): - # print( - # Fore.YELLOW - # + "Rule {} has a malformed 'modified' (should be YYYY/MM/DD).".format( - # file - # ) - # ) - # faulty_rules.append(file) - # elif len(modifiedfield) != 10: - # print( - # Fore.YELLOW - # + "Rule {} has a malformed 'modified' (not 10 chars, should be YYYY/MM/DD).".format( - # file - # ) + # datefield = self.get_rule_part(file_path=file, part_name="date") + # if not datefield: + # print(Fore.YELLOW + "Rule {} has no field 'date'.".format(file)) + # faulty_rules.append(file) + # elif not isinstance(datefield, str): + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'date' (should be YYYY/MM/DD).".format( + # file # ) - # faulty_rules.append(file) - # elif modifiedfield[4] != "/" or modifiedfield[7] != "/": - # print( - # Fore.YELLOW - # + "Rule {} has a malformed 'modified' (should be YYYY/MM/DD).".format( - # file - # ) + # ) + # faulty_rules.append(file) + # elif len(datefield) != 10: + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'date' (not 10 chars, should be YYYY/MM/DD).".format( + # file # ) - # faulty_rules.append(file) + # ) + # faulty_rules.append(file) + # elif datefield[4] != "/" or datefield[7] != "/": + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'date' (should be YYYY/MM/DD).".format( + # file + # ) + # ) + # faulty_rules.append(file) # self.assertEqual( # faulty_rules, # [], # Fore.RED - # + "There are rules with malformed 'modified' fields. (create one, e.g. date: 2019/01/14)", + # + "There are rules with missing or malformed 'date' fields. (create one, e.g. date: 2019/01/14)", + # ) +^ + # sigma validators description_existence description_length + # def test_missing_description(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # descriptionfield = self.get_rule_part( + # file_path=file, part_name="description" + # ) + # if not descriptionfield: + # print(Fore.YELLOW + "Rule {} has no field 'description'.".format(file)) + # faulty_rules.append(file) + # elif not isinstance(descriptionfield, str): + # print( + # Fore.YELLOW + # + "Rule {} has a 'description' field that isn't a string.".format( + # file + # ) + # ) + # faulty_rules.append(file) + # elif len(descriptionfield) < 16: + # print( + # Fore.YELLOW + # + "Rule {} has a really short description. Please elaborate.".format( + # file + # ) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with missing or malformed 'description' field. (create one, e.g. description: Detects the suspicious behaviour of process XY doing YZ)", # ) - # sigma-cli error and validator status_existence status_unsupported - # def test_optional_status(self): + # sigma error validators level_existence + # def test_level(self): # faulty_rules = [] - # valid_status = ["stable", "test", "experimental", "deprecated", "unsupported"] + # valid_level = [ + # "informational", + # "low", + # "medium", + # "high", + # "critical", + # ] # for file in self.yield_next_rule_file_path(self.path_to_rules): - # status_str = self.get_rule_part(file_path=file, part_name="status") - # if status_str: - # if not status_str in valid_status: + # level_str = self.get_rule_part(file_path=file, part_name="level") + # if not level_str: + # print(Fore.YELLOW + "Rule {} has no field 'level'.".format(file)) + # faulty_rules.append(file) + # elif not level_str in valid_level: + # print( + # Fore.YELLOW + # + "Rule {} has a invalid 'level' (check wiki).".format(file) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with missing or malformed 'level' fields. (check https://github.com/SigmaHQ/sigma-specification)", + # ) + + # sigma error + # def test_optional_fields(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # fields_str = self.get_rule_part(file_path=file, part_name="fields") + # if fields_str: + # # it exists but isn't a list + # if not isinstance(fields_str, list): # print( # Fore.YELLOW - # + "Rule {} has a invalid 'status' (check wiki).".format(file) + # + "Rule {} has a 'fields' field that isn't a list.".format(file) # ) # faulty_rules.append(file) - # elif status_str == "unsupported": + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed optional 'fields' fields. (has to be a list of values even if it contains only a single value)", + # ) + + # sigma error + # def test_optional_falsepositives_listtype(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # falsepositives_str = self.get_rule_part( + # file_path=file, part_name="falsepositives" + # ) + # if falsepositives_str: + # # it exists but isn't a list + # if not isinstance(falsepositives_str, list): # print( # Fore.YELLOW - # + "Rule {} has the unsupported 'status', can not be in rules directory".format( + # + "Rule {} has a 'falsepositives' field that isn't a list.".format( # file # ) # ) # faulty_rules.append(file) - # else: - # print( - # Fore.YELLOW + "Rule {} is missing the 'status' field".format(file) - # ) - # faulty_rules.append(file) # self.assertEqual( # faulty_rules, # [], # Fore.RED - # + "There are rules with malformed or missing 'status' fields. (check https://github.com/SigmaHQ/sigma-specification)", + # + "There are rules with malformed optional 'falsepositives' fields. (has to be a list of values even if it contains only a single value)", # ) - def test_level(self): - faulty_rules = [] - valid_level = [ - "informational", - "low", - "medium", - "high", - "critical", - ] - for file in self.yield_next_rule_file_path(self.path_to_rules): - level_str = self.get_rule_part(file_path=file, part_name="level") - if not level_str: - print(Fore.YELLOW + "Rule {} has no field 'level'.".format(file)) - faulty_rules.append(file) - elif not level_str in valid_level: - print( - Fore.YELLOW - + "Rule {} has a invalid 'level' (check wiki).".format(file) - ) - faulty_rules.append(file) - - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with missing or malformed 'level' fields. (check https://github.com/SigmaHQ/sigma-specification)", - ) - - def test_optional_fields(self): - faulty_rules = [] - for file in self.yield_next_rule_file_path(self.path_to_rules): - fields_str = self.get_rule_part(file_path=file, part_name="fields") - if fields_str: - # it exists but isn't a list - if not isinstance(fields_str, list): - print( - Fore.YELLOW - + "Rule {} has a 'fields' field that isn't a list.".format(file) - ) - faulty_rules.append(file) - - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with malformed optional 'fields' fields. (has to be a list of values even if it contains only a single value)", - ) - - def test_optional_falsepositives_listtype(self): - faulty_rules = [] - for file in self.yield_next_rule_file_path(self.path_to_rules): - falsepositives_str = self.get_rule_part( - file_path=file, part_name="falsepositives" - ) - if falsepositives_str: - # it exists but isn't a list - if not isinstance(falsepositives_str, list): - print( - Fore.YELLOW - + "Rule {} has a 'falsepositives' field that isn't a list.".format( - file - ) - ) - faulty_rules.append(file) - - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with malformed optional 'falsepositives' fields. (has to be a list of values even if it contains only a single value)", - ) - def test_optional_falsepositives_capital(self): faulty_rules = [] for file in self.yield_next_rule_file_path(self.path_to_rules): @@ -897,28 +697,29 @@ def test_optional_falsepositives_blocked_content(self): + "There are rules with invalid false positive definitions (e.g. Pentest, None or common typos)", ) - # Upgrade Detection Rule License 1.1 - def test_optional_author(self): - faulty_rules = [] - for file in self.yield_next_rule_file_path(self.path_to_rules): - author_str = self.get_rule_part(file_path=file, part_name="author") - if author_str: - # it exists but isn't a string - if not isinstance(author_str, str): - print( - Fore.YELLOW - + "Rule {} has a 'author' field that isn't a string.".format( - file - ) - ) - faulty_rules.append(file) + # sigma error + # # Upgrade Detection Rule License 1.1 + # def test_optional_author(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # author_str = self.get_rule_part(file_path=file, part_name="author") + # if author_str: + # # it exists but isn't a string + # if not isinstance(author_str, str): + # print( + # Fore.YELLOW + # + "Rule {} has a 'author' field that isn't a string.".format( + # file + # ) + # ) + # faulty_rules.append(file) - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with malformed 'author' fields. (has to be a string even if it contains many author)", - ) + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed 'author' fields. (has to be a string even if it contains many author)", + # ) def test_optional_license(self): faulty_rules = [] @@ -941,60 +742,6 @@ def test_optional_license(self): + "There are rules with malformed 'license' fields. (has to be a string )", ) - # sigma-cli validators tlptag - # def test_optional_tlp(self): - # faulty_rules = [] - # valid_tlp = [ - # "WHITE", - # "GREEN", - # "AMBER", - # "RED", - # ] - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # tlp_str = self.get_rule_part(file_path=file, part_name="tlp") - # if tlp_str: - # # it exists but isn't a string - # if not isinstance(tlp_str, str): - # print( - # Fore.YELLOW - # + "Rule {} has a 'tlp' field that isn't a string.".format(file) - # ) - # faulty_rules.append(file) - # elif not tlp_str.upper() in valid_tlp: - # print( - # Fore.YELLOW - # + "Rule {} has a 'tlp' field with not valid value.".format(file) - # ) - # faulty_rules.append(file) - - # self.assertEqual( - # faulty_rules, - # [], - # Fore.RED - # + "There are rules with malformed optional 'tlp' fields. (https://www.cisa.gov/tlp)", - # ) - - # Not in the specification - # def test_optional_target(self): - # faulty_rules = [] - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # target = self.get_rule_part(file_path=file, part_name="target") - # if target: - # # it exists but isn't a list - # if not isinstance(target, list): - # print( - # Fore.YELLOW - # + "Rule {} has a 'target' field that isn't a list.".format(file) - # ) - # faulty_rules.append(file) - - # self.assertEqual( - # faulty_rules, - # [], - # Fore.RED - # + "There are rules with malformed 'target' fields. (has to be a list of values even if it contains only a single value)", - # ) - def test_references(self): faulty_rules = [] for file in self.yield_next_rule_file_path(self.path_to_rules): @@ -1053,20 +800,21 @@ def test_references_in_description(self): + "There are rules with malformed 'description' fields. (links and external references have to be in a seperate field named 'references'. see specification https://github.com/SigmaHQ/sigma-specification)", ) - def test_references_plural(self): - faulty_rules = [] - for file in self.yield_next_rule_file_path(self.path_to_rules): - reference = self.get_rule_part(file_path=file, part_name="reference") - if reference: - # it exists but in singular form - faulty_rules.append(file) + # sigma validator custom_attributes + # def test_references_plural(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # reference = self.get_rule_part(file_path=file, part_name="reference") + # if reference: + # # it exists but in singular form + # faulty_rules.append(file) - self.assertEqual( - faulty_rules, - [], - Fore.RED - + "There are rules with malformed 'references' fields. (has to be 'references' in plural form, not singular)", - ) + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed 'references' fields. (has to be 'references' in plural form, not singular)", + # ) def test_file_names(self): faulty_rules = [] @@ -1362,80 +1110,20 @@ def test_title_in_first_line(self): + "There are rules without the 'title' attribute in their first line.", ) - # sigma validators duplicate_title - # def test_duplicate_titles(self): - # # This test ensure that every rule has a unique title - # faulty_rules = [] - # titles_dict = {} - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # title = ( - # self.get_rule_part(file_path=file, part_name="title").lower().rstrip() - # ) - # duplicate = False - # for rule, title_ in titles_dict.items(): - # if title == title_: - # print( - # Fore.RED - # + "Rule {} has an already used title in {}.".format(file, rule) - # ) - # duplicate = True - # faulty_rules.append(file) - # continue - # if not duplicate: - # titles_dict[file] = title - - # self.assertEqual( - # faulty_rules, - # [], - # Fore.RED - # + "There are rules that share the same 'title'. Please check: https://github.com/SigmaHQ/sigma/wiki/Rule-Creation-Guide#title", - # ) - - # def test_invalid_logsource_attributes(self): - # faulty_rules = [] - # valid_logsource = [ - # 'category', - # 'product', - # 'service', - # 'definition', - # ] - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # logsource = self.get_rule_part( - # file_path=file, part_name="logsource") - # if not logsource: - # print(Fore.RED + "Rule {} has no 'logsource'.".format(file)) - # faulty_rules.append(file) - # continue - # valid = True - # for key in logsource: - # if key.lower() not in valid_logsource: - # print( - # Fore.RED + "Rule {} has a logsource with an invalid field ({})".format(file, key)) - # valid = False - # elif not isinstance(logsource[key], str): - # print( - # Fore.RED + "Rule {} has a logsource with an invalid field type ({})".format(file, key)) - # valid = False - # if not valid: - # faulty_rules.append(file) - - # self.assertEqual(faulty_rules, [], Fore.RED + - # "There are rules with non-conform 'logsource' fields. Please check: https://github.com/SigmaHQ/sigma/wiki/Rule-Creation-Guide#log-source") - - def test_selection_list_one_value(self): - def treat_list(file, values, valid_, selection_name): - # rule with only list of Keywords term - if len(values) == 1 and not isinstance(values[0], str): - print( - Fore.RED - + "Rule {} has the selection ({}) with a list of only 1 element in detection".format( - file, key - ) - ) - valid_ = False - elif isinstance(values[0], dict): - valid_ = treat_dict(file, values, valid_, selection_name) - return valid_ + def test_selection_list_one_value(self): + def treat_list(file, values, valid_, selection_name): + # rule with only list of Keywords term + if len(values) == 1 and not isinstance(values[0], str): + print( + Fore.RED + + "Rule {} has the selection ({}) with a list of only 1 element in detection".format( + file, key + ) + ) + valid_ = False + elif isinstance(values[0], dict): + valid_ = treat_dict(file, values, valid_, selection_name) + return valid_ def treat_dict(file, values, valid_, selection_name): if isinstance(values, list): @@ -1580,84 +1268,6 @@ def test_unused_selection(self): faulty_rules, [], Fore.RED + "There are rules with unused selections" ) - # def test_field_name_typo(self): - # # add "OriginalFilename" after Aurora switched to SourceFilename - # # add "ProviderName" after special case powershell classic is resolved - # faulty_rules = [] - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # # typos is a list of tuples where each tuple contains ("The typo", "The correct version") - # typos = [("ServiceFilename", "ServiceFileName"), ("TargetFileName", "TargetFilename"), ("SourceFileName", "OriginalFileName"), ("Commandline", "CommandLine"), ("Targetobject", "TargetObject"), ("OriginalName", "OriginalFileName"), ("ImageFileName", "OriginalFileName"), ("details", "Details")] - # # Some fields exists in certain log sources in different forms than other log sources. We need to handle these as special cases - # # We check first the logsource to handle special cases - # logsource = self.get_rule_part(file_path=file, part_name="logsource").values() - # # add more typos in specific logsources below - # if "windefend" in logsource: - # typos += [("New_Value", "NewValue"), ("Old_Value", "OldValue"), ('Source_Name', 'SourceName'), ("Newvalue", "NewValue"), ("Oldvalue", "OldValue"), ('Sourcename', 'SourceName')] - # elif "registry_set" in logsource or "registry_add" in logsource or "registry_event" in logsource: - # typos += [("Targetobject", "TargetObject"), ("Eventtype", "EventType"), ("Newname", "NewName")] - # elif "process_creation" in logsource: - # typos += [("Parentimage", "ParentImage"), ("Integritylevel", "IntegrityLevel"), ("IntegritiLevel", "IntegrityLevel")] - # elif "file_access" in logsource: - # del(typos[typos.index(("TargetFileName", "TargetFilename"))]) # We remove the entry to "TargetFileName" to avoid confusion - # typos += [("TargetFileName", "FileName"), ("TargetFilename","FileName")] - # detection = self.get_rule_part(file_path=file, part_name="detection") - # if detection: - # for search_identifier in detection: - # if isinstance(detection[search_identifier], dict): - # for field in detection[search_identifier]: - # for typo in typos: - # if typo[0] in field: - # print(Fore.RED + "Rule {} has a common typo ({}) which should be ({}) in selection ({}/{})".format(file, typo[0], typo[1], search_identifier, field)) - # faulty_rules.append(file) - - # self.assertEqual(faulty_rules, [], Fore.RED + "There are rules with common typos in field names.") - - # Sigma error SigmaModifierError - # def test_unknown_value_modifier(self): - # known_modifiers = [ - # "contains", - # "startswith", - # "endswith", - # "all", - # "base64offset", - # "base64", - # "utf16le", - # "utf16be", - # "wide", - # "utf16", - # "windash", - # "re", - # "cidr", - # ] - # faulty_rules = [] - # for file in self.yield_next_rule_file_path(self.path_to_rules): - # detection = self.get_rule_part(file_path=file, part_name="detection") - # if detection: - # for search_identifier in detection: - # if isinstance(detection[search_identifier], dict): - # for field in detection[search_identifier]: - # if "|" in field: - # for current_modifier in field.split("|")[1:]: - # found = False - # for target_modifier in known_modifiers: - # if current_modifier == target_modifier: - # found = True - # if not found: - # print( - # Fore.RED - # + "Rule {} uses an unknown field modifier ({}/{})".format( - # file, search_identifier, field - # ) - # ) - # faulty_rules.append(file) - - # self.assertEqual( - # faulty_rules, - # [], - # Fore.RED - # + "There are rules with unknown value modifiers. Most often it is just a typo.", - # ) - def test_all_value_modifier_single_item(self): faulty_rules = [] for file in self.yield_next_rule_file_path(self.path_to_rules): @@ -1919,6 +1529,405 @@ def check_item_for_bad_escapes(item): faulty_rules, [], Fore.RED + "There are rules using illegal re-escapes" ) + + # def test_confirm_extension_is_yml(self): + # files_with_incorrect_extensions = [] + + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # file_name_and_extension = os.path.splitext(file) + # if len(file_name_and_extension) == 2: + # extension = file_name_and_extension[1] + # if extension != ".yml": + # files_with_incorrect_extensions.append(file) + + # self.assertEqual(files_with_incorrect_extensions, [], Fore.RED + + # "There are rule files with extensions other than .yml") + # sigma-cli validators attacktag + # def test_confirm_correct_mitre_tags(self): + # files_with_incorrect_mitre_tags = [] + + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # tags = self.get_rule_part(file_path=file, part_name="tags") + # if tags: + # for tag in tags: + # if tag.startswith("attack.") and tag not in self.MITRE_ALL: + # print( + # Fore.RED + # + "Rule {} has the following incorrect MITRE tag {}".format( + # file, tag + # ) + # ) + # files_with_incorrect_mitre_tags.append(file) + + # self.assertEqual( + # files_with_incorrect_mitre_tags, + # [], + # Fore.RED + # + "There are rules with incorrect/unknown MITRE Tags. (please inform us about new tags that are not yet supported in our tests) and check the correct tags here: https://attack.mitre.org/ ", + # ) + + # sigma validators duplicate_tag + # def test_duplicate_tags(self): + # files_with_incorrect_mitre_tags = [] + + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # tags = self.get_rule_part(file_path=file, part_name="tags") + # if tags: + # known_tags = [] + # for tag in tags: + # if tag in known_tags: + # print( + # Fore.RED + # + "Rule {} has the duplicate tag {}".format(file, tag) + # ) + # files_with_incorrect_mitre_tags.append(file) + # else: + # known_tags.append(tag) + + # self.assertEqual( + # files_with_incorrect_mitre_tags, + # [], + # Fore.RED + "There are rules with duplicate tags", + # ) + + # sigma validators duplicate_references + # def test_duplicate_references(self): + # files_with_duplicate_references = [] + + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # references = self.get_rule_part(file_path=file, part_name="references") + # if references: + # known_references = [] + # for reference in references: + # if reference in known_references: + # print( + # Fore.RED + # + "Rule {} has the duplicate reference {}".format( + # file, reference + # ) + # ) + # files_with_duplicate_references.append(file) + # else: + # known_references.append(reference) + + # self.assertEqual( + # files_with_duplicate_references, + # [], + # Fore.RED + "There are rules with duplicate references", + # ) + + # sigma validator identifier_existence identifier_uniqueness + # def test_missing_id(self): + # faulty_rules = [] + # dict_id = {} + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # id = self.get_rule_part(file_path=file, part_name="id") + # if not id: + # print(Fore.YELLOW + "Rule {} has no field 'id'.".format(file)) + # faulty_rules.append(file) + # elif len(id) != 36: + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'id' (not 36 chars).".format(file) + # ) + # faulty_rules.append(file) + # elif id.lower() in dict_id.keys(): + # print( + # Fore.YELLOW + # + "Rule {} has the same 'id' as {}. Ids have to be unique.".format( + # file, dict_id[id] + # ) + # ) + # faulty_rules.append(file) + # else: + # dict_id[id.lower()] = file + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with missing or malformed 'id' fields. Generate an id (e.g. here: https://www.uuidgenerator.net/version4) and add it to the reported rule(s).", + # ) + # sigma-cli error + # def test_optional_date_modified(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # modifiedfield = self.get_rule_part(file_path=file, part_name="modified") + # if modifiedfield: + # if not isinstance(modifiedfield, str): + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'modified' (should be YYYY/MM/DD).".format( + # file + # ) + # ) + # faulty_rules.append(file) + # elif len(modifiedfield) != 10: + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'modified' (not 10 chars, should be YYYY/MM/DD).".format( + # file + # ) + # ) + # faulty_rules.append(file) + # elif modifiedfield[4] != "/" or modifiedfield[7] != "/": + # print( + # Fore.YELLOW + # + "Rule {} has a malformed 'modified' (should be YYYY/MM/DD).".format( + # file + # ) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed 'modified' fields. (create one, e.g. date: 2019/01/14)", + # ) + + # sigma-cli error and validator status_existence status_unsupported + # def test_optional_status(self): + # faulty_rules = [] + # valid_status = ["stable", "test", "experimental", "deprecated", "unsupported"] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # status_str = self.get_rule_part(file_path=file, part_name="status") + # if status_str: + # if not status_str in valid_status: + # print( + # Fore.YELLOW + # + "Rule {} has a invalid 'status' (check wiki).".format(file) + # ) + # faulty_rules.append(file) + # elif status_str == "unsupported": + # print( + # Fore.YELLOW + # + "Rule {} has the unsupported 'status', can not be in rules directory".format( + # file + # ) + # ) + # faulty_rules.append(file) + # else: + # print( + # Fore.YELLOW + "Rule {} is missing the 'status' field".format(file) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed or missing 'status' fields. (check https://github.com/SigmaHQ/sigma-specification)", + # ) + + # Sigma validator all_of_them_condition + # def test_all_of_them_condition(self): + # faulty_detections = [] + + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # detection = self.get_rule_part(file_path=file, part_name="detection") + + # if "all of them" in detection["condition"]: + # faulty_detections.append(file) + + # self.assertEqual( + # faulty_detections, + # [], + # Fore.RED + # + "There are rules using 'all of them'. Better use e.g. 'all of selection*' instead (and use the 'selection_' prefix as search-identifier).", + # ) + + # sigma-cli validators tlptag + # def test_optional_tlp(self): + # faulty_rules = [] + # valid_tlp = [ + # "WHITE", + # "GREEN", + # "AMBER", + # "RED", + # ] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # tlp_str = self.get_rule_part(file_path=file, part_name="tlp") + # if tlp_str: + # # it exists but isn't a string + # if not isinstance(tlp_str, str): + # print( + # Fore.YELLOW + # + "Rule {} has a 'tlp' field that isn't a string.".format(file) + # ) + # faulty_rules.append(file) + # elif not tlp_str.upper() in valid_tlp: + # print( + # Fore.YELLOW + # + "Rule {} has a 'tlp' field with not valid value.".format(file) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed optional 'tlp' fields. (https://www.cisa.gov/tlp)", + # ) + + # Not in the specification + # def test_optional_target(self): + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # target = self.get_rule_part(file_path=file, part_name="target") + # if target: + # # it exists but isn't a list + # if not isinstance(target, list): + # print( + # Fore.YELLOW + # + "Rule {} has a 'target' field that isn't a list.".format(file) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with malformed 'target' fields. (has to be a list of values even if it contains only a single value)", + # ) + # sigma validators duplicate_title + # def test_duplicate_titles(self): + # # This test ensure that every rule has a unique title + # faulty_rules = [] + # titles_dict = {} + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # title = ( + # self.get_rule_part(file_path=file, part_name="title").lower().rstrip() + # ) + # duplicate = False + # for rule, title_ in titles_dict.items(): + # if title == title_: + # print( + # Fore.RED + # + "Rule {} has an already used title in {}.".format(file, rule) + # ) + # duplicate = True + # faulty_rules.append(file) + # continue + # if not duplicate: + # titles_dict[file] = title + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules that share the same 'title'. Please check: https://github.com/SigmaHQ/sigma/wiki/Rule-Creation-Guide#title", + # ) + + # def test_invalid_logsource_attributes(self): + # faulty_rules = [] + # valid_logsource = [ + # 'category', + # 'product', + # 'service', + # 'definition', + # ] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # logsource = self.get_rule_part( + # file_path=file, part_name="logsource") + # if not logsource: + # print(Fore.RED + "Rule {} has no 'logsource'.".format(file)) + # faulty_rules.append(file) + # continue + # valid = True + # for key in logsource: + # if key.lower() not in valid_logsource: + # print( + # Fore.RED + "Rule {} has a logsource with an invalid field ({})".format(file, key)) + # valid = False + # elif not isinstance(logsource[key], str): + # print( + # Fore.RED + "Rule {} has a logsource with an invalid field type ({})".format(file, key)) + # valid = False + # if not valid: + # faulty_rules.append(file) + + # self.assertEqual(faulty_rules, [], Fore.RED + + # "There are rules with non-conform 'logsource' fields. Please check: https://github.com/SigmaHQ/sigma/wiki/Rule-Creation-Guide#log-source") + # def test_field_name_typo(self): + # # add "OriginalFilename" after Aurora switched to SourceFilename + # # add "ProviderName" after special case powershell classic is resolved + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # # typos is a list of tuples where each tuple contains ("The typo", "The correct version") + # typos = [("ServiceFilename", "ServiceFileName"), ("TargetFileName", "TargetFilename"), ("SourceFileName", "OriginalFileName"), ("Commandline", "CommandLine"), ("Targetobject", "TargetObject"), ("OriginalName", "OriginalFileName"), ("ImageFileName", "OriginalFileName"), ("details", "Details")] + # # Some fields exists in certain log sources in different forms than other log sources. We need to handle these as special cases + # # We check first the logsource to handle special cases + # logsource = self.get_rule_part(file_path=file, part_name="logsource").values() + # # add more typos in specific logsources below + # if "windefend" in logsource: + # typos += [("New_Value", "NewValue"), ("Old_Value", "OldValue"), ('Source_Name', 'SourceName'), ("Newvalue", "NewValue"), ("Oldvalue", "OldValue"), ('Sourcename', 'SourceName')] + # elif "registry_set" in logsource or "registry_add" in logsource or "registry_event" in logsource: + # typos += [("Targetobject", "TargetObject"), ("Eventtype", "EventType"), ("Newname", "NewName")] + # elif "process_creation" in logsource: + # typos += [("Parentimage", "ParentImage"), ("Integritylevel", "IntegrityLevel"), ("IntegritiLevel", "IntegrityLevel")] + # elif "file_access" in logsource: + # del(typos[typos.index(("TargetFileName", "TargetFilename"))]) # We remove the entry to "TargetFileName" to avoid confusion + # typos += [("TargetFileName", "FileName"), ("TargetFilename","FileName")] + # detection = self.get_rule_part(file_path=file, part_name="detection") + # if detection: + # for search_identifier in detection: + # if isinstance(detection[search_identifier], dict): + # for field in detection[search_identifier]: + # for typo in typos: + # if typo[0] in field: + # print(Fore.RED + "Rule {} has a common typo ({}) which should be ({}) in selection ({}/{})".format(file, typo[0], typo[1], search_identifier, field)) + # faulty_rules.append(file) + + # self.assertEqual(faulty_rules, [], Fore.RED + "There are rules with common typos in field names.") + + # Sigma error SigmaModifierError + # def test_unknown_value_modifier(self): + # known_modifiers = [ + # "contains", + # "startswith", + # "endswith", + # "all", + # "base64offset", + # "base64", + # "utf16le", + # "utf16be", + # "wide", + # "utf16", + # "windash", + # "re", + # "cidr", + # ] + # faulty_rules = [] + # for file in self.yield_next_rule_file_path(self.path_to_rules): + # detection = self.get_rule_part(file_path=file, part_name="detection") + # if detection: + # for search_identifier in detection: + # if isinstance(detection[search_identifier], dict): + # for field in detection[search_identifier]: + # if "|" in field: + # for current_modifier in field.split("|")[1:]: + # found = False + # for target_modifier in known_modifiers: + # if current_modifier == target_modifier: + # found = True + # if not found: + # print( + # Fore.RED + # + "Rule {} uses an unknown field modifier ({}/{})".format( + # file, search_identifier, field + # ) + # ) + # faulty_rules.append(file) + + # self.assertEqual( + # faulty_rules, + # [], + # Fore.RED + # + "There are rules with unknown value modifiers. Most often it is just a typo.", + # ) + + # sigma-cli validators attacktag # def get_mitre_data(): # """ From 2c95741c9d95b7594622af27d80cbca56dca6571 Mon Sep 17 00:00:00 2001 From: frack113 <62423083+frack113@users.noreply.github.com> Date: Sat, 25 Nov 2023 08:32:42 +0100 Subject: [PATCH 6/8] =?UTF-8?q?docs:=20=F0=9F=93=9A=20Add=20Summiting=20th?= =?UTF-8?q?e=20Pyramid=20tags?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account_management/win_security_access_token_abuse.yml | 1 + .../pipe_created_hktl_cobaltstrike_susp_pipe_patterns.yml | 1 + .../powershell/powershell_script/posh_ps_get_acl_service.yml | 1 + .../proc_creation_win_hktl_cobaltstrike_bloopers_cmd.yml | 1 + .../process_creation/proc_creation_win_pua_adfind_susp_usage.yml | 1 + .../process_creation/proc_creation_win_schtasks_creation.yml | 1 + 6 files changed, 6 insertions(+) diff --git a/rules/windows/builtin/security/account_management/win_security_access_token_abuse.yml b/rules/windows/builtin/security/account_management/win_security_access_token_abuse.yml index 588b3189f38..b0f532c5294 100644 --- a/rules/windows/builtin/security/account_management/win_security_access_token_abuse.yml +++ b/rules/windows/builtin/security/account_management/win_security_access_token_abuse.yml @@ -12,6 +12,7 @@ tags: - attack.defense_evasion - attack.privilege_escalation - attack.t1134.001 + - stp.4u logsource: product: windows service: security diff --git a/rules/windows/pipe_created/pipe_created_hktl_cobaltstrike_susp_pipe_patterns.yml b/rules/windows/pipe_created/pipe_created_hktl_cobaltstrike_susp_pipe_patterns.yml index 7a8334821b7..b881a6e61fc 100644 --- a/rules/windows/pipe_created/pipe_created_hktl_cobaltstrike_susp_pipe_patterns.yml +++ b/rules/windows/pipe_created/pipe_created_hktl_cobaltstrike_susp_pipe_patterns.yml @@ -17,6 +17,7 @@ tags: - attack.defense_evasion - attack.privilege_escalation - attack.t1055 + - stp.1k logsource: product: windows category: pipe_created diff --git a/rules/windows/powershell/powershell_script/posh_ps_get_acl_service.yml b/rules/windows/powershell/powershell_script/posh_ps_get_acl_service.yml index 66a861bb0fd..3a916a38434 100644 --- a/rules/windows/powershell/powershell_script/posh_ps_get_acl_service.yml +++ b/rules/windows/powershell/powershell_script/posh_ps_get_acl_service.yml @@ -13,6 +13,7 @@ date: 2021/12/30 tags: - attack.persistence - attack.t1574.011 + - stp.2a logsource: product: windows category: ps_script diff --git a/rules/windows/process_creation/proc_creation_win_hktl_cobaltstrike_bloopers_cmd.yml b/rules/windows/process_creation/proc_creation_win_hktl_cobaltstrike_bloopers_cmd.yml index 93d713b1d99..35ac828df1d 100644 --- a/rules/windows/process_creation/proc_creation_win_hktl_cobaltstrike_bloopers_cmd.yml +++ b/rules/windows/process_creation/proc_creation_win_hktl_cobaltstrike_bloopers_cmd.yml @@ -15,6 +15,7 @@ modified: 2023/01/30 tags: - attack.execution - attack.t1059.003 + - stp.1u logsource: category: process_creation product: windows diff --git a/rules/windows/process_creation/proc_creation_win_pua_adfind_susp_usage.yml b/rules/windows/process_creation/proc_creation_win_pua_adfind_susp_usage.yml index 606e4b6806a..0464748680d 100644 --- a/rules/windows/process_creation/proc_creation_win_pua_adfind_susp_usage.yml +++ b/rules/windows/process_creation/proc_creation_win_pua_adfind_susp_usage.yml @@ -24,6 +24,7 @@ tags: - attack.t1087.002 - attack.t1482 - attack.t1069.002 + - stp.1u logsource: category: process_creation product: windows diff --git a/rules/windows/process_creation/proc_creation_win_schtasks_creation.yml b/rules/windows/process_creation/proc_creation_win_schtasks_creation.yml index 3e10b8fcae5..14c91908c3e 100644 --- a/rules/windows/process_creation/proc_creation_win_schtasks_creation.yml +++ b/rules/windows/process_creation/proc_creation_win_schtasks_creation.yml @@ -12,6 +12,7 @@ tags: - attack.t1053.005 - attack.s0111 - car.2013-08-001 + - stp.1u logsource: category: process_creation product: windows From f8e3a5aea43b315dfe5960d4aa9d0e727c20771b Mon Sep 17 00:00:00 2001 From: frack113 <62423083+frack113@users.noreply.github.com> Date: Sat, 25 Nov 2023 08:38:05 +0100 Subject: [PATCH 7/8] =?UTF-8?q?refactor:=20=F0=9F=94=A8=20Remove=20useless?= =?UTF-8?q?=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_rules.py b/tests/test_rules.py index 2962d747834..cec4df221b6 100755 --- a/tests/test_rules.py +++ b/tests/test_rules.py @@ -525,7 +525,7 @@ def test_sysmon_rule_without_eventid(self): # Fore.RED # + "There are rules with missing or malformed 'date' fields. (create one, e.g. date: 2019/01/14)", # ) -^ + # sigma validators description_existence description_length # def test_missing_description(self): # faulty_rules = [] From ccc20410cd8c3f860f49d49b50da250ce9de41ef Mon Sep 17 00:00:00 2001 From: Nasreddine Bencherchali <8741929+nasbench@users.noreply.github.com> Date: Mon, 27 Nov 2023 01:10:19 +0100 Subject: [PATCH 8/8] Update sigma_cli_conf.yml --- tests/sigma_cli_conf.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/sigma_cli_conf.yml b/tests/sigma_cli_conf.yml index 33f145f4055..4853caac3b8 100644 --- a/tests/sigma_cli_conf.yml +++ b/tests/sigma_cli_conf.yml @@ -2,8 +2,8 @@ validators: - all_of_them_condition - attacktag - cartag - - cvetag - custom_attributes + - cvetag - date_existence - description_existence - description_length