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 2075254f04..afd1c5d019 100644 --- a/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Invoke-AtomicTest.ps1 +++ b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Invoke-AtomicTest.ps1 @@ -51,7 +51,7 @@ function Invoke-AtomicTest { [Parameter(Mandatory = $false, ParameterSetName = 'technique')] [String] - $PathToAtomicsFolder = "C:\AtomicRedTeam\atomic-red-team-master\atomics", + $PathToAtomicsFolder = $( if($IsLinux -or $IsMacOS) {$Env:HOME + "/AtomicRedTeam/atomic-red-team-master/atomics"} else{$env:HOMEDRIVE + "\AtomicRedTeam\atomic-red-team-master\atomics"}), [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true, @@ -90,11 +90,14 @@ function Invoke-AtomicTest { # $InformationPrefrence = 'Continue' Write-Verbose -Message 'Attempting to run Atomic Techniques' $isElevated = $false + $targetPlatform = "linux" if ($IsLinux -or $IsMacOS){ + if ($IsMacOS){ $targetPlatform = "macos"} $privid = id -u if ($privid -eq 0){ $isElevated = $true } } else { + $targetPlatform = "windows" $isElevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } @@ -138,9 +141,15 @@ function Invoke-AtomicTest { function Invoke-AtomicTestSingle ($AT) { - - $AtomicTechniqueHash = Get-AtomicTechnique -Path $PathToAtomicsFolder\$AT\$AT.yaml + $AT=$AT.ToUpper() + $pathToYaml = Join-Path $PathToAtomicsFolder "\$AT\$AT.yaml" + if (Test-Path -Path $pathToYaml){$AtomicTechniqueHash = Get-AtomicTechnique -Path $pathToYaml} + else{ + Write-Host -ForegroundColor Red "ERROR: $PathToYaml does not exist`nCheck your Atomic Number and Path to Atomics" + continue + } $techniqueCount = 0 + foreach ($technique in $AtomicTechniqueHash) { $techniqueCount++ @@ -156,6 +165,14 @@ function Invoke-AtomicTest { $testCount = 0 foreach ($test in $technique.atomic_tests) { + + Write-Verbose -Message 'Determining tests for target operating system' + + if (-Not $test.supported_platforms.Contains($targetPlatform)) { + Write-Verbose -Message "Unable to run non-$targetPlatform tests" + continue + } + $testCount++ if ($null -ne $TestNumbers) { @@ -173,13 +190,6 @@ function Invoke-AtomicTest { } Write-Progress @props - Write-Verbose -Message 'Determining tests for Windows' - - if (-Not $test.supported_platforms.Contains('windows')) { - Write-Verbose -Message 'Unable to run non-Windows tests' - continue - } - Write-Verbose -Message 'Determining manual tests' if ($test.executor.name.Contains('manual')) { @@ -231,42 +241,38 @@ function Invoke-AtomicTest { if ($pscmdlet.ShouldProcess($testName, 'Execute Atomic Test')) { $testId = "$AT-$testCount $testName" $attackExecuted = $false - switch ($test.executor.name) { - "command_prompt" { - Write-Information -MessageData "Command Prompt:`n $finalCommand" -Tags 'AtomicTest' - $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 - if (!$CheckPrereqs -and !$Cleanup) { $attackExecuted = $true } - } - $nonZeroExitCodes = $exitCodes | Where-Object { $_ -ne 0 } - Write-PrereqResults ($nonZeroExitCodes.Count -eq 0) $testId - if (-not $NoExecutionLog -and $attackExecuted) { Write-ExecutionLog $startTime $AT $testCount $testName $ExecutionLogPath } - continue + $executor = $test.executor.name + $finalCommandEscaped = $finalCommand -replace "`"", "```"" + Write-Information -MessageData $finalCommandEscaped + if ($executor -eq "command_prompt" -or $executor -eq "sh" -or $executor -eq "bash"){ + $execCommand = $finalCommandEscaped.Split("`n") | Where-Object { $_ -ne "" } + $exitCodes = New-Object System.Collections.ArrayList + $execPrefix = "cmd.exe /c" + if ($executor -eq "sh"){$execPrefix = "sh -c"} + if ($executor -eq "bash"){$execPrefix = "bash -c"} + $execCommand | ForEach-Object { + Invoke-Expression "$execPrefix `"$_`" " + $exitCodes.Add($LASTEXITCODE) | Out-Null } - "powershell" { - Write-Information -MessageData "PowerShell`n $finalCommand" -Tags 'AtomicTest' - $execCommand = "Invoke-Command -ScriptBlock {$finalCommand}" - $res = Invoke-Expression $execCommand - if (!$CheckPrereqs -and !$Cleanup) { $attackExecuted = $true } - Write-PrereqResults ([string]::IsNullOrEmpty($finalCommand) -or $res -eq 0) $testId - if (-not $NoExecutionLog -and $attackExecuted) { Write-ExecutionLog $startTime $AT $testCount $testName $ExecutionLogPath } - continue - } - default { - Write-Warning -Message "Unable to generate or execute the command line properly." - continue - } - } # End of executor switch + $nonZeroExitCodes = $exitCodes | Where-Object { $_ -ne 0 } + $success = $nonZeroExitCodes.Count -eq 0 + } + elseif ($executor -eq "powershell"){ + $execCommand = "Invoke-Command -ScriptBlock {$finalCommand}" + $res = Invoke-Expression $execCommand + $success = [string]::IsNullOrEmpty($finalCommand) -or $res -eq 0 + } + else { + Write-Warning -Message "Unable to generate or execute the command line properly." + continue + } + if (!$CheckPrereqs -and !$Cleanup) { $attackExecuted = $true } + Write-PrereqResults ($success) $testId + if (-not $NoExecutionLog -and $attackExecuted) { Write-ExecutionLog $startTime $AT $testCount $testName $ExecutionLogPath} } # End of if ShouldProcess block } # End of else statement + Write-Information -MessageData "[!!!!!!!!END TEST!!!!!!!]`n`n" -Tags 'Details' } # End of foreach Test in single Atomic Technique - - Write-Information -MessageData "[!!!!!!!!END TEST!!!!!!!]`n`n" -Tags 'Details' - } # End of foreach Technique in Atomic Tests } # End of Invoke-AtomicTestSingle function @@ -291,4 +297,4 @@ function Invoke-AtomicTest { } # End of PROCESS block END { } # Intentionally left blank and can be removed -} \ No newline at end of file +} diff --git a/execution-frameworks/Invoke-AtomicRedTeam/README.md b/execution-frameworks/Invoke-AtomicRedTeam/README.md index b7a4ef1b94..5f174eaa9a 100644 --- a/execution-frameworks/Invoke-AtomicRedTeam/README.md +++ b/execution-frameworks/Invoke-AtomicRedTeam/README.md @@ -12,13 +12,20 @@ solution in place, and that the endpoint is checking in and active. It is best t We made installing Atomic Red Team extremely easy. +For those running Atomic Red Team on MacOS or Linux download and install PowerShell Core. + +[Linux](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-linux?view=powershell-6) +[MacOS](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-macos?view=powershell-6) + Once the environment is ready, run PowerShell as an adminstrator and run the following PowerShell one liner: `IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/execution-frameworks/Invoke-AtomicRedTeam/install-atomicredteam.ps1'); Install-AtomicRedTeam -verbose` [Source](install-atomicredteam.ps1) -By default, it will download and Install Atomic Red Team to `c:\AtomicRedTeam` +By default, it will download and Install Atomic Red Team to `\AtomicRedTeam` + +Where `` is `C:` in Windows or `~` in Linux/MacOS Running the [Install script](install-atomicredteam.ps1) locally provides three parameters: @@ -32,6 +39,11 @@ DownloadPath `Install-AtomicRedTeam -DownloadPath c:\tools\` +Force +- Force the new installation removing any previous installations in -InstallPath. **BE CAREFUL this will delete the entire install path folder** + + `Install-AtomicRedTeam -Force` + ### Development If you will be contributing to Atomic Red Team or plan on running it from a cloned github repo, move it to the following folder on your Windows computer for compatibility with most tests as many of them still have hard-coded paths. @@ -69,7 +81,9 @@ Execute all Atomic tests: Invoke-AtomicTest All ``` -This assumes your atomics folder is in the default location of `C:\AtomicRedTeam\atomic-red-team-master\atomics` +This assumes your atomics folder is in the default location of `\AtomicRedTeam\atomic-red-team-master\atomics` + +Where `` is `C:` in Windows or `~` in Linux/MacOS You can override the default path to the atomics folder using the `$PSDefaultParameterValues` preference variable as shown below. @@ -132,7 +146,7 @@ By default, test execution details are written to `Invoke-AtomicTest-ExecutionLo Invoke-AtomicTest T1117 -CheckPrereqs ``` -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 "command_prompt", "bash", and "sh" executors, 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 if the pre-requisites are met. Example: **if(Test-Path C:\Windows\System32\cmd.exe) { 0 } else { -1 }** diff --git a/execution-frameworks/Invoke-AtomicRedTeam/install-atomicredteam.ps1 b/execution-frameworks/Invoke-AtomicRedTeam/install-atomicredteam.ps1 index 8195f7b142..c606675293 100644 --- a/execution-frameworks/Invoke-AtomicRedTeam/install-atomicredteam.ps1 +++ b/execution-frameworks/Invoke-AtomicRedTeam/install-atomicredteam.ps1 @@ -19,6 +19,10 @@ function Install-AtomicRedTeam { Specifies the desired path for where to install Atomic Red Team. + .PARAMETER Force + + Delete the existing InstallPath before installation if it exists. + .EXAMPLE Install Atomic Red Team @@ -32,57 +36,80 @@ function Install-AtomicRedTeam { [CmdletBinding()] Param( [Parameter(Mandatory = $False, Position = 0)] - [string]$InstallPath = 'C:\AtomicRedTeam', + [string]$InstallPath = $( if ($IsLinux -or $IsMacOS) { $Env:HOME + "/AtomicRedTeam" } else { $env:HOMEDRIVE + "\AtomicRedTeam" }), - [Parameter(Mandatory = $False, Position = 0)] - [string]$DownloadPath = 'C:\AtomicRedTeam' + [Parameter(Mandatory = $False, Position = 1)] + [string]$DownloadPath = $( if ($IsLinux -or $IsMacOS) { $Env:HOME + "/AtomicRedTeam" } else { $env:HOMEDRIVE + "\AtomicRedTeam" }), + [Parameter(Mandatory = $False)] + [switch]$Force = $False # delete the existing install directory and reinstall ) Write-Verbose "Checking if we are Admin" - $isElevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) - if (-not $isElevated) { Write-Error "This script must be run as an administrator."; exit} - + $isElevated = $false + if ($IsLinux -or $IsMacOS) { + $privid = id -u + if ($privid -eq 0) { + $isElevated = $true + } + } + else { + $isElevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) + } + if (-not $isElevated) { Write-Error "This script must be run as an administrator."; return } - if (!(Test-Path -Path $InstallPath )) { + $modulePath = Join-Path "$InstallPath" "atomic-red-team-master\execution-frameworks\Invoke-AtomicRedTeam\Invoke-AtomicRedTeam\Invoke-AtomicRedTeam.psm1" + if ($Force -or -Not (Test-Path -Path $InstallPath )) { write-verbose "Directory Creation" + if ($Force) { + Try { + if (Test-Path $InstallPath){ Remove-Item -Path $InstallPath -Recurse -Force -ErrorAction Stop | Out-Null } + } + Catch { + Write-Host -ForegroundColor Red $_.Exception.Message + return + } + } New-Item -ItemType directory -Path $InstallPath | Out-Null - write-verbose "Setting Execution Policy to Unrestricted" - set-executionpolicy Unrestricted + + if ($IsWindows -or $null -eq $IsWindows) { + write-verbose "Setting Execution Policy to Unrestricted" + set-executionpolicy Unrestricted + } write-verbose "Setting variables for remote URL and download Path" $url = "https://github.com/redcanaryco/atomic-red-team/archive/master.zip" - $path = "$DownloadPath\master.zip" + $path = Join-Path $DownloadPath "master.zip" [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $webClient = new-object System.Net.WebClient write-verbose "Beginning download from Github" $webClient.DownloadFile( $url, $path ) - write-verbose "Extracting ART to C:\AtomicRedTeam\" - expand-archive -LiteralPath "$DownloadPath\master.zip" -DestinationPath "$InstallPath" + write-verbose "Extracting ART to $InstallPath" + $lp = Join-Path "$DownloadPath" "master.zip" + expand-archive -LiteralPath $lp -DestinationPath "$InstallPath" -Force:$Force - write-verbose "Installing NuGet PackageProvider" - Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force + if (-not $IsMacOS -and -not $IsLinux) { + write-verbose "Installing NuGet PackageProvider" + Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force + } + else { + chown -R $env:SUDO_USER $InstallPath + } write-verbose "Installing powershell-yaml" Install-Module -Name powershell-yaml -Force write-verbose "Importing invoke-atomicRedTeam module" - Import-Module "$InstallPath\atomic-red-team-master\execution-frameworks\Invoke-AtomicRedTeam\Invoke-AtomicRedTeam\Invoke-AtomicRedTeam.psm1" - - write-verbose "Changing current work directory Invoke-AtomicRedTeam" - cd "$InstallPath\atomic-red-team-master\execution-frameworks\Invoke-AtomicRedTeam\" + Import-Module $modulePath -Force - write-verbose "Clearing screen" - clear - - Write-Host "Installation of Invoke-AtomicRedTeam is complete" -Fore Yellow + Write-Host "Installation of Invoke-AtomicRedTeam is complete. You can now use the Invoke-AtomicTest function" -Fore Yellow + Write-Host "See README at https://github.com/redcanaryco/atomic-red-team/tree/master/execution-frameworks/Invoke-AtomicRedTeam for complete details" -Fore Yellow } else { - Write-Host "Atomic Redteam already exists at $InstallPath. Importing existing Invoke-atomicRedTeam module" - cd "$InstallPath\atomic-red-team-master\execution-frameworks\Invoke-AtomicRedTeam\" - Import-Module "$InstallPath\atomic-red-team-master\execution-frameworks\Invoke-AtomicRedTeam\Invoke-AtomicRedTeam\Invoke-AtomicRedTeam.psm1" -Force - + Write-Host -ForegroundColor Yellow "Atomic Redteam already exists at $InstallPath. No changes were made." + Write-Host -ForegroundColor Cyan "Try the install again with the '-Force' parameter if you want to delete the existing installion and re-install." + Write-Host -ForegroundColor Red "Warning: All files within the install directory ($InstallPath) will be deleted when using the '-Force' parameter." } -} \ No newline at end of file +} diff --git a/execution-frameworks/README.md b/execution-frameworks/README.md index 0eb57ceaea..3d61774726 100644 --- a/execution-frameworks/README.md +++ b/execution-frameworks/README.md @@ -6,7 +6,7 @@ Here is an [example markdown file](https://github.com/redcanaryco/atomic-red-tea ## Invoke-AtomicRedTeam -Invoke-AtomicRedTeam is written in PowerShell, which can be used cross-platform, but currently only supports the **_powershell_** and **_command_prompt_** executors (Windows stuff). +Invoke-AtomicRedTeam is written in PowerShell, which can be executed cross-platform using PowerShell Core for Linux and MacOS. For detailed installation and usage instructions refer to the [README](https://github.com/redcanaryco/atomic-red-team/tree/master/execution-frameworks/Invoke-AtomicRedTeam) file inside of the **_Invoke-AtomicRedTeam_** folder. ## Python @@ -15,4 +15,4 @@ Surprise, this framework is written in Python. For detailed installation and usa ## Ruby -Ruby version of the execution framework. \ No newline at end of file +Ruby version of the execution framework.