A client of mine is using a monitoring suite which checks for certain running Windows services once an hour. It uses the Windows scheduled tasks feature to run. When running as a normal user, it returns no services even though the user is able to list all services in services.msc or on the command line.
Lets demonstrate this:
get-service
returns 157 services on a Windows 8 machine.
Create a script to enumerate the services from task scheduler:
'get-service | out-file $env:temp\services.txt' | Set-Content $env:temp\test.ps1schedule it:
schtasks /create /TN test /TR "powershell.exe %temp%\test.ps1" /SC monthly /NP /RU $env:usernamerun the task:
schtasks /run /tn testcheck the content:
gc $env:temp\services.txtit is empty. In the pre-Vista days any user would be able to enumerate services. Since Vista the security on what users can do with services has been tightened. While a logged on (interactive) user can still list all services. One that is just running as a task can not see any. Why is this? Lets look at the permissions on the Service Control Manager, the process that is responsible in managing Windows services.
sc.exe sdshow scmanageron Windows 8 we get:
D:(A;;CC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CC;;;AC)this looks pretty cryptic, each set between the brackets describe the permissions for one user(group). The last two letters define the group, AU stands for authenticated users while IU stands for Interactive users. We see the IU has a permission of LC (each two letter between the semicolons define a permission). LC stands for the 'SERVICE_QUERY_STATUS' which is what we need. All we have to do is give this permission to authenticated users. To do this, we copy the existing security descriptor and add the LC in the first section:
sc.exe sdset scmanager D:(A;;LCCC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CC;;;AC)this command fails in a PowerShell because the brackets have a special meaning to the parser, we also need to run this command elevated.
Start-Process "$psHome\powershell.exe" -Verb Runasand then use quotes around the parameter with brackets:
sc.exe sdset scmanager "D:(A;;LCCC;;;AU)(A;;CCLCRPRC;;;IU)(A;;CCLCRPRC;;;SU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)(A;;CC;;;AC)"lets run our test again:
schtasks /run /tn test; start-sleep 3;gc $env:temp\services.txtwe now get a list with 40 services, hmm why not all 157? Lets look at the permissions for a service we get and one we don't:
sc.exe sdshow Spooler
D:(A;;CCLCSWLOCRRC;;;AU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWRPWPDTLOCRRC;;;SY)
sc.exe sdshow themes
D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)We see AC has LC access to the spooler service, while no access at all to the themes service. after adding this:
sc.exe sdset themes "D:(A;;LC;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)"we can now see the themes service as well:
schtasks /run /tn test; start-sleep 3;gc $env:temp\services.txtIf we want to change these permissions for many or all services, a script would make sense:
param( [switch]$fix, [string]$pattern = ".", [string]$principal = "AU", [string]$permission = "LC") function FixIt ([string]$name,[string]$rawSD,[string]$newSD) { Write-Host $name -ForegroundColor White if ($fix) { sc.exe sdset $name $newSD } else { Write-Host $rawSD -ForegroundColor Yellow Write-Host $newSD -ForegroundColor Cyan } } function ProcessService([string]$name,[string]$permission,[string]$group) { $rawSD = & sc.exe sdshow $name $match = ([regex]"\(A;;[;A-Z]+;;;$group\)").match($rawSD) # LC - SERVICE_QUERY_STATUS if ($match.Success) { if ($match.groups[0].value -notmatch $permission) { # no, so add it $newtoken = $match.groups[0].value -replace "\(A;;","(A;;$permission" # remove the brackets: $newtoken = $newtoken -replace "[()]","" #write-host $newtoken -ForegroundColor red $newSD = $rawSD -replace $match.groups[0].value,$newtoken FixIt $name $rawSD $newSD } else { Write-Host $name -ForegroundColor green } } else { # it has no AC token yet, lets add one if ($rawSD -match "D:\(") { # starts with D:, just replace the beginning $newSD = $rawSD -replace "D:\(","D:(A;;$permission;;;$group)" FixIt $name $rawSD $newSD } else { Write-Host $_.Name -ForegroundColor Red Write-Host $rawSD -ForegroundColor Yellow } } } get-service | Where-Object {$_.Name -match $pattern}| ForEach-Object { ProcessService $_.Name $permission $principal } ProcessService "scmanager" $permission $principal
copy this to a file Set-ServiceSecurityDescriptors.ps1
When just running it, it shows the descriptors for all services and how it would change them. You can limit it to one or certain services
.\Set-ServiceSecurityDescriptors.ps1 -pattern spooler
.\Set-ServiceSecurityDescriptors.ps1 -pattern svcTo change the permission use the -fix switch:
.\Set-ServiceSecurityDescriptors.ps1 -pattern time -fixor for all:
.\Set-ServiceSecurityDescriptors.ps1 -fixYou will get a few access denied errors, because for certain services even an elevated administrator has no permissions to change the permissions. Run our task again:
schtasks /run /tn test; start-sleep 3;gc $env:temp\services.txt
We now get a list of 152 services, the five we can't see are: DPS, gpsvc, msiserver, wdiservicehost and whisystemhost
To get to these, open a powershell as system user:
psexec.exe -i -s powershell.exe
.\Set-ServiceSecurityDescriptors.ps1 -fixnow we get all but msiserver. You should be able to set different permissions for different users as well, but I haven't tested this. For more information about the meaning of the various two letters codes check: http://msmvps.com/blogs/erikr/archive/2007/09/26/set-permissions-on-a-specific-service-windows.aspx