Unquoted Service Paths
Another interesting attack vector
that can lead to privilege escalation on Windows operating systems
revolves around unquoted service paths.
We can use this attack when we have Write permissions
to a service’s main directory or subdirectories
but cannot replace files within them.
As we learned in the previous sections,
each Windows service maps to an executable file
that will be run when the service is started.
If the path of this file contains one or more spaces
and is not enclosed within quotes,
it may be turned into an opportunity for a privilege escalation attack.
When a service is started and a process is created,
the Windows CreateProcess function is used.
Reviewing the first parameter of the function,
lpApplicationName is used to specify the name
and optionally the path to the executable file.
If the provided string contains spaces
and is not enclosed within quotation marks,
it can be interpreted in various ways
because it is unclear to the function
where the file name ends and the arguments begin.
To determine this, the function starts interpreting the path from left to right
until a space is reached.
For every space in the file path,
the function uses the preceding part as file name
by adding .exe and the rest as arguments.
Let’s show this in an example with the following unquoted service binary path.
When Windows starts the service,
it will use this order to try to start the executable file
due to the spaces in the path.
In order to exploit this and subvert the original unquoted service call,
we must create a malicious executable,
place it in a directory that corresponds to one of the interpreted paths,
and match its name to the interpreted filename.
Then, once the service is started,
our file gets executed with the same privileges that the service starts with.
Often, this happens to be the LocalSystem account,
which results in a successful privilege escalation attack.
In the context of the example,
we could name our executable Program.exe and place it in C:\,
or My.exe and place it in C:\Program Files\,
or My.exe and place it in C:\Program Files\My Program\.
However, the first two options would require some unlikely permissions
since standard users don’t have the permissions
to write to these directories by default.
The third option is more likely as it is the software’s main directory.
If an administrative user or developer set the permissions
for this directory too open,
we can place our malicious binary there.
Now that we have a basic understanding of this vulnerability,
let’s use it in an example.
Let’s connect to CLIENTWK220 as steve with RDP.
We’ll open PowerShell and enumerate running and stopped services.
The output shows a stopped service named GammaService.
The unquoted service binary path contains multiple spaces
and is therefore potentially vulnerable to this attack vector.
A more effective way to identify spaces in the paths and missing quotes
is using the WMI command-line (WMIC)[^wmic] utility.
We can enter service to obtain service information and the verb get
with name and pathname as arguments
to retrieve only these specific property values.
We’ll pipe the output of this command to findstr
with /i for case-insensitive searching and /v
to only print lines that don’t match.
As the argument for this command, we’ll enter “C:\Windows\”
to show only services with a binary path outside of the Windows directory.
We’ll pipe the output of this command to another findstr command,
matching lines that do not contain quotes.
We’re using cmd.exe instead of PowerShell
to avoid escaping issues for the quote in the second findstr command.
Alternatively, we could use Select-String in PowerShell.
The output of this command only lists services
that are potentially vulnerable to our attack vector,
such as GammaService.
Before we go on, let’s check if we can start
and stop the identified service as steve
with Start-Service and Stop-Service.
The output indicates
that steve has permissions to start and stop GammaService.
Since we can restart the service ourselves,
we don’t need to issue a reboot to restart the service.
Next, let’s list the paths Windows uses
to attempt locating the executable file of the service.
Let’s check our access rights in these paths with icacls.
We’ll begin with the first two paths we just reviewed.
As expected, our user steve, as a member of BUILTIN\Users
and NT AUTHORITY\AUTHENTICATED Users,
has no Write permissions in either of these paths.
Now, let’s check the path of the third option.
We can skip the fourth path since it represents the service binary itself.
The output shows that BUILTIN\Users has Write permissions on the path
C:\Program Files\Enterprise Apps.
Our goal now is to place a malicious file named Current.exe in
C:\Program Files\Enterprise Apps\.
We can reuse the adduser.exe binary by compiling the C code
from the “Service Binary Hijacking” section.
On Kali,
we’ll start a Python3 web server in the directory
of the executable file in order to serve it.
Once the web server is running,
we can download adduser.exe on the target machine as steve.
To save the file with the correct name for our privilege escalation attack,
we enter Current.exe as argument for -Outfile.
After the file is downloaded,
we’ll copy it to C:\Program Files\Enterprise Apps\.
Now, everything is ready for us to start the service,
which should execute Current.exe
instead of the original service binary GammaServ.exe.
For this, we’ll use Start-Service with GammaService as an argument.
We can use net user to check if dave2 was created and net localgroup administrators
to confirm that dave2 was added to the local Administrators group.
The Start-Service command
displayed an error stating that Windows cannot start the service.
The error stems from the fact that our cross-compiled C code
does not accept the parameters
that are a leftover of the original service binary path.
However, Current.exe was still executed
and dave2 was created as a member of the local Administrators group.
Great!
To restore the functionality of the original service,
we’d have to stop the service and delete our binary Current.exe.
After the executable file is removed,
Windows will use the service binary GammaServ.exe again
once the service is started.
Before we conclude this section,
let’s check if PowerUp identifies this vulnerability.
Let’s download PowerUp with iwr
and import it into a PowerShell session as we did previously.
Then, we’ll set the ExecutionPolicy to Bypass ...
and use Get-UnquotedService.
The GammaService was identified as vulnerable.
Let’s use the AbuseFunction
and restart the service to attempt to elevate our privileges.
For -Path, we’ll enter the same path for Current.exe as we did previously.
As stated before, the default behavior is to create a new local user called john
with the password Password123!.
Additionally, the user is added to the local Administrators group.
The output shows that by using the AbuseFunction, Write-ServiceBinary,
the user john was created as a local administrator.
Very nice!
In this section, we covered yet another privilege escalation vector
related to Windows services.
We learned how to identify services missing quotes
and containing spaces in the service binary path.
Furthermore, we learned how Windows tries to determine
the correct executable file path in this situation.
By finding a writable directory suitable for this vulnerability,
we could place a malicious executable file in the path,
which is executed by starting the service.
Finally, we automated the exploitation of this vector
by using PowerUp’s Get-UnquotedService
and Write-ServiceBinary functions.
Although this vulnerability requires a specific combination of requirements,
it is easily exploitable
and a privilege escalation attack vector worth considering.