diff --git a/envs/monkey_zoo/packer/setup_mimikatz_15.yml b/envs/monkey_zoo/packer/setup_mimikatz_15.yml index 6c2a95ad0dd..c2dbe59b871 100644 --- a/envs/monkey_zoo/packer/setup_mimikatz_15.yml +++ b/envs/monkey_zoo/packer/setup_mimikatz_15.yml @@ -1,8 +1,10 @@ --- - name: Create a mimikatz-15 machine image hosts: all + become_method: runas vars: ansible_remote_tmp: C:\Windows\Temp + ansible_become_password: pAJfG56JX>< tasks: - name: Create user win_user: @@ -33,6 +35,33 @@ win_command: cmd: netsh advfirewall firewall add rule name="Allow Port 445" dir=in action=allow protocol=TCP localport=445 + - name: Allow port 135 DCOM + win_command: + cmd: netsh advfirewall firewall add rule name="Allow Port 135" dir=in action=allow protocol=TCP localport=135 + + - name: Allow DCOM Connection + win_firewall_rule: + name: Allow DCOM Connection + localport: any + direction: in + program: C:\Windows\System32\svchost.exe + action: allow + state: present + enabled: yes + + - name: Disable Windows Defender using registry + win_regedit: + path: HKLM:\SOFTWARE\Policies\Microsoft\Windows Defender + name: DisableAntiSpyware + data: 1 + type: dword + state: present + + - name: Disable Windows Defender + win_shell: Set-MpPreference -DisableIntrusionPreventionSystem $true -DisableIOAVProtection $true -DisableRealtimeMonitoring $true -EnableNetworkProtection AuditMode -Force + become: yes + become_user: m0nk3y + - name: Change the hostname to mimikatz-15 ansible.windows.win_hostname: name: mimikatz-15 diff --git a/monkey/agent_plugins/exploiters/wmi/src/wmi_client.py b/monkey/agent_plugins/exploiters/wmi/src/wmi_client.py index 82fcbcfb078..1e9024242b7 100644 --- a/monkey/agent_plugins/exploiters/wmi/src/wmi_client.py +++ b/monkey/agent_plugins/exploiters/wmi/src/wmi_client.py @@ -1,3 +1,4 @@ +import logging from typing import Optional from impacket.dcerpc.v5.dcom import wmi @@ -8,6 +9,8 @@ from common.credentials import Credentials, LMHash, NTHash, Password from infection_monkey.i_puppet import TargetHost +logger = logging.getLogger(__name__) + def secret_of_type(credentials, type) -> Optional[SecretStr]: if type is Password and isinstance(credentials.secret, Password): @@ -30,6 +33,7 @@ class WMIClient: def __init__(self): self._wbem_services: Optional[wmi.IWbemServices] = None self._connected = False + self._dcom: Optional[DCOMConnection] = None def connected(self) -> bool: return self._connected @@ -42,34 +46,41 @@ def login(self, host: TargetHost, credentials: Credentials): :param credentials: Credentials to use :raises Exception: If login fails """ - # Impacket has a hard-coded timeout of 30 seconds - dcom = DCOMConnection( - str(host.ip), - username=credentials.identity.username, - password=get_plaintext(secret_of_type(credentials, Password)), - domain=str(host.ip), - lmhash=get_plaintext(secret_of_type(credentials, LMHash)), - nthash=get_plaintext(secret_of_type(credentials, NTHash)), - oxidResolver=True, - ) - + # Impacket has a hard-coded timeout of 120 seconds try: - self._wbem_services = self._wbem_login(dcom) - except Exception as err: - dcom.disconnect() - raise err - - self._connected = True - - def _wbem_login(self, dcom: DCOMConnection) -> wmi.IWbemServices: - iface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login) - iWbemLevel1Login = wmi.IWbemLevel1Login(iface) + self._dcom = DCOMConnection( + str(host.ip), + username=credentials.identity.username, # type: ignore[union-attr] + password=get_plaintext(secret_of_type(credentials, Password)), + domain=str(host.ip), + lmhash=get_plaintext(secret_of_type(credentials, LMHash)), + nthash=get_plaintext(secret_of_type(credentials, NTHash)), + oxidResolver=True, + ) + iInterface = self._dcom.CoCreateInstanceEx( + wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login + ) + except Exception: + try: + self._dcom.disconnect() # type: ignore[union-attr] + except KeyError: + logger.exception("Disconnecting the DCOMConnection failed") + + raise + + iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface) try: - return iWbemLevel1Login.NTLMLogin("//./root/cimv2", NULL, NULL) + self._wbem_services = iWbemLevel1Login.NTLMLogin("//./root/cimv2", NULL, NULL) + except Exception: + self._dcom.disconnect() + + raise finally: iWbemLevel1Login.RemRelease() + self._connected = True + def execute_remote_process(self, command, path) -> bool: """ Execute a process on the remote host @@ -86,3 +97,27 @@ def execute_remote_process(self, command, path) -> bool: # not need to use the dropper at all. result = win32_process.Create(command, path, None) return (result.ProcessId != 0) and (result.ReturnValue == 0) + + def __del__(self): + if self._wbem_services: + self._wbem_services.RemRelease() + self._wbem_services = None + + if self._dcom: + self._dcom.disconnect() + self._dcom = None + WMIClient._dcom_cleanup() + + @staticmethod + def _dcom_cleanup(): + for port_map in list(DCOMConnection.PORTMAPS.keys()): + del DCOMConnection.PORTMAPS[port_map] + for oid_set in list(DCOMConnection.OID_SET.keys()): + del DCOMConnection.OID_SET[oid_set] + + DCOMConnection.OID_SET = {} + DCOMConnection.PORTMAPS = {} + if DCOMConnection.PINGTIMER: + DCOMConnection.PINGTIMER.cancel() + DCOMConnection.PINGTIMER.join() + DCOMConnection.PINGTIMER = None diff --git a/monkey/agent_plugins/exploiters/wmi/src/wmi_remote_access_client.py b/monkey/agent_plugins/exploiters/wmi/src/wmi_remote_access_client.py index bd86c026013..9cdde94bd32 100644 --- a/monkey/agent_plugins/exploiters/wmi/src/wmi_remote_access_client.py +++ b/monkey/agent_plugins/exploiters/wmi/src/wmi_remote_access_client.py @@ -63,8 +63,8 @@ def login(self, credentials: Credentials, tags: Set[AgentEventTag]): tags.update(LOGIN_TAGS) try: - self._wmi_client.login(self._host, credentials) self._smb_client.login(credentials, tags) + self._wmi_client.login(self._host, credentials) except Exception as err: raise RemoteAuthenticationError from err