SMBExec.py is a powerful tool in a pentester’s arsenal. It has been analyzed and blogged about countless times. However, in my experience these posts are mostly from the standpoint of the defender— what Event Logs are generated, what static IOCs exist, etc. This post will take a different approach, examining SMBExec.py from the standpoint of an attacker looking to gain a deeper understanding of their tooling. This will then enable us in future posts to look at ways that defenders detect this tool, and later to modify the tool to evade those detections.
How Does it Work?
There are a few different ways to go about examining this particular tool. While it is open source, and therefore we could just read through the code, we wanted to demonstrate analyzing the tool from a blackbox standpoint. Additionally, it is often beneficial to visually inspect and trace the actions taken by the binary during runtime. As such, we primarily leveraged Process Monitor (“procmon”) for this purpose, running it on the target Windows 10 device, while firing off smbexec.py against it from a Linux VM. Procmon recorded the actions taken, allowing us to further sort and filter to view the results we were interested in. Let’s take a look.
First off, Windows Defender had to be disabled on the target host. It’s a bit of a spoiler, but in the screenshot below you can see what payload it is concerned with.
And over on the Linux VM side, the following error was received when the tool was blocked by Defender. The error isn’t very intuitive, so we figured this was worth sharing.
After disabling Defender and re-running smbexec.py, we were able to record the associated actions with Procmon. You can see the list of actions pertaining to starting the connection and authenticating below.
A TCP connection is initiated from the Linux VM to the “microsoft-ds” service (which is SMB on TCP port 445), followed by some TCP sends and receives. The authentication process begins, with some LSA-related registry keys being queried and local account look-up being performed. As this is a domain account, local account look-up fails, and the host queries the Domain Controller instead. After successfully authenticating, more traffic takes place with the Linux VM over SMB, and finally we get into the functionality unique to smbexec.py.
A new Windows Service is created with the name “BTOBTO”, and we can see some registry key values being set. One notable key value “ImagePath”, holds a command line one-liner.
The “%COMSPEC%” variable is the full system path to cmd.exe. We can see that this command will create an “execute.bat” in %TEMP% which will run “cd” and write its output to the local default C$ SMB share, execute it, and then delete it. This is how smbexec.py creates the text in the prompt that is displayed when it is awaiting commands (ex: “C:\Windows\System32>”). Walking through the list of actions, we can see that all of this takes place. Cmd.exe is run, creating execute.bat.
Execute.bat is run, it runs “cd”, and writes its output to the “__output” file on the local C$ share.
A number of TCP sends and receives occur across the SMB connection, presumably sending the results from the “__output” file, and then execute.bat is deleted. This file delete activity was hard to find within Procmon, as it was not listed as a FileDelete operation or anything else aptly named. Instead, under a CreateFile operation you can see a “Delete on Close” option being set, followed by a CloseFile operation. This seems to be the deletion of execute.bat that is referenced in the command one-liner. If you need to search for this in Procmon, you can leverage the “CTRL+F” search feature to search for “Delete on”.
Finally, the BTOBTO Service is deleted.
This entire process chain happens as a result of the “cd” command that is run to generate the text prompt within the smbexec.py window. Luckily, the process of executing a command is nearly identical, where the command-to-be-run is echo’d into execute.bat rather than the “cd” command. Below is an example of the “whoami” command being run via smbexec.py.
As you can see, the “BTOBTO” Service name is reused, as well as the “execute.bat” file name. Furthermore, the “echo cd” command occurs in between every command run by a user via smbexec.py.
After all of the above, the following general execution flow can be described for smbexec.py:
- TCP connection via SMB from attacker to target
- Authentication (Local first, falling back to domain auth if local fails)
- Execution of the “cd” command
a. Creation of BTOBTO service
b. Execution of BTOBTO service
i. Echoing command to %TEMP%\execute.bat
ii. Executing execute.bat
iii. Deleting execute.bat
c. Deletion of BTOBTO service
- Execution of command entered by user
a. Same process as 3.a – 3.c
- Execution of the “cd” command
a. Identical to step 3
- Execution of next command entered by user
The pattern then continues for as long as a user continues to enter commands for smbexec.py. These steps now make a lot of sense, for anyone that has ever experienced smbexec.py terminating prematurely, and then being unable to run smbexec.py again— the process chain must have terminated before execute.bat or BTOBTO was able to be deleted. As the code reuses these names over and over, conflicts with those filenames are very possible.
This should also give us plenty to think about from a Detection standpoint. Let’s look at some basic, static Indicators of Compromise (IOC) that we can list out from what we have observed.
• The Service name created on a target endpoint is always “BTOBTO”
• The path “%TEMP%\execute.bat” is always created on the target endpoint’s filesystem
• The actual unwrapped path may change, where the path pointed to by %TEMP% differs. In our case, it was C:\Windows\Temp
• In between running user-provided commands via smbexec.py in its default mode of operation, the tool always executes a command beginning with the following:
• %COMSPEC% /Q /c echo cd^> \127.0.0.1\C$__output 2>&1 [snip]
• Again, %COMSPEC% will unroll to the full path to cmd.exe. For the vast majority of all systems, this will point to C:\Windows\System32\cmd.exe
• The C:__output file for retrieving command output always has this same name, and is at the same location (when smbexec.py is run with default options)
That’s a good place to start for understanding how smbexec.py works behind the scenes, and creating some basic detection via static IOCs. In the next post in this series we will go over bypassing endpoint security controls’ detection and blocking of smbexec.py.