Get-CimInstance Win32_Service |
Where-Object { $_.PathName -like '*.exe*'} |
Select-Object Name, State, Pathname, StartName |
ForEach-Object {
$_.PathName = ($_.PathName -split '(?<=\.exe\b)')[0].Trim('"')
Add-Member -PassThru -InputObject $_ Acl (Get-Acl -LiteralPath $_.PathName)
} |
Where-Object {
$_.Acl.Access.Where({
$_.IdentityReference -ceq 'BUILTIN\Utilisateurs' -and
$_.FileSystemRights -eq 'FullControl'
}, 'First').Count -gt 0
}
Note that I've replaced Get-WmiObject
with Get-CimInstance
, because the CIM cmdlets superseded the WMI cmdlets in PowerShell v3 (released in September 2012). Therefore, the WMI cmdlets should be avoided, not least because PowerShell (Core) (v6+), where all future effort will go, doesn't even have them anymore. Note that WMI still underlies the CIM cmdlets, however. For more information, see this answer.
The above uses the ForEach-Object
call to:
update the .PathName
property of each object to contain only the - unquoted - path of the executable with each service.
add an .Acl
property to each object via Add-Member
, containing the service executable's ACL, obtained via Get-Acl
.
The resulting list of objects is then filtered by those whose service-executable ACL contains an entry for identity BUILTIN\Utilisateurs
[1] with full control over the executable.
That is, the resulting objects are effectively those for which you meant to issue Write-Warning " Exploit detected "
As for what you tried:
$services = ... | Out-Null
by definition captures nothing[2] in variable $services
, given that Out-Null
's purpose is to suppress output.
While $var = "{0}.exe" -f ($Service.PathName -Split ".exe")[0]
does extract the executable path (although .exe
should be \.exe\b
, for robustness), it may include enclosing double quotes, which should be stripped.
It's unclear where $rights
comes from.
You cannot use -ccontains
to match across multiple properties of an object, and note that the purpose of the -contains
operator and its variants is to test presence of a value in full, in a collection, not to look for a substring in a single string.
[1] It's interesting to see that these identity references are localized; the equivalent on a US-English system would be BUILTIN\Users
. Generally, it would be better to obtain a culture-independent representation of this identity, namely its SID, and use that for comparison: $_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value -eq 'S-1-5-32-545'
[2] Loosely speaking, $null
; technically, it is the singleton value that PowerShell uses to signal "no output received", [System.Management.Automation.Internal.AutomationNull]::Value
.