Service Binary Hijacking
Each Windows service has an associated binary file.
These binary files are executed when the service is started
or transitioned into a running state.
For this section, let’s consider a scenario in which a software developer
creates a program and installs an application as a Windows service.
During the installation,
the developer does not secure the permissions of the program,
allowing full Read and Write access to all members of the Users group.
As a result, a lower-privileged user could replace the program with a malicious one.
To execute the replaced binary, the user can restart the service or,
in case the service is configured to start automatically, reboot the machine.
Once the service is restarted, the malicious binary will be executed
with the privileges of the service, such as LocalSystem.
We’ll begin by connecting to CLIENTWK220 as dave over RDP.
For the purpose of this example, let’s assume the vectors to elevate
our privileges from the previous Learning Unit are out of scope.
To get a list of all installed Windows services,
we can choose various methods such as the GUI snap-in services.msc,
the Get-Service Cmdlet, or the Get-CimInstance Cmdlet,
superseding Get- WmiObject.
Once connected, we’ll start PowerShell,
and choose Get-CimInstance to query the WMI class win32_service.
We are interested in the name, state, and path of the binaries for each service
and therefore, we’ll use Select with the arguments Name, State, and PathName.
We’ll also filter out any services that are not in a Running state
by using Where-Object.
Based on the output, the two XAMPP services Apache2.4
and mysql stand out, since the binaries are located in the C:\xampp\ directory
instead of C:\Windows\System32.
This means the service is user-installed and the software developer is in charge of
the directory structure as well as permissions of the software.
These circumstances make it potentially prone to service binary hijacking.
Next, let’s enumerate the permissions on both service binaries.
We can choose between the traditional icacls Windows utility
or the PowerShell Cmdlet Get-ACL.
For this example, we’ll use icacls since it’s usable both in PowerShell
and the Windows command line.
The icacls utility outputs the corresponding principals
and their permission mask.
Let’s quickly review the most relevant permissions and their masks:
Let’s use icacls on the Apache binary httpd.exe first.
As member of the built-in Users group, dave only has Read and Execute (RX) rights
on httpd.exe, meaning we cannot replace the file with a malicious binary.
Next, we’ll check mysqld.exe from the mysql service.
The output shows that members of the Users group have the Full Access (F) permission,
allowing us to write to and modify the binary and therefore, replace it.
Due to the missing indicator (I) preceding this permission,
we know that it was set on purpose and not inherited by the parent directory.
Administrators often set Full Access permissions when they configure a service
and are not entirely sure about the required permissions.
Setting it to Full Access avoids most permission problems,
but creates a security risk as we’ll show in this example.
Let’s create a small binary on Kali,
which we’ll use to replace the original mysqld.exe.
This C code will create a user named dave2
and add that user to the local Administrators group
using the system function.
The cross-compiled version of this code will serve as our malicious binary.
Next, we’ll cross-compile the code on our Kali machine with mingw-64
as we learned in the “Fixing Exploits” Topic.
Since we know that the target machine is 64-bit,
we’ll cross-compile the C code to a 64-bit application.
In addition, we’ll use adduser.exe as an argument for -o
to specify the name of the compiled executable.
Once adduser.exe is cross-compiled, we can transfer it to our target
and replace the original mysqld.exe binary with our malicious copy.
For this, we’ll start a Python3 web server in the directory
where adduser.exe is located on our Kali machine.
We’ll use iwr on the target machine in a PowerShell window
to download our executable file.
In addition, we’ll move the original mysqld.exe to our home directory.
This way, we can restore the service binary
after a successful privilege escalation attempt.
In order to execute the binary through the service, we need to restart it.
We can use the net stop command to stop the service.
Unfortunately, dave doesn’t have sufficient permissions
to stop the service.
This is expected as most services are only managed by administrative users.
Since we do not have permission to manually restart the service,
we must consider another approach.
If the service Startup Type is set to “Automatic”,
we may be able to restart the service by rebooting the machine.
Let’s check the Startup Type of the mysql service
with the help of the Get-CimInstance Cmdlet
by selecting Name and StartMode,
as well as a filter for the string “mysql” with Where-Object.
The service is set to Auto meaning it will start automatically after a reboot.
In order to issue a reboot,
our user needs to have the SeShutDownPrivilege privilege assigned
We can use whoami with /priv to get a list of all privileges
Our user has the privilege in question (among others) and therefore,
we should be able to initiate a system shutdown or reboot.
The Disabled state only indicates if the privilege is currently enabled
for the running process
In our case, it means that whoami has not requested
and is not currently using the SeShutdownPrivilege privilege.
If the SeShutdownPrivilege privilege was not present, we would have to wait for
the victim to manually start the service, which would be much less convenient.
We can issue a reboot with shutdown
and the arguments /r for reboot instead of shutdown,
and /t 0 for zero seconds.
Once the reboot is complete,
we’ll connect again as dave via RDP and open a PowerShell window.
Because of the issued reboot and the Startup type auto,
the service should have executed the executable file we placed
to replace the original mysql service binary.
To confirm that our attack worked,
let’s list the members of the local Administrators group
with Get-LocalGroupMember to check if dave2 was created and added to it.
User dave2 was created and added to the local Administrators group.
Great!
As in the previous sections, we can use RunAs to obtain an interactive shell.
In addition, we could also use msfvenom
to create an executable file, starting a reverse shell.
To restore the original state of the service,
we’ll have to delete our mysqld.exe binary,
restore the backed up original binary, and restart the system.
Before we conclude this section, let’s review an automated tool named PowerUp.ps1
and check if it detects this privilege escalation vector.
Let’s copy the script to kali’s home directory
and start a Python3 web server to serve it.
On the target machine, we’ll download it as dave with iwr in PowerShell,
and start powershell with the ExecutionPolicy Bypass.
Otherwise, it won’t be possible to run scripts as they are blocked.
After we import PowerUp.ps1, we can use Get-ModifiableServiceFile.
This function displays services the current user can modify,
such as the service binary or configuration files.
The output shows us that PowerUp identified mysql
(among others) to be vulnerable.
In addition, it provides information about the file path,
the principal,
and if we have permissions to restart the service.
PowerUp also provides us an AbuseFunction, which is a built-in function to replace
the binary and, if we have sufficient permissions, restart it.
The default behavior is to create a new local user called john
with the password Password123!
and add it to the local Administrators group.
Because we don’t have enough permissions to restart the service,
we still need to reboot the machine.
However, if we use the AbuseFunction
Install-ServiceBinary with the service name,
we receive an error.
The output states that the service binary file is not modifiable by the current user
However, we already established that we have Full Access permissions
on the service binary
and demonstrated that we could replace the file manually.
Reviewing the code for PowerUp and checking the output of the commands
used in Get-ModifiableServiceFile,
we identify that Get-ModifiablePath
is used to return modifiable paths for the current user.
However, for us, it provides an empty result, leading to this error.
Let’s examine this behavior in order to understand why AbuseFunction
is throwing an error.
First, we’ll try the service binary path without any arguments for
Get-ModifiablePath.
Then, we’ll add another argument
to check if the function still provides the correct output.
Finally, we’ll use an argument with a path inside.
The output shows that while the service binary with or without another argument
works as expected, a path as an argument creates an empty result.
Because the mysql service specifies the argument for defaults-file
in the service binary path covered previously,
the AbuseFunction throws an error.
In a situation like this, we should use the result of the identified vulnerable
service file and do a manual exploitation as we did in this section.
Our investigation shows that we should never blindly trust
or rely on the output of automated tools.
However, PowerUp is a great tool
to identify potential privilege escalation vectors,
which can be used to automatically check if the vulnerability can be exploited.
If this is not the case, we should do some manual analysis if the potential vector
is not vulnerable or the AbuseFunction just cannot exploit it.
In this section, we first enumerated services and their binary permissions.
After we identified that the group Users, and therefore, our user dave,
has Full Access to the binary mysqld.exe,
we cross-compiled a small binary creating an administrative user named dave2.
After we copied it to the target system, we replaced mysqld.exe with our binary.
Since we couldn’t restart the service, we needed to reboot the system.
After the system was rebooted, the service mysql started and dave2 was created
and added to the local Administrators group.