🎁 New User? Get 20% off your first purchase with code NEWUSER20 Register Now β†’
Menu

Categories

Windows Service Security Audit with PowerShell (2026)

Windows Service Security Audit with PowerShell (2026)
Windows service security audit with PowerShell - Dargslan 2026

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.

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

  1. Zero unquoted vulnerable paths (1 pt)
  2. Zero LocalSystem services with writable folders (1 pt)
  3. 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.

Related Dargslan resources

Share this article:
Dargslan Editorial Team (Dargslan)
About the Author

Dargslan Editorial Team (Dargslan)

Collective of Software Developers, System Administrators, DevOps Engineers, and IT Authors

Dargslan is an independent technology publishing collective formed by experienced software developers, system administrators, and IT specialists.

The Dargslan editorial team works collaboratively to create practical, hands-on technology books focused on real-world use cases. Each publication is developed, reviewed, and...

Programming Languages Linux Administration Web Development Cybersecurity Networking

Stay Updated

Subscribe to our newsletter for the latest tutorials, tips, and exclusive offers.