def get_nt_challenge_response(self, lm_challenge_response, server_certificate_hash): """ [MS-NLMP] v28.0 2016-07-14 3.3.1 - NTLM v1 Authentication 3.3.2 - NTLM v2 Authentication This method returns the NtChallengeResponse key based on the ntlm_compatibility chosen and the target_info supplied by the CHALLENGE_MESSAGE. It is quite different from what is set in the document as it combines the NTLMv1, NTLM2 and NTLMv2 methods into one and calls separate methods based on the ntlm_compatibility value chosen. :param lm_challenge_response: The LmChallengeResponse calculated beforeand, used to get the key_exchange_key value :param server_certificate_hash: The SHA256 hash of the server certificate (DER encoded) NTLM is authenticated to. Used in Channel Binding Tokens if present, default value is None. See AuthenticateMessage in messages.py for more details :return response: (NtChallengeResponse) - The NT response to the server challenge. Computed by the client :return session_base_key: (SessionBaseKey) - A session key calculated from the user password challenge :return target_info: (AV_PAIR) - The AV_PAIR structure used in the nt_challenge calculations """ if self._negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY and self._ntlm_compatibility < 3: # The compatibility level is less than 3 which means it doesn't support NTLMv2 but we want extended security so use NTLM2 which is different from NTLMv2 # [MS-NLMP] - 3.3.1 NTLMv1 Authentication response, session_base_key = ComputeResponse._get_NTLM2_response(self._password, self._server_challenge, self._client_challenge) key_exchange_key = compkeys._get_exchange_key_ntlm_v1(self._negotiate_flags, session_base_key, self._server_challenge, lm_challenge_response, comphash._lmowfv1(self._password)) target_info = None elif 0 <= self._ntlm_compatibility < 3: response, session_base_key = ComputeResponse._get_NTLMv1_response(self._password, self._server_challenge) key_exchange_key = compkeys._get_exchange_key_ntlm_v1(self._negotiate_flags, session_base_key, self._server_challenge, lm_challenge_response, comphash._lmowfv1(self._password)) target_info = None else: if self._server_target_info is None: target_info = TargetInfo() else: target_info = self._server_target_info if target_info[TargetInfo.MSV_AV_TIMESTAMP] is None: timestamp = get_windows_timestamp() else: timestamp = target_info[TargetInfo.MSV_AV_TIMESTAMP][1] # [MS-NLMP] If the CHALLENGE_MESSAGE TargetInfo field has an MsvAvTimestamp present, the client SHOULD provide a MIC target_info[TargetInfo.MSV_AV_FLAGS] = struct.pack("<L", AvFlags.MIC_PROVIDED) if server_certificate_hash != None: channel_bindings_hash = ComputeResponse._get_channel_bindings_value(server_certificate_hash) target_info[TargetInfo.MSV_AV_CHANNEL_BINDINGS] = channel_bindings_hash response, session_base_key = ComputeResponse._get_NTLMv2_response(self._user_name, self._password, self._domain_name, self._server_challenge, self._client_challenge, timestamp, target_info) key_exchange_key = compkeys._get_exchange_key_ntlm_v2(session_base_key) return response, key_exchange_key, target_info
def test_get_exchange_key_ntlm_v1_extended_session_security(self): test_flags = ntlmv1_with_ess_negotiate_flags expected = ntlmv1_with_ess_key_exchange_key actual = compkeys._get_exchange_key_ntlm_v1( test_flags, ntlmv1_with_ess_session_base_key, server_challenge, ntlmv1_with_ess_lmv1_response, ntlmv1_with_ess_lmowfv1) assert actual == expected
def test_get_exchange_key_ntlm_v1_no_keys(self): test_flags = ntlmv1_negotiate_flags expected = ntlmv1_session_base_key actual = compkeys._get_exchange_key_ntlm_v1(test_flags, ntlmv1_session_base_key, server_challenge, ntlmv1_lmv1_response, ntlmv1_lmowfv1) assert actual == expected
def test_get_seal_key_ntlm2_56(self): test_flags = ntlmv1_with_ess_negotiate_flags test_session_base_key = ntlmv1_with_ess_session_base_key test_exported_session_key = compkeys._get_exchange_key_ntlm_v1( test_flags, test_session_base_key, server_challenge, ntlmv1_with_ess_lmv1_response, ntlmv1_with_ess_lmowfv1) expected = ntlmv1_with_ess_seal_key actual = compkeys.get_seal_key(test_flags, test_exported_session_key, SignSealConstants.CLIENT_SEALING) assert actual == expected
def test_get_exchange_key_ntlm_v1_lm_key(self): test_flags = NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN | \ NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL | \ NegotiateFlags.NTLMSSP_NEGOTIATE_LM_KEY # Not in document, custom test expected = HexToByte('b0 9e 37 9f 7f be cb 1e af 0a fd cb 03 83 c8 a0') actual = compkeys._get_exchange_key_ntlm_v1(test_flags, ntlmv1_session_base_key, server_challenge, ntlmv1_lmv1_response, ntlmv1_lmowfv1) assert actual == expected
def test_get_exchange_key_ntlm_v1_non_nt_key(self): test_flags = NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN | \ NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL | \ NegotiateFlags.NTLMSSP_REQUEST_NON_NT_SESSION_KEY # Not in document, custom test expected = HexToByte('e5 2c ac 67 41 9a 9a 22 00 00 00 00 00 00 00 00') actual = compkeys._get_exchange_key_ntlm_v1(test_flags, ntlmv1_session_base_key, server_challenge, ntlmv1_lmv1_response, ntlmv1_lmowfv1) assert actual == expected
def test_get_exchange_key_ntlm_v1_no_keys(self): test_flags = 3791815219 expected = b"\xd8\x72\x62\xb0\xcd\xe4\xb1\xcb" \ b"\x74\x99\xbe\xcc\xcd\xf1\x07\x84" session_base_key = expected server_challenge = b"\x01\x23\x45\x67\x89\xab\xcd\xef" ntlmv1_lmv1_response = b"\x98\xde\xf7\xb8\x7f\x88\xaa\x5d" \ b"\xaf\xe2\xdf\x77\x96\x88\xa1\x72" \ b"\xde\xf1\x1c\x7d\x5c\xcd\xef\x13" ntlmv1_lmowfv1 = b"\xe5\x2c\xac\x67\x41\x9a\x9a\x22" \ b"\x4a\x3b\x10\x8f\x3f\xa6\xcb\x6d" actual = compute_keys._get_exchange_key_ntlm_v1( test_flags, session_base_key, server_challenge, ntlmv1_lmv1_response, ntlmv1_lmowfv1) assert actual == expected
def test_get_seal_key_ntlm2_40(self): test_flags = NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN | \ NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL | \ NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY test_session_base_key = ntlmv1_with_ess_session_base_key test_exported_session_key = compkeys._get_exchange_key_ntlm_v1( test_flags, test_session_base_key, server_challenge, ntlmv1_with_ess_lmv1_response, ntlmv1_with_ess_lmowfv1) # Not in document, custom test expected = HexToByte('26 b2 c1 e7 7b e4 53 3d 55 5a 22 0a 0f de b9 6c') actual = compkeys.get_seal_key(test_flags, test_exported_session_key, SignSealConstants.CLIENT_SEALING) assert actual == expected
def test_get_exchange_key_ntlm_v1_extended_session_security(self): test_flags = 2181726771 expected = b"\xeb\x93\x42\x9a\x8b\xd9\x52\xf8" \ b"\xb8\x9c\x55\xb8\x7f\x47\x5e\xdc" session_base_key = b"\xd8\x72\x62\xb0\xcd\xe4\xb1\xcb" \ b"\x74\x99\xbe\xcc\xcd\xf1\x07\x84" server_challenge = b"\x01\x23\x45\x67\x89\xab\xcd\xef" lm_response = b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" lm_hash = b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" actual = compute_keys._get_exchange_key_ntlm_v1( test_flags, session_base_key, server_challenge, lm_response, lm_hash) assert actual == expected
def test_get_seal_key_ntlm2_56(self): test_flags = 2181726771 expected = b"\x04\xdd\x7f\x01\x4d\x85\x04\xd2" \ b"\x65\xa2\x5c\xc8\x6a\x3a\x7c\x06" session_base_key = b"\xd8\x72\x62\xb0\xcd\xe4\xb1\xcb" \ b"\x74\x99\xbe\xcc\xcd\xf1\x07\x84" server_challenge = b"\x01\x23\x45\x67\x89\xab\xcd\xef" lm_challenge_response = b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" lm_hash = b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" exported_session_key = compute_keys._get_exchange_key_ntlm_v1( test_flags, session_base_key, server_challenge, lm_challenge_response, lm_hash) actual = compute_keys.get_seal_key(test_flags, exported_session_key, SignSealConstants.CLIENT_SEALING) assert actual == expected
def test_get_exchange_key_ntlm_v1_lm_key(self): test_flags = NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN | \ NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL | \ NegotiateFlags.NTLMSSP_NEGOTIATE_LM_KEY # Not in document, custom test expected = b"\xb0\x9e\x37\x9f\x7f\xbe\xcb\x1e" \ b"\xaf\x0a\xfd\xcb\x03\x83\xc8\xa0" session_base_key = b"\xd8\x72\x62\xb0\xcd\xe4\xb1\xcb" \ b"\x74\x99\xbe\xcc\xcd\xf1\x07\x84" server_challenge = b"\x01\x23\x45\x67\x89\xab\xcd\xef" ntlmv1_lmv1_response = b"\x98\xde\xf7\xb8\x7f\x88\xaa\x5d" \ b"\xaf\xe2\xdf\x77\x96\x88\xa1\x72" \ b"\xde\xf1\x1c\x7d\x5c\xcd\xef\x13" ntlmv1_lmowfv1 = b"\xe5\x2c\xac\x67\x41\x9a\x9a\x22" \ b"\x4a\x3b\x10\x8f\x3f\xa6\xcb\x6d" actual = compute_keys._get_exchange_key_ntlm_v1( test_flags, session_base_key, server_challenge, ntlmv1_lmv1_response, ntlmv1_lmowfv1) assert actual == expected
def test_get_seal_key_ntlm2_40(self): test_flags = NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN | \ NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL | \ NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY # Not in document, custom test expected = b"\x26\xb2\xc1\xe7\x7b\xe4\x53\x3d" \ b"\x55\x5a\x22\x0a\x0f\xde\xb9\x6c" session_base_key = b"\xd8\x72\x62\xb0\xcd\xe4\xb1\xcb" \ b"\x74\x99\xbe\xcc\xcd\xf1\x07\x84" server_challenge = b"\x01\x23\x45\x67\x89\xab\xcd\xef" lm_challenge_response = b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" lm_hash = b"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" exported_session_key = compute_keys._get_exchange_key_ntlm_v1( test_flags, session_base_key, server_challenge, lm_challenge_response, lm_hash) actual = compute_keys.get_seal_key(test_flags, exported_session_key, SignSealConstants.CLIENT_SEALING) assert actual == expected
def get_nt_challenge_response(self, lm_challenge_response, server_certificate_hash=None, cbt_data=None): """ [MS-NLMP] v28.0 2016-07-14 3.3.1 - NTLM v1 Authentication 3.3.2 - NTLM v2 Authentication This method returns the NtChallengeResponse key based on the ntlm_compatibility chosen and the target_info supplied by the CHALLENGE_MESSAGE. It is quite different from what is set in the document as it combines the NTLMv1, NTLM2 and NTLMv2 methods into one and calls separate methods based on the ntlm_compatibility value chosen. :param lm_challenge_response: The LmChallengeResponse calculated beforehand, used to get the key_exchange_key value :param server_certificate_hash: This is deprecated and will be removed in a future version, use cbt_data instead :param cbt_data: The GssChannelBindingsStruct to bind in the NTLM response :return response: (NtChallengeResponse) - The NT response to the server challenge. Computed by the client :return session_base_key: (SessionBaseKey) - A session key calculated from the user password challenge :return target_info: (AV_PAIR) - The AV_PAIR structure used in the nt_challenge calculations """ if self._negotiate_flags & \ NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY and \ self._ntlm_compatibility < 3: # The compatibility level is less than 3 which means it doesn't # support NTLMv2 but we want extended security so use NTLM2 which # is different from NTLMv2 # [MS-NLMP] - 3.3.1 NTLMv1 Authentication response, session_base_key = \ self._get_NTLM2_response(self._password, self._server_challenge, self._client_challenge) lm_hash = comphash._lmowfv1(self._password) key_exchange_key = \ compkeys._get_exchange_key_ntlm_v1(self._negotiate_flags, session_base_key, self._server_challenge, lm_challenge_response, lm_hash) target_info = None elif 0 <= self._ntlm_compatibility < 3: response, session_base_key = \ self._get_NTLMv1_response(self._password, self._server_challenge) lm_hash = comphash._lmowfv1(self._password) key_exchange_key = \ compkeys._get_exchange_key_ntlm_v1(self._negotiate_flags, session_base_key, self._server_challenge, lm_challenge_response, lm_hash) target_info = None else: if self._server_target_info is None: target_info = ntlm_auth.messages.TargetInfo() else: target_info = self._server_target_info if target_info[AvId.MSV_AV_TIMESTAMP] is None: timestamp = get_windows_timestamp() else: timestamp = target_info[AvId.MSV_AV_TIMESTAMP] # [MS-NLMP] If the CHALLENGE_MESSAGE TargetInfo field has an # MsvAvTimestamp present, the client SHOULD provide a MIC target_info[AvId.MSV_AV_FLAGS] = \ struct.pack("<L", AvFlags.MIC_PROVIDED) if server_certificate_hash is not None and cbt_data is None: warnings.warn( "Manually creating the cbt stuct from the cert " "hash will be removed in a newer version of " "ntlm-auth. Send the actual CBT struct using " "cbt_data instead", DeprecationWarning) certificate_digest = base64.b16decode(server_certificate_hash) cbt_data = GssChannelBindingsStruct() cbt_data[cbt_data.APPLICATION_DATA] = \ b'tls-server-end-point:' + certificate_digest if cbt_data is not None: cbt_bytes = cbt_data.get_data() cbt_hash = hashlib.md5(cbt_bytes).digest() target_info[AvId.MSV_AV_CHANNEL_BINDINGS] = cbt_hash response, session_base_key = \ self._get_NTLMv2_response(self._user_name, self._password, self._domain_name, self._server_challenge, self._client_challenge, timestamp, target_info) key_exchange_key = \ compkeys._get_exchange_key_ntlm_v2(session_base_key) return response, key_exchange_key, target_info
def get_nt_challenge_response(self, lm_challenge_response, server_certificate_hash): """ [MS-NLMP] v28.0 2016-07-14 3.3.1 - NTLM v1 Authentication 3.3.2 - NTLM v2 Authentication This method returns the NtChallengeResponse key based on the ntlm_compatibility chosen and the target_info supplied by the CHALLENGE_MESSAGE. It is quite different from what is set in the document as it combines the NTLMv1, NTLM2 and NTLMv2 methods into one and calls separate methods based on the ntlm_compatibility value chosen. :param lm_challenge_response: The LmChallengeResponse calculated beforeand, used to get the key_exchange_key value :param server_certificate_hash: The SHA256 hash of the server certificate (DER encoded) NTLM is authenticated to. Used in Channel Binding Tokens if present, default value is None. See AuthenticateMessage in messages.py for more details :return response: (NtChallengeResponse) - The NT response to the server challenge. Computed by the client :return session_base_key: (SessionBaseKey) - A session key calculated from the user password challenge :return target_info: (AV_PAIR) - The AV_PAIR structure used in the nt_challenge calculations """ if self._negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY and self._ntlm_compatibility < 3: # The compatibility level is less than 3 which means it doesn't support NTLMv2 but we want extended security so use NTLM2 which is different from NTLMv2 # [MS-NLMP] - 3.3.1 NTLMv1 Authentication response, session_base_key = ComputeResponse._get_NTLM2_response( self._password, self._server_challenge, self._client_challenge) key_exchange_key = compkeys._get_exchange_key_ntlm_v1( self._negotiate_flags, session_base_key, self._server_challenge, lm_challenge_response, comphash._lmowfv1(self._password)) target_info = None elif 0 <= self._ntlm_compatibility < 3: response, session_base_key = ComputeResponse._get_NTLMv1_response( self._password, self._server_challenge) key_exchange_key = compkeys._get_exchange_key_ntlm_v1( self._negotiate_flags, session_base_key, self._server_challenge, lm_challenge_response, comphash._lmowfv1(self._password)) target_info = None else: if self._server_target_info is None: target_info = TargetInfo() else: target_info = self._server_target_info if target_info[TargetInfo.MSV_AV_TIMESTAMP] is None: timestamp = get_windows_timestamp() else: timestamp = target_info[TargetInfo.MSV_AV_TIMESTAMP][1] # [MS-NLMP] If the CHALLENGE_MESSAGE TargetInfo field has an MsvAvTimestamp present, the client SHOULD provide a MIC target_info[TargetInfo.MSV_AV_FLAGS] = struct.pack( "<L", AvFlags.MIC_PROVIDED) if server_certificate_hash != None: channel_bindings_hash = ComputeResponse._get_channel_bindings_value( server_certificate_hash) target_info[ TargetInfo.MSV_AV_CHANNEL_BINDINGS] = channel_bindings_hash response, session_base_key = ComputeResponse._get_NTLMv2_response( self._user_name, self._password, self._domain_name, self._server_challenge, self._client_challenge, timestamp, target_info) key_exchange_key = compkeys._get_exchange_key_ntlm_v2( session_base_key) return response, key_exchange_key, target_info