@@ -45,6 +45,8 @@ def __init__(self, args, db, host):
4545 "0000052B" : "STATUS_WRONG_PASSWORD" ,
4646 "00000721" : "RPC_S_SEC_PKG_ERROR"
4747 }
48+ self .iWbemLevel1Login = None
49+ self .dcom_conn = None
4850
4951 connection .__init__ (self , args , db , host )
5052
@@ -58,6 +60,14 @@ def proto_logger(self):
5860 }
5961 )
6062
63+ # Redefine disconnect function.
64+ def disconnect (self ):
65+ if self .conn :
66+ self .conn .disconnect ()
67+ if self .dcom_conn :
68+ self .dcom_conn .disconnect ()
69+ return
70+
6171 def create_conn_obj (self ):
6272 connection_target = fr"ncacn_ip_tcp:{ self .remoteName } [{ self .port !s} ]"
6373 self .logger .debug (f"Creating WMI connection object to { connection_target } " )
@@ -145,18 +155,15 @@ def print_host_info(self):
145155
146156 def check_if_admin (self ):
147157 try :
148- dcom = DCOMConnection (self .remoteName , self .username , self .password , self .domain , self .lmhash , self .nthash , oxidResolver = True , doKerberos = self .doKerberos , kdcHost = self .kdcHost , aesKey = self .aesKey , remoteHost = self .host )
149- iInterface = dcom .CoCreateInstanceEx (CLSID_WbemLevel1Login , IID_IWbemLevel1Login )
158+ self . dcom_conn = DCOMConnection (self .remoteName , self .username , self .password , self .domain , self .lmhash , self .nthash , oxidResolver = True , doKerberos = self .doKerberos , kdcHost = self .kdcHost , aesKey = self .aesKey , remoteHost = self .host )
159+ iInterface = self . dcom_conn .CoCreateInstanceEx (CLSID_WbemLevel1Login , IID_IWbemLevel1Login )
150160 flag , self .stringBinding = dcom_FirewallChecker (iInterface , self .host , self .args .rpc_timeout )
151161 except Exception as e :
152162 self .logger .debug (f"Received error while checking admin: { e } " )
153- if "dcom" in locals ():
154- dcom .disconnect ()
155163 if "access_denied" not in str (e ).lower ():
156164 self .logger .fail (str (e ))
157165 else :
158166 if not flag or not self .stringBinding :
159- dcom .disconnect ()
160167 error_msg = f'Check admin error: dcom initialization failed with stringbinding: "{ self .stringBinding } ", please try "--rpc-timeout" option. (probably is admin)'
161168
162169 if not self .stringBinding :
@@ -165,16 +172,14 @@ def check_if_admin(self):
165172 self .logger .fail (error_msg ) if not flag else self .logger .debug (error_msg )
166173 else :
167174 try :
168- iWbemLevel1Login = IWbemLevel1Login (iInterface )
169- iWbemServices = iWbemLevel1Login .NTLMLogin ("//./root/cimv2" , NULL , NULL )
175+ self .iWbemLevel1Login = IWbemLevel1Login (iInterface )
176+ _ = self .iWbemLevel1Login .NTLMLogin ("//./root/cimv2" , NULL , NULL )
177+ self .iWbemLevel1Login .RemRelease ()
170178 except Exception as e :
171- dcom .disconnect ()
172-
173179 if "access_denied" not in str (e ).lower ():
174180 self .logger .fail (str (e ))
175181 return False
176182 else :
177- dcom .disconnect ()
178183 self .logger .extra ["protocol" ] = "WMI"
179184 self .admin_privs = True
180185
@@ -360,48 +365,60 @@ def hash_login(self, domain, username, ntlm_hash):
360365 self .logger .success (out )
361366 return True
362367
363- # It's very complex to use wmi from rpctansport "convert" to dcom, so let we use dcom directly.
364368 @requires_admin
365- def wmi (self , wql = None , namespace = None ):
366- """Execute WQL syntax via WMI
367-
368- This is done via the --wmi flag
369- """
369+ def wmi_query (self , wql = None , namespace = None , callback_func = None ):
370370 records = []
371371 if not wql :
372- wql = self .args .wmi .strip ("\n " )
372+ wql = self .args .wmi_query .strip ("\n " )
373373
374374 if not namespace :
375375 namespace = self .args .wmi_namespace
376376
377377 try :
378- dcom = DCOMConnection (self .remoteName , self .username , self .password , self .domain , self .lmhash , self .nthash , oxidResolver = True , doKerberos = self .doKerberos , kdcHost = self .kdcHost , aesKey = self .aesKey , remoteHost = self .host )
379- iInterface = dcom .CoCreateInstanceEx (CLSID_WbemLevel1Login , IID_IWbemLevel1Login )
380- iWbemLevel1Login = IWbemLevel1Login (iInterface )
381- iWbemServices = iWbemLevel1Login .NTLMLogin (namespace , NULL , NULL )
382- iWbemLevel1Login .RemRelease ()
378+ iWbemServices = self .iWbemLevel1Login .NTLMLogin (namespace , NULL , NULL )
379+ self .iWbemLevel1Login .RemRelease ()
383380 iEnumWbemClassObject = iWbemServices .ExecQuery (wql )
384381 except Exception as e :
385- dcom .disconnect ()
386382 self .logger .debug (str (e ))
387383 self .logger .fail (f"Execute WQL error: { e } " )
388384 return False
389385 else :
390386 self .logger .info (f"Executing WQL syntax: { wql } " )
391387 try :
392- while True :
393- wmi_results = iEnumWbemClassObject .Next (0xFFFFFFFF , 1 )[0 ]
394- record = wmi_results .getProperties ()
395- records .append (record )
396- for k , v in record .items ():
397- self .logger .highlight (f"{ k } => { v ['value' ]} " )
388+ if not callback_func :
389+ while True :
390+ wmi_results = iEnumWbemClassObject .Next (0xFFFFFFFF , 1 )[0 ]
391+ record = wmi_results .getProperties ()
392+ records .append (record )
393+ for k , v in record .items ():
394+ self .logger .highlight (f"{ k } => { v ['value' ]} " )
395+ else :
396+ callback_func (iEnumWbemClassObject , records )
398397 except Exception as e :
399398 if str (e ).find ("S_FALSE" ) < 0 :
400399 self .logger .debug (e )
400+ return records
401401
402- dcom .disconnect ()
402+ def list_snapshots (self ):
403+ drive = self .args .list_snapshots
404+ self .logger .info (f"Retrieving volume shadow copies of drive { drive } ." )
405+ wql = "select ID, DeviceObject, ClientAccessible, InstallDate from win32_shadowcopy"
403406
404- return records
407+ def callback_func (iEnumWbemClassObject , records ):
408+ while True :
409+ wmi_results = iEnumWbemClassObject .Next (0xFFFFFFFF , 1 )[0 ]
410+ record = dict (wmi_results .getProperties ())
411+ records .append (record )
412+
413+ snapshots = self .wmi_query (wql = wql , namespace = "root\\ cimv2" , callback_func = callback_func )
414+ if not snapshots :
415+ self .logger .info ("No volume shadow copies found." )
416+ return
417+
418+ self .logger .highlight (f"{ 'Drive' :<8} { 'Shadow Copy ID' :<40} { 'ClientAccessible' :<18} { 'InstallDate' :<27} { 'Device Object' :<50} " )
419+ self .logger .highlight (f"{ '------' :<8} { '--------------' :<40} { '----------------' :<18} { '-----------' :<27} { '-------------' :<50} " )
420+ for record in snapshots :
421+ self .logger .highlight (f"{ drive :<8} { record ['ID' ]['value' ]:<40} { record ['ClientAccessible' ]['value' ]:<18} { record ['InstallDate' ]['value' ]:<27} { record ['DeviceObject' ]['value' ]:<50} " )
405422
406423 @requires_admin
407424 def execute (self , command = None , get_output = False , use_powershell = False ):
@@ -422,14 +439,23 @@ def execute(self, command=None, get_output=False, use_powershell=False):
422439 return ""
423440
424441 if self .args .exec_method == "wmiexec" :
425- exec_method = wmiexec .WMIEXEC (self .remoteName , self .username , self .password , self .domain , self .lmhash , self .nthash , self .doKerberos , self .kdcHost , self .host , self .aesKey , self .logger , self .args .exec_timeout , self .args .codec )
426- output = exec_method .execute (command , get_output , use_powershell = use_powershell )
427-
442+ exec_method = wmiexec .WMIEXEC (
443+ self .remoteName ,
444+ self .iWbemLevel1Login ,
445+ self .logger ,
446+ self .args .exec_timeout ,
447+ self .args .codec
448+ )
428449 elif self .args .exec_method == "wmiexec-event" :
429- exec_method = wmiexec_event .WMIEXEC_EVENT (self .remoteName , self .username , self .password , self .domain , self .lmhash , self .nthash , self .doKerberos , self .kdcHost , self .host , self .aesKey , self .logger , self .args .exec_timeout , self .args .codec )
430- output = exec_method .execute (command , get_output , use_powershell = use_powershell )
450+ exec_method = wmiexec_event .WMIEXEC_EVENT (
451+ self .remoteName ,
452+ self .iWbemLevel1Login ,
453+ self .logger ,
454+ self .args .exec_timeout ,
455+ self .args .codec
456+ )
457+ output = exec_method .execute (command , get_output , use_powershell = use_powershell )
431458
432- self .conn .disconnect ()
433459 if self .args .execute and get_output :
434460 self .logger .success (f'Executed command: "{ command } " via { self .args .exec_method } ' )
435461 buf = StringIO (output ).readlines ()
0 commit comments