aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Privesc/PowerUp.ps1119
-rw-r--r--Tests/Privesc.tests.ps1131
2 files changed, 195 insertions, 55 deletions
diff --git a/Privesc/PowerUp.ps1 b/Privesc/PowerUp.ps1
index 0d71b14..a8f55e5 100644
--- a/Privesc/PowerUp.ps1
+++ b/Privesc/PowerUp.ps1
@@ -122,8 +122,7 @@ function Test-ServiceDaclPermission {
# check if sc.exe exists
if (-not (Test-Path ("$env:SystemRoot\system32\sc.exe"))){
- Write-Warning "[!] Could not find $env:SystemRoot\system32\sc.exe"
- return $False
+ throw [System.IO.FileNotFoundException] "$env:SystemRoot\system32\sc.exe not found"
}
$ServiceAccessFlags = @{
@@ -151,68 +150,60 @@ function Test-ServiceDaclPermission {
# make sure we got a result back
if (-not ($TargetService)){
- Write-Warning "[!] Target service '$ServiceName' not found on the machine"
- return $False
+ throw [System.Management.Instrumentation.InstanceNotFoundException] "Target service '$ServiceName' not found on the machine"
}
- try {
- # retrieve DACL from sc.exe
- $Result = sc.exe sdshow $TargetService.Name | where {$_}
+ # retrieve DACL from sc.exe (only possible if 'RC' DACL is set)
+ $Result = sc.exe sdshow $TargetService.Name | where {$_}
- if ($Result -like "*OpenService FAILED*"){
- Write-Warning "[!] Access to service $($TargetService.Name) denied"
- return $False
- }
+ if ($Result -like "*OpenService FAILED*"){
+ throw [System.Management.Automation.ApplicationFailedException] "Could not retrieve DACL permissions for '$($TargetService.Name)'"
+ }
- $SecurityDescriptors = New-Object System.Security.AccessControl.RawSecurityDescriptor($Result)
+ $SecurityDescriptors = New-Object System.Security.AccessControl.RawSecurityDescriptor($Result)
- # populate a list of group SIDs that the current user is a member of
- $Sids = whoami /groups /FO csv | ConvertFrom-Csv | select "SID" | ForEach-Object {$_.Sid}
+ # populate a list of group SIDs that the current user is a member of
+ $Sids = whoami /groups /FO csv | ConvertFrom-Csv | select "SID" | ForEach-Object {$_.Sid}
- # add to the list the SID of the current user
- $Sids += [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
+ # add to the list the SID of the current user
+ $Sids += [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
- ForEach ($Sid in $Sids){
- ForEach ($Ace in $SecurityDescriptors.DiscretionaryAcl){
-
- # check if the group/user SID is included in the ACE
- if ($Sid -eq $Ace.SecurityIdentifier){
-
- # convert the AccessMask to a service DACL string
- $DaclString = $($ServiceAccessFlags.Keys | Foreach-Object {
- if (($ServiceAccessFlags[$_] -band $Ace.AccessMask) -eq $ServiceAccessFlags[$_]) {
- $_
- }
- }) -join ""
-
- # convert the input DACL to an array
- $DaclArray = [array] ($Dacl -split '(.{2})' | Where-Object {$_})
-
- # counter to check how many DACL permissions were found
- $MatchedPermissions = 0
+ ForEach ($Sid in $Sids){
+ ForEach ($Ace in $SecurityDescriptors.DiscretionaryAcl){
+
+ # check if the group/user SID is included in the ACE
+ if ($Sid -eq $Ace.SecurityIdentifier){
- # check if each of the permissions exists
- ForEach ($DaclPermission in $DaclArray){
- if ($DaclString.Contains($DaclPermission.ToUpper())){
- $MatchedPermissions += 1
- }
- else{
- break
- }
+ # convert the AccessMask to a service DACL string
+ $DaclString = $($ServiceAccessFlags.Keys | Foreach-Object {
+ if (($ServiceAccessFlags[$_] -band $Ace.AccessMask) -eq $ServiceAccessFlags[$_]) {
+ $_
}
- # found all permissions - success
- if ($MatchedPermissions -eq $DaclArray.Count){
- return $True
+ }) -join ""
+
+ # convert the input DACL to an array
+ $DaclArray = [array] ($Dacl -split '(.{2})' | Where-Object {$_})
+
+ # counter to check how many DACL permissions were found
+ $MatchedPermissions = 0
+
+ # check if each of the permissions exists
+ ForEach ($DaclPermission in $DaclArray){
+ if ($DaclString.Contains($DaclPermission.ToUpper())){
+ $MatchedPermissions += 1
}
- }
- }
+ else{
+ break
+ }
+ }
+ # found all permissions - success
+ if ($MatchedPermissions -eq $DaclArray.Count){
+ return $True
+ }
+ }
}
- return $False
- }
- catch{
- Write-Warning "Error: $_"
- return $False
}
+ return $False
}
function Invoke-ServiceStart {
@@ -369,7 +360,7 @@ function Invoke-ServiceEnable {
try {
# enable the service
- Write-Verbose "Enabling service '$TargetService.Name'"
+ Write-Verbose "Enabling service '$($TargetService.Name)'"
$Null = sc.exe config "$($TargetService.Name)" start= demand
return $True
}
@@ -417,7 +408,7 @@ function Invoke-ServiceDisable {
try {
# disable the service
- Write-Verbose "Disabling service '$TargetService.Name'"
+ Write-Verbose "Disabling service '$($TargetService.Name)'"
$Null = sc.exe config "$($TargetService.Name)" start= disabled
return $True
}
@@ -458,11 +449,17 @@ function Get-ServiceUnquoted {
if ($VulnServices) {
ForEach ($Service in $VulnServices){
+ try {
+ $CanRestart = Test-ServiceDaclPermission -ServiceName $Service.name -Dacl 'WPRP'
+ } catch {
+ $CanRestart = "Cannot be determined through DACL, try manually."
+ }
$Out = New-Object PSObject
$Out | Add-Member Noteproperty 'ServiceName' $Service.name
$Out | Add-Member Noteproperty 'Path' $Service.pathname
$Out | Add-Member Noteproperty 'StartName' $Service.startname
$Out | Add-Member Noteproperty 'AbuseFunction' "Write-ServiceBinary -ServiceName '$($Service.name)' -Path <HijackPath>"
+ $Out | Add-Member Noteproperty 'CanRestart' $CanRestart
$Out
}
}
@@ -492,12 +489,18 @@ function Get-ServiceFilePermission {
$ServiceStartName = $_.startname
$ServicePath | Get-ModifiableFile | ForEach-Object {
+ try {
+ $CanRestart = Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'WPRP'
+ } catch {
+ $CanRestart = "Cannot be determined through DACL, try manually."
+ }
$Out = New-Object PSObject
$Out | Add-Member Noteproperty 'ServiceName' $ServiceName
$Out | Add-Member Noteproperty 'Path' $ServicePath
$Out | Add-Member Noteproperty 'ModifiableFile' $_
$Out | Add-Member Noteproperty 'StartName' $ServiceStartName
$Out | Add-Member Noteproperty 'AbuseFunction' "Install-ServiceBinary -ServiceName '$ServiceName'"
+ $Out | Add-Member Noteproperty 'CanRestart' $CanRestart
$Out
}
}
@@ -510,7 +513,7 @@ function Get-ServicePermission {
This function enumerates all available services and tries to
open the service for modification, returning the service object
- if the process doesn't failed.
+ if the process didn't fail.
.EXAMPLE
@@ -541,11 +544,17 @@ function Get-ServicePermission {
# means the change was successful
if ($Result -contains "[SC] ChangeServiceConfig SUCCESS"){
+ try {
+ $CanRestart = Test-ServiceDaclPermission -ServiceName $Service.name -Dacl 'WPRP'
+ } catch {
+ $CanRestart = "Cannot be determined through DACL, try manually."
+ }
$Out = New-Object PSObject
$Out | Add-Member Noteproperty 'ServiceName' $Service.name
$Out | Add-Member Noteproperty 'Path' $Service.pathname
$Out | Add-Member Noteproperty 'StartName' $Service.startname
$Out | Add-Member Noteproperty 'AbuseFunction' "Invoke-ServiceAbuse -ServiceName '$($Service.name)'"
+ $Out | Add-Member Noteproperty 'CanRestart' $CanRestart
$Out
}
}
diff --git a/Tests/Privesc.tests.ps1 b/Tests/Privesc.tests.ps1
index 095c946..56dfd2c 100644
--- a/Tests/Privesc.tests.ps1
+++ b/Tests/Privesc.tests.ps1
@@ -74,6 +74,137 @@ Describe 'Get-ModifiableFile' {
}
}
+Describe 'Test-ServiceDaclPermission' {
+
+ if(-not $(Test-IsAdmin)) {
+ Throw "'Test-ServiceDaclPermission' Pester test needs local administrator privileges."
+ }
+
+ It "Should fail finding 'sc.exe'." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ $DirectoryName = Get-RandomName
+ $env:SystemRoot = 'C:\\' + $DirectoryName
+ { Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' } | Should Throw "sc.exe not found"
+
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ $env:SystemRoot = 'C:\Windows'
+ }
+
+ It "Should succeed finding 'sc.exe'." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ $DirectoryName = Get-RandomName
+ New-Item -Path $env:Temp -Name "$DirectoryName\System32" -ItemType Directory
+ New-Item -Path $env:Temp -Name "$DirectoryName\System32\sc.exe" -ItemType File
+ $env:SystemRoot = $env:Temp + "\$DirectoryName"
+ Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' | Should Be $True
+
+ Remove-Item -Recurse -Force "$env:Temp\$DirectoryName"
+ $env:SystemRoot = 'C:\Windows'
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ It "Should fail querying WMI for a non-existent service." {
+ $ServiceName = Get-RandomName
+ { Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' } | Should Throw "not found on the machine"
+ }
+
+ It "Should succeed querying WMI for an existenting service." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' | Should Be $True
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ It "Should fail querying WMI for an existing service due to insufficient DACL permissions." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+ $UserSid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ sc.exe sdset $ServiceName "D:(A;;CCDCSWRPWPDTLOCRSDRCWDWO;;;$UserSid)" | Should Match "SUCCESS"
+ { Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' } | Should Throw "not found on the machine"
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ It "Should succeed querying WMI for an existing service due to sufficient DACL permissions." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+ $UserSid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ sc.exe sdset $ServiceName "D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$UserSid)" | Should Match "SUCCESS"
+ Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' | Should Be $True
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ It "Should fail running 'sc.exe sdshow' due to insufficient permissions." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+ $UserSid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ sc.exe sdset $ServiceName "D:(A;;CCDCLCSWRPWPDTLOCRSDWDWO;;;$UserSid)" | Should Match "SUCCESS"
+ { Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' } | Should Throw "Could not retrieve DACL permissions"
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ It "Should succeed running 'sc.exe sdshow' due to sufficient permissions." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+ $UserSid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ sc.exe sdset $ServiceName "D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;$UserSid)" | Should Match "SUCCESS"
+ Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'DC' | Should Be $True
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ it "Should fail finding the service DACL value of 'WP' for the current user." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ sc.exe sdset $ServiceName "D:(A;;CCDCLCSWRPDTLOCRSDRCWDWO;;;S-1-5-4)" | Should Match "SUCCESS"
+ Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'WP' | Should Be $False
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+
+ it "Should succeed finding the service DACL value of 'WP' for the current user." {
+ $ServiceName = Get-RandomName
+ $ServicePath = "C:\Program Files\service.exe"
+
+ sc.exe create $ServiceName binPath= $ServicePath | Should Match "SUCCESS"
+ Start-Sleep -Seconds 1
+
+ sc.exe sdset $ServiceName "D:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-4)" | Should Match "SUCCESS"
+ Test-ServiceDaclPermission -ServiceName $ServiceName -Dacl 'WP' | Should Be $True
+ sc.exe delete $ServiceName | Should Match "SUCCESS"
+ }
+}
########################################################
#