From 2f26d9917ae85dce4ebe9842ae68dd8a9cb89db7 Mon Sep 17 00:00:00 2001 From: Hare Sudhan Date: Fri, 17 Nov 2023 12:28:12 -0500 Subject: [PATCH] Duplicate guid fix (#2609) * duplicate guid fix * duplicate guid fix --- atomics/T1055/T1055.yaml | 1 - bin/validate/validate.py | 39 ++++++++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/atomics/T1055/T1055.yaml b/atomics/T1055/T1055.yaml index 6de0d7d9..fa94cb2c 100644 --- a/atomics/T1055/T1055.yaml +++ b/atomics/T1055/T1055.yaml @@ -130,7 +130,6 @@ atomic_tests: name: powershell elevation_required: false - name: Read-Write-Execute process Injection - auto_generated_guid: 49543237-25db-497b-90df-d0a0a6e8fe2c description: | This test exploited the vulnerability in legitimate PE formats where sections have RWX permission and enough space for shellcode. The RWX injection avoided the use of VirtualAlloc, WriteVirtualMemory, and ProtectVirtualMemory, thus evading detection mechanisms diff --git a/bin/validate/validate.py b/bin/validate/validate.py index 927f9e22..c1f3c447 100644 --- a/bin/validate/validate.py +++ b/bin/validate/validate.py @@ -23,6 +23,19 @@ class InvalidFileName(BaseError): return f"Invalid filename. Rename file from .yml to .yaml" +class ReusedGuid(BaseError): + def __init__(self, path, guid, test_number): + super().__init__(path) + self.guid = guid + self.test_number = test_number + + def __str__(self): + return ( + f"GUID {self.guid} reused for test {self.test_number}. GUIDs are auto generated." + f"You can remove atomic_tests[{self.test_number}].auto_generated_guid" + ) + + class UnusedArgument(BaseError): def __init__(self, path, argument, test_number): super().__init__(path) @@ -37,8 +50,13 @@ class Validator: errors = defaultdict(list) def __init__(self): - with open(f"{os.path.dirname(os.path.abspath(__file__))}/atomic-red-team.schema.yaml", "r") as f: + schema_path = f"{os.path.dirname(os.path.abspath(__file__))}/atomic-red-team.schema.yaml" + used_guids_path = "./atomics/used_guids.txt" + with open(used_guids_path, "r") as f: + self.used_guids = [x.strip() for x in f.readlines()] + with open(schema_path, "r") as f: self.schema = yaml.safe_load(f) + self.guids = [] def validate(self, obj: DirEntry): if obj.is_file(): @@ -50,14 +68,21 @@ class Validator: def validate_file(self, file: DirEntry): """Performs file validation""" self.validate_yaml_extension(file) - self.validate_input_args(file) + self.validate_atomic(file) self.validate_json_schema(file) - def validate_input_args(self, file: DirEntry): + def validate_atomic(self, file: DirEntry): """Validates whether the defined input args are used.""" with open(file.path, "r") as f: atomic = yaml.safe_load(f) for index, t in enumerate(atomic["atomic_tests"]): + if t.get("auto_generated_guid"): + if t["auto_generated_guid"] not in self.guids: + self.guids.append(t["auto_generated_guid"]) + else: + self.errors[file.path].append( + ReusedGuid(file.path, t["auto_generated_guid"], index + 1) + ) if args := t.get("input_arguments"): for k in list(args.keys()): variable = f"#{{{k}}}" @@ -65,7 +90,11 @@ class Validator: deps = t.get("dependencies", []) if executor: - commands = [executor.get("command"), executor.get("cleanup_command"), executor.get("steps")] + commands = [ + executor.get("command"), + executor.get("cleanup_command"), + executor.get("steps"), + ] if deps: commands += [d.get("get_prereq_command") for d in deps] commands += [d.get("prereq_command") for d in deps] @@ -96,7 +125,7 @@ class Validator: self.validate_directory_path(directory) def validate_directory_path(self, directory: DirEntry): - """Validated whether the directory is a allowed directory name (`src` or `bin`) """ + """Validated whether the directory is a allowed directory name (`src` or `bin`)""" if directory.name not in ["src", "bin"]: self.errors[directory.path].append(InvalidPath(directory.path))