Skip to content

Commit

Permalink
Multi platform invoke art (#641)
Browse files Browse the repository at this point in the history
* Non-Windows OS Support

Added OS Identification to determine tests to run
Added SH and Bash executors for Linux and MacOS
Changed some Print statement oddities in ART
Updated Installation script to work on non-windows machines

* Updated Documentation

Edited the readme to be more OS neutral
Added information for the -force option in the installer
Added instructions for downloading powershell core on Mac and Linux

* Last Bugs

added chown to install script

* Install -force test install path

if (Test-Path $InstallPath){ Remove-Item -Path $InstallPath -Recurse -Force -ErrorAction Stop | Out-Null }

* minor changes 

Write-Host error messages
Installer - Import-Module $modulePath -Force

* Chown weird on MacOS

chown -R $env:SUDO_USER $InstallPath

* README edits

clearing up $home $homedrive shenanigans

* \n in mardown issues

* Readme edits #2
  • Loading branch information
Andras32 authored and clr2of8 committed Nov 11, 2019
1 parent 26e0f44 commit 6c3da68
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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++
Expand All @@ -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) {
Expand All @@ -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')) {
Expand Down Expand Up @@ -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

Expand All @@ -291,4 +297,4 @@ function Invoke-AtomicTest {

} # End of PROCESS block
END { } # Intentionally left blank and can be removed
}
}
20 changes: 17 additions & 3 deletions execution-frameworks/Invoke-AtomicRedTeam/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<BASEPATH>\AtomicRedTeam`

Where `<BASEPATH>` is `C:` in Windows or `~` in Linux/MacOS

Running the [Install script](install-atomicredteam.ps1) locally provides three parameters:

Expand All @@ -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.
Expand Down Expand Up @@ -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 `<BASEPATH>\AtomicRedTeam\atomic-red-team-master\atomics`

Where `<BASEPATH>` 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.

Expand Down Expand Up @@ -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 }**

Expand Down
81 changes: 54 additions & 27 deletions execution-frameworks/Invoke-AtomicRedTeam/install-atomicredteam.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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."
}
}
}
4 changes: 2 additions & 2 deletions execution-frameworks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -15,4 +15,4 @@ Surprise, this framework is written in Python. For detailed installation and usa

## Ruby

Ruby version of the execution framework.
Ruby version of the execution framework.

0 comments on commit 6c3da68

Please sign in to comment.