diff --git a/atomic_red_team/atomic_doc_template.md.erb b/atomic_red_team/atomic_doc_template.md.erb index 0fb00e32..f6de6a56 100644 --- a/atomic_red_team/atomic_doc_template.md.erb +++ b/atomic_red_team/atomic_doc_template.md.erb @@ -45,5 +45,20 @@ end.join(', ') %> <%= test['executor']['command'].to_s.strip %> ``` <%- end -%> + +<%- if test['executor']['prereq_command'] != nil -%> +#### Commands to Check Prerequisites: +``` +<%= test['executor']['prereq_command'].to_s.strip %> +``` +<%- end -%> + +<%- if test['executor']['cleanup_command'] != nil -%> +#### Cleanup Commands: +``` +<%= test['executor']['cleanup_command'].to_s.strip %> +``` +<%- end -%> +
-<%- end -%> \ No newline at end of file +<%- end -%> diff --git a/atomic_red_team/atomic_test_template.yaml b/atomic_red_team/atomic_test_template.yaml index 3a6ea634..d3eba62c 100644 --- a/atomic_red_team/atomic_test_template.yaml +++ b/atomic_red_team/atomic_test_template.yaml @@ -23,5 +23,10 @@ atomic_tests: executor: name: command_prompt elevation_required: true # indicates whether command must be run with admin privileges. If the elevation_required attribute is not defined, the value is assumed to be false + prereq_command: | # for the "command_prompt" executor, if any command returns a non-zero exit code, the pre-requisites are not met. For the "powershell" executor, all commands are run as a script block and the script block must return 0 for success. You can remove the prereq_command section if there are no pre-req's + TODO + command: | # these are the actaul attack commands, at least one command must be provided + TODO + cleanup_command: | # you can remove the cleanup_command section if there are no cleanup commands command: | TODO diff --git a/atomics/T1089/T1089.yaml b/atomics/T1089/T1089.yaml index e9469151..7d45362b 100644 --- a/atomics/T1089/T1089.yaml +++ b/atomics/T1089/T1089.yaml @@ -109,8 +109,14 @@ atomic_tests: executor: name: command_prompt elevation_required: true + prereq_command: | + fltmc.exe filters | findstr #{sysmon_driver} command: | fltmc.exe unload #{sysmon_driver} + cleanup_command: | + sc stop sysmon + fltmc.exe load #{sysmon_driver} + sc start sysmon - name: Disable Windows IIS HTTP Logging description: | @@ -138,5 +144,9 @@ atomic_tests: executor: name: command_prompt elevation_required: true + prereq_command: | + sc query sysmon command: | sysmon -u + cleanup_command: | + sysmon -i -accepteula diff --git a/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Invoke-AtomicTest.ps1 b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Invoke-AtomicTest.ps1 index 41f3291f..0bca5e4a 100644 --- a/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Invoke-AtomicTest.ps1 +++ b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Invoke-AtomicTest.ps1 @@ -3,9 +3,13 @@ Invokes specified Atomic test(s) .DESCRIPTION Invokes specified Atomic tests(s). Optionally, you can specify if you want to generate Atomic test(s) only. +.EXAMPLE Check if Prerequisites for Atomic Test are met + PS/> Invoke-AtomicTest T1117 -CheckPrereqs .EXAMPLE Invokes Atomic Test PS/> Invoke-AtomicTest T1117 -.EXAMPLE Generate Atomic Test +.EXAMPLE Run the Cleanup Commmand for the given Atomic Test + PS/> Invoke-AtomicTest T1117 -Cleanup +.EXAMPLE Generate Atomic Test (Output Test Definition Details) PS/> Invoke-AtomicTest T1117 -GenerateOnly .NOTES Create Atomic Tests from yaml files described in Atomic Red Team. https://github.com/redcanaryco/atomic-red-team @@ -47,7 +51,19 @@ function Invoke-AtomicTest { [Parameter(Mandatory = $false, ParameterSetName = 'technique')] [String] - $PathToAtomicsFolder = "..\..\atomics" + $PathToAtomicsFolder = "..\..\atomics", + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + ParameterSetName = 'technique')] + [switch] + $CheckPrereqs = $false, + + [Parameter(Mandatory = $false, + ValueFromPipelineByPropertyName = $true, + ParameterSetName = 'technique')] + [switch] + $Cleanup = $false ) BEGIN { } # Intentionally left blank and can be removed PROCESS { @@ -109,7 +125,9 @@ function Invoke-AtomicTest { Write-Debug -Message 'Gathering final Atomic test command' - $finalCommand = $test.executor.command + $prereqCommand = $test.executor.prereq_command + $command = $test.executor.command + $cleanupCommand = $test.executor.cleanup_command if ($test.input_arguments.Count -gt 0) { Write-Verbose -Message 'Replacing inputArgs with default values' @@ -118,10 +136,22 @@ function Invoke-AtomicTest { for ($i = 0; $i -lt $inputArgs.Length; $i++) { $findValue = '#{' + $inputArgs[$i] + '}' - $finalCommand = $finalCommand.Replace($findValue, $inputDefaults[$i]) + if( $nul -ne $prereqCommand ) { $prereqCommand = $prereqCommand.Replace($findValue, $inputDefaults[$i]) } else { $prereqCommand = "" } + $Command = $command.Replace($findValue, $inputDefaults[$i]) + if( $nul -ne $cleanupCommand ) { $cleanupCommand = $cleanupCommand.Replace($findValue, $inputDefaults[$i]) } else { $cleanupCommand = "" } } } + if ($CheckPrereqs) { + $finalCommand = $prereqCommand + } + elseif ($Cleanup) { + $finalCommand = $cleanupCommand + } + else { + $finalCommand = $command + } + Write-Debug -Message 'Getting executor and build command script' if ($GenerateOnly) { @@ -129,18 +159,41 @@ function Invoke-AtomicTest { } else { Write-Verbose -Message 'Invoking Atomic Tests using defined executor' - if ($pscmdlet.ShouldProcess(($test.name.ToString()), 'Execute Atomic Test')) { + $testName = $test.name.ToString() + if ($pscmdlet.ShouldProcess($testName, 'Execute Atomic Test')) { switch ($test.executor.name) { "command_prompt" { Write-Information -MessageData "Command Prompt:`n $finalCommand" -Tags 'AtomicTest' - $execCommand = $finalCommand.Split("`n") - $execCommand | ForEach-Object { Invoke-Expression "cmd.exe /c `"$_`" " } + $finalCommandEscaped = $finalCommand -replace "`"", "```"" + $execCommand = $finalCommandEscaped.Split("`n") | Where-Object { $_ -ne "" } + $exitCodes = New-Object System.Collections.ArrayList + $execCommand | ForEach-Object { + Invoke-Expression "cmd.exe /c `"$_`" " + $exitCodes.Add($LASTEXITCODE) | Out-Null + } + $nonZeroExitCodes = $exitCodes | Where-Object { $_ -ne 0 } + if ($CheckPrereqs ) { + if ($nonZeroExitCodes.Count -ne 0) { + Write-Host -ForegroundColor Red "Prerequisites not met: $testName" + } + else { + Write-Host -ForegroundColor Green "Prerequisites met: $testName" + } + } continue } "powershell" { Write-Information -MessageData "PowerShell`n $finalCommand" -Tags 'AtomicTest' $execCommand = "Invoke-Command -ScriptBlock {$finalCommand}" - Invoke-Expression $execCommand + $res = Invoke-Expression $execCommand + if ($CheckPrereqs ) { + if ([string]::IsNullOrEmpty($finalCommand) -or $res -ne 0) { + Write-Host -ForegroundColor Red "Prerequisites not met: $testName" + } + else { + Write-Host -ForegroundColor Green "Prerequisites met: $testName" + } + } continue } default { diff --git a/execution-frameworks/Invoke-AtomicRedTeam/README.md b/execution-frameworks/Invoke-AtomicRedTeam/README.md index 19c67f0f..26d726fa 100644 --- a/execution-frameworks/Invoke-AtomicRedTeam/README.md +++ b/execution-frameworks/Invoke-AtomicRedTeam/README.md @@ -45,7 +45,7 @@ Verbose ### Generate Tests -This process generates all Atomic tests and allows for easy copy and paste execution. +This process generates all Atomic tests (prints test details to screen) and allows for easy copy and paste execution. Note: you may need to change the path. Invoke-AllAtomicTests -GenerateOnly @@ -68,6 +68,15 @@ Specify a path to atomics folder, example C:\AtomicRedTeam\atomics Invoke-AtomicTest T1117 ``` +#### Check that Prerequistes for a Given TTP are met + +For the "command_prompt" executor, if any of the prereq_command's return a non-zero exit code, the pre-requisites are not met. Example: **fltmc.exe filters | findstr #{sysmon_driver}** +For the "powershell" executor, the prereq_command's are run as a script block and the script must return 0 for success. Example: **if(Test-Path C:\Windows\System32\cmd.exe) { 0 } else { -1 }** + +```powershell +Invoke-AtomicTest T1117 -CheckPrereqs +``` + #### Execute Specific Attacks (by Attack Number) for a Given TTP ```powershell @@ -79,6 +88,11 @@ Invoke-AtomicTest T1117 -TestNumbers 1, 2 ```powershell Invoke-AtomicTest T1117 -TestNames "Regsvr32 remote COM scriptlet execution","Regsvr32 local DLL execution" ``` +#### Run the Cleanup Commands For the Specified Test + +```powershell +Invoke-AtomicTest T1089 -TestNames "Uninstall Sysmon" -Cleanup +``` ## Additional Examples