Windows services are quietly one of the largest attack surfaces on a server. Three patterns turn up every time we audit a fleet: unquoted service paths with spaces (a 25-year-old privilege escalation), services running as LocalSystem from user-writable folders (DLL hijacking gold), and obsolete services that should have been removed years ago.
This guide shows how to find all three from PowerShell, includes a defensible scoring rule, and ships the Dargslan.WinServiceSecurity module plus a free PDF cheat sheet.
Table of Contents
Why audit services at all?
The CIS benchmarks and MITRE ATT&CK technique T1574 both call out service-related privilege escalation as a recurring real-world attack path. Defender will catch the obvious payloads, but the structural problem β a binary with weak ACLs running as LocalSystem β has to be found by inspection.
Step 1: Inventory every service
The richest source is Win32_Service via CIM (faster and more reliable than Get-Service for this purpose):
Get-CimInstance Win32_Service |
Select Name, DisplayName, State, StartMode, StartName, PathName |
Sort StartName, Name
Sort by StartName first to group services by the account they run as. Anything running as a domain user or a custom account with no service account naming convention is worth a closer look.
Step 2: Find unquoted service paths
If a service's PathName is, for example, C:\\Program Files\\My App\\agent.exe -run with no quotes, Windows tries to launch C:\\Program.exe, then C:\\Program Files\\My.exe, then C:\\Program Files\\My App\\agent.exe in that order. If any of those folders is writable by a non-admin and they can drop a binary, the service launches the planted binary as LocalSystem on next boot.
Get-CimInstance Win32_Service | Where-Object {
$_.PathName -and
$_.PathName -notmatch '^"' -and
$_.PathName -match ' ' -and
$_.PathName -notmatch '^[A-Za-z]:\\Windows\\'
} | Select Name, StartName, PathName
The C:\\Windows\\ exclusion is important β every Microsoft service path is unquoted but the parent folders are not writable, so it is not exploitable. Anything else is a finding. Fix it with sc config <svc> binpath= ""C:\\path\\with spaces.exe" -arg".
Step 3: Find weak service folder ACLs
The other half of the same problem: a quoted path is fine, but if non-admins can write to the folder containing the binary, they can replace it. Read the ACL of every service folder and look for Users, Authenticated Users, Everyone or INTERACTIVE with Write / Modify / FullControl:
$acl = Get-Acl 'C:\Program Files\Vendor'
$acl.Access | Where-Object {
$_.IdentityReference -match 'Users|Everyone|INTERACTIVE' -and
$_.FileSystemRights -match 'Write|Modify|FullControl' -and
$_.AccessControlType -eq 'Allow'
}
Step 4: Risky run-as accounts
Two patterns to flag:
- LocalSystem service running from a non-Microsoft folder where any user can write β DLL hijacking risk.
- Domain user as a service account, especially with Logon as a service assigned by hand. Should be a Group Managed Service Account (gMSA) in 2026.
The module's Get-DargslanRiskyServiceAccounts automates the first check. The second is policy-level β a one-line Get-CimInstance Win32_Service | Where StartName -match '^DOMAIN\\\\' gives you the list to review.
A defensible PASS / WARN / FAIL score
- Zero unquoted vulnerable paths (1 pt)
- Zero LocalSystem services with writable folders (1 pt)
- At least one Auto-start service (sanity check that the inventory ran) (1 pt)
3/3 PASS, 1-2 WARN, 0 FAIL.
Dargslan.WinServiceSecurity module
Install-Module Dargslan.WinServiceSecurity -Scope CurrentUser
Import-Module Dargslan.WinServiceSecurity
Export-DargslanServiceAuditReport -OutDir C:\reports
FAQ
Will the audit modify any service?
No β every cmdlet is read-only.
How long does it take?
Less than 5 seconds on a typical workstation, ~15 seconds on a busy server.
What about gMSA accounts?
They show up as DOMAIN\\svc-foo$ β recognise the trailing $ as a service account, not a user.
How do I fix an unquoted path?
sc config "ServiceName" binPath= "\"C:\\Program Files\\Vendor\\agent.exe\" -arg" (note the literal space after binPath=).
Cheat sheet?
Free PDF at /cheat-sheets/windows-service-security-audit-2026.