From c8a70997dace7ec75581e67883a069fb1107ae1a Mon Sep 17 00:00:00 2001 From: Hare Sudhan Date: Wed, 10 Jul 2024 08:54:26 -0500 Subject: [PATCH] Adding more YAML validations (#2837) * Update T1202.yaml * fix all atomics * changing to macos to fix pytest issue * changing to macos to fix pytest issue * adding gitignore --- .github/workflows/run-python-tests.yml | 4 +-- .gitignore | 1 + atomic_red_team/models.py | 28 ++++++++++++++----- atomic_red_team/runner.py | 2 +- .../empty_dependency_executor_name.yaml | 20 +++++++++++++ .../test_data/empty_input_arguments.yaml | 15 ++++++++++ .../{int_type.yaml => int_parsing.yaml} | 0 atomics/T1112/T1112.yaml | 2 +- atomics/T1202/T1202.yaml | 1 - atomics/T1218.011/T1218.011.yaml | 1 - atomics/T1652/T1652.yaml | 2 -- 11 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 atomic_red_team/test_data/empty_dependency_executor_name.yaml create mode 100644 atomic_red_team/test_data/empty_input_arguments.yaml rename atomic_red_team/test_data/{int_type.yaml => int_parsing.yaml} (100%) diff --git a/.github/workflows/run-python-tests.yml b/.github/workflows/run-python-tests.yml index 927b67ec5..2826fa9e4 100644 --- a/.github/workflows/run-python-tests.yml +++ b/.github/workflows/run-python-tests.yml @@ -9,7 +9,7 @@ on: jobs: validate-python-file-changes: - runs-on: ubuntu-latest + runs-on: macos-latest steps: - name: checkout repo uses: actions/checkout@v4 @@ -19,7 +19,7 @@ jobs: uses: actions/setup-python@v5 id: setup-python with: - python-version: "3.11.2" + python-version: "3.12.4" cache: "poetry" - name: Install dependencies diff --git a/.gitignore b/.gitignore index aeeff8d6e..142da7abb 100644 --- a/.gitignore +++ b/.gitignore @@ -149,3 +149,4 @@ node_modules/ # Python __pycache__/ *.pyc +.hypothesis/ \ No newline at end of file diff --git a/atomic_red_team/models.py b/atomic_red_team/models.py index 36538db9b..003f0da8c 100644 --- a/atomic_red_team/models.py +++ b/atomic_red_team/models.py @@ -10,7 +10,6 @@ from pydantic import ( Field, IPvAnyAddress, StrictFloat, - StrictInt, StringConstraints, conlist, constr, @@ -105,7 +104,7 @@ class StringArg(BaseArgument): class IntArg(BaseArgument): - default: Optional[StrictInt] + default: Optional[int] type: Literal["integer", "Integer"] @@ -152,10 +151,8 @@ class Atomic(BaseModel): supported_platforms: conlist(Platform, min_length=1) executor: Union[ManualExecutor, CommandExecutor] = Field(..., discriminator="name") dependencies: Optional[List[Dependency]] = [] - input_arguments: Optional[ - Dict[constr(min_length=2, pattern=r"^[\w_-]+$"), Argument] - ] = {} - dependency_executor_name: Optional[ExecutorType] = None + input_arguments: Dict[constr(min_length=2, pattern=r"^[\w_-]+$"), Argument] = {} + dependency_executor_name: ExecutorType = "manual" auto_generated_guid: Optional[UUID] = None @classmethod @@ -170,11 +167,28 @@ class Atomic(BaseModel): commands.extend([d.get_prereq_command, d.prereq_command]) return extract_mustached_keys(commands) + @field_validator("dependency_executor_name", mode="before") # noqa + @classmethod + def validate_dep_executor(cls, v, info: ValidationInfo): + if v is None: + 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": ["dependency_executor_name"], "input": None}, + ) + return v + @field_validator("input_arguments", mode="before") # noqa @classmethod def validate(cls, v, info: ValidationInfo): if v is None: - return v + raise PydanticCustomError( + "empty_input_arguments", + "'input_arguments' shouldn't be empty. Provide a valid value or remove the key from YAML", + {"loc": ["input_arguments"], "input": None}, + ) + atomic = info.data keys = cls.extract_mustached_keys(atomic) for key, _value in v.items(): diff --git a/atomic_red_team/runner.py b/atomic_red_team/runner.py index 607c48ed8..b7e3c0242 100644 --- a/atomic_red_team/runner.py +++ b/atomic_red_team/runner.py @@ -84,7 +84,7 @@ def validate(): validator = Validator() errors = defaultdict(list) - for folder in glob.glob(f"{atomics_path}/atomics/T*"): + for folder in glob.glob(f"{atomics_path}/T*"): for item in os.scandir(folder): try: validator.validate(item) diff --git a/atomic_red_team/test_data/empty_dependency_executor_name.yaml b/atomic_red_team/test_data/empty_dependency_executor_name.yaml new file mode 100644 index 000000000..289081f59 --- /dev/null +++ b/atomic_red_team/test_data/empty_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: \ No newline at end of file diff --git a/atomic_red_team/test_data/empty_input_arguments.yaml b/atomic_red_team/test_data/empty_input_arguments.yaml new file mode 100644 index 000000000..c2937e6c8 --- /dev/null +++ b/atomic_red_team/test_data/empty_input_arguments.yaml @@ -0,0 +1,15 @@ +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: + executor: + command: | + "#{gsecdump_exe}" -a + name: command_prompt + elevation_required: true diff --git a/atomic_red_team/test_data/int_type.yaml b/atomic_red_team/test_data/int_parsing.yaml similarity index 100% rename from atomic_red_team/test_data/int_type.yaml rename to atomic_red_team/test_data/int_parsing.yaml diff --git a/atomics/T1112/T1112.yaml b/atomics/T1112/T1112.yaml index 2feb14dc3..bb1011890 100644 --- a/atomics/T1112/T1112.yaml +++ b/atomics/T1112/T1112.yaml @@ -1239,7 +1239,7 @@ atomic_tests: name: command_prompt elevation_required: true - name: Modify EnableNonTPM Registry entry - auto_generated_guid: 02d8b9f7-1a51-4011-8901-2d55cca667f9 + auto_generated_guid: description: | Allow Bitlocker without TPM for Bitlocker tool supported_platforms: diff --git a/atomics/T1202/T1202.yaml b/atomics/T1202/T1202.yaml index 86fd17218..5d0dae9c8 100644 --- a/atomics/T1202/T1202.yaml +++ b/atomics/T1202/T1202.yaml @@ -70,7 +70,6 @@ atomic_tests: description: Path to the executable type: String default: C:\Windows\System32\calc.exe - dependency_executor_name: executor: command: Scriptrunner.exe -appvscript "#{payload_path}" cleanup_command: diff --git a/atomics/T1218.011/T1218.011.yaml b/atomics/T1218.011/T1218.011.yaml index fd245275b..9f2ae103d 100644 --- a/atomics/T1218.011/T1218.011.yaml +++ b/atomics/T1218.011/T1218.011.yaml @@ -352,7 +352,6 @@ atomic_tests: description: Path of the executable to launch type: path default: "'%windir%\\System32\\calc.exe'" - dependency_executor_name: executor: command: rundll32.exe zipfldr.dll,RouteTheCall "#{exe_to_launch}" cleanup_command: diff --git a/atomics/T1652/T1652.yaml b/atomics/T1652/T1652.yaml index 7e22ac10f..230ebba98 100644 --- a/atomics/T1652/T1652.yaml +++ b/atomics/T1652/T1652.yaml @@ -11,8 +11,6 @@ atomic_tests: /si /fo list - Provides information about signed drivers and outputs it in a list format supported_platforms: - windows - input_arguments: - dependency_executor_name: executor: command: | driverquery /v /fo list