Merge pull request #108 from sagishahar/master
Add 'CanRestart' to output and Pester tests
This commit is contained in:
commit
43c4c69b38
|
|
@ -122,8 +122,7 @@ function Test-ServiceDaclPermission {
|
||||||
|
|
||||||
# check if sc.exe exists
|
# check if sc.exe exists
|
||||||
if (-not (Test-Path ("$env:SystemRoot\system32\sc.exe"))){
|
if (-not (Test-Path ("$env:SystemRoot\system32\sc.exe"))){
|
||||||
Write-Warning "[!] Could not find $env:SystemRoot\system32\sc.exe"
|
throw [System.IO.FileNotFoundException] "$env:SystemRoot\system32\sc.exe not found"
|
||||||
return $False
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$ServiceAccessFlags = @{
|
$ServiceAccessFlags = @{
|
||||||
|
|
@ -151,68 +150,60 @@ function Test-ServiceDaclPermission {
|
||||||
|
|
||||||
# make sure we got a result back
|
# make sure we got a result back
|
||||||
if (-not ($TargetService)){
|
if (-not ($TargetService)){
|
||||||
Write-Warning "[!] Target service '$ServiceName' not found on the machine"
|
throw [System.Management.Instrumentation.InstanceNotFoundException] "Target service '$ServiceName' not found on the machine"
|
||||||
return $False
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
# retrieve DACL from sc.exe (only possible if 'RC' DACL is set)
|
||||||
# retrieve DACL from sc.exe
|
$Result = sc.exe sdshow $TargetService.Name | where {$_}
|
||||||
$Result = sc.exe sdshow $TargetService.Name | where {$_}
|
|
||||||
|
|
||||||
if ($Result -like "*OpenService FAILED*"){
|
if ($Result -like "*OpenService FAILED*"){
|
||||||
Write-Warning "[!] Access to service $($TargetService.Name) denied"
|
throw [System.Management.Automation.ApplicationFailedException] "Could not retrieve DACL permissions for '$($TargetService.Name)'"
|
||||||
return $False
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$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
|
# 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}
|
$Sids = whoami /groups /FO csv | ConvertFrom-Csv | select "SID" | ForEach-Object {$_.Sid}
|
||||||
|
|
||||||
# add to the list the SID of the current user
|
# add to the list the SID of the current user
|
||||||
$Sids += [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
|
$Sids += [System.Security.Principal.WindowsIdentity]::GetCurrent().User.value
|
||||||
|
|
||||||
ForEach ($Sid in $Sids){
|
ForEach ($Sid in $Sids){
|
||||||
ForEach ($Ace in $SecurityDescriptors.DiscretionaryAcl){
|
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 ""
|
||||||
|
|
||||||
# check if the group/user SID is included in the ACE
|
# convert the input DACL to an array
|
||||||
if ($Sid -eq $Ace.SecurityIdentifier){
|
$DaclArray = [array] ($Dacl -split '(.{2})' | Where-Object {$_})
|
||||||
|
|
||||||
# convert the AccessMask to a service DACL string
|
# counter to check how many DACL permissions were found
|
||||||
$DaclString = $($ServiceAccessFlags.Keys | Foreach-Object {
|
$MatchedPermissions = 0
|
||||||
if (($ServiceAccessFlags[$_] -band $Ace.AccessMask) -eq $ServiceAccessFlags[$_]) {
|
|
||||||
$_
|
# check if each of the permissions exists
|
||||||
}
|
ForEach ($DaclPermission in $DaclArray){
|
||||||
}) -join ""
|
if ($DaclString.Contains($DaclPermission.ToUpper())){
|
||||||
|
$MatchedPermissions += 1
|
||||||
# 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
|
else{
|
||||||
if ($MatchedPermissions -eq $DaclArray.Count){
|
break
|
||||||
return $True
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
# found all permissions - success
|
||||||
|
if ($MatchedPermissions -eq $DaclArray.Count){
|
||||||
|
return $True
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $False
|
|
||||||
}
|
|
||||||
catch{
|
|
||||||
Write-Warning "Error: $_"
|
|
||||||
return $False
|
|
||||||
}
|
}
|
||||||
|
return $False
|
||||||
}
|
}
|
||||||
|
|
||||||
function Invoke-ServiceStart {
|
function Invoke-ServiceStart {
|
||||||
|
|
@ -369,7 +360,7 @@ function Invoke-ServiceEnable {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
# enable the service
|
# enable the service
|
||||||
Write-Verbose "Enabling service '$TargetService.Name'"
|
Write-Verbose "Enabling service '$($TargetService.Name)'"
|
||||||
$Null = sc.exe config "$($TargetService.Name)" start= demand
|
$Null = sc.exe config "$($TargetService.Name)" start= demand
|
||||||
return $True
|
return $True
|
||||||
}
|
}
|
||||||
|
|
@ -417,7 +408,7 @@ function Invoke-ServiceDisable {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
# disable the service
|
# disable the service
|
||||||
Write-Verbose "Disabling service '$TargetService.Name'"
|
Write-Verbose "Disabling service '$($TargetService.Name)'"
|
||||||
$Null = sc.exe config "$($TargetService.Name)" start= disabled
|
$Null = sc.exe config "$($TargetService.Name)" start= disabled
|
||||||
return $True
|
return $True
|
||||||
}
|
}
|
||||||
|
|
@ -458,11 +449,17 @@ function Get-ServiceUnquoted {
|
||||||
|
|
||||||
if ($VulnServices) {
|
if ($VulnServices) {
|
||||||
ForEach ($Service in $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 = New-Object PSObject
|
||||||
$Out | Add-Member Noteproperty 'ServiceName' $Service.name
|
$Out | Add-Member Noteproperty 'ServiceName' $Service.name
|
||||||
$Out | Add-Member Noteproperty 'Path' $Service.pathname
|
$Out | Add-Member Noteproperty 'Path' $Service.pathname
|
||||||
$Out | Add-Member Noteproperty 'StartName' $Service.startname
|
$Out | Add-Member Noteproperty 'StartName' $Service.startname
|
||||||
$Out | Add-Member Noteproperty 'AbuseFunction' "Write-ServiceBinary -ServiceName '$($Service.name)' -Path <HijackPath>"
|
$Out | Add-Member Noteproperty 'AbuseFunction' "Write-ServiceBinary -ServiceName '$($Service.name)' -Path <HijackPath>"
|
||||||
|
$Out | Add-Member Noteproperty 'CanRestart' $CanRestart
|
||||||
$Out
|
$Out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -492,12 +489,18 @@ function Get-ServiceFilePermission {
|
||||||
$ServiceStartName = $_.startname
|
$ServiceStartName = $_.startname
|
||||||
|
|
||||||
$ServicePath | Get-ModifiableFile | ForEach-Object {
|
$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 = New-Object PSObject
|
||||||
$Out | Add-Member Noteproperty 'ServiceName' $ServiceName
|
$Out | Add-Member Noteproperty 'ServiceName' $ServiceName
|
||||||
$Out | Add-Member Noteproperty 'Path' $ServicePath
|
$Out | Add-Member Noteproperty 'Path' $ServicePath
|
||||||
$Out | Add-Member Noteproperty 'ModifiableFile' $_
|
$Out | Add-Member Noteproperty 'ModifiableFile' $_
|
||||||
$Out | Add-Member Noteproperty 'StartName' $ServiceStartName
|
$Out | Add-Member Noteproperty 'StartName' $ServiceStartName
|
||||||
$Out | Add-Member Noteproperty 'AbuseFunction' "Install-ServiceBinary -ServiceName '$ServiceName'"
|
$Out | Add-Member Noteproperty 'AbuseFunction' "Install-ServiceBinary -ServiceName '$ServiceName'"
|
||||||
|
$Out | Add-Member Noteproperty 'CanRestart' $CanRestart
|
||||||
$Out
|
$Out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -510,7 +513,7 @@ function Get-ServicePermission {
|
||||||
|
|
||||||
This function enumerates all available services and tries to
|
This function enumerates all available services and tries to
|
||||||
open the service for modification, returning the service object
|
open the service for modification, returning the service object
|
||||||
if the process doesn't failed.
|
if the process didn't fail.
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
|
|
||||||
|
|
@ -541,11 +544,17 @@ function Get-ServicePermission {
|
||||||
|
|
||||||
# means the change was successful
|
# means the change was successful
|
||||||
if ($Result -contains "[SC] ChangeServiceConfig SUCCESS"){
|
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 = New-Object PSObject
|
||||||
$Out | Add-Member Noteproperty 'ServiceName' $Service.name
|
$Out | Add-Member Noteproperty 'ServiceName' $Service.name
|
||||||
$Out | Add-Member Noteproperty 'Path' $Service.pathname
|
$Out | Add-Member Noteproperty 'Path' $Service.pathname
|
||||||
$Out | Add-Member Noteproperty 'StartName' $Service.startname
|
$Out | Add-Member Noteproperty 'StartName' $Service.startname
|
||||||
$Out | Add-Member Noteproperty 'AbuseFunction' "Invoke-ServiceAbuse -ServiceName '$($Service.name)'"
|
$Out | Add-Member Noteproperty 'AbuseFunction' "Invoke-ServiceAbuse -ServiceName '$($Service.name)'"
|
||||||
|
$Out | Add-Member Noteproperty 'CanRestart' $CanRestart
|
||||||
$Out
|
$Out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
########################################################
|
########################################################
|
||||||
#
|
#
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue