skip to content

On Windows, why is write access for a non-administrator to a service's key under HKLM\SYSTEM\CurrentControlSet\Services treated as a privilege escalation, and what related weaknesses are checked alongside it?

level: seniorimportance: should knowfreq 36%

answer

  1. the key says what runs and as whom
  2. the SCM does the launching for you
  3. a reboot is the trigger
  4. four routes, one class of defect
  5. quotes are free, spaces are not

basics

~20 s

That key defines which binary the service runs and which account runs it. Anyone who can write it can point ImagePath at their own executable and have the Service Control Manager launch it as LocalSystem at the next start, turning a registry permission into full machine control.

solid answer

~50 s

A Windows service is nothing more than a registry key plus a security descriptor. `ImagePath` says what to run and `ObjectName` says who to run it as, so write access to that key is equivalent to "run arbitrary code as this service's account" — usually `LocalSystem`. The Service Control Manager does the launching, so the attacker does not even need to be able to start the service; they wait for a reboot. The same reasoning covers the neighbouring weaknesses auditors check together: a permissive service DACL granting `SERVICE_CHANGE_CONFIG` to non-admins (visible with `sc.exe sdshow`), which reaches the same outcome through the SCM's own API; an unquoted `ImagePath` containing spaces, where the loader tries each truncated prefix in turn; and write access to the service binary or its directory. All four are the same class of finding — a low-privilege write that a privileged process later executes.

code

powershell · 7 lines
powershell
# Services whose configured command line is unquoted and contains a space
Get-CimInstance Win32_Service |
    Where-Object { $_.PathName -and $_.PathName -notmatch '^"' -and $_.PathName -match '^\S+\s' } |
    Select-Object Name, StartName, PathName

# Who can reconfigure a given service object
sc.exe sdshow Spooler

go deeper

for a junior

Know that a Windows service is defined by a registry key holding the path to its binary and the account it runs as, and that this key is protected by permissions like any other object.

for a middle

Explain the chain concretely: ImagePath plus ObjectName means write access equals code execution as that account, and the SCM performs the launch at the next start without the attacker needing start rights.

for a senior

Cover all four routes — key ACL, service DACL via sc.exe sdshow, unquoted paths, writable binary directories — and remediate durably by tightening the ACL, quoting the path and moving the service off LocalSystem.

for a principal

Own the systemic answer: baseline the estate's service configurations, treat third-party installers as the main source of these defects, decide which groups are administrator-equivalent because of backup and restore privileges, and require auditing on the keys that matter.

## The service definition is the payload Everything the SCM knows about a service lives in `HKLM\SYSTEM\CurrentControlSet\Services\<name>`: | Value | Meaning | |---|---| | `ImagePath` | The command line the SCM executes | | `ObjectName` | The account it runs as, e.g. `LocalSystem` | | `Start` | When it starts | | `Type` | Own process, shared process, or driver | | `ErrorControl` | How badly a failure is treated at boot | Registry keys carry their own security descriptors, so this key is ACL'd like any object — and by default only administrators and SYSTEM can write it. When a badly written installer loosens that (granting `Users` or `Authenticated Users` full control on its own service key, a startlingly common packaging mistake), an unprivileged account can rewrite `ImagePath` to point at a binary it controls. Nothing exploits a bug: at the next start, `services.exe` faithfully launches that binary as `LocalSystem`. The attacker's only cost is patience, because a reboot will come. That is the whole mechanism, and it explains why hardening baselines and audit tooling treat a writable service key as a *critical*, not a *hygiene*, finding. ## The same outcome by four routes Auditors check these together because they are one class of defect — a low-privilege write feeding a high-privilege execution: **1. Writable service registry key.** Rewrite `ImagePath`; wait for start. **2. Permissive service DACL.** The SCM object itself has a security descriptor, separate from the registry key's. If it grants `SERVICE_CHANGE_CONFIG` (or `SERVICE_ALL_ACCESS`) to a non-admin group, they can reconfigure the binary path through `ChangeServiceConfig` without touching the registry at all, and `SERVICE_START`/`SERVICE_STOP` lets them trigger it immediately rather than waiting. ``` sc.exe sdshow Spooler ``` prints the SDDL for the service object; the pieces to look for are access-allowed ACEs for `IU` (interactive users), `SU`, `AU` or `BU` (built-in users) that include change-config rights. **3. Unquoted service path.** If `ImagePath` is `C:\Program Files\Contoso App\svc.exe` with no quotes, `CreateProcess` resolves the ambiguous command line by trying `C:\Program.exe`, then `C:\Program Files\Contoso.exe`, before the intended target. Anyone who can create a file at one of those earlier names — which requires write access to the relevant directory, so the two findings compound — gets their binary launched instead. The fix is to quote the path, and it costs nothing. **4. Writable binary or install directory.** If the service account executes `D:\apps\svc.exe` and the `apps` directory is writable by `Users`, the registry ACL is irrelevant — replace the file. ## Why the registry angle keeps recurring Two structural reasons. First, applications legitimately need to store per-service state and frequently ACL a whole subtree loosely to make an updater or a settings dialog work as a standard user, sweeping the service's own key into the grant. Second, `CurrentControlSet` is a link to a numbered control set, so a permission set on one control set's subtree is easy to reason about wrongly. There is also a privilege-based bypass to be aware of: an account holding `SeBackupPrivilege` or `SeRestorePrivilege` can open registry keys with backup/restore semantics, which bypasses the key's DACL entirely. That is why "Backup Operators" is treated as an administrator-equivalent group in any serious threat model, and why membership in it is audited alongside the Administrators group. ## What to actually do Enumerate the estate's services and compare each one's key ACL, service DACL, `ImagePath` quoting and binary-path permissions against a baseline; the finding density is usually in third-party software rather than Windows' own services. Remediate by tightening the ACL back to Administrators and SYSTEM, quoting the path, and — the durable fix — moving the service off `LocalSystem` to a virtual account, so that even a successful hijack yields a restricted token rather than SYSTEM. Finally, put a SACL on the service keys of anything that matters, so a modification is auditable rather than merely possible.

  • An account can reconfigure a service through sc.exe but has no write access to the service's registry key. How?
    The SCM object has its own security descriptor, independent of the registry key's ACL. If it grants `SERVICE_CHANGE_CONFIG` to that account, `ChangeServiceConfig` succeeds and the SCM performs the privileged write on the caller's behalf. Inspect it with `sc.exe sdshow <name>` and correct it with `sc.exe sdset`; auditing only the registry ACL misses this route entirely.
  • Why does an unquoted service path only matter sometimes?
    Because the attacker still needs to create a file at one of the earlier candidate names the loader tries — `C:\Program.exe` for a path under Program Files, for example. The root of `C:` and Program Files are not writable by standard users on a correctly configured system, so the finding is only exploitable where a second weakness, a writable directory, exists alongside it. Quote the path regardless; it is a free fix.
  • How does moving a service off LocalSystem reduce the impact of these weaknesses?
    It changes what a successful hijack yields. If the service runs under a virtual account with a per-service SID and trimmed required privileges, an attacker who replaces the binary gets a restricted token that can only touch what that one service was explicitly permitted, rather than SYSTEM. The weakness still needs fixing, but the blast radius is bounded.
  • Which privileges let an account bypass a registry key's ACL outright?
    `SeBackupPrivilege` and `SeRestorePrivilege`, which allow opening keys and files with backup/restore semantics that skip the discretionary access check. Any group holding them — Backup Operators being the standard example — is effectively administrator-equivalent and should be audited as such rather than treated as a limited operational role.

It is like being able to edit the sign on a security guard's clipboard that says which contractor to admit at 6am — you never touch the door yourself, you just change the name and wait for the shift to start.

saying these in an interview costs you the question

  • Says only administrators can ever write service keys, so it cannot happen
  • Thinks the attacker must be able to start the service
  • Assumes the registry ACL and the service DACL are the same thing
  • Calls an unquoted service path a cosmetic issue
  • Fixes the ACL but leaves the service running as LocalSystem

context