diff options
-rw-r--r-- | Privesc/PowerUp.ps1 | 119 | ||||
-rw-r--r-- | Tests/Privesc.tests.ps1 | 131 |
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" + } +} ######################################################## # |