def test_compute_response_v2(): # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/946f54bd-76b5-4b18-ace8-6e8c992d5847 time = FileTime.unpack(TEST_TIME) av_pairs = TargetInfo.unpack( b"\x02\x00\x0C\x00\x44\x00\x6F\x00\x6D\x00\x61\x00\x69\x00\x6E\x00" b"\x01\x00\x0C\x00\x53\x00\x65\x00\x72\x00\x76\x00\x65\x00\x72\x00" b"\x00\x00\x00\x00") actual_nt, actual_lm, actual_kek = crypto.compute_response_v2( crypto.ntowfv2(TEST_USER, crypto.ntowfv1(TEST_PASSWD), TEST_USER_DOM), TEST_SERVER_CHALLENGE, TEST_CLIENT_CHALLENGE, time, av_pairs) # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/946f54bd-76b5-4b18-ace8-6e8c992d5847 temp = b"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \ b"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\x00\x00\x00\x00\x02\x00\x0C\x00" \ b"\x44\x00\x6F\x00\x6D\x00\x61\x00\x69\x00\x6E\x00\x01\x00\x0C\x00" \ b"\x53\x00\x65\x00\x72\x00\x76\x00\x65\x00\x72\x00\x00\x00\x00\x00" \ b"\x00\x00\x00\x00" # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/fa2bc0f0-9efa-40d7-a165-adfccd7f6da7 assert actual_nt == b"\x68\xCD\x0A\xB8\x51\xE5\x1C\x96\xAA\xBC\x92\x7B\xEB\xEF\x6A\x1C" + temp # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/7e2b35f9-fe90-49fb-8c9d-30639a899160 assert actual_lm == b"\x86\xC3\x50\x97\xAC\x9C\xEC\x10\x25\x54\x76\x4A\x57\xCC\xCC\x19" \ b"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/54973495-20d2-49e8-9925-c399a403ed4a assert actual_kek == b"\x8D\xE4\x0C\xCA\xDB\xC1\x4A\x82\xF1\x5C\xB0\xAD\x0D\xE9\x5C\xA3" # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/4a84eb20-870a-421e-984f-29a842cd6504 actual_enc_key = crypto.rc4k(actual_kek, TEST_RANDOM_SESSION_KEY) assert actual_enc_key == b"\xC5\xDA\xD2\x54\x4F\xC9\x79\x90\x94\xCE\x1C\xE9\x0B\xC9\xD0\x3E"
def _step_initiate(self, in_token=None): if not self._temp_msg['negotiate']: negotiate_msg = Negotiate(self._context_req) self._temp_msg['negotiate'] = negotiate_msg return negotiate_msg.pack() challenge = Challenge.unpack(in_token) auth_kwargs = { 'domain_name': self._credential.domain, 'username': self._credential.username, } if challenge.flags & NegotiateFlags.version: auth_kwargs['version'] = Version.get_current() auth_kwargs['workstation'] = _get_workstation() nt_challenge, lm_challenge, key_exchange_key = self._compute_response( challenge, self._credential) if challenge.flags & NegotiateFlags.key_exch: # This is only documented on the server side for MS-NLMP but is also valid for the client. The actual # session key is the KeyExchangeKey like normal unless sign or seal is negotiated. if challenge.flags & NegotiateFlags.sign or challenge.flags & NegotiateFlags.seal: self._session_key = os.urandom(16) auth_kwargs['encrypted_session_key'] = rc4k( key_exchange_key, self._session_key) else: self._session_key = key_exchange_key auth_kwargs[ 'encrypted_session_key'] = b"\x00" # Must be set to some value but this can be anything. else: self._session_key = key_exchange_key authenticate = Authenticate(challenge.flags, lm_challenge, nt_challenge, **auth_kwargs) if self._mic_required: authenticate.mic = self._calculate_mic( self._temp_msg['negotiate'].pack(), in_token, authenticate.pack()) self._context_attr = authenticate.flags self._complete = True return authenticate.pack()
def test_compute_response_v1_no_session_security(): actual_nt, actual_lm, actual_kek = crypto.compute_response_v1( TEST_NTLMV1_FLAGS, crypto.ntowfv1(TEST_PASSWD), crypto.lmowfv1(TEST_PASSWD), TEST_SERVER_CHALLENGE, TEST_CLIENT_CHALLENGE) # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/a9dc740e-e12f-4fdd-8f2b-61a471731a14 assert actual_nt == b"\x67\xC4\x30\x11\xF3\x02\x98\xA2\xAD\x35\xEC\xE6\x4F\x16\x33\x1C" \ b"\x44\xBD\xBE\xD9\x27\x84\x1F\x94" # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/859a18d1-d7b2-4e98-a261-5b38cdf4b11d assert actual_lm == actual_nt # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/0b344a44-7cd8-4ab5-b07c-ff2b3d8c15f4 assert actual_kek == b"\xD8\x72\x62\xB0\xCD\xE4\xB1\xCB\x74\x99\xBE\xCC\xCD\xF1\x07\x84" # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/2d7f9599-f849-4550-9579-91aeea8078b0 actual_enc_key = crypto.rc4k(actual_kek, TEST_RANDOM_SESSION_KEY) assert actual_enc_key == b"\x51\x88\x22\xB1\xB3\xF3\x50\xC8\x95\x86\x82\xEC\xBB\x3E\x3C\xB7"
def test_compute_response_v1_no_session_security_lm_key(): flags = TEST_NTLMV1_FLAGS | NegotiateFlags.lm_key actual_nt, actual_lm, actual_kek = crypto.compute_response_v1( flags, crypto.ntowfv1(TEST_PASSWD), crypto.lmowfv1(TEST_PASSWD), TEST_SERVER_CHALLENGE, TEST_CLIENT_CHALLENGE, no_lm_response=False) # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/a9dc740e-e12f-4fdd-8f2b-61a471731a14 assert actual_nt == b"\x67\xC4\x30\x11\xF3\x02\x98\xA2\xAD\x35\xEC\xE6\x4F\x16\x33\x1C" \ b"\x44\xBD\xBE\xD9\x27\x84\x1F\x94" # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/859a18d1-d7b2-4e98-a261-5b38cdf4b11d assert actual_lm == b"\x98\xDE\xF7\xB8\x7F\x88\xAA\x5D\xAF\xE2\xDF\x77\x96\x88\xA1\x72" \ b"\xDE\xF1\x1C\x7D\x5C\xCD\xEF\x13" assert actual_kek == b"\xB0\x9E\x37\x9F\x7F\xBE\xCB\x1E\xAF\x0A\xFD\xCB\x03\x83\xC8\xA0" # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/2d7f9599-f849-4550-9579-91aeea8078b0 actual_enc_key = crypto.rc4k(actual_kek, TEST_RANDOM_SESSION_KEY) assert actual_enc_key == b"\x4C\xD7\xBB\x57\xD6\x97\xEF\x9B\x54\x9F\x02\xB8\xF9\xB3\x78\x64"
def _step_accept_authenticate(self, token): # type: (bytes) -> None """ Process the Authenticate message from the initiator. """ challenge = self._temp_msg['challenge'] server_challenge = challenge.server_challenge auth = Authenticate.unpack(token) # TODO: Add anonymous user support. if not auth.user_name or (not auth.nt_challenge_response and (not auth.lm_challenge_response or auth.lm_challenge_response == b"\x00")): raise OperationNotAvailableError( context_msg="Anonymous user authentication not implemented") self._credential = _NTLMCredential(domain=auth.domain_name, username=auth.user_name) expected_mic = None if auth.nt_challenge_response and len(auth.nt_challenge_response) > 24: nt_hash = ntowfv2(self._credential.username, self._credential.nt_hash, self._credential.domain) nt_challenge = NTClientChallengeV2.unpack( auth.nt_challenge_response[16:]) time = nt_challenge.time_stamp client_challenge = nt_challenge.challenge_from_client target_info = nt_challenge.av_pairs expected_nt, expected_lm, key_exchange_key = compute_response_v2( nt_hash, server_challenge, client_challenge, time, target_info) if self.channel_bindings: if AvId.channel_bindings not in target_info: raise BadBindingsError( context_msg= "Acceptor bindings specified but not present in initiator " "response") expected_bindings = target_info[AvId.channel_bindings] actual_bindings = md5(self.channel_bindings.pack()) if expected_bindings not in [actual_bindings, b"\x00" * 16]: raise BadBindingsError( context_msg= "Acceptor bindings do not match initiator bindings") if target_info.get(AvId.flags, 0) & AvFlags.mic: expected_mic = auth.mic else: if not self._nt_v1: raise InvalidTokenError( context_msg= "Acceptor settings are set to reject NTv1 responses") elif not auth.nt_challenge_response and not self._lm: raise InvalidTokenError( context_msg= "Acceptor settings are set to reject LM responses") client_challenge = None if auth.flags & NegotiateFlags.extended_session_security: client_challenge = auth.lm_challenge_response[:8] expected_nt, expected_lm, key_exchange_key = compute_response_v1( auth.flags, self._credential.nt_hash, self._credential.lm_hash, server_challenge, client_challenge, no_lm_response=not self._lm) auth_success = False if auth.nt_challenge_response: auth_success = auth.nt_challenge_response == expected_nt elif auth.lm_challenge_response: auth_success = auth.lm_challenge_response == expected_lm if not auth_success: raise InvalidTokenError( context_msg="Invalid NTLM response from initiator") if auth.flags & NegotiateFlags.key_exch and \ (auth.flags & NegotiateFlags.sign or auth.flags & NegotiateFlags.seal): self._session_key = rc4k(key_exchange_key, auth.encrypted_random_session_key) else: self._session_key = key_exchange_key if expected_mic: auth.mic = b"\x00" * 16 actual_mic = self._calculate_mic( self._temp_msg['negotiate'].pack(), challenge.pack(), auth.pack()) if actual_mic != expected_mic: raise InvalidTokenError( context_msg="Invalid MIC in NTLM authentication message") self._context_attr = auth.flags self._complete = True