PowerUp update:

-Standardized documentation, including adding output object types and required dependencies to all functions
-Added Get-ProcessTokenPrivilege to enumerate the current (or remote) process token privileges, replacing Get-CurrentUserTokenGroupSid
-Added Enable-Privilege to enable privileges using RtlAdjustPrivilege
-Added @enigma0x3's Invoke-WScriptUACBypass function
-Renamed Invoke-AllChecks to Invoke-PrivescAudit, added alias mapping
-Added tests for Get-ProcessTokenPrivilege, Enable-Privilege, and Invoke-WScriptUACBypass
-Renamed helper functions for consistency
-Passes PSScriptAnalyzer!
This commit is contained in:
HarmJ0y 2016-12-12 14:25:20 -05:00
parent 863699d97e
commit eae4695b13
3 changed files with 2253 additions and 1150 deletions

File diff suppressed because one or more lines are too long

View File

@ -10,7 +10,7 @@ ModuleVersion = '3.0.0.0'
GUID = 'efb2a78f-a069-4bfd-91c2-7c7c0c225f56' GUID = 'efb2a78f-a069-4bfd-91c2-7c7c0c225f56'
# Author of this module # Author of this module
Author = 'Will Schroeder' Author = 'Will Schroeder (@harmj0y)'
# Copyright statement for this module # Copyright statement for this module
Copyright = 'BSD 3-Clause' Copyright = 'BSD 3-Clause'
@ -24,37 +24,40 @@ PowerShellVersion = '2.0'
# Functions to export from this module # Functions to export from this module
FunctionsToExport = @( FunctionsToExport = @(
'Add-ServiceDacl', 'Add-ServiceDacl',
'Enable-Privilege',
'Find-PathDLLHijack', 'Find-PathDLLHijack',
'Find-ProcessDLLHijack', 'Find-ProcessDLLHijack',
'Get-ApplicationHost', 'Get-ApplicationHost',
'Get-CachedGPPPassword', 'Get-CachedGPPPassword',
'Get-CurrentUserTokenGroupSid',
'Get-ModifiablePath', 'Get-ModifiablePath',
'Get-ModifiableRegistryAutoRun', 'Get-ModifiableRegistryAutoRun',
'Get-ModifiableScheduledTaskFile', 'Get-ModifiableScheduledTaskFile',
'Get-ModifiableService', 'Get-ModifiableService',
'Get-ModifiableServiceFile', 'Get-ModifiableServiceFile',
'Get-ProcessTokenGroup',
'Get-ProcessTokenPrivilege',
'Get-RegistryAlwaysInstallElevated', 'Get-RegistryAlwaysInstallElevated',
'Get-RegistryAutoLogon', 'Get-RegistryAutoLogon',
'Get-ServiceDetail', 'Get-ServiceDetail',
'Get-ServiceUnquoted',
'Get-SiteListPassword', 'Get-SiteListPassword',
'Get-System', 'Get-TokenInformation',
'Get-UnquotedService',
'Get-UnattendedInstallFile', 'Get-UnattendedInstallFile',
'Get-Webconfig', 'Get-WebConfig',
'Install-ServiceBinary', 'Install-ServiceBinary',
'Invoke-AllChecks',
'Invoke-ServiceAbuse', 'Invoke-ServiceAbuse',
'Invoke-WScriptUACBypass',
'Invoke-PrivescAudit',
'Restore-ServiceBinary', 'Restore-ServiceBinary',
'Set-ServiceBinPath', 'Set-ServiceBinaryPath',
'Test-ServiceDaclPermission', 'Test-ServiceDaclPermission',
'Write-UserAddMSI',
'Write-HijackDll', 'Write-HijackDll',
'Write-ServiceBinary', 'Write-ServiceBinary',
'Write-UserAddMSI' 'Get-System'
) )
# List of all files packaged with this module # List of all files packaged with this module
FileList = 'Privesc.psm1', 'Get-System.ps1', 'PowerUp.ps1', 'README.md' FileList = 'Privesc.psm1', 'Get-System.ps1', 'PowerUp.ps1', 'README.md'
} }

View File

@ -126,37 +126,139 @@ Describe 'Get-ModifiablePath' {
} }
} }
Describe 'Get-CurrentUserTokenGroupSid' {
Describe 'Get-ProcessTokenGroup' {
if (-not $(Test-IsAdmin)) { if (-not $(Test-IsAdmin)) {
Throw "'Get-CurrentUserTokenGroupSid' Pester test needs local administrator privileges." Throw "'Get-ProcessTokenGroup' Pester test needs local administrator privileges."
} }
It 'Should not throw.' { It 'Should not throw.' {
{Get-CurrentUserTokenGroupSid} | Should Not Throw {Get-ProcessTokenGroup} | Should Not Throw
} }
It 'Should return SIDs and Attributes.' { It 'Should return SID, Attribute, and ProcessID.' {
$Output = Get-CurrentUserTokenGroupSid | Select-Object -First 1 $Output = Get-ProcessTokenGroup | Select-Object -First 1
if ($Output.PSObject.Properties.Name -notcontains 'SID') { if ($Output.PSObject.Properties.Name -notcontains 'SID') {
Throw "Get-CurrentUserTokenGroupSid result doesn't contain 'SID' field." Throw "Get-ProcessTokenGroup result doesn't contain 'SID' field."
} }
if ($Output.PSObject.Properties.Name -notcontains 'Attributes') { if ($Output.PSObject.Properties.Name -notcontains 'Attributes') {
Throw "Get-CurrentUserTokenGroupSid result doesn't contain 'Attributes' field." Throw "Get-ProcessTokenGroup result doesn't contain 'Attributes' field."
}
if ($Output.PSObject.Properties.Name -notcontains 'ProcessID') {
Throw "Get-ProcessTokenGroup result doesn't contain 'ProcessID' field."
}
}
It 'Should accept a process object on the pipeline.' {
$Output = Get-Process -Id $PID | Get-ProcessTokenGroup | Select-Object -First 1
$Output | Should Not BeNullOrEmpty
}
It 'Should accept multiple process objects on the pipeline.' {
$Output = @($(Get-Process -Id $PID), $(Get-Process -Id $PID)) | Get-ProcessTokenGroup | Where-Object {$_.SID -match 'S-1-5-32-544'}
if ($Output.Length -lt 2) {
Throw "'Get-ProcessTokenGroup' doesn't return Dacls for multiple service objects on the pipeline."
} }
} }
It 'Should return the local administrators group SID.' { It 'Should return the local administrators group SID.' {
$CurrentUserSids = Get-CurrentUserTokenGroupSid | Select-Object -ExpandProperty SID $CurrentUserSids = Get-ProcessTokenGroup | Select-Object -ExpandProperty SID
if ($CurrentUserSids -notcontains 'S-1-5-32-544') { if ($CurrentUserSids -notcontains 'S-1-5-32-544') {
Throw "Get-CurrentUserTokenGroupSid result doesn't contain local administrators 'S-1-5-32-544' sid" Throw "Get-ProcessTokenGroup result doesn't contain local administrators 'S-1-5-32-544' sid"
} }
} }
} }
Describe 'Get-ProcessTokenPrivilege' {
if (-not $(Test-IsAdmin)) {
Throw "'Get-ProcessTokenPrivilege' Pester test needs local administrator privileges."
}
It 'Should not throw.' {
{Get-ProcessTokenPrivilege} | Should Not Throw
}
It 'Should return Privilege, Attribute, and ProcessID.' {
$Output = Get-ProcessTokenPrivilege | Select-Object -First 1
if ($Output.PSObject.Properties.Name -notcontains 'Privilege') {
Throw "Get-ProcessTokenPrivilege result doesn't contain 'Privilege' field."
}
if ($Output.PSObject.Properties.Name -notcontains 'Attributes') {
Throw "Get-ProcessTokenPrivilege result doesn't contain 'Attributes' field."
}
if ($Output.PSObject.Properties.Name -notcontains 'ProcessID') {
Throw "Get-ProcessTokenPrivilege result doesn't contain 'ProcessID' field."
}
}
It 'Should accept the -Special argument' {
$Output = Get-Process -Id $PID | Get-ProcessTokenPrivilege -Special | Select-Object -First 1
$Output | Should Not BeNullOrEmpty
}
It 'Should accept a process object on the pipeline.' {
$Output = Get-Process -Id $PID | Get-ProcessTokenPrivilege | Select-Object -First 1
$Output | Should Not BeNullOrEmpty
}
It 'Should accept multiple process objects on the pipeline.' {
$Output = @($(Get-Process -Id $PID), $(Get-Process -Id $PID)) | Get-ProcessTokenPrivilege | Where-Object {$_.Privilege -match 'SeShutdownPrivilege'}
if ($Output.Length -lt 2) {
Throw "'Get-ProcessTokenPrivilege' doesn't return Dacls for multiple service objects on the pipeline."
}
}
It 'Should return the correct privileges.' {
$Privileges = Get-ProcessTokenPrivilege | Select-Object -ExpandProperty Privilege
if ($Privileges -NotContains 'SeShutdownPrivilege') {
Throw "Get-ProcessTokenPrivilege result doesn't the SeShutdownPrivilege"
}
}
}
Describe 'Enable-Privilege' {
if (-not $(Test-IsAdmin)) {
Throw "'Enable-Privilege' Pester test needs local administrator privileges."
}
It 'Should not accept an invalid privilege.' {
{Enable-Privilege -Privilege 'nonexistent'} | Should Throw
}
It 'Should successfully enable a specified privilege.' {
$Output = Get-ProcessTokenPrivilege | Where-Object {$_.Privilege -match 'SeShutdownPrivilege'}
if ($Output.Attributes -ne 0) {
Throw "'SeShutdownPrivilege is already enabled."
}
{Enable-Privilege -Privilege 'SeShutdownPrivilege'} | Should Not Throw
$Output = Get-ProcessTokenPrivilege | Where-Object {$_.Privilege -match 'SeShutdownPrivilege'}
if ($Output.Attributes -eq 0) {
Throw "'SeShutdownPrivilege not successfully enabled."
}
}
It 'Should accept the output from Get-ProcessTokenPrivilege.' {
{Get-ProcessTokenPrivilege | Enable-Privilege} | Should Not Throw
$Output = Get-ProcessTokenPrivilege | Where-Object {$_.Privilege -match 'SeBackupPrivilege'}
if ($Output.Attributes -eq 0) {
Throw "'SeBackupPrivilege not successfully enabled."
}
}
}
Describe 'Add-ServiceDacl' { Describe 'Add-ServiceDacl' {
if (-not $(Test-IsAdmin)) { if (-not $(Test-IsAdmin)) {
@ -238,10 +340,10 @@ Describe 'Add-ServiceDacl' {
} }
} }
Describe 'Set-ServiceBinPath' { Describe 'Set-ServiceBinaryPath' {
if (-not $(Test-IsAdmin)) { if (-not $(Test-IsAdmin)) {
Throw "'Set-ServiceBinPath' Pester test needs local administrator privileges." Throw "'Set-ServiceBinaryPath' Pester test needs local administrator privileges."
} }
It 'Should fail for a non-existent service.' { It 'Should fail for a non-existent service.' {
@ -249,13 +351,13 @@ Describe 'Set-ServiceBinPath' {
$ServicePath = 'C:\Program Files\service.exe' $ServicePath = 'C:\Program Files\service.exe'
$Result = $False $Result = $False
{$Result = Set-ServiceBinPath -Name $ServiceName -binPath $ServicePath} | Should Throw {$Result = Set-ServiceBinaryPath -Name $ServiceName -Path $ServicePath} | Should Throw
$Result | Should Be $False $Result | Should Be $False
} }
It 'Should throw with an empty binPath.' { It 'Should throw with an empty Path.' {
$ServiceName = Get-RandomName $ServiceName = Get-RandomName
{Set-ServiceBinPath -Name $ServiceName -binPath ''} | Should Throw {Set-ServiceBinaryPath -Name $ServiceName -Path ''} | Should Throw
} }
It 'Should correctly set a service binary path.' { It 'Should correctly set a service binary path.' {
@ -264,7 +366,7 @@ Describe 'Set-ServiceBinPath' {
sc.exe create $ServiceName binPath= $ServicePath | Should Match 'SUCCESS' sc.exe create $ServiceName binPath= $ServicePath | Should Match 'SUCCESS'
Start-Sleep -Seconds 1 Start-Sleep -Seconds 1
$Result = Set-ServiceBinPath -Name $ServiceName -binPath $ServicePath $Result = Set-ServiceBinaryPath -Name $ServiceName -Path $ServicePath
$Result | Should Be $True $Result | Should Be $True
$ServiceDetails = Get-WmiObject -Class win32_service -Filter "Name='$ServiceName'" $ServiceDetails = Get-WmiObject -Class win32_service -Filter "Name='$ServiceName'"
$ServiceDetails.PathName | Should be $ServicePath $ServiceDetails.PathName | Should be $ServicePath
@ -278,7 +380,7 @@ Describe 'Set-ServiceBinPath' {
sc.exe create $ServiceName binPath= $ServicePath | Should Match 'SUCCESS' sc.exe create $ServiceName binPath= $ServicePath | Should Match 'SUCCESS'
Start-Sleep -Seconds 1 Start-Sleep -Seconds 1
$Result = $ServiceName | Set-ServiceBinPath -binPath $ServicePath $Result = $ServiceName | Set-ServiceBinaryPath -Path $ServicePath
$Result | Should Be $True $Result | Should Be $True
$ServiceDetails = Get-WmiObject -Class win32_service -Filter "Name='$ServiceName'" $ServiceDetails = Get-WmiObject -Class win32_service -Filter "Name='$ServiceName'"
@ -293,7 +395,7 @@ Describe 'Set-ServiceBinPath' {
sc.exe create $ServiceName binPath= $ServicePath | Should Match 'SUCCESS' sc.exe create $ServiceName binPath= $ServicePath | Should Match 'SUCCESS'
Start-Sleep -Seconds 1 Start-Sleep -Seconds 1
$Result = Get-Service $ServiceName | Set-ServiceBinPath -binPath $ServicePath $Result = Get-Service $ServiceName | Set-ServiceBinaryPath -Path $ServicePath
$Result | Should Be $True $Result | Should Be $True
$ServiceDetails = Get-WmiObject -Class win32_service -Filter "Name='$ServiceName'" $ServiceDetails = Get-WmiObject -Class win32_service -Filter "Name='$ServiceName'"
@ -445,14 +547,14 @@ Describe 'Test-ServiceDaclPermission' {
# #
######################################################## ########################################################
Describe 'Get-ServiceUnquoted' { Describe 'Get-UnquotedService' {
if (-not $(Test-IsAdmin)) { if (-not $(Test-IsAdmin)) {
Throw "'Get-ServiceUnquoted' Pester test needs local administrator privileges." Throw "'Get-UnquotedService' Pester test needs local administrator privileges."
} }
It "Should not throw." { It "Should not throw." {
{Get-ServiceUnquoted} | Should Not Throw {Get-UnquotedService} | Should Not Throw
} }
It 'Should return service with a space in an unquoted binPath.' { It 'Should return service with a space in an unquoted binPath.' {
@ -463,7 +565,7 @@ Describe 'Get-ServiceUnquoted' {
sc.exe create $ServiceName binPath= $ServicePath | Should Match 'SUCCESS' sc.exe create $ServiceName binPath= $ServicePath | Should Match 'SUCCESS'
Start-Sleep -Seconds 1 Start-Sleep -Seconds 1
$Output = Get-ServiceUnquoted | Where-Object { $_.ServiceName -eq $ServiceName } $Output = Get-UnquotedService | Where-Object { $_.ServiceName -eq $ServiceName }
sc.exe delete $ServiceName | Should Match 'SUCCESS' sc.exe delete $ServiceName | Should Match 'SUCCESS'
$Output | Should Not BeNullOrEmpty $Output | Should Not BeNullOrEmpty
@ -478,7 +580,7 @@ Describe 'Get-ServiceUnquoted' {
sc.exe create $ServiceName binPath= $ServicePath | Should Match 'SUCCESS' sc.exe create $ServiceName binPath= $ServicePath | Should Match 'SUCCESS'
Start-Sleep -Seconds 1 Start-Sleep -Seconds 1
$Output = Get-ServiceUnquoted | Where-Object { $_.ServiceName -eq $ServiceName } $Output = Get-UnquotedService | Where-Object { $_.ServiceName -eq $ServiceName }
sc.exe delete $ServiceName | Should Match 'SUCCESS' sc.exe delete $ServiceName | Should Match 'SUCCESS'
$Output | Should BeNullOrEmpty $Output | Should BeNullOrEmpty
@ -883,6 +985,7 @@ Describe 'Install-ServiceBinary' {
} }
} }
# TODO: Describe 'Restore-ServiceBinary' {}
######################################################## ########################################################
# #
@ -1262,14 +1365,39 @@ Describe 'Get-CachedGPPPassword' {
} }
} }
# TODO: Describe 'Write-UserAddMSI' {}
Describe 'Invoke-AllChecks' { Describe 'Invoke-WScriptUACBypass' {
$OSVersion = [Environment]::OSVersion.Version
if (($OSVersion -ge (New-Object 'Version' 6,0)) -and ($OSVersion -lt (New-Object 'Version' 6,2))) {
It 'Should launch an elevated command.' {
Invoke-WScriptUACBypass -Command 'powershell -enc JwAxADIAMwAnACAAfAAgAE8AdQB0AC0ARgBpAGwAZQAgAC0ARgBpAGwAZQBQAGEAdABoACAAIgBDADoAXABXAGkAbgBkAG8AdwBzAFwAUwB5AHMAdABlAG0AMwAyAFwAcwBrAGEAZABqAGYAbgAuAHQAeAB0ACIA'
if (-not (Test-Path -Path "C:\Windows\System32\skadjfn.txt")) {
Throw "'Invoke-WScriptUACBypass' did not write a privileged file."
}
{Test-Path -Path "C:\Windows\System32\skadjfn.txt"} | Should Not Throw
Remove-Item -Path "C:\Windows\System32\skadjfn.txt" -Force
}
It "Should accept -WindowStyle 'Visible'" {
Invoke-WScriptUACBypass -Command notepad.exe -WindowStyle 'Visible'
$Process = Get-Process 'notepad'
$Process | Should Not BeNullOrEmpty
$Process | Stop-Process -Force
}
}
else {
Write-Warning 'Target machine is not vulnerable to Invoke-WScriptUACBypass.'
}
}
Describe 'Invoke-PrivescAudit' {
It 'Should return results to stdout.' { It 'Should return results to stdout.' {
$Output = Invoke-AllChecks $Output = Invoke-PrivescAudit
$Output | Should Not BeNullOrEmpty $Output | Should Not BeNullOrEmpty
} }
It 'Should produce a HTML report with -HTMLReport.' { It 'Should produce a HTML report with -HTMLReport.' {
$Output = Invoke-AllChecks -HTMLReport $Output = Invoke-PrivescAudit -HTMLReport
$Output | Should Not BeNullOrEmpty $Output | Should Not BeNullOrEmpty
$HtmlReportFile = "$($Env:ComputerName).$($Env:UserName).html" $HtmlReportFile = "$($Env:ComputerName).$($Env:UserName).html"