Hardening

Unquoted Service Paths and Weak Service ACLs on Windows

22 September 2026 9 min read

A Windows service with an unquoted ImagePath and a space in its install directory is not automatically exploitable, and treating every scanner hit as critical wastes analyst hours you do not have. Whether the finding is real depends on three independent facts: whether the path is genuinely unquoted, whether any directory along the parsed path chain is writable by a non-administrative principal, and whether the account the service runs under is worth the trouble of escalating to. This piece covers the CreateProcess parsing behavior that creates the risk, how to enumerate it at scale with PowerShell, how to separate a real privilege escalation path from noise, and how to fix it in a way that survives the next vendor patch.

How an unquoted path plus a writable folder turns into SYSTEM in one reboot

The Service Control Manager stores each service’s launch command in the ImagePath value under HKLMSYSTEMCurrentControlSetServices<name>. When that value is not wrapped in quotation marks and contains one or more spaces, Windows does not know where the executable name ends and the arguments begin, so CreateProcess tries every space-delimited break point in order, from shortest to longest, until one resolves to a file that exists.

Take a service registered as C:Program FilesContosoBackup Agentagent.exe. Without quotes, the loader attempts C:Program.exe, then C:Program FilesContosoBackup.exe, then C:Program FilesContosoBackup Agentagent.exe, in that order. Every one of those is a legitimate, resolvable path from the loader’s point of view. If an attacker can place a file at any earlier break point, that file runs instead of the real binary, and it inherits whatever account the service was configured to run as.

The part that turns this from an interesting quirk into a privilege escalation path is the service account. A huge share of Windows services still run as LocalSystem or NetworkService because that was the path of least resistance when they were installed. Pair that with an autostart service, so the trigger is any reboot, crash recovery, or manual restart, and a single writable folder segment is enough for a standard user to plant a binary that executes as SYSTEM the next time the box comes back up. No exploit development, no memory corruption, just a file copy and a wait.

Finding affected services with WMI/PowerShell and separating real risk from noise

Enumerating candidates is a one-liner. Against a live host or a remote session, this pulls every service whose PathName starts without a quote and contains a space outside a single unbroken token: Get-CimInstance Win32_Service | Where-Object { $_.PathName -notmatch ‘^”‘ -and $_.PathName -match ‘ ‘ -and $_.PathName -notmatch ‘^S+$’ } | Select Name, DisplayName, StartMode, StartName, PathName. Run it through Invoke-Command against a computer list, or fold it into a login script and log the output to a central share if you are covering more than a handful of endpoints.

The list this produces is not a list of findings, it is a list of candidates. Confirming exploitability means walking the path chain and checking who can write to each segment. Get-Acl on every directory from the root down to the executable’s own folder, or accesschk64.exe -uwdq from Sysinternals against each candidate directory, will tell you whether BUILTINUsers, Everyone, or Authenticated Users hold Write, Modify, or FullControl anywhere in that chain. If every segment is locked to Administrators, TrustedInstaller, and SYSTEM, the scanner finding is technically true and practically inert.

Use a simple decision table to separate what needs an emergency change from what goes into the next patch cycle:

Condition Priority
Writable folder segment, autostart, runs as LocalSystem or NetworkService Critical, fix before next reboot window
Writable folder segment, manual start, runs as LocalSystem High, fix within the standard patch SLA
Unquoted path, all segments admin-only, any start mode Low, quote it for hygiene but no active escalation path
Unquoted path, service runs as a low-privilege dedicated account Low to medium, escalation ceiling is that account, not SYSTEM

This is also where volume becomes a problem on its own. A mid-size Windows estate scanned with two or three different tools will report the same unquoted-path service under different plugin IDs from each engine, with slightly different path strings depending on how each scanner queries the registry. Platforms that consume output from multiple scanners, including SITEY during its discovery and scanning phase, need to normalize and deduplicate on the service name and host before triage even starts, otherwise the same fixable issue shows up as three tickets with three different owners.

Weak service permissions: SERVICE_CHANGE_CONFIG, binary and folder write rights

Unquoted paths get most of the attention because they are easy to grep for, but a discretionary access control list granted directly on the service object is a more direct route to the same outcome, and it does not require a space in the path at all. Every service in the SCM has its own DACL, visible with sc.exe sdshow <servicename>, which returns an SDDL string. If that string contains an ACE granting CCLCSWRPWPDTLOCRRC or similar to a SID other than SY, BA, or a specific service account, translate it: CC is SERVICE_CHANGE_CONFIG, WP is WRITE_DAC. Either one held by a low-privileged group means that group can run sc config <servicename> binPath= “C:tempevil.exe” directly, with no dependency on writable folders or missing quotes whatsoever.

This class needs no filesystem reconnaissance, a single sc query and a single sc config call is the entire attack, but it is also easier to find deterministically: accesschk64.exe -uwcqv “authenticated users” * enumerates every service where that group holds a configurable right in one pass.

There is a third variant worth checking even after ImagePath is correctly quoted: write access to the binary file itself, or to the folder that directly contains it. A correctly quoted path that points at C:ProgramDataVendorAppsvc.exe is still a privilege escalation if ProgramDataVendorApp grants Modify to a non-admin group, because the attacker simply overwrites svc.exe rather than exploiting the parsing behavior. Quoting fixes one attack technique; it does not fix the underlying permission problem if the permission problem also exists one level down.

Fixing without breaking vendor software: quoting, ACL correction, vendor escalation

Quoting the path is the least risky change and should be done first. Set the ImagePath value to the full path wrapped in escaped quotes, for example through sc.exe: sc config <servicename> binPath= “”C:Program FilesVendorAppsvc.exe” -arg1″. Test that the service still starts immediately after the change, some services parse their own argument string in a way that is sensitive to how the quoting interacts with trailing switches, and a bad edit here can stop a production service rather than just closing a finding.

ACL correction on the folder chain needs more care than the registry edit. Do not blanket-strip write access from every directory under the vendor’s install path without checking whether a legitimate, lower-privileged process, an agent updater or a logging component, actually needs to write there. Separate concerns instead: lock the executable’s own folder and every parent folder down to admin-only, and if the application genuinely needs a writable location for logs or config, point it at a dedicated subfolder that is never on the CreateProcess search path for any service binary. icacls “C:Program FilesVendorApp” /inheritance:r followed by explicit /grant entries for Administrators, SYSTEM, and TrustedInstaller is the concrete command; verify with icacls again before closing the change.

When the software is commercial and you do not control the installer, quoting the registry value yourself is still valid as an immediate compensating control, but it will not survive an upgrade in most cases, so open a ticket with the vendor referencing CWE-428, unquoted search path or element, and ask for a committed fix version or an installer switch that applies quoting by default. Vendors that ship security-relevant Windows services usually already have an internal tracking item for this once you ask, because it is a common finding across their whole customer base, not just your environment.

Third-party installers that reintroduce the problem after every update

The recurring failure mode in mature environments is not the initial fix, it is the fix not sticking. Many MSI and EXE-based installers regenerate the ImagePath value and reset folder ACLs to their own defaults on every version bump, silently undoing whatever quoting or permission correction was applied after the previous patch. The symptom is a hardening finding that closes in one scan cycle and reopens on the next, tied to the same host and the same service, right after a scheduled update window.

Three approaches hold up over time. First, attach a post-install remediation step to the patch management workflow itself, so the quoting and ACL check runs immediately after the known installer executes, rather than waiting for the next audit cycle. Second, for vendors who will not commit to a fix, schedule an idempotent verification task, the same PowerShell query from earlier, on every patch Tuesday, that logs drift and re-applies the correction if the unquoted value has come back. Third, when neither option is available, record the recurrence as a standing compensating control in the risk register rather than reopening the same ticket every month, since repeated churn on one finding is itself a sign the control needs to be structural, not procedural.

The detail that gets missed most often is trusting the installer’s own exit code as proof the fix held. A patch job reporting success tells you the installer ran without error, not that the ImagePath is still quoted or that the folder ACL is still correct. Confirming that requires re-querying the actual registry value and the actual ACL after the patch completes, which is exactly the kind of check that belongs in a retest step before closure rather than a deployment log. This is where an automated pipeline earns its keep: a platform like SITEY that re-tests the specific ImagePath and ACL state after remediation, instead of trusting the patch command’s return code, is what catches the regression on the same day the update ran rather than three weeks later on the next full scan.

Making the check part of routine host verification rather than a one-off audit

Unquoted service paths and weak service ACLs belong in the same category as missing patches: a state that drifts continuously, not a one-time finding to remediate and forget. Two practical anchor points make that sustainable. Bake the enumeration query into golden image validation, so a new server template cannot ship with a known-bad service configuration in the first place, and add it to the recurring compliance scan cadence rather than only running it during an annual pentest.

The check also maps cleanly onto frameworks most regulated estates already report against. CIS benchmarks for Windows Server include explicit service-hardening controls, PCI DSS requirement 2.2 expects documented secure configuration standards that this falls under directly, and ISO 27001 Annex A 8.9 on configuration management expects exactly this kind of drift to be detected and corrected on a schedule, not discovered by an external auditor. Wiring the check into whatever handles compliance mapping in your existing tooling means the same evidence trail that closes the internal ticket also satisfies the audit request, instead of maintaining two separate records of the same fact.

Once the check is continuous, the operational question shifts from “did we find it” to “did it come back,” which is a retest-and-closure problem rather than a discovery problem. That shift in framing is usually what determines whether this specific finding stays fixed across a fleet of a few hundred Windows hosts or keeps reappearing every quarter under a new ticket number.

About SITEY

SITEY is an autonomous vulnerability management platform. It discovers, validates, prioritizes, remediates and re-tests vulnerabilities through an eight-phase automated pipeline, unifying output from 17 integrated scanners. SITEY is self-hosted: it runs in your own infrastructure and your findings are stored there. Outbound connections are limited to licence activation and the optional services you enable, such as an AI provider, CVE enrichment and patch catalogues. Pricing is 599 USD per month or 5,999 USD for a perpetual lifetime license. See pricing or how the platform works.

SITEY closes the loop, not just the report.Discover, validate, fix and verify in your own infrastructure.

See pricing