
Comparing Windows Execution Methods
Functionality first, security second.
The design model for Windows has traditionally focused on creating useful features. In contrast, Linux is designed for simplicity, security, and stability, Windows tries to be backwards compatible, easy to administrate, and easily integrated with other Microsoft products. These philosophies can also be seen in Microsoft's choice to store credentials of logged in users in memory. Storing credentials is necessary to allow single sign-on after a user logs in, but presents a security risk if an attacker can gain access to credential memory.
So what's the point? Windows and Active Directory have been designed with a "functionality first" approach, and often this design comes at the cost of security. It sounds like a bad thing, but this approach has largely led to Microsoft's domination of the computer market. When you log into your Windows computer at school or work, it's all but guaranteed you are authenticating against Active Directory.
Easy to administrate.
No different from other Operating Systems, Windows has the need for remote administration capabilities. During the late 1900's, our grandparents used programs like Telnet or Rlogin to perform admin tasks on their systems. Today we use SSH or Remote Desktop instead. As Windows developed, Microsoft created new methods of remote management to ease the burden on administrators -- but, for backwards compatibility reasons, they have not removed old methods. Currently, Windows computers have a plethora of remote execution methods Microsoft has collected over the years. Some of these need to be explicitly enabled, like Remote Desktop, but others are enabled by default, like SMB.
As an attacker, we have to make an important decision when we run commands on other people's computers. What execution method are we going to use? Execution methods are not created equal, and each have pros and cons, depending on the situation.
Currently, these are the legitimate methods of remote access Windows supports. Each method is covered in detail below. Unless configured otherwise, these methods require having local admin privileges on the target host.
- Remote Desktop
- PsExec
- SMBexec
- WMIexec
- DCOMexec/MMCexec
- Atexec
- WinRM/PSRemoting
Lateral movement galore
Remote Desktop
Remote Desktop is a proprietary protocol that Microsoft developed to allow administrators graphical remote access to a target computer. RDP runs over TCP/3389 and must be allowed for Remote Desktop connections.
Remote Desktop Connection (mstsc.exe)
Using the Microsoft native RDP client, we can log in to systems that are set up to allow Remote Desktop.


Microsoft's Remote Desktop client is a GUI tool, and necessarily requires GUI access to use. As attackers, we do not always have GUI access, so the native Remote Desktop client is often not useful to us. The native client also does not have the option to authenticate with NTLM hashes.
XFreeRDP
XFreeRDP is a Remote Desktop client that comes with Kali Linux. It can also be used with Proxychains, making it an ideal tool in many circumstances. XFreeRDP also supports NTLM hashes instead of passwords, and can be used to pass-the-hash when RestrictedAdmin mode is not enabled.


SharpRDP
SharpRDP is a C Sharp project that provides us with the ability to run commands over RDP by sending virtual keystrokes. This is especially great when we only have access to a command line.

PsExec
PsExec.exe
PsExec.exe is a Windows Sysinternals tool to perform agentless remote administration by running commands remotely.

Pretty neat, but how does it work? Let's start with the network traffic.

PsExec connects to the ADMIN$ share, and creates the PSEXESVC.exe file. This uploaded file ends up in C:\Windows, and we can go look at it when a PsExec session is active. This is all SMB traffic and goes over TCP/445.

Let's see if Virustotal knows anything about this file.

Virustotal tells us this file is the "PsExec service host" -- seems reasonable considering its name. Let's look at the network traffic to see if we can find anything else.
After uploading the service host executable, PsExec contacts the Endpoint Mapper service on MSRPC/135 and negotiates a high level port for RPC communications.

The client can now talk to the Service Control Manager (SVCCTL) remotely over our negotiated port.

We can look at services on the endpoint and see that PsExec created a new service!

As you can see, the PSEXEC service is started, and the executable it starts is the PSEXESVC.exe file that was uploaded to the ADMIN$ share earlier.
We can see the process tree lineage -- parent and child processes of PSEXESVC.exe.

Let's go back to Wireshark to see if we can figure out how PsExec communication works between the two hosts.

The PsExec service opens three different pipes in the packet capture above: stdin, stdout, and stderr. Let's see if we can find these pipes on the host itself.

Input and output for the PsExec remote command is sent and received through these named pipes -- in our case, cmd.exe. In this way, the named pipes that PsExec creates act like the netcat -e flag does, redirecting a binary's input and output over the network.
So to summarize, PsExec:
- Logs into the
ADMIN$share on SMB/445 - Uploads the
PSEXESVC.exeservice binary to the share - Creates a new service, PSEXESVC with executable =
PSEXESVC.exe - Starts the PSEXESVC service which runs the remote command
- Uses named pipes to send input and output to and from the remote host
- Stops and deletes the PSEXESVC service
- Deletes the
PSEXESVC.exefile fromADMIN$
PsExec.exe can situationally be a good tool for attackers since it is legitimately used by Windows administrators, but it is a Windows EXE, which limits an attacker to Windows -- not ideal. If only there were a Linux version of PsExec...
psexec.py
Introducing psexec.py the Linux version of PsExec.exe. psexec.py is an Impacket script which replicates the same execution behavior explored above.

Notice some key differences in the screenshot above: psexec.py gives a random name to the created service and uploaded executable. Seems a little bit less legitimate than the Sysinternals PsExec.exe.
We can take a look at the executable file psexec.py creates.

Oh no! It looks like the binary psexec.py creates is not viewed favorably by most antivirus. Checking the source code for psexec.py, we can see the executable is from the RemCom project -- an open source replacement for PsExec.exe. We can see the similar, but slightly different named pipes opened up by the RemCom binary.

Would it be possible to get psexec.py to use the original PsExec.exe service executable? We probably could. Outside of copyright infringement, there should be no reason we can't build a Linux compatible client for the real PsExec service. But the reality of it is, PsExec is not stealthy, and is generally a bad method of execution for an attacker. There are too many artifacts that PsExec leaves, legitimate or not. A binary dropped to disk and a new service created on the remote host. Any competent blue team should be able to log on these -- especially the created service. So what should we use instead if PsExec is so bad?
SMBexec
Introducing SMBExec, a slightly better method to execute commands remotely. Before we talk about the tools, it's important to understand that SMBExec is a technique, not a tool -- though there are tools with the same name. Some tools that take advantage of the SMBexec method: CrackMapExec, secretsdump, smbexec.py, Invoke-SMBExec, SCShell, and more.
So what's the difference between PsExec and SMBExec methods? Let's observe the network traffic of smbexec.py to figure that out.
smbexec.py
We can see Impacket's smbexec.py looks identical to psexec.py at first glance.

But it's a different method. Let's check the packet capture to see how.

To start it off, smbexec.py logs in to SMB, connects to the IPC$ share, which contains the svcctl named pipe -- another way to access the Service Control Manager. Remember the method standard PsExec connected to the Service Control Manager was MSRPC on TCP/135 and negotiated a high level port to talk to the Service Control Manager.

After opening the Service Control Manager, smbexec.py creates a new service, starts it, and then immediately deletes it. What? Let's look at the logs on our remote host to find out more about that service.

There are a few things to note here. First, event type 7045 is a creation of a new service. Notice that smbexec.py produces many of these service creation events. Every command executed using SMBexec creates a new service. This means that if you run 100 commands with SMBexec, there will be 100 events of type 7045. It also means any commands you run using SMBexec end up in someone's logs.
Additionally, output is redirected to a file named __output and placed on the C$ share. I had to make a few modifications to the code to be able to view this file because normally __output is read from a share and then immediately deleted.

Now that we removed the script's ability to delete the __output file, we should be able to see it on the C$ share.

The smbexec.py script also unfortunately uses static values for many of its undesirable artifacts. Every service smbexec.py creates will be named BTOBTO unless changed in the code.

To summarize, the SMBexec technique:
- Logs into the Service Control Manager
- Creates a new service with the executable being a single command
- Runs the new service, optionally saving command output to an SMB share
- Stops and removes the newly created service
- Repeat from step 2 for additional commands run
This leaves us with a lot of services created, command output written to disk, and all commands run end up in Windows logs. SMBExec ends up not much better than PsExec for stealth. Let's see if we can still salvage this exec method...
SCShell
SCShell is a twist on the traditional SMBExec method that modifies existing services to run commands instead of creating new services. To aid with stealth, command output is also not returned.

Going back to Wireshark, we can see that the targeted service was modified -- no services were created by SCShell.

We can view TapiSrv, the hijacked service using services.msc and confirm the executable was modified.

The data that shows up in Event Viewer is also much more terse, as apparently service modifications are not logged by default.

Downsides of SCShell include a lack of command output and the possibility for artifacts if a service isn't set back the way it was before modification.
Though no tool like this exists, I think a good compromise between stealth and usability would be:
- Create a new service with random or innocent-looking name
- Set executable to be blank or innocent-looking binary. This executable would show up in Event Viewer logs.
- Modify the service with desired command and start the service
- Save output to a randomly named file stored in
%TEMP% - Modify and restart the same service for additional commands
- Finally, delete the service when finished
Advantages of this method would be:
- Only one service created
- No commands stored in Event Viewer
- Command output retrieved
- No statically named services or files
- No possibility of accidentally messing up a real service.
WMIexec
Shifting gears away from Services, WMIexec is an execution method that relies on a specific feature of the Windows Management Instrumentation (WMI). WMI was intended to be a standard that would allow software or administrators to query Windows machines for information. WMI can be used to identify the Operating System type and version, find currently logged on users, or get file contents. For some reason it can also be used to create processes on the remote host -- Ouch. Since we're talking about remote execution methods, we'll be focusing on this "feature" of WMI.
WMI is enabled on Windows by default, making it a prime candidate for lateral movement. The WMI service relies on DCOM, which is present on MSRPC (TCP/135).
From one of the creators of Impacket:
A similar approach to smbexec but executing commands through WMI. Main advantage here is it runs under the user (has to be Admin) account, not SYSTEM, plus, it doesn't generate noisy messages in the event log that smbexec.py does when creating a service. Drawback is it needs DCOM, hence, I have to be able to access DCOM ports at the target machine.
wmic.exe
The old school method for WMI lateral movement is the Windows builtin executable: wmic.exe. This WMI client was apparently deprecated in 2012, and could be removed sometime in the future. That being said, we all know Microsoft's stance on keeping antiquated parts of Windows around.

Notice how there is no command output with wmic.exe. We can, however, see the return value of 0, meaning the command ran successfully.

The above command failed, and gives us a return value of 9. If the command fails, it will show a non-zero return value.
Additionally, we can use the current logon session instead of specifying credentials.

Unfortunately, wmic.exe does not support passing-the-hash.
WMI using Powershell
The replacement for wmic.exe are Powershell cmdlets Invoke-CimMethod and Invoke-WmiMethod. We can use these cmdlets to execute commands just like we did with wmic.exe.

Like wmic.exe, we can specify alternate credentials.

We can also create a wmiclass object and use it directly. Unfortunately, I don't think it's possible to feed it alternate credentials.

WMIOps
WMIOps is an extension of the basic WMI Powershell cmdlets tailored for offensive security. It supports many functions, including WMI command execution. Here we execute an mshta.exe command on the Domain Controller.

We can also read files remotely using WMI. If we save the output from a command to a file, then read the file with WMI, we can effectively gain command output!

wmiexec.py
Surprise! Impacket has an example script to do WMIexec from Python platforms. wmiexec.py does exactly what it says on the tin.

By default, the Blue Team won't see lateral movement over WMI. No services are created, or files dropped to disk. However with some additional audit settings, we can observe the processes created from WMIexec. Put your blue team hat on for this next part.
First, we need to enable auditing for Process Creation. This allows us to see Event logs with ID 4688.

Next we need to enable command line auditing for process creations. This allows us to see the full command line of created processes.

Now when we go check out process creation logs for WMIExec, we can pretty clearly see the evil that is happening on our system.

That's enough Blue Teaming for now. Since the detections above are non-default, WMIexec is miles ahead of PsExec or SMBexec in terms of stealth.
DCOMexec/MMCexec
Rather than trying to explain how DCOMexec works myself, I'm going to defer to the original blog post regarding this discovery. In essence, through the use of remote COM objects, it is possible to execute commands remotely through the MMC20 application.
Quoting Matt Nelson's blog post:
While enumerating the different DCOM applications, I came across the MMC Application Class (MMC20.Application). This COM object allows you to script components of MMC snap-in operations. While enumerating the different methods and properties within this COM object, I noticed that there is a method named “ExecuteShellCommand” under Document.ActiveView.
Read the full blog post to see manual DCOM execution. Impacket also has a script to establish a DCOM shell.

As Matt Nelson's blog post states, this technique is blocked by the Windows firewall by default. The firewall on the target host was disabled for dcomexec.py to work.
We can see legitimate behavior of MMC used to manage remote systems here.

Though not a true "remote access" method yet, we can see that MMC has the capability to remotely interact with another machine's administrative facilities.
This is where I'm going to leave this exec method. There are a couple of different object types we could use instead of MMC20, but none of them worked for me unless the firewall was disabled. It's a cool proof of concept, but has never been useful to me during client engagements.
Atexec
The Atexec method uses the Windows Task Scheduler to run commands remotely. We can create a new immediate scheduled task and have it delete itself after it completes.
Task Scheduler
With the correct privileges, Task Scheduler supports interacting with tasks on remote computers.

We can create a new scheduled task that runs immediately on the target computer. Right click the Task Scheduler Library and select "create task".



As soon as we click "OK" the scheduled task we just made executes and then deletes itself. We can see that the task command ran in our web logs.

We do not get command output from this manual method -- it is blind execution.
Schtasks.exe
We can create the same immediate tasks with the command line tool schtasks.exe. This command sucks, but it's still good to have options. Instead of creating a task that deletes itself when finished, we need to create a new task, manually run the task, then manually delete it.
set RAND=%RANDOM% & schtasks.exe /Create /S %TARGET% /U %USER% /P %PASS% /TN %RAND% /SC MONTHLY /TR %COMMAND% & schtasks.exe /Run /S %TARGET% /U %USER% /P %PASS% /TN %RAND% & schtasks.exe /Delete /S %TARGET% /U %USER% /P %PASS% /TN %RAND% /F
If desired, omit the /U %USER% and /P %PASS% to use the current user's session token.
New-ScheduledTask
Instead, we can use the New-ScheduledTask family of Powershell cmdlets. The following stacked command creates a new task, starts it, then deletes it.
$Rand = Get-Random; $Cim = New-CimSession -Credential (new-object PSCredential $User,(ConvertTo-SecureString -AsPlainText -Force $Pass)) -SessionOption (New-CimSessionOption -Protocol Dcom) -ComputerName $Target; Register-ScheduledTask -TaskName $Rand -Action (New-ScheduledTaskAction -Execute $Command) -Trigger (New-ScheduledTaskTrigger -Once -At (Get-Date)) -Settings (New-ScheduledTaskSettingsSet) -CimSession $Cim | Start-ScheduledTask; Unregister-ScheduledTask -CimSession $Cim -TaskName $Rand
Note that you need to fill out or set $User, $Pass, $Target, and $Command before pasting this into Powershell. Here's an example of the command in use:

Our lateral movement command executes successfully. We get a web request from mshta.exe, and our payload is executed on the remote host.

Note that even though though the scheduled task is deleted, our desired lateral movement process is not terminated.
atexec.py
Finally, Impacket has a python script that is similar to the other Impacket exec scripts, but limited to running one command at a time. The script operates in a similar create, run, delete fashion as the Powershell above.

WinRM and PSRemoting
WinRM is the last execution method covered in this already very long blog post. PSRemoting is included here because it relies upon the WinRM service to run commands -- no WinRM, no PSRemoting. I should also note that WinRM is not enabled by default. This already makes WinRM less useful than other methods. At best, it is a niche execution method that can be used during some engagements. If hosts have TCP/5985 or TCP/5986, it's a likely bet they have WinRM enabled.
These ports, if scanned with a port/service/vulnerability scanner like Nessus, will show up as web service ports, because WinRM actually uses SOAP, a web based protocol to perform management under the hood.
The first thing we need to do to test out WinRM is to enable it on an endpoint. We can either use winrm.exe or Enable-PSRemoting. Both of these require local administrator privileges.

Note here that the -q flag will answer yes to all [Y/n] prompts that would normally be shown without the flag.

Once WinRM is enabled, we can use tools to execute commands remotely.
winrs.exe
The first tool is winrs.exe, a builtin Windows binary client for WinRM.

Like other legitimate Microsoft tools, we can authenticate with the current user's session or alternate plaintext credentials, but not NTLM hashes.
PSRemoting
Powershell comes with a cmdlet to run remote Powershell on WinRM enabled computers.

If interactive isn't your style, single commands can be run using Invoke-Command.

Evil-WinRM
If Powershell isn't available on your system, or you need to pass-the-hash, Evil-WinRM has your back. Evil-WinRM is a WinRM/PSRemoting client written in Ruby intended for penetration testers.

It has a lot of nice features like pass-the-hash and supports loading Powershell scripts and C Sharp executables on the fly.

Epilogue
While each execution method has their own situational strengths and weaknesses, there is a general order I follow when attempting lateral movement:
- WMIexec
- WinRM (When available)
- SMBexec
- PsExec
- Remote Desktop (When available)
- Atexec
- DCOMexec
The list above is a good start, but consider the stealth/opsec requirements for every situation before moving laterally.