Merge pull request #108 from sagishahar/master

Add 'CanRestart' to output and Pester tests
This commit is contained in:
HarmJ0y 2016-01-25 15:19:20 -08:00
commit 43c4c69b38
2 changed files with 195 additions and 55 deletions

View File

@ -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
} }
} }

View File

@ -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"
}
}
######################################################## ########################################################
# #