diff --git a/Invoke-AtomicRedTeam.psd1 b/Invoke-AtomicRedTeam.psd1 index 7b9daab..b62446a 100644 --- a/Invoke-AtomicRedTeam.psd1 +++ b/Invoke-AtomicRedTeam.psd1 @@ -41,6 +41,7 @@ 'New-AtomicTestDependency', 'Start-AtomicGUI', 'Stop-AtomicGUI', + 'Set-Sudo' 'Invoke-SetupAtomicRunner', 'Invoke-GenerateNewSchedule', 'Invoke-RefreshExistingSchedule', diff --git a/Private/Invoke-ExecuteCommand.ps1 b/Private/Invoke-ExecuteCommand.ps1 index 13686a6..1fb0a1c 100644 --- a/Private/Invoke-ExecuteCommand.ps1 +++ b/Private/Invoke-ExecuteCommand.ps1 @@ -1,11 +1,18 @@ -function Invoke-ExecuteCommand ($finalCommand, $executor, $executionPlatform, $TimeoutSeconds, $session = $null, $interactive) { +function Invoke-ExecuteCommand ($finalCommand, $executor, $elevationreq, $can_sudo, $executionPlatform, $TimeoutSeconds, $session = $null, $interactive) { $null = @( if ($null -eq $finalCommand) { return 0 } $finalCommand = $finalCommand.trim() Write-Verbose -Message 'Invoking Atomic Tests using defined executor' if ($executor -eq "command_prompt" -or $executor -eq "sh" -or $executor -eq "bash") { - $execPrefix = "-c" - $execExe = $executor + if (($executor -eq "sh" -or $executor -eq "bash") -and ($elevationreq -eq $true) -and ($can_sudo -eq $true)) { + $execExe = "$(which sudo)" + $execPrefix = "$(which $executor) -c" + } + else { + $execExe = $executor + $execPrefix = "-c" + } + if ($executor -eq "command_prompt") { $execPrefix = "/c"; $execExe = "cmd.exe"; diff --git a/Public/Invoke-AtomicRunner.ps1 b/Public/Invoke-AtomicRunner.ps1 index dbe9da4..6d3620a 100755 --- a/Public/Invoke-AtomicRunner.ps1 +++ b/Public/Invoke-AtomicRunner.ps1 @@ -95,8 +95,13 @@ function Invoke-AtomicRunner { LogRunnerMsg "exiting script because $($artConfig.stopFile) exists" exit } - if ($IsLinux) { + # Check if linux Host can use sudo without a password. + $can_sudo = Set-Sudo($false) + if($can_sudo){ + if ($shouldRename) { Invoke-Expression $("sudo hostnamectl set-hostname $newHostName") } + Invoke-Expression $("sudo shutdown -r now") + } if ($shouldRename) { Invoke-Expression $("hostnamectl set-hostname $newHostName") } Invoke-Expression $("shutdown -r now") } diff --git a/Public/Invoke-AtomicTest.ps1 b/Public/Invoke-AtomicTest.ps1 index a567507..c4f6644 100644 --- a/Public/Invoke-AtomicTest.ps1 +++ b/Public/Invoke-AtomicTest.ps1 @@ -429,6 +429,8 @@ function Invoke-AtomicTest { Write-Debug -Message 'Gathering final Atomic test command' + # Check if linux Host can use sudo without a password. + $can_sudo = Set-Sudo($false) if ($CheckPrereqs) { Write-KeyValue "CheckPrereq's for: " $testId @@ -441,7 +443,12 @@ function Invoke-AtomicTest { } Write-KeyValue "GetPrereq's for: " $testId if ( $test.executor.elevation_required -and -not $isElevated) { + if ($can_sudo -eq $true) { + Write-Host -ForegroundColor Yellow "Elevation required but not provided, but host supports passwordless sudo" + } + else{ Write-Host -ForegroundColor Red "Elevation required but not provided" + } } if ($nul -eq $test.dependencies) { Write-KeyValue "No Preqs Defined"; continue } foreach ($dep in $test.dependencies) { @@ -484,7 +491,7 @@ function Invoke-AtomicTest { $startTime = Get-Date $final_command = Merge-InputArgs $test.executor.command $test $InputArgs $PathToPayloads if (Get-Command 'Invoke-ARTPreAtomicHook' -errorAction SilentlyContinue) { Invoke-ARTPreAtomicHook $test $InputArgs } - $res = Invoke-ExecuteCommand $final_command $test.executor.name $executionPlatform $TimeoutSeconds $session -Interactive:$Interactive + $res = Invoke-ExecuteCommand $final_command $test.executor.name $test.executor.elevation_required $can_sudo $executionPlatform $TimeoutSeconds $session -Interactive:$Interactive Write-Host "Exit code: $($res.ExitCode)" if (Get-Command 'Invoke-ARTPostAtomicHook' -errorAction SilentlyContinue) { Invoke-ARTPostAtomicHook $test $InputArgs } $stopTime = Get-Date diff --git a/Public/Invoke-SetupAtomicRunner.ps1 b/Public/Invoke-SetupAtomicRunner.ps1 index aa0b343..be3369d 100755 --- a/Public/Invoke-SetupAtomicRunner.ps1 +++ b/Public/Invoke-SetupAtomicRunner.ps1 @@ -12,7 +12,13 @@ function Invoke-SetupAtomicRunner { } else { # linux and macos check - doesn't auto-elevate - if ((id -u) -ne 0 ) { + # Check if current user has passwordless sudo privleges. If not, attempt to configure it for current user. + $can_sudo = Set-Sudo($true) + if ($can_sudo -eq $true -and (sudo id -u) -ne 0 ) { + Throw "You must run the Invoke-SetupAtomicRunner script as root" + exit + } + elseif ($can_sudo -eq $false -and (id -u) -ne 0 ) { Throw "You must run the Invoke-SetupAtomicRunner script as root" exit } @@ -86,15 +92,20 @@ function Invoke-SetupAtomicRunner { } } else { + # sets cronjob string using basepath from config.ps1 $pwshPath = which pwsh - $job = "@reboot root sleep 60;$pwshPath -Command Invoke-KickoffAtomicRunner" + $job = "@reboot $env:USER sleep 60;$pwshPath -Command Invoke-KickoffAtomicRunner" $exists = cat /etc/crontab | Select-String -Quiet "KickoffAtomicRunner" #checks if the Kickoff-AtomicRunner job exists. If not appends it to the system crontab. - if ($null -eq $exists) { - $(Write-Output "$job" >> /etc/crontab) + if ($null -eq $exists -and $can_sudo -eq $true) { + $(Write-Output "$job" | sudo tee -a /etc/crontab) write-host "setting cronjob" } + elseif ($null -eq $exists -and $can_sudo -eq $false) { + $(Write-Output "$job" >> /etc/crontab) + write-host "setting cronjob" + } else { write-host "cronjob already exists" } @@ -102,6 +113,10 @@ function Invoke-SetupAtomicRunner { # Add Import-Module statement to the PowerShell profile $root = Split-Path $PSScriptRoot -Parent + if($IsLinux -or $IsMacOS){ + mkdir (Split-Path $PROFILE) + touch $PROFILE + } $pathToPSD1 = Join-Path $root "Invoke-AtomicRedTeam.psd1" $importStatement = "Import-Module ""$pathToPSD1"" -Force" New-Item $PROFILE -ErrorAction Ignore diff --git a/Public/Set-Sudo.ps1 b/Public/Set-Sudo.ps1 new file mode 100644 index 0000000..ea33f89 --- /dev/null +++ b/Public/Set-Sudo.ps1 @@ -0,0 +1,33 @@ +function Set-Sudo ($set_sudo) { + + $ErrorActionPreference = "Stop" + $env:SUDO_ASKPASS="/bin/false" + + try { + if ((sudo -A whoami) -and ((sudo grep -r $env:USER /etc/sudoers | grep NOPASSWD:ALL) -or (sudo grep -r $env:USER /etc/sudoers.d | grep NOPASSWD:ALL))){ + + if($set_sudo){ + Write-Host "Passwordless logon already configured.`n" + } + $nopassword_enabled = $true + + } + elseif ($set_sudo -eq $true){ + + Write-Host "Configuring Passwordless logon...`n" + Write-Output "$env:USER ALL=(ALL) NOPASSWD:ALL" > /tmp/90-$env:USER-sudo-access + sudo install -m 440 /tmp/90-$env:USER-sudo-access /etc/sudoers.d/90-$env:USER-sudo-access + rm -f /tmp/90-$env:USER-sudo-access + $nopassword_enabled = $true + } + else { + write-host "Host not configured for passwordless logon" + $nopassword_enabled = $false + } + } + catch { + write-host "Error configuring passwordless logon" + $nopassword_enabled = $false + } +return $nopassword_enabled +}