Windows Fundamentals · beginner · ~10 min

Windows services

**What you will learn** - Explain what a Windows service is, how the Service Control Manager (SCM) launches it, and which service accounts (especially `LocalSystem`/SYSTEM) matter for security. - Read a service's real configuration (binary path, start type, account) with `sc qc` and the `services.msc` GUI. - Identify the three classic service misconfigurations that enable local privilege escalation: unquoted service paths, weak service reconfigure rights, and writable service binaries. - Use `accesschk` (Sysinternals) to audit who can modify a service or its executable, and interpret the ACL output. - Apply the defensive fix for each weakness and **verify** the fix with a repeatable check. - Know what to log and monitor so a service being tampered with is detected, not just prevented.

Overview

Security objective. The asset you are protecting is the integrity of code that runs as SYSTEM. On Windows, many services run under the all-powerful LocalSystem (SYSTEM) account. The threat is local privilege escalation: a low-privileged user who can influence what a SYSTEM service executes gains full control of the machine. In this lesson you will learn to detect those weaknesses and, more importantly, prevent and verify the fix for them.

A Windows service is a background program run by the operating system rather than by a logged-in user. It has no window, starts before (or without) any user logging in, and is supervised by the Service Control Manager (SCM) — the Windows equivalent of Linux's init/systemd. Every service is defined by three properties that together decide what runs and with what power:

  • Binary path — the executable (and arguments) the SCM launches.
  • Start type — automatic, manual, or disabled.
  • Service account — the identity the process runs under (often SYSTEM).

This builds directly on your prerequisite, "Windows users, groups, and SIDs". A service account is just an identity with a SID, and the decision "can this user reconfigure this service?" is an access-control check against that user's SID and group memberships. If SIDs, well-known accounts (SYSTEM, Administrators, Authenticated Users), and ACLs are fuzzy for you, review that lesson first — everything here is an application of those ideas to a specific, high-value object type.

Where you meet this: every Windows workstation and server runs dozens of services, many installed by third-party software. Misconfigured third-party services are one of the most common real-world privilege-escalation paths, which is why service auditing appears early in any authorized Windows security assessment — and why hardening services is a standard part of secure system administration.

Why it matters

In authorized professional work — system hardening, a Windows security assessment, or a compliance audit — services are where a huge share of local privilege-escalation risk lives. Three weaknesses do most of the damage:

Weakness What an attacker gains Root cause
Unquoted service path with spaces Windows may run an attacker-planted binary earlier in the path Missing quotes in the registry ImagePath
Weak reconfigure rights on the service A normal user runs sc config to point the service at their own binary, then restarts it Over-permissive service DACL (SERVICE_CHANGE_CONFIG)
Writable service binary The attacker overwrites the .exe the service runs Loose NTFS ACL on the executable or its folder

Each one lets an ordinary user run code as SYSTEM. For a defender or sysadmin, understanding these means you can harden a fleet before an attacker or auditor finds them: quote paths, tighten service DACLs, install binaries into protected directories, and monitor for tampering. For an authorized assessor, enumerating services with sc qc and accesschk is a standard early step — but the value you deliver is the remediation and verification, not the finding alone. A report that says "service X is misconfigured" is worth little; a report that says "here is the exact fix and here is how to confirm it worked" is what gets systems hardened.

Core concepts

Service Control Manager (SCM)

Definition. The Windows component (services.exe, exposed through the sc command and services.msc) that starts, stops, and stores the configuration of every service.

How it works. Each service is a registry key under HKLM\SYSTEM\CurrentControlSet\Services\<name>. Key values include ImagePath (the binary path), Start (start type), and ObjectName (the service account). The SCM reads these and launches the process with the specified identity.

When it matters. Any change to those values changes what runs as SYSTEM. Access to change them is controlled by the service's security descriptor (DACL) — an ACL just like the ones on files and registry keys.

Pitfall. People assume "only admins can change a service." Not true — a service's DACL can be set (by a sloppy installer) to grant SERVICE_CHANGE_CONFIG to Authenticated Users or Users. Always check the actual ACL, don't assume.

Service account (the identity a service runs as)

Definition. The security principal the service process runs under. Common ones:

Account Privilege level Notes
LocalSystem (SYSTEM) Highest — full control of the machine The high-value target
NetworkService Limited local; network as the computer account
LocalService Limited local; network as anonymous
A named user (e.g. a service account) Whatever that user has Depends on configuration

How it works. Whoever controls the code a SYSTEM service runs effectively is SYSTEM when that code executes.

When not to worry. A service running as LocalService that an attacker hijacks yields only LocalService — still a problem, but not full machine compromise. The account is what turns "run my code" into "run my code as SYSTEM."

Pitfall. Running third-party or line-of-business services as SYSTEM "to avoid permission problems" — this maximizes blast radius. Prefer the least-privileged account that works.

Misconfiguration class 1 — Unquoted service path

Definition. An ImagePath that contains spaces but no surrounding quotes, e.g. C:\Program Files\My App\service.exe.

How it works. When the path is unquoted, the SCM tries each candidate in turn: C:\Program.exe, then C:\Program Files\My.exe, then the full path. If a user can create C:\Program Files\My.exe (or C:\Program.exe), it runs first — as the service account.

When not exploitable. If no attacker-writable directory sits earlier in the path, it cannot be abused — but it is still a hardening defect worth fixing.

Pitfall. Assuming unquoted always equals exploitable. Exploitability requires write access to an earlier folder. Report accurately.

Misconfiguration class 2 — Weak service permissions (reconfigure rights)

Definition. A service DACL that grants a low-privileged principal SERVICE_CHANGE_CONFIG (or WRITE_DAC/SERVICE_ALL_ACCESS).

How it works. With change-config rights, a normal user can run sc config <name> binPath= <their exe> and then restart the service; the SCM launches their binary as SYSTEM.

Pitfall. Overlooking SERVICE_CHANGE_CONFIG because it is not called "admin." It is the single most dangerous service right to hand out.

Misconfiguration class 3 — Writable service binary

Definition. An NTFS ACL on the service's .exe (or its folder) that lets a low-privileged user modify or replace it.

How it works. The attacker overwrites the executable with their own; on next start, the SCM runs it as the service account. No sc config needed.

Pitfall. Checking only the file and not the folder. Write access to the containing folder often lets an attacker rename/replace the file even if the file's own ACL looks tight.

WINDOWS SERVICE — THREAT MODEL (single host)

  TRUST BOUNDARY: standard user  <----->  SYSTEM (LocalSystem)

  ENTRY POINTS an unprivileged user can touch:
    (1) Service DACL           -- sc config / change binPath
    (2) ImagePath (registry)   -- unquoted path -> plant earlier .exe
    (3) Service binary / folder-- NTFS write -> overwrite the .exe
         |
         v
  SCM launches the (now attacker-influenced) code
         |
         v
  ASSET: code execution as SYSTEM  ==> full host compromise

  DEFENDER CONTROLS:
    - Quote ImagePath
    - Tighten service DACL (remove CHANGE_CONFIG from non-admins)
    - Restrict NTFS ACL on binary + folder
    - Log 7045 (new service) / 4697 / config changes

Knowledge check.

  1. What asset is ultimately being protected here, and which single service account turns a service compromise into full host compromise?
  2. Where is the trust boundary in this threat model, and which three entry points cross it?
  3. An unquoted service path exists but every directory in the path is only writable by Administrators. What insecure assumption would lead you to wrongly report it as "exploitable," and why is it not?
  4. Which Windows event ID would help you detect a brand-new service being installed by an attacker, and why is that a signal worth alerting on?
  5. Why must any hands-on testing of these weaknesses happen only in a lab you own or are explicitly authorized to test?

Syntax notes

The core enumeration commands. All are read-only and safe to run on a system you administer or are authorized to assess.

sc query                     # list running services (state, not full config)
sc query state= all          # list ALL services incl. stopped (note the space after '=')
sc qc <ServiceName>           # QUERY CONFIG: shows BINARY_PATH_NAME, START_TYPE,
                              #   SERVICE_START_NAME (the account) -- the key command
sc qc "My Service"            # quote names that contain spaces

Annotated sc qc output (what each line tells you):

SERVICE_NAME: ExampleSvc
        TYPE               : 10  WIN32_OWN_PROCESS
        START_TYPE         : 2   AUTO_START          <- launches automatically
        BINARY_PATH_NAME   : C:\Program Files\App\svc.exe   <- UNQUOTED + has a space (red flag)
        SERVICE_START_NAME : LocalSystem              <- runs as SYSTEM (high value)

Auditing who can do what, using Sysinternals accesschk (download from Microsoft's official Sysinternals site; run with -accepteula):

accesschk.exe -accepteula -uwcqv "Authenticated Users" ExampleSvc
   # -u quiet errors, -w writable only, -c service, -q no banner, -v verbose
   # Shows whether that group has SERVICE_CHANGE_CONFIG / SERVICE_ALL_ACCESS

accesschk.exe -accepteula -quv "C:\Program Files\App\svc.exe"
   # Shows the NTFS ACL on the binary -- look for FILE_WRITE_DATA / (W) for non-admins

Defensive commands (require admin) used later to fix issues:

sc config ExampleSvc binPath= "\"C:\Program Files\App\svc.exe\""   # add quotes
sc sdset ExampleSvc <SDDL>                                       # replace the service DACL
icacls "C:\Program Files\App" /inheritance:r ...                 # tighten NTFS ACL

Lesson

A Windows service is a background program managed by the Service Control Manager (SCM). It is the Windows equivalent of a Linux daemon or a systemd unit.

What defines a service

Every service has three properties:

  • Binary path — the executable plus any arguments.
  • Start type — automatic, manual, or disabled.
  • Service account — the identity it runs as.

Service accounts

Services run under one of these accounts:

  • LocalSystem — the same as SYSTEM. All-powerful.
  • LocalService / NetworkService — limited privileges.
  • A specific user account.

A service running as SYSTEM is the prize. If you control its code, you become SYSTEM.

Why services are a privesc goldmine

The most common misconfigurations (covered in detail in the privesc track):

  • Unquoted service path with spaces — Windows may execute an attacker-placed binary that appears earlier in the path.
  • Weak service permissions — a normal user can change the service binary with sc config, then restart the service.
  • Writable service binary (an NTFS ACL problem) — the attacker simply replaces the executable.

Tools

Use these to inspect services:

  • sc query and sc qc <name> — show service configuration from the command line.
  • services.msc — the graphical interface.

Listing services, their accounts, and the ACLs on their binaries is core Windows enumeration.

Code examples

This example shows the INSECURE -> SECURE -> VERIFY shape for the writable service binary weakness, using only safe, standard commands. Run every step in an isolated lab (see the authorization checklist below), never on production.

(1) WARNING: intentionally vulnerable — use only in a local, isolated, authorized lab. Do not deploy.

Suppose a sloppy installer created a service whose binary folder is writable by ordinary users. We first observe the weakness read-only (we do NOT plant a malicious binary — observation is enough to prove the finding):

C:\> sc qc VulnSvc
[SC] QueryServiceConfig SUCCESS
SERVICE_NAME: VulnSvc
        BINARY_PATH_NAME   : C:\vuln\svc.exe
        SERVICE_START_NAME : LocalSystem

C:\> accesschk.exe -accepteula -quv "C:\vuln\svc.exe"
C:\vuln\svc.exe
  RW BUILTIN\Users                 <-- any user can WRITE/replace a SYSTEM binary
  RW NT AUTHORITY\SYSTEM

The finding: Users has RW on a binary that runs as LocalSystem. That is a privilege-escalation path. We stop at proving it; we do not exploit it.

(2) SECURE — remove the dangerous access

Run as an administrator. Strip inherited permissions, then grant only what is needed:

C:\> icacls "C:\vuln\svc.exe" /inheritance:r
C:\> icacls "C:\vuln\svc.exe" /grant:r "SYSTEM:(RX)" "Administrators:(F)" "Users:(RX)"

Users now has read/execute (needed to run the service) but not write. Do the same on the containing folder so nobody can rename/replace the file:

C:\> icacls "C:\vuln" /inheritance:r
C:\> icacls "C:\vuln" /grant:r "SYSTEM:(OI)(CI)(F)" "Administrators:(OI)(CI)(F)" "Users:(OI)(CI)(RX)"

(3) VERIFY — prove the fix rejects bad input and accepts good input

Rejects bad input (a non-admin can no longer write the binary). From a standard-user shell:

C:\> echo test > "C:\vuln\svc.exe"
Access is denied.

C:\> accesschk.exe -accepteula -quv "C:\vuln\svc.exe"
C:\vuln\svc.exe
  R  BUILTIN\Users                 <-- write is gone; only Read/Execute remains
  RW NT AUTHORITY\SYSTEM
  RW BUILTIN\Administrators

Accepts good input (the service still starts and functions):

C:\> sc start VulnSvc
SERVICE_NAME: VulnSvc
        STATE              : 4  RUNNING

Expected result. The write attempt is denied (Access is denied.), accesschk shows Users reduced to R, and the service still reaches RUNNING. If all three hold, the weakness is remediated without breaking functionality. (A C11 note for cross-track learners: the same idea in code is opening the binary path with fopen(path, "rb") succeeding while fopen(path, "wb") fails with errno == EACCES for a non-privileged process — the OS ACL, not the program, enforces it.)

Line by line

Walkthrough of the INSECURE -> SECURE -> VERIFY flow above.

Step Command What happens Why it matters
1 sc qc VulnSvc SCM prints BINARY_PATH_NAME and SERVICE_START_NAME Confirms the service runs as LocalSystem — so its binary is a SYSTEM-equivalent asset
2 accesschk -quv svc.exe Reads the NTFS DACL on the file Shows Users : RW — an unprivileged group can replace SYSTEM code. This is the finding
3 (stop) We do not overwrite the binary Proving the ACL grants write is sufficient evidence; exploitation adds risk without adding information in a defensive audit
4 icacls ... /inheritance:r Removes inherited ACEs so no loose parent permission sneaks back in Prevents the folder's permissive inheritance from re-granting write
5 icacls ... /grant:r Users:(RX) Re-grants Users only Read+Execute Least privilege: users can run the service but not modify it
6 echo test > svc.exe (std user) OS returns Access is denied. Demonstrates the fix rejects the malicious action (overwrite)
7 accesschk again Users : R Independent confirmation the write ACE is gone
8 sc start VulnSvc -> RUNNING Service launches normally Demonstrates the fix accepts legitimate use — no functional regression

How the values change: before, the file's DACL contained an ACE (A;;FA;;;BU)-style full/write for Users; after icacls /grant:r Users:(RX), that becomes read+execute only (RX). The state that matters — "can a non-admin write this file?" — flips from yes to no, which is exactly what closes the escalation path, while "can the service still start?" stays yes.

Common mistakes

1. Assuming only administrators can change services.

  • WRONG: Skipping the service-DACL audit because "a normal user can't touch services."
  • WHY WRONG: Third-party installers frequently grant SERVICE_CHANGE_CONFIG to Users or Authenticated Users.
  • CORRECTED: Always run accesschk -uwcqv "Authenticated Users" * (or per service) to see the actual rights.
  • RECOGNISE/PREVENT: Treat every service's DACL as untrusted until measured.

2. Reporting an unquoted path as automatically exploitable.

  • WRONG: Flagging every unquoted ImagePath as a critical privesc.
  • WHY WRONG: Exploitation requires an attacker-writable directory earlier in the path. Without it, it's a hardening defect, not an escalation.
  • CORRECTED: Check write access on each earlier folder (e.g. icacls "C:\Program Files"). Rate severity by actual exploitability.
  • RECOGNISE/PREVENT: Severity depends on exploitability, access, and impact — not on the mere presence of a pattern.

3. Fixing the file ACL but ignoring the folder.

  • WRONG: Tightening svc.exe but leaving its folder writable.
  • WHY WRONG: Write on the folder lets an attacker delete/rename and drop a replacement .exe.
  • CORRECTED: Lock down both the binary and its directory (as shown in the code section).
  • RECOGNISE/PREVENT: Audit the whole path, not just the leaf file.

4. Confusing "the service is stopped" with "the service is safe."

  • WRONG: Ignoring a misconfigured service because it's not currently running.
  • WHY WRONG: START_TYPE : AUTO_START or a user-triggerable start means the planted/replaced binary runs later, still as SYSTEM.
  • CORRECTED: Assess config regardless of current state; check sc qc start type.

5. Running everything as SYSTEM "to be safe."

  • WRONG: Configuring a new service under LocalSystem by default.
  • WHY WRONG: It maximizes impact if the service is ever hijacked.
  • CORRECTED: Use LocalService/NetworkService or a dedicated low-privilege account with only the rights the service needs.

Debugging tips

sc qc returns OpenService FAILED 1060 — the service name is wrong. Use sc query state= all (note the space after =) to list exact names; service display names differ from the short name sc expects.

sc query state=all shows nothing / syntax errorsc requires a space after each = and none before it: state= all, binPath= "...". This is the single most common sc mistake.

accesschk prints a EULA prompt or does nothing — add -accepteula. If "not recognized," you are not in the folder where you unzipped Sysinternals, or you downloaded the wrong architecture (use the 64-bit accesschk64.exe on 64-bit Windows).

icacls says "Access is denied" — you are not running as administrator. Open an elevated prompt. Remember ACL changes need admin even though reading them does not.

The service won't start after you tightened the ACL — you removed an access the service genuinely needs. Re-check: the account in SERVICE_START_NAME needs at least Read+Execute on the binary and Read on its folder. Re-grant (RX) to that specific account.

Questions to ask when a finding "doesn't reproduce":

  • Am I checking the right principal? (Test as the actual low-priv user/group, not as admin.)
  • Is there an earlier writable directory (unquoted path) or only the leaf file?
  • Did inheritance re-add a permission I removed? Re-run icacls to confirm the effective ACL.
  • Is the start type actually reachable by a non-admin (auto-start, or a triggerable/manual start the user can invoke)?

Memory safety

Security & safety — detection and logging.

Prevention closes the hole; logging tells you when someone tries it. On a system you administer, ensure these are captured and forwarded to a central log:

What to log (and alert on):

Event Windows source Why it matters
New service installed Security 4697, System 7045 Attackers create services for SYSTEM execution/persistence
Service config changed audit on the service object / 4697 Detects sc config binPath= tampering
Service binary file modified file-system audit (4663) on service folders Catches a replaced .exe
Service start/stop System 7036 A restart right after a config change is a strong signal

Each record should include: timestamp, source (which user/SID and host), resource (service name and binary path), result (success/failure), the security decision (was the change permitted?), and a correlation id so a config-change event and the subsequent start can be tied together.

Never log: passwords, service-account credentials or tokens, session cookies, private keys, or full secrets embedded in binPath arguments. If a service command line contains a secret, redact it (binPath= <redacted>); log the fact of the change, not the secret. Avoid logging unnecessary PII.

Events that signal abuse: a non-admin SID appearing as the actor on a 4697/7045; a service config change immediately followed by a 7036 start; a new .exe written into a service folder by a user account; a service binPath suddenly pointing at C:\Users\..., %TEMP%, or a world-writable path.

How false positives arise: legitimate software updates install/reconfigure services (expect 7045/4697 during patch windows); management tools (SCCM, Intune) change services routinely; administrators do maintenance. Reduce noise by allow-listing known deployment accounts and maintenance windows, and by alerting on the combination (non-privileged actor + config change + immediate start) rather than any single event.

Two misconceptions to correct: (1) passing an automated privesc scanner with no findings does not prove a host is secure — scanners miss context-dependent paths; combine tooling with manual review. (2) Nothing is ever "completely secure" — the goal is to raise cost and increase the chance of detection.

Real-world uses

Authorized real-world use case. A sysadmin onboarding a fleet of Windows workstations runs a scheduled audit that enumerates every service with sc qc, checks each service DACL and binary ACL with accesschk, and flags unquoted paths, SERVICE_CHANGE_CONFIG granted to non-admins, and user-writable binaries. Findings are remediated (quote paths, tighten DACLs/NTFS ACLs, move binaries into C:\Program Files with inherited-admin-only write) and each fix is re-tested before the machine is marked compliant. This is standard CIS-benchmark-style hardening — done on systems the organization owns.

Professional best-practice habits:

Habit Beginner Advanced
Validation Read config with sc qc before changing anything Script the whole fleet; diff against a known-good baseline
Least privilege Run new services as LocalService, not SYSTEM, when possible Use per-service managed/virtual accounts with minimal rights
Secure defaults Always quote binary paths; install into protected dirs Enforce via GPO / deployment templates so misconfig can't be introduced
Logging Enable service-install auditing (4697/7045) Central SIEM correlation of config-change + start events
Error handling If a service breaks after hardening, restore least-priv access it needs Automated verification tests that assert both "non-admin can't write" and "service still starts"

For an authorized assessor, the deliverable is a finding with a safe reproduction, the exact remediation, and a retest step — not an exploit. The measure of quality is whether the client can fix and confirm the fix from your report.

Practice tasks

All tasks are lab-only. Use a Windows VM you own or an intentionally-vulnerable training VM (e.g. a local Windows evaluation image or a purpose-built privesc lab), with networking isolated. Every task ends with remediate + verify.

Authorization checklist (complete before any task):

  • The VM is one I own or am explicitly authorized to test.
  • It is isolated (host-only/NAT, no access to other networks).
  • I have a snapshot to roll back to.
  • I will only observe weaknesses, not deploy real malicious payloads.

Beginner 1 — Enumerate.

  • Objective: List all services and identify which run as LocalSystem.
  • Requirements: Use sc query state= all and sc qc <name>.
  • Output: A short table of service name, start type, and account for 5 services.
  • Constraints: Read-only commands only.
  • Hints: SERVICE_START_NAME is the account line.
  • Concepts: SCM, service accounts, enumeration.

Beginner 2 — Spot the unquoted path.

  • Objective: Find any service whose BINARY_PATH_NAME has a space but no surrounding quotes.
  • Requirements: Inspect sc qc output; note the path.
  • Output: The service name and its unquoted path, plus a one-line note on whether an earlier folder is writable by non-admins (icacls to check).
  • Constraints: Do not create any file in those folders.
  • Hints: C:\Program Files\... unquoted is the classic case.
  • Concepts: Unquoted service path, exploitability depends on writable earlier folder.

Intermediate 1 — Audit service DACLs.

  • Objective: Determine whether any non-admin group has SERVICE_CHANGE_CONFIG.
  • Requirements: accesschk -accepteula -uwcqv "Authenticated Users" * and "Users" *.
  • Output: List of services (if any) where a non-admin can reconfigure.
  • Constraints: Do not run sc config; auditing only.
  • Hints: Look for SERVICE_CHANGE_CONFIG / SERVICE_ALL_ACCESS.
  • Concepts: Service DACL, reconfigure rights.

Intermediate 2 — Remediate a writable binary and verify.

  • Objective: Given a lab service whose binary is user-writable, fix it.
  • Requirements: Confirm with accesschk (RW for Users), then icacls /inheritance:r + /grant:r to leave Users:(RX) on file and folder.
  • Input/Output: Before = Users : RW; after = Users : R; service still RUNNING.
  • Constraints: Must show both "write denied for non-admin" AND "service starts."
  • Hints: Fix the folder too, not just the file.
  • Concepts: NTFS ACL, least privilege, mitigation verification.
  • Defensive conclusion: Document before/after ACL and the two verification results.

Challenge — Full service-hardening pass with detection.

  • Objective: Produce a mini hardening report for one lab service that has two weaknesses (unquoted path AND a permissive DACL).
  • Requirements: (1) Enumerate and document both findings with evidence; (2) rate severity by real exploitability; (3) remediate both (quote the path via sc config binPath=, tighten the DACL to remove non-admin CHANGE_CONFIG); (4) verify each fix (path now quoted in sc qc; accesschk shows the right removed; service still starts); (5) enable auditing so a future 4697/7045 or config change is logged.
  • Constraints: No exploitation of third-party systems; lab VM only; snapshot first.
  • Hints: Verify the DACL change by re-running the same accesschk command you used to find it.
  • Concepts: Threat modeling, remediation, mitigation verification, detection/logging, severity by impact.
  • Defensive conclusion: The report must let a reader fix and re-test independently.

Lab cleanup / reset (all tasks): revert the VM to the pre-task snapshot (or restore original ACLs with icacls <path> /reset and re-quote/re-config as needed), stop any test service you started, and confirm sc qc shows the original configuration before ending the session.

Summary

Main concepts. A Windows service is a background program the SCM launches with a defined binary path, start type, and service account. Because many services run as LocalSystem (SYSTEM), controlling the code a service runs can mean controlling the whole machine — this is the core local-privilege-escalation risk, and it builds directly on SIDs and ACLs from the prerequisite lesson.

The three weaknesses (and their fixes).

Weakness Fix Verify
Unquoted service path Quote binPath with sc config sc qc shows quoted path; no writable earlier folder
Weak service DACL (SERVICE_CHANGE_CONFIG) Remove the right from non-admins (sc sdset) accesschk no longer shows the right for that group
Writable service binary/folder Tighten NTFS ACL (icacls /inheritance:r + /grant:r ...:(RX)) Non-admin write denied AND service still starts

Key commands. sc query state= all, sc qc <name>, accesschk -accepteula -uwcqv <group> <svc>, icacls <path>, sc config <name> binPath= "...".

Common mistakes. Assuming only admins can change services; reporting every unquoted path as exploitable; fixing the file but not the folder; running everything as SYSTEM.

What to remember. Every fix needs a verification step that proves it rejects the bad action and still accepts the legitimate one. Prevention closes the hole; logging (4697/7045, config-change + start correlation) tells you when someone tries. Decoding config is not the same as securing it, a clean scanner is not proof of security, and nothing is ever "completely secure." Only test systems you own or are explicitly authorized to test, in an isolated lab with a rollback snapshot.