Unquoted Service Path: How a Missing Pair of Quotes Can Hand Over SYSTEM Privileges

 Introduction

Not every critical vulnerability lives inside an application's source code. Some of the most reliable local privilege escalation paths on Windows come from a single misconfigured line in the registry — no buffer overflow, no malicious input, no clever exploit chain. The Unquoted Service Path vulnerability is one of these: a decades-old, still-common misconfiguration that turns a missing pair of double quotes into a way for a standard user to run code as SYSTEM.

This post walks through what's actually vulnerable, how Windows' path-parsing behavior creates the opening, how to find and reproduce the issue, and how to fix it for good.


What's Actually Vulnerable — It's Not the Application's Code

The first thing to understand is what isn't the problem. The application's executable — say, target.exe — is not buggy, not modified, and not touched in any way by this vulnerability. It sits safely on disk, completely unchanged, before, during, and after exploitation.

The vulnerability is purely in one line of configuration: the registry value that tells Windows where to find target.exe when starting the service. That registry value is just a text string — the ImagePath. The entire bug comes down to whether that string has quotes around it.

So there are two completely separate things at play:

  • target.exe — the real binary, sitting safely on disk, unchanged
  • ImagePath — a string in the registry, written by the installer, that tells Windows "go run this path"

The vulnerability lives only in the second one. The application is the victim of this misconfiguration, not the cause of it.


How Windows Resolves an Unquoted Path

To understand why an unquoted path is dangerous, you need to understand how Windows parses it. When a path contains spaces and isn't wrapped in quotes, Windows has no reliable way to know where the file path ends and the command-line arguments begin — because spaces appear both inside legitimate folder names (like Program Files) and between the path and its arguments.

Windows resolves this ambiguity by splitting the string at each space and trying to execute each resulting combination in order, treating everything before the split as the executable and everything after as arguments.


Take this example path:

C:\Program Files\My Application\service.exe

Here's the sequence Windows actually attempts:

Attempt

Path tried

Result

1

C:\Program.exe

Not found → skip

2

C:\Program Files\My.exe

Not found → skip

3

C:\Program Files\My Application\service.exe

Found → run this

In normal circumstances, attempts 1 and 2 simply don't exist, so Windows reaches step 3 and launches the real service. No harm done — which is exactly why this misconfiguration can sit unnoticed in production for years. The service works perfectly fine every single day.


Where the Application Comes In — As the Victim, Not the Cause

The vulnerability only becomes exploitable if an attacker — a standard, unprivileged user — is able to create a file at one of those intermediate fake paths. For example, dropping a malicious file named My.exe directly inside C:\Program Files\ (or whatever the equivalent intermediate folder is for the target path).

The next time the service starts — on reboot, a manual restart, or crash recovery — Windows runs through the exact same guessing sequence. But this time, C:\Program Files\My.exe does exist, because the attacker just put it there. Windows runs it immediately and never even reaches the real service.exe.

The application's binary is never touched, never modified, and never executed at all in this scenario. The attacker's file runs instead of it — but critically, it inherits the exact same privilege level the service was configured to run as, which is very often SYSTEM, the highest privilege level on Windows.


Steps to Reproduce:

Step 1 — Find auto-start services with unquoted paths

wmic service get name,displayname,pathname,startmode | findstr /i "auto" | findstr /i /v "c:\windows\" | findstr /i /v """"

This filters for services that auto-start, aren't OS built-ins, and have no quotes around their path.


Step 2 — Confirm the exact path for the target service

sc qc <ServiceName>

Check the BINARY_PATH_NAME value. Confirm it's unquoted and contains at least one space — a path with no spaces is not exploitable, even if it's technically unquoted.


Step 3 — Identify the intermediate folders Windows will try

Split the path at each space to map out every candidate Windows will attempt, in order. For example, for:

C:\Program Files\ABC\XYZ\Target App\target.exe

the candidates are:

C:\Program.exe
C:\Program Files\ABC.exe
C:\Program Files\ABC\XYZ.exe
C:\Program Files\ABC\XYZ\Target.exe


Step 4 — Check write access on each parent folder

Check every parent folder leading up to the real executable (not the real exe's own folder):

icacls "C:\"
icacls "C:\Program Files"
icacls "C:\Program Files\ABC"
icacls "C:\Program Files\ABC\XYZ"

Look for BUILTIN\Users, Everyone, or Authenticated Users with write, modify, or full-control rights.


Step 5 — Plant a payload at the writable split point

If any folder is writable, plant a payload there, named to match that exact split point. For example, if C:\Program Files\ABC\XYZ is writable:

copy malicious.exe "C:\Program Files\ABC\XYZ\Target.exe"


Step 6 — Restart the service and confirm execution

net stop <ServiceName>
net start <ServiceName>

Confirm via Task Manager or Process Monitor that the planted executable launched under the service account — often SYSTEM.

Result: A standard user's planted binary executes with the service's privilege level — confirming the unquoted service path vulnerability.


Mitigation:

1. Wrap the path in quotes (the primary fix)

The entire fix lives in the installer. Whatever script or installer technology writes the service's ImagePath registry value during install must wrap that value in quotes when it writes it:

"C:\Program Files\ABC\XYZ\Target App\target.exe" -startup

Wrapping the path in double quotes (under HKLM\SYSTEM\CurrentControlSet\Services\<ServiceName>) tells Windows to treat the full string as a single atomic unit, eliminating any ambiguity about where the path ends and the arguments begin.

2. Avoid spaces in the install path entirely

Alternatively, or in addition, installing to a path with no spaces — for example, C:\VendorApp\ instead of C:\Program Files\Vendor App\ — eliminates the ambiguity altogether, independent of quoting.

3. Harden ACLs as defense-in-depth

Harden ACLs on all parent directories in the install path so standard users cannot write to any folder above the application's own directory. This won't fix the unquoted path itself, but it closes the door even if quoting is missed somewhere in a future release.

 

Closing Thoughts

The unquoted service path issue is a good reminder that not all privilege escalation paths require breaking application logic — sometimes they only require understanding how the operating system interprets a string. It costs nothing to exploit, requires no special tooling beyond what ships with Windows, and the fix is a single line in an installer script. That combination — trivial to exploit, trivial to fix, and still showing up in audits today — is exactly why it's worth checking for on every Windows service deployment.

Comments

Popular posts from this blog

Unrestricted File Uploads

SQL Injection – “Let’s dump the database”

Broken authentication and Session management