Implementing Fileless Lateral Movement in C#
To implement this, we’ll begin by creating a new C# console application project.
The first API we must call is OpenSCManagerW.
From our discussion of the function prototype of OpenSCManagerW,
we know that the first argument is the hostname of the target machine,
or appsrv01 in our case.
We’ll set the second argument, the database name, to null,
and the third argument to the desired
access right to the service control manager.
We’ll request SC_MANAGER_ALL_ACCESS for full access,
by specifying its numerical value.
Once the authentication is complete, we must open an existing service.
To avoid any issues, we must select a service that is not
vital to the function of the operating system
and is not in use by default.
One candidate is SensorService, which manages various sensors.
This service is present on both Windows 10 and Windows 2016/2019
by default but is not run automatically at boot.
The first API we need to use is OpenService.
As the first argument,
we must supply the handle to the service control manager we received from
OpenSCManager.
The second parameter is the name of the service,
in this case, “SensorService”,
and the last argument is the desired access to the service.
We can request full access with
SERVICE_ALL_ACCESS, by using its numerical value.
To continue, we’ll add the P/invoke import for OpenService.
Now that the import is complete and we understand the arguments we need to pass,
we can update the code to call OpenService:
After the SensorService service has been opened,
we must change the service binary with the ChangeServiceConfigA API.
While the API accepts many arguments, we only need to specify some of them.
The first is the handle to the service we obtained from calling OpenService.
Next, dwServiceType allows us to specify the type of the service.
We only want to modify the service binary
so we’ll specify SERVICE_NO_CHANGE by its numerical value.
We can modify the service start options through the third argument, dwStartType.
Since we want to have the service start once we have modified the service binary,
we’ll set it to SERVICE_DEMAND_START.
As the fourth argument, dwErrorControl will set the error action,
and we’ll specify SERVICE_NO_CHANGE
with its numerical value of 0 to avoid modifying it.
The fifth argument, lpBinaryPathName,
contains the path of the binary that the service will execute when started.
This is what we want to update
and as an initial proof of concept, we’ll set this to notepad.exe.
The final six arguments are not relevant to us and we can set them to null.
The final piece we need is the P/invoke import of ChangeServiceConfig.
At this point, we can update our code
to invoke the call with the discussed arguments:
Once the proof of concept is compiled,
we can execute it on the Windows 10 client in the context of the dave user.
This will change the service binary of SensorService to notepad.exe.
We can log in to appsrv01 and verify this from the services manager.
The final step is to start the service,
which we can do through the StartService API.
The function prototype for this API is relatively simple.
The first argument is the service handle created by OpenService.
The third argument, lpServiceArgVectors_,
is an array of strings that are passed as arguments to the service.
We do not require any so we can set it to null
and then set dwNumServiceArgs, which is the number of arguments, to 0 as well.
Next, we’ll add the P/invoke import for StartService to our code.
Finally, we’ll add the code to invoke the API.
Once this code has been added to the project, we can compile
and execute it in the context of the dave user.
On appsrv01, we find the Notepad process running as SYSTEM.
Since Notepad is not a service executable,
the service control manager will terminate the process after a short period of time,
but we have obtained code execution.
In this section, we have discussed and
implemented a technique that expands on PsExec
to provide lateral movement without creating a new service.