From 23d78e9c2c9c21950905c49b7d1035fa2b961bdd Mon Sep 17 00:00:00 2001 From: Hare Sudhan Date: Fri, 5 Sep 2025 06:51:14 -0400 Subject: [PATCH] Updating Windows reviewers list (#3165) --- atomic_red_team/labels.py | 2 +- atomic_red_team/models.py | 28 +++++++++++++++---- .../invalid_dependency_executor_name.yaml | 20 +++++++++++++ atomic_red_team/test_data/reused_guid.yaml | 1 - atomic_red_team/tests/test_models.py | 1 + atomics/T1529/T1529.yaml | 3 +- 6 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 atomic_red_team/test_data/invalid_dependency_executor_name.yaml diff --git a/atomic_red_team/labels.py b/atomic_red_team/labels.py index a983b1ec..60f36944 100644 --- a/atomic_red_team/labels.py +++ b/atomic_red_team/labels.py @@ -47,7 +47,7 @@ class GithubAPI: } maintainers = { - "windows": ["clr2of8", "MHaggis"], + "windows": ["clr2of8", "MHaggis", "cyberbuff"], "linux": ["josehelps", "cyberbuff"], "macos": ["josehelps", "cyberbuff"], "containers": ["patel-bhavin"], diff --git a/atomic_red_team/models.py b/atomic_red_team/models.py index 08cf1f5d..2542dba6 100644 --- a/atomic_red_team/models.py +++ b/atomic_red_team/models.py @@ -155,7 +155,7 @@ class Atomic(BaseModel): executor: Union[ManualExecutor, CommandExecutor] = Field(..., discriminator="name") dependencies: Optional[List[Dependency]] = [] input_arguments: Dict[constr(min_length=2, pattern=r"^[\w_-]+$"), Argument] = {} - dependency_executor_name: ExecutorType = "manual" + dependency_executor_name: Optional[ExecutorType] = None auto_generated_guid: Optional[UUID] = None @classmethod @@ -173,11 +173,10 @@ class Atomic(BaseModel): @field_validator("dependency_executor_name", mode="before") # noqa @classmethod def validate_dep_executor(cls, v, info: ValidationInfo): - if v is None: + if v is not None and info.data.get("dependencies") == []: raise PydanticCustomError( - "empty_dependency_executor_name", - "'dependency_executor_name' shouldn't be empty. Provide a valid value ['manual','powershell', 'sh', " - "'bash', 'command_prompt'] or remove the key from YAML", + "invalid_dependency_executor_name", + "'dependency_executor_name' is not needed if there are no dependencies. Remove the key from YAML", {"loc": ["dependency_executor_name"], "input": None}, ) return v @@ -241,6 +240,25 @@ class Technique(BaseModel): display_name: str = Field(..., min_length=5) atomic_tests: List[Atomic] = Field(min_length=1) + @model_validator(mode="before") + @classmethod + def validate_dependency_executor_names(cls, data): + """Check if dependency_executor_name keys are present with empty/None values in atomic tests""" + if isinstance(data, dict) and "atomic_tests" in data: + atomic_tests = data.get("atomic_tests", []) + for i, test in enumerate(atomic_tests): + if isinstance(test, dict) and "dependency_executor_name" in test: + value = test.get("dependency_executor_name") + # If the key exists but value is None or empty string, that's an error + if value is None or value == "": + raise PydanticCustomError( + "empty_dependency_executor_name", + "'dependency_executor_name' shouldn't be empty. Provide a valid value ['manual','powershell', 'sh', " + "'bash', 'command_prompt'] or remove the key from YAML", + {"loc": ["atomic_tests", i, "dependency_executor_name"], "input": value}, + ) + return data + def model_post_init(self, __context) -> None: for index in range(len(self.atomic_tests)): test_number = f"{self.attack_technique}-{index + 1}" diff --git a/atomic_red_team/test_data/invalid_dependency_executor_name.yaml b/atomic_red_team/test_data/invalid_dependency_executor_name.yaml new file mode 100644 index 00000000..3f5fdec0 --- /dev/null +++ b/atomic_red_team/test_data/invalid_dependency_executor_name.yaml @@ -0,0 +1,20 @@ +attack_technique: T1003 +display_name: OS Credential Dumping +atomic_tests: +- name: Gsecdump + auto_generated_guid: + description: | + Dump credentials from memory using Gsecdump. + supported_platforms: + - windows + input_arguments: + gsecdump_exe: + description: Path to the Gsecdump executable + type: path + default: PathToAtomicsFolder\..\ExternalPayloads\gsecdump.exe + executor: + command: | + "#{gsecdump_exe}" -a + name: command_prompt + elevation_required: true + dependency_executor_name: "bash" \ No newline at end of file diff --git a/atomic_red_team/test_data/reused_guid.yaml b/atomic_red_team/test_data/reused_guid.yaml index cfefbc31..70d937df 100644 --- a/atomic_red_team/test_data/reused_guid.yaml +++ b/atomic_red_team/test_data/reused_guid.yaml @@ -28,7 +28,6 @@ atomic_tests: description: Path to the Gsecdump executable type: path default: PathToAtomicsFolder\..\ExternalPayloads\gsecdump.exe - dependency_executor_name: powershell executor: command: | "#{gsecdump_exe}" -a diff --git a/atomic_red_team/tests/test_models.py b/atomic_red_team/tests/test_models.py index f24b454d..fa59ba46 100644 --- a/atomic_red_team/tests/test_models.py +++ b/atomic_red_team/tests/test_models.py @@ -92,6 +92,7 @@ def atomic_command_executor_builder(): executor=CommandExecutor( name=executor_name, command=f"{formatted_args} Custom steps here...", + elevation_required="sudo" in formatted_args, ), input_arguments=input_arguments, **kwargs, diff --git a/atomics/T1529/T1529.yaml b/atomics/T1529/T1529.yaml index c49c85a1..8f341fd4 100644 --- a/atomics/T1529/T1529.yaml +++ b/atomics/T1529/T1529.yaml @@ -278,13 +278,12 @@ atomic_tests: echo "" | "#{plink_file}" -batch "#{vm_host}" -ssh -l #{vm_user} -pw "#{vm_pass}" "for i in `vim-cmd vmsvc/getallvms | awk 'NR>1 {print $1}'`; do vim-cmd vmsvc/power.off $i & done" name: command_prompt elevation_required: false -- name: abuse of linux magic system request key for reboot +- name: Abuse of Linux Magic System Request Key for Reboot auto_generated_guid: d2a1f4bc-a064-4223-8281-a086dce5423c description: | adversaries with root or sufficient privileges to silently manipulate or destabilize a system. By writing to /proc/sysrq-trigger, they can forced to reboot. supported_platforms: - linux - dependency_executor_name: bash executor: command: | echo "b" > /proc/sysrq-trigger