Asynchronous Attack Execution and other handy things (#790)

* execute attack in separate process

* install from custom repoOwner and branch

* remove zip after install

* added showdetails brief and sleep for linux output

* remove positional param spec

* replacing special PathToAtomicsFolder in commands

* use pwsh on linux

* kill proc tree linux

* include path in remove-item

* update readme

* update readme

* update readme

Co-authored-by: Tony M Lambert <ForensicITGuy@users.noreply.github.com>
This commit is contained in:
Carrie Roberts
2020-01-22 20:36:20 -07:00
committed by Tony M Lambert
parent 3ef533126a
commit 0dcde71a15
14 changed files with 192 additions and 97 deletions
+7 -7
View File
@@ -36,7 +36,7 @@ atomic_tests:
- description: |
Windows Credential Editor must exist on disk at specified location (#{gsecdump_exe})
prereq_command: |
if (Test-Path #{gsecdump_exe}) {0} else {1}
if (Test-Path #{gsecdump_exe}) {exit 0} else {exit 1}
get_prereq_command: |
Write-Host Automated installer not implemented yet
@@ -75,7 +75,7 @@ atomic_tests:
- description: |
Windows Credential Editor must exist on disk at specified location (#{wce_exe})
prereq_command: |
if (Test-Path #{wce_exe}) {0} else {1}
if (Test-Path #{wce_exe}) {exit 0} else {exit 1}
get_prereq_command: |
$parentpath = Split-Path "#{wce_exe}"; $zippath = "$parentpath\wce.zip"
if(Invoke-WebRequestVerifyHash "#{wce_url}" "$zippath" #{wce_zip_hash}){
@@ -131,7 +131,7 @@ atomic_tests:
- description: |
ProcDump tool from Sysinternals must exist on disk at specified location (#{procdump_exe})
prereq_command: |
if (Test-Path #{procdump_exe}) {0} else {1}
if (Test-Path #{procdump_exe}) {exit 0} else {exit 1}
get_prereq_command: |
Invoke-WebRequest "https://download.sysinternals.com/files/Procdump.zip" -OutFile "$env:TEMP\Procdump.zip"
Expand-Archive $env:TEMP\Procdump.zip $env:TEMP\Procdump -Force
@@ -308,7 +308,7 @@ atomic_tests:
- description: |
Computer must be domain joined
prereq_command: |
if((Get-CIMInstance -Class Win32_ComputerSystem).PartOfDomain) {0} else {1}
if((Get-CIMInstance -Class Win32_ComputerSystem).PartOfDomain) {exit 0} else {exit 1}
get_prereq_command: |
Write-Host Joining this computer to a domain must be done manually
@@ -340,14 +340,14 @@ atomic_tests:
- description: |
Get-GPPPassword PowerShell Script must exist at #{gpp_script_path}
prereq_command: |
if(Test-Path "#{gpp_script_path}") { 0 } else { 1 }
if(Test-Path "#{gpp_script_path}") {exit 0 } else {exit 1 }
get_prereq_command: |
New-Item -ItemType Directory (Split-Path "#{gpp_script_path}") -Force | Out-Null
Invoke-WebRequest #{gpp_script_url} -OutFile "#{gpp_script_path}"
- description: |
Computer must be domain joined
prereq_command: |
if((Get-CIMInstance -Class Win32_ComputerSystem).PartOfDomain) {0} else {1}
if((Get-CIMInstance -Class Win32_ComputerSystem).PartOfDomain) {exit 0} else {exit 1}
get_prereq_command: |
Write-Host Joining this computer to a domain must be done manually
@@ -385,7 +385,7 @@ atomic_tests:
- description: |
Invoke-NinjaCopy PowerShell Script must exist at #{ninjacopy_script_path}
prereq_command: |
if(Test-Path "#{ninjacopy_script_path}") { 0 } else { 1 }
if(Test-Path "#{ninjacopy_script_path}") {exit 0 } else {exit 1 }
get_prereq_command: |
New-Item -ItemType Directory (Split-Path "#{ninjacopy_script_path}") -Force | Out-Null
Invoke-WebRequest #{ninjacopy_script_url} -OutFile "#{ninjacopy_script_path}"
+1 -1
View File
@@ -45,7 +45,7 @@ atomic_tests:
- description: |
PsExec tool from Sysinternals must exist on disk at specified location (#{psexec_exe})
prereq_command: |
if (Test-Path "#{psexec_exe}"") {0} else {1}
if (Test-Path "#{psexec_exe}"") { exit 0} else { exit 1}
get_prereq_command: |
Invoke-WebRequest "https://download.sysinternals.com/files/PSTools.zip" -OutFile "$env:TEMP\PsTools.zip"
Expand-Archive $env:TEMP\PsTools.zip $env:TEMP\PsTools -Force
+1 -1
View File
@@ -37,7 +37,7 @@ atomic_tests:
- description: |
Computer must be domain joined
prereq_command: |
if((Get-CIMInstance -Class Win32_ComputerSystem).PartOfDomain) {0} else {1}
if((Get-CIMInstance -Class Win32_ComputerSystem).PartOfDomain) { exit 0} else { exit 1}
get_prereq_command: |
Write-Host Joining this computer to a domain must be done manually
+2 -2
View File
@@ -208,7 +208,7 @@ atomic_tests:
- description: |
PowerShell version 2 must be installed
prereq_command: |
if(2 -in $PSVersionTable.PSCompatibleVersions.Major) {0} else {1}
if(2 -in $PSVersionTable.PSCompatibleVersions.Major) {exit 0} else {exit 1}
get_prereq_command: |
Write-Host Automated installer not implemented yet, please install PowerShell v2 manually
@@ -233,7 +233,7 @@ atomic_tests:
- description: |
Homedrive must be an NTFS drive
prereq_command: |
if((Get-Volume -DriveLetter $env:HOMEDRIVE[0]).FileSystem -contains "NTFS") {0} else {1}
if((Get-Volume -DriveLetter $env:HOMEDRIVE[0]).FileSystem -contains "NTFS") {exit 0} else {exit 1}
get_prereq_command: |
Write-Host Prereq's for this test cannot be met automatically
+3 -3
View File
@@ -143,7 +143,7 @@ atomic_tests:
executor:
name: powershell
prereq_command: |
if(Test-Path C:\Windows\System32\inetsrv\appcmd.exe) {0} else {1}
if(Test-Path C:\Windows\System32\inetsrv\appcmd.exe) {exit 0} else {exit 1}
command: |
C:\Windows\System32\inetsrv\appcmd.exe set config "#{website_name}" /section:httplogging /dontLog:true
cleanup_command: |
@@ -166,7 +166,7 @@ atomic_tests:
- description: |
Sysmon executable must be available
prereq_command: |
if(cmd /c where sysmon) {0} else {1}
if(cmd /c where sysmon) {exit 0} else {exit 1}
get_prereq_command: |
$parentpath = Split-Path "#{sysmon_exe}"; $zippath = "$parentpath\Sysmon.zip"
New-Item -ItemType Directory $parentpath -Force | Out-Null
@@ -176,7 +176,7 @@ atomic_tests:
- description: |
Sysmon must be installed
prereq_command: |
if(cmd /c sc query sysmon) {0} else {1}
if(cmd /c sc query sysmon) { exit 0} else { exit 1}
get_prereq_command: |
cmd /c sysmon -i -accepteula
+1 -1
View File
@@ -46,7 +46,7 @@ atomic_tests:
command: |
Start-BitsTransfer -Priority foreground -Source #{remote_file} -Destination #{local_file}
cleanup_command: |
Remove-Item #{local_file}
Remove-Item #{local_file} -ErrorAction Ignore
- name: Persist, Download, & Execute
description: |
@@ -1,4 +1,4 @@
function Invoke-CheckPrereqs ($test, $isElevated, $customInputArgs, $PathToAtomicsFolder) {
function Invoke-CheckPrereqs ($test, $isElevated, $customInputArgs, $PathToAtomicsFolder, $TimeoutSeconds) {
$FailureReasons = New-Object System.Collections.ArrayList
if ($test.executor.elevation_required -and -not $isElevated) {
$FailureReasons.add("Elevation required but not provided`n") | Out-Null
@@ -6,9 +6,10 @@ function Invoke-CheckPrereqs ($test, $isElevated, $customInputArgs, $PathToAtomi
foreach ($dep in $test.dependencies) {
$executor = Get-PrereqExecutor $test
$final_command = Merge-InputArgs $dep.prereq_command $test $customInputArgs $PathToAtomicsFolder
$success = Invoke-ExecuteCommand $final_command $executor
$final_command = ($final_Command.trim()).Replace("`n", " && ")
$res = Invoke-ExecuteCommand $final_command $executor $TimeoutSeconds
$description = Merge-InputArgs $dep.description $test $customInputArgs $PathToAtomicsFolder
if (-not $success) {
if ($res -ne 0) {
$FailureReasons.add($description) | Out-Null
}
}
@@ -1,33 +1,25 @@
function Invoke-ExecuteCommand ($finalCommand, $executor) {
$null = @(
$success = $true
if($null -eq $finalCommand) { return $success}
if($null -eq $finalCommand){return 0}
$finalCommand = $finalCommand.trim()
Write-Verbose -Message 'Invoking Atomic Tests using defined executor'
$finalCommandEscaped = $finalCommand -replace "`"", "```""
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 {
$retval = Invoke-Expression "$execPrefix `"$_`" "
if ($nul -ne $retval) { $retval | Write-Host }
$exitCodes.Add($LASTEXITCODE) | Out-Null
}
$nonZeroExitCodes = $exitCodes | Where-Object { $_ -ne 0 }
$success = $nonZeroExitCodes.Count -eq 0
$execCommand = $finalCommand.Replace("`n", " & ")
$execPrefix = "-c"
$execExe = $executor
if ($executor -eq "command_prompt") { $execPrefix = "/c"; $execExe = "cmd.exe" }
$res = Invoke-Process -filename $execExe -Arguments "$execPrefix `"$execCommand`"" -TimeoutSeconds $TimeoutSeconds
}
elseif ($executor -eq "powershell") {
$finalCommand = $finalCommand.trim()
$execCommand = "Invoke-Command -ScriptBlock {$finalCommand}"
$res = Invoke-Expression $execCommand
$success = [string]::IsNullOrEmpty($finalCommand) -or $res -eq 0
$execCommand = $finalCommand -replace "`"", "`\`"`""
$execExe = "powershell.exe"; if ($IsLinux -or $IsMacOS) { $execExe = "pwsh" }
$res = Invoke-Process -filename $execExe -Arguments "& {$execCommand}" -TimeoutSeconds $TimeoutSeconds
}
else {
Write-Warning -Message "Unable to generate or execute the command line properly. Unknown executor"
$success = $false
$res = -1
}
)
$success
$res
}
@@ -0,0 +1,12 @@
function Invoke-KillProcessTree {
Param([int]$ppid)
if ($IsLinux -or $IsMacOS) {
sh -c pkill -9 -P $ppid
}
else {
while ($null -ne ($gcim = Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq $ppid })) {
$gcim | ForEach-Object { Invoke-KillProcessTree $_.ProcessId; Start-Sleep -Seconds 0.5 }
}
Stop-Process -Id $ppid -ErrorAction Ignore
}
}
@@ -0,0 +1,44 @@
# The Invoke-Process function is loosely based on code from https://github.com/guitarrapc/PowerShellUtil/blob/master/Invoke-Process/Invoke-Process.ps1
function Invoke-Process {
[OutputType([PSCustomObject])]
[CmdletBinding()]
param
(
[Parameter(Mandatory = $false, Position = 0)]
[string]$FileName = "PowerShell.exe",
[Parameter(Mandatory = $false, Position = 1)]
[string]$Arguments = "",
[Parameter(Mandatory = $false, Position = 2)]
[string]$WorkingDirectory = ".",
[Parameter(Mandatory = $false, Position = 3)]
[Int]$TimeoutSeconds = 120
)
end {
try {
# new Process
$process = Start-Process -FilePath $FileName -ArgumentList $Arguments -WorkingDirectory $WorkingDirectory -NoNewWindow -PassThru
$handle = $process.Handle # cache process.Handle, otherwise ExitCode is null from powershell processes
# wait for complete
$Timeout = [System.TimeSpan]::FromSeconds(($TimeoutSeconds))
if (-not $process.WaitForExit($Timeout.TotalMilliseconds)) {
Write-Host -ForegroundColor Red "Process Timed out after $TimeoutSeconds seconds, use '-TimeoutSeconds' to specify a different timeout"
Invoke-KillProcessTree $process.id
}
if ($IsLinux -or $IsMacOS) {
Start-Sleep -Seconds 5 # On nix, the last 4 lines of stdout get overwritten upon return so pause for a bit to ensure user can view results
}
# Get Process result
return $process.ExitCode
}
finally {
if ($null -ne $process) { $process.Dispose() }
}
}
}
@@ -10,10 +10,6 @@ function Get-InputArgs([hashtable]$ip, $customInputArgs, $PathToAtomicsFolder) {
$defaultArgs.set_Item($key, $customInputArgs[$key])
}
}
# Replace $PathToAtomicsFolder or PathToAtomicsFolder with the actual -PathToAtomicsFolder value
foreach ($key in $defaultArgs.Clone().Keys) {
$defaultArgs.set_Item($key, ($defaultArgs[$key] -replace "\`$PathToAtomicsFolder", $PathToAtomicsFolder -replace "PathToAtomicsFolder", $PathToAtomicsFolder))
}
$defaultArgs
}
@@ -27,5 +23,9 @@ function Merge-InputArgs($finalCommand, $test, $customInputArgs, $PathToAtomicsF
$finalCommand = $finalCommand.Replace($findValue, $inputArgs[$key])
}
}
# Replace $PathToAtomicsFolder or PathToAtomicsFolder with the actual -PathToAtomicsFolder value
$finalCommand = ($finalCommand -replace "\`$PathToAtomicsFolder", $PathToAtomicsFolder) -replace "PathToAtomicsFolder", $PathToAtomicsFolder
$finalCommand
}
}
@@ -32,12 +32,17 @@ function Invoke-AtomicTest {
$AtomicTechnique,
[Parameter(Mandatory = $false,
Position = 1,
ValueFromPipelineByPropertyName = $true,
ParameterSetName = 'technique')]
[switch]
$ShowDetails,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
ParameterSetName = 'technique')]
[switch]
$ShowDetailsBrief,
[Parameter(Mandatory = $false,
ParameterSetName = 'technique')]
[String[]]
@@ -89,7 +94,12 @@ function Invoke-AtomicTest {
[Parameter(Mandatory = $false,
ParameterSetName = 'technique')]
[HashTable]
$InputArgs
$InputArgs,
[Parameter(Mandatory = $false,
ParameterSetName = 'technique')]
[Int]
$TimeoutSeconds = 120
)
BEGIN { } # Intentionally left blank and can be removed
PROCESS {
@@ -166,38 +176,43 @@ function Invoke-AtomicTest {
continue
}
$testId = "$AT-$testCount $($test.name)"
if ($ShowDetails) {
Show-Details $test $testCount $technique $InputArgs $PathToAtomicsFolder
continue
}
if($ShowDetailsBrief){
Write-KeyValue $testId
continue
}
Write-Debug -Message 'Gathering final Atomic test command'
$testId = "$AT-$testCount $($test.name)"
if ($CheckPrereqs) {
Write-KeyValue "CheckPrereq's for: " $testId
$failureReasons = Invoke-CheckPrereqs $test $isElevated $InputArgs $PathToAtomicsFolder
$failureReasons = Invoke-CheckPrereqs $test $isElevated $InputArgs $PathToAtomicsFolder $TimeoutSeconds
Write-PrereqResults $FailureReasons $testId
}
elseif ($GetPrereqs) {
Write-KeyValue "GetPrereq's for: " $testId
if ($nul -eq $test.dependencies) { Write-KeyValue "No Preqs Defined"; continue}
if ($nul -eq $test.dependencies) { Write-KeyValue "No Preqs Defined"; continue }
foreach ($dep in $test.dependencies) {
$executor = Get-PrereqExecutor $test
$description = (Merge-InputArgs $dep.description $test $InputArgs $PathToAtomicsFolder).trim()
Write-KeyValue "Attempting to satisfy prereq: " $description
$final_command_prereq = Merge-InputArgs $dep.prereq_command $test $InputArgs $PathToAtomicsFolder
$final_command_prereq = ($final_command_prereq.trim()).Replace("`n", " && ")
$final_command_get_prereq = Merge-InputArgs $dep.get_prereq_command $test $InputArgs $PathToAtomicsFolder
$success = Invoke-ExecuteCommand $final_command_prereq $executor
if ($success) {
$res = Invoke-ExecuteCommand $final_command_prereq $executor $TimeoutSeconds
if ($res -eq 0) {
Write-KeyValue "Prereq already met: " $description
}
else {
$retval = Invoke-ExecuteCommand $final_command_get_prereq $executor
$success = Invoke-ExecuteCommand $final_command_prereq $executor
if ($success) {
$res = Invoke-ExecuteCommand $final_command_get_prereq $executor $TimeoutSeconds
$res = Invoke-ExecuteCommand $final_command_prereq $executor $TimeoutSeconds
if ($res -eq 0) {
Write-KeyValue "Prereq successfully met: " $description
}
else {
@@ -210,18 +225,18 @@ function Invoke-AtomicTest {
}
}
elseif ($Cleanup) {
Write-KeyValue "Executing Cleanup for Test: " $testId
Write-KeyValue "Executing cleanup for test: " $testId
$final_command = Merge-InputArgs $test.executor.cleanup_command $test $InputArgs $PathToAtomicsFolder
Invoke-ExecuteCommand $final_command $test.executor.name | Out-Null
Write-KeyValue "Done"
$res = Invoke-ExecuteCommand $final_command $test.executor.name $TimeoutSeconds
Write-KeyValue "Done executing cleanup for test: " $testId
}
else {
Write-KeyValue "Executing Test: " $testId
Write-KeyValue "Executing test: " $testId
$startTime = get-date
$final_command = Merge-InputArgs $test.executor.command $test $InputArgs $PathToAtomicsFolder
Invoke-ExecuteCommand $final_command $test.executor.name | Out-Null
Write-ExecutionLog $startTime $AT $testCount $testName $ExecutionLogPath
Write-KeyValue "Done"
$res = Invoke-ExecuteCommand $final_command $test.executor.name $TimeoutSeconds
Write-ExecutionLog $startTime $AT $testCount $testName $ExecutionLogPath $TimeoutSeconds
Write-KeyValue "Done executing test: " $testId
}
} # End of foreach Test in single Atomic Technique
@@ -238,7 +253,7 @@ function Invoke-AtomicTest {
$AllAtomicTests.GetEnumerator() | Foreach-Object { Invoke-AtomicTestSingle $_ }
}
if ( ($Force -or $CheckPrereqs -or $ShowDetails -or $GetPrereqs) -or $psCmdlet.ShouldContinue( 'Do you wish to execute all tests?',
if ( ($Force -or $CheckPrereqs -or $ShowDetails -or $ShowDetailsBrief -or $GetPrereqs) -or $psCmdlet.ShouldContinue( 'Do you wish to execute all tests?',
"Highway to the danger zone, Executing All Atomic Tests!" ) ) {
Invoke-AllTests
}
@@ -249,4 +264,5 @@ function Invoke-AtomicTest {
} # End of PROCESS block
END { } # Intentionally left blank and can be removed
}
}
# Invoke-AtomicTest t1003 -TestNumbers 13 -GetPrereqs
@@ -46,6 +46,16 @@ Force
`Install-AtomicRedTeam -Force`
RepoOwner
- Install ART from another repo. Default RepoOwner is "redcanaryco"
`Install-AtomicRedTeam -RepoOwner clr2of8`
Branch
- Install ART from another branch. Default Branch is "master"
`Install-AtomicRedTeam -RepoOwner clr2of8 -Branch start-process-branch`
### Manual Installation
@@ -69,12 +79,26 @@ Import-Module C:\AtomicRedTeam\execution-frameworks\Invoke-AtomicRedTeam\Invoke-
Note: Your path to the **_Invoke-AtomicRedTeam.psm1_** may be different.
#### Execute All Tests
Execute all Atomic tests:
#### Display Test Details for the given Technique Number without Executing any Tests
```powershell
Invoke-AtomicTest All
Invoke-AtomicTest T1089 -ShowDetails
```
Using the `ShowDetails` switch causes the test details to be printed to the screen and allows for easy copy and paste execution.
Note: you may need to change the path where the test definitions are found with the `PathToAtomicsFolder` parameter.
#### Display Only Test Names and Numbers
```powershell
Invoke-AtomicTest All -ShowDetailsBrief
```
#### Execute All Attacks for a Given Technique
```powershell
Invoke-AtomicTest T1117
```
This assumes your atomics folder is in the default location of `<BASEPATH>\AtomicRedTeam\atomics`
@@ -89,29 +113,6 @@ $PSDefaultParameterValues = @{"Invoke-AtomicTest:PathToAtomicsFolder"="C:\Users\
Tip: Add this to your PowerShell profile so it is always set to your preferred default value.
#### Execute All Tests - Specific Directory
Specify a path to atomics folder, example C:\AtomicRedTeam\atomics
```powershell
Invoke-AtomicTest All -PathToAtomicsFolder C:\AtomicRedTeam\atomics
```
#### Display Test Details without Executing the Test
```powershell
Invoke-AtomicTest All -ShowDetails
```
Using the `ShowDetails` switch causes the test details to be printed to the screen and allows for easy copy and paste execution.
Note: you may need to change the path where the test definitions are found with the `PathToAtomicsFolder` parameter.
#### Execute All Attacks for a Given Technique
```powershell
Invoke-AtomicTest T1117
```
#### Execute Specific Attacks (by Attack Number) for a Given Technique
```powershell
@@ -124,7 +125,29 @@ Invoke-AtomicTest T1117 -TestNumbers 1, 2
Invoke-AtomicTest T1117 -TestNames "Regsvr32 remote COM scriptlet execution","Regsvr32 local DLL execution"
```
By default, test execution details are written to `Invoke-AtomicTest-ExecutionLog.csv` in the current directory.
#### Speficy a Process Timeout
```powershell
Invoke-AtomicTest T1117 -TimeoutSeconds 15
```
If the attack commands do not exit (return) within in the specified `-TimeoutSeconds`, the process and it's children will be forcefully terminated. The default value of `-TimeoutSeconds` is 120. This allows the `Invoke-AtomicTest` script to move on to the next test.
#### Execute All Tests
Execute all Atomic tests:
```powershell
Invoke-AtomicTest All
```
#### Execute All Tests from a Specific Directory
Specify a path to atomics folder, example C:\AtomicRedTeam\atomics
```powershell
Invoke-AtomicTest All -PathToAtomicsFolder C:\AtomicRedTeam\atomics
```
#### Specify an Alternate Path for the Execution Log
@@ -142,7 +165,7 @@ Invoke-AtomicTest T1117 -TestNumber 1 -CheckPrereqs
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 }**
For the "powershell" executor, the prereq_command's are run as a script block and the script must exit 0 if the pre-requisites are met. Example: **if(Test-Path C:\Windows\System32\cmd.exe) { exit 0 } else { exit 1 }**
Pre-requisites will also be reported as not met if the test is defined with `elevation_required: true` but the current context is not elevated. You can still execute an attack even if the pre-requisites are not met but execution may fail.
@@ -41,6 +41,12 @@ function Install-AtomicRedTeam {
[Parameter(Mandatory = $False, Position = 1)]
[string]$DownloadPath = $( if ($IsLinux -or $IsMacOS) { $Env:HOME + "/AtomicRedTeam" } else { $env:HOMEDRIVE + "\AtomicRedTeam" }),
[Parameter(Mandatory = $False, Position = 2)]
[string]$RepoOwner = "redcanaryco",
[Parameter(Mandatory = $False, Position = 3)]
[string]$Branch = "master",
[Parameter(Mandatory = $False)]
[switch]$Force = $False # delete the existing install directory and reinstall
)
@@ -60,19 +66,20 @@ function Install-AtomicRedTeam {
New-Item -ItemType directory -Path $InstallPath | Out-Null
write-verbose "Setting variables for remote URL and download Path"
$url = "https://github.com/redcanaryco/atomic-red-team/archive/master.zip"
$path = Join-Path $DownloadPath "master.zip"
$url = "https://github.com/$RepoOwner/atomic-red-team/archive/$Branch.zip"
$path = Join-Path $DownloadPath "$Branch.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 $InstallPath"
$lp = Join-Path "$DownloadPath" "master.zip"
$lp = Join-Path "$DownloadPath" "$Branch.zip"
expand-archive -LiteralPath $lp -DestinationPath "$InstallPath" -Force:$Force
$unzipPath = Join-Path $InstallPath "atomic-red-team-master"
$unzipPath = Join-Path $InstallPath "atomic-red-team-$Branch"
Get-ChildItem $unzipPath -Force | Move-Item -dest $InstallPath
Remove-Item $unzipPath
Remove-Item $path
if (-not (Get-InstalledModule -Name "powershell-yaml" -ErrorAction:SilentlyContinue)) {
write-verbose "Installing powershell-yaml"
@@ -83,7 +90,7 @@ function Install-AtomicRedTeam {
Import-Module $modulePath -Force
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
Write-Host "See README at https://github.com/$RepoOwner/atomic-red-team/tree/$Branch/execution-frameworks/Invoke-AtomicRedTeam for complete details" -Fore Yellow
}
else {