def step(self, input_token=None): if self._negotiate_message is None: self._negotiate_message = NegotiateMessage(self.negotiate_flags, self.domain, self.workstation) return self._negotiate_message.get_data() else: self._challenge_message = ChallengeMessage(input_token) self._authenticate_message = AuthenticateMessage( self.username, self.password, self.domain, self.workstation, self._challenge_message, self.ntlm_compatibility, server_certificate_hash=self._server_certificate_hash, cbt_data=self.cbt_data ) self._authenticate_message.add_mic(self._negotiate_message, self._challenge_message) flag_bytes = self._authenticate_message.negotiate_flags flags = struct.unpack("<I", flag_bytes)[0] if flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL or \ flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN: self._session_security = SessionSecurity( flags, self.session_key ) self.complete = True return self._authenticate_message.get_data()
def test_sign_and_seal_message_ntlm2_key_exchange(self): test_flags = ntlmv2_negotiate_flags test_session_security = SessionSecurity(test_flags, session_base_key) expected_seal = ntlmv2_output_message expected_sign = ntlmv2_signature actual_seal, actual_sign = test_session_security.wrap(plaintext_data) assert actual_seal == expected_seal assert actual_sign == expected_sign
def test_nosign_or_seal_message(self): test_flags = ntlmv2_negotiate_flags - NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL - NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN test_session_security = SessionSecurity(test_flags, session_base_key) expected_seal = plaintext_data expected_sign = None actual_seal, actual_sign = test_session_security.wrap(plaintext_data) assert actual_seal == expected_seal assert actual_sign == expected_sign
def test_sign_no_seal_message(self): test_flags = ntlmv2_negotiate_flags - NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL test_session_security = SessionSecurity(test_flags, session_base_key) expected_seal = plaintext_data # Because we aren't sealing beforehand the signature will be different from the example expected_sign = HexToByte('01 00 00 00 74 d0 45 34 2c 4f 1c d5 00 00 00 00') actual_seal, actual_sign = test_session_security.wrap(plaintext_data) assert actual_seal == expected_seal assert actual_sign == expected_sign
def test_sign_and_seal_message_ntlmv1(self): test_session_security = SessionSecurity(3791815219, b"\x55" * 16) expected_seal = b"\x56\xfe\x04\xd8\x61\xf9\x31\x9a" \ b"\xf0\xd7\x23\x8a\x2e\x3b\x4d\x45" \ b"\x7f\xb8" expected_sign = b"\x01\x00\x00\x00\x00\x00\x00\x00" \ b"\x09\xdc\xd1\xdf\x2e\x45\x9d\x36" plaintext_data = b"\x50\x00\x6c\x00\x61\x00\x69\x00" \ b"\x6e\x00\x74\x00\x65\x00\x78\x00" \ b"\x74\x00" actual_seal, actual_sign = test_session_security.wrap(plaintext_data) assert actual_seal == expected_seal assert actual_sign == expected_sign
def test_sign_no_seal_message(self): negotiate_flags = 3800728116 - NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL test_session_security = SessionSecurity(negotiate_flags, b"\x55" * 16) expected_seal = b"\x50\x00\x6c\x00\x61\x00\x69\x00" \ b"\x6e\x00\x74\x00\x65\x00\x78\x00" \ b"\x74\x00" # Because we aren't sealing beforehand the signature will be different # from the example expected_sign = b"\x01\x00\x00\x00\x74\xd0\x45\x34" \ b"\x2c\x4f\x1c\xd5\x00\x00\x00\x00" actual_seal, actual_sign = test_session_security.wrap(expected_seal) assert actual_seal == expected_seal assert actual_sign == expected_sign
def test_nosign_or_seal_message(self): negotiate_flags = 3800728116 - \ NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL - \ NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN test_session_security = SessionSecurity(negotiate_flags, b"\x55" * 16) plaintext_data = b"\x50\x00\x6c\x00\x61\x00\x69\x00" \ b"\x6e\x00\x74\x00\x65\x00\x78\x00" \ b"\x74\x00" expected_seal = plaintext_data expected_sign = None actual_seal, actual_sign = test_session_security.wrap(plaintext_data) assert actual_seal == expected_seal assert actual_sign == expected_sign
def test_sign_and_seal_message_ntlm2_key_exchange(self): test_session_security = SessionSecurity(3800728116, b"\x55" * 16) expected_seal = b"\x54\xe5\x01\x65\xbf\x19\x36\xdc" \ b"\x99\x60\x20\xc1\x81\x1b\x0f\x06" \ b"\xfb\x5f" expected_sign = b"\x01\x00\x00\x00\x7f\xb3\x8e\xc5" \ b"\xc5\x5d\x49\x76\x00\x00\x00\x00" plaintext_data = b"\x50\x00\x6c\x00\x61\x00\x69\x00" \ b"\x6e\x00\x74\x00\x65\x00\x78\x00" \ b"\x74\x00" actual_seal, actual_sign = test_session_security.wrap(plaintext_data) assert actual_seal == expected_seal assert actual_sign == expected_sign
def create_authenticate_message(self, user_name, password=None, nthash=None, lmhash=None, domain_name=None, workstation=None, server_certificate_hash=None): """ Create an NTLM AUTHENTICATE_MESSAGE based on the Ntlm context and the previous messages sent and received :param user_name: The user name of the user we are trying to authenticate with :param password: The password of the user we are trying to authenticate with :param domain_name: The domain name of the user account we are authenticated with, default is None :param workstation: The workstation we are using to authenticate with, default is None :param server_certificate_hash: The SHA256 hash string of the server certificate (DER encoded) NTLM is authenticating to. Used for Channel Binding Tokens. If nothing is supplied then the CBT hash will not be sent. See messages.py AuthenticateMessage for more details :return: A base64 encoded string of the AUTHENTICATE_MESSAGE """ self.authenticate_message = AuthenticateMessage( user_name, password, nthash, lmhash, domain_name, workstation, self.challenge_message, self.ntlm_compatibility, server_certificate_hash) self.authenticate_message.add_mic(self.negotiate_message, self.challenge_message) # Setups up the session_security context used to sign and seal messages if wanted if self.negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL or self.negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN: self.session_security = SessionSecurity( struct.unpack("<I", self.authenticate_message.negotiate_flags)[0], self.authenticate_message.exported_session_key) return base64.b64encode(self.authenticate_message.get_data())
def test_sign_and_seal_message_ntlm2_no_key_exchange(self): session_key = b"\xeb\x93\x42\x9a\x8b\xd9\x52\xf8" \ b"\xb8\x9c\x55\xb8\x7f\x47\x5e\xdc" test_session_security = SessionSecurity(2181726771, session_key) expected_seal = b"\xa0\x23\x72\xf6\x53\x02\x73\xf3" \ b"\xaa\x1e\xb9\x01\x90\xce\x52\x00" \ b"\xc9\x9d" expected_sign = b"\x01\x00\x00\x00\xff\x2a\xeb\x52" \ b"\xf6\x81\x79\x3a\x00\x00\x00\x00" plaintext_data = b"\x50\x00\x6c\x00\x61\x00\x69\x00" \ b"\x6e\x00\x74\x00\x65\x00\x78\x00" \ b"\x74\x00" actual_seal, actual_sign = test_session_security.wrap(plaintext_data) assert actual_seal == expected_seal assert actual_sign == expected_sign
def test_wrap_unwrap(self): context = NTLMContext("username", "password", None) context.init_context() gen = context.step() next(gen) msg2 = b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" \ b"\x02\x00\x00\x00\x2f\x82\x88\xe2" \ b"\x38\x00\x00\x00\x33\x82\x8a\xe2" \ b"\x01\x23\x45\x67\x89\xab\xcd\xef" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" \ b"\x24\x00\x24\x00\x44\x00\x00\x00" \ b"\x06\x00\x70\x17\x00\x00\x00\x0f" \ b"\x53\x00\x65\x00\x72\x00\x76\x00" \ b"\x65\x00\x72\x00\x02\x00\x0c\x00" \ b"\x44\x00\x6f\x00\x6d\x00\x61\x00" \ b"\x69\x00\x6e\x00\x01\x00\x0c\x00" \ b"\x53\x00\x65\x00\x72\x00\x76\x00" \ b"\x65\x00\x72\x00\x00\x00\x00\x00" gen.send(msg2) plaintext_client = b"client" plaintext_server = b"server" server_sec = SessionSecurity( context._context._session_security.negotiate_flags, context._context._session_security.exported_session_key, "server" ) client_header, client_wrap = context.wrap(plaintext_client) assert client_header.startswith(b"\x01\x00\x00\x00") assert len(client_header) == 16 assert client_wrap != plaintext_client client_plaintext = server_sec.unwrap(client_wrap, client_header) assert client_plaintext == plaintext_client server_wrap, server_header = server_sec.wrap(plaintext_server) assert server_header.startswith(b"\x01\x00\x00\x00") assert len(server_header) == 16 assert server_wrap != plaintext_server server_plaintext = context.unwrap(server_header, server_wrap) assert server_plaintext == plaintext_server
def test_unseal_message_incorrect_checksum(self): test_flags = ntlmv2_negotiate_flags test_session_security = SessionSecurity(test_flags, session_base_key) test_server_session_security = SessionSecurity(test_flags, session_base_key, source="server") test_message, test_signature = test_server_session_security.wrap(plaintext_data) # Overwrite signature to produce a bad checksum test_signature = HexToByte('01 00 00 00 b1 98 b8 47 ce 7c 58 07 00 00 00 00') with self.assertRaises(Exception) as context: test_session_security.unwrap(test_message, test_signature) self.assertTrue('The signature checksum does not match, message has been altered' in context.exception.args)
def test_unseal_message_incorrect_seq_num(self): test_flags = ntlmv2_negotiate_flags test_session_security = SessionSecurity(test_flags, session_base_key) test_server_session_security = SessionSecurity(test_flags, session_base_key, source="server") test_message, test_signature = test_server_session_security.wrap(plaintext_data) # Overwrite signature to produce a bad checksum test_signature = HexToByte('01 00 00 00 b2 98 b8 47 ce 7c 58 07 00 00 00 01') with self.assertRaises(Exception) as context: test_session_security.unwrap(test_message, test_signature) self.assertTrue('The signature sequence number does not match up, message not received in the correct sequence' in context.exception.args)
def test_unseal_message_ntlm2_key_exchange(self): test_flags = ntlmv2_negotiate_flags test_session_security = SessionSecurity(test_flags, session_base_key) test_server_session_security = SessionSecurity(test_flags, session_base_key, source="server") test_message, test_signature = test_server_session_security.wrap(plaintext_data) expected_data = plaintext_data actual_data = test_session_security.unwrap(test_message, test_signature) assert actual_data == expected_data
def test_verify_sign_no_unseal_message(self): test_flags = ntlmv2_negotiate_flags - NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL test_session_security = SessionSecurity(test_flags, session_base_key) test_server_session_security = SessionSecurity(test_flags, session_base_key, source="server") test_message, test_signature = test_server_session_security.wrap(plaintext_data) expected_data = plaintext_data actual_data = test_session_security.unwrap(test_message, test_signature) assert test_message == expected_data assert actual_data == expected_data
def test_unseal_message_incorrect_checksum(self): negotiate_flags = 3800728116 session_key = b"\xff" * 16 test_session_security = SessionSecurity(negotiate_flags, session_key) test_server_session_security = SessionSecurity(negotiate_flags, session_key, source="server") plaintext_data = b"\x50\x00\x6c\x00\x61\x00\x69\x00" \ b"\x6e\x00\x74\x00\x65\x00\x78\x00" \ b"\x74\x00" test_message, test_signature = \ test_server_session_security.wrap(plaintext_data) # Overwrite signature to produce a bad checksum test_signature = b"\x01\x00\x00\x00\xb1\x98\xb8\x47" \ b"\xce\x7c\x58\x07\x00\x00\x00\x00" with pytest.raises(Exception) as exc: test_session_security.unwrap(test_message, test_signature) assert str(exc.value) == "The signature checksum does not match, " \ "message has been altered"
def test_unseal_message_ntlm2_key_exchange(self): negotiate_flags = 3800728116 session_key = b"\xff" * 16 test_session_security = SessionSecurity(negotiate_flags, session_key) test_server_session_security = SessionSecurity(negotiate_flags, session_key, source="server") plaintext_data = b"\x50\x00\x6c\x00\x61\x00\x69\x00" \ b"\x6e\x00\x74\x00\x65\x00\x78\x00" \ b"\x74\x00" test_message, test_signature = \ test_server_session_security.wrap(plaintext_data) expected_data = plaintext_data actual_data = test_session_security.unwrap(test_message, test_signature) assert actual_data == expected_data
def test_unseal_message_incorrect_seq_num(self): negotiate_flags = 3800728116 session_key = b"\xff" * 16 test_session_security = SessionSecurity(negotiate_flags, session_key) test_server_session_security = SessionSecurity(negotiate_flags, session_key, source="server") plaintext_data = b"\x50\x00\x6c\x00\x61\x00\x69\x00" \ b"\x6e\x00\x74\x00\x65\x00\x78\x00" \ b"\x74\x00" test_message, test_signature = \ test_server_session_security.wrap(plaintext_data) # Overwrite signature to produce a bad checksum test_signature = test_signature[:-4] + b"\x01\x00\x00\x00" with pytest.raises(Exception) as exc: test_session_security.unwrap(test_message, test_signature) assert str(exc.value) == "The signature sequence number does not " \ "match up, message not received in the " \ "correct sequence"
def test_unseal_message_ntlm2_no_key_exchange(self): negotiate_flags = 2181726771 session_key = b"\xeb\x93\x42\x9a\x8b\xd9\x52\xf8" \ b"\xb8\x9c\x55\xb8\x7f\x47\x5e\xdc" test_session_security = SessionSecurity(negotiate_flags, session_key) test_server_session_security = SessionSecurity(negotiate_flags, session_key, source="server") plaintext_data = b"\x50\x00\x6c\x00\x61\x00\x69\x00" \ b"\x6e\x00\x74\x00\x65\x00\x78\x00" \ b"\x74\x00" test_message, test_signature = \ test_server_session_security.wrap(plaintext_data) expected_data = plaintext_data actual_data = test_session_security.unwrap(test_message, test_signature) assert actual_data == expected_data
def test_verify_sign_no_unseal_message(self): negotiate_flags = 3800728116 - NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL session_key = b"\x55" * 16 test_session_security = SessionSecurity(negotiate_flags, session_key) test_server_session_security = SessionSecurity(negotiate_flags, session_key, source="server") plaintext_data = b"\x50\x00\x6c\x00\x61\x00\x69\x00" \ b"\x6e\x00\x74\x00\x65\x00\x78\x00" \ b"\x74\x00" test_message, test_signature = \ test_server_session_security.wrap(plaintext_data) expected_data = plaintext_data actual_data = test_session_security.unwrap(test_message, test_signature) assert test_message == expected_data assert actual_data == expected_data
def test_unseal_message_ntlm1(self): negotiate_flags = 3791815219 session_key = b"\xd8\x72\x62\xb0\xcd\xe4\xb1" \ b"\xcb\x74\x99\xbe\xcc\xcd\xf1" \ b"\x07\x84" test_session_security = SessionSecurity(negotiate_flags, session_key) test_server_session_security = SessionSecurity(negotiate_flags, session_key, source="server") plaintext_data = b"\x50\x00\x6c\x00\x61\x00\x69\x00" \ b"\x6e\x00\x74\x00\x65\x00\x78\x00" \ b"\x74\x00" test_message, test_signature = \ test_server_session_security.wrap(plaintext_data) expected_data = plaintext_data actual_data = test_session_security.unwrap(test_message, test_signature) assert actual_data == expected_data
class NtlmContext(object): def __init__(self, username, password, domain=None, workstation=None, cbt_data=None, ntlm_compatibility=3): r""" Initialises a NTLM context to use when authenticating using the NTLM protocol. Initialises the NTLM context to use when sending and receiving messages to and from the server. You should be using this object as it supports NTLMv2 authenticate and it easier to use than before. It also brings in the ability to use signing and sealing with session_security and generate a MIC structure. :param username: The username to authenticate with :param password: The password for the username :param domain: The domain part of the username (None if n/a) :param workstation: The localworkstation (None if n/a) :param cbt_data: A GssChannelBindingsStruct or None to bind channel data with the auth process :param ntlm_compatibility: (Default 3) The Lan Manager Compatibility Level to use with the auth message This is set by an Administrator in the registry key 'HKLM\SYSTEM\CurrentControlSet\Control\Lsa\LmCompatibilityLevel' The values correspond to the following; 0 : LM and NTLMv1 1 : LM, NTLMv1 and NTLMv1 with Extended Session Security 2 : NTLMv1 and NTLMv1 with Extended Session Security 3-5 : NTLMv2 Only Note: Values 3 to 5 are no different from a client perspective """ self.username = username self.password = password self.domain = domain self.workstation = workstation self.cbt_data = cbt_data self._server_certificate_hash = None # deprecated for backwards compat self.ntlm_compatibility = ntlm_compatibility self.complete = False # Setting up our flags so the challenge message returns the target info # block if supported self.negotiate_flags = NegotiateFlags.NTLMSSP_NEGOTIATE_TARGET_INFO | \ NegotiateFlags.NTLMSSP_NEGOTIATE_128 | \ NegotiateFlags.NTLMSSP_NEGOTIATE_56 | \ NegotiateFlags.NTLMSSP_NEGOTIATE_UNICODE | \ NegotiateFlags.NTLMSSP_NEGOTIATE_VERSION | \ NegotiateFlags.NTLMSSP_NEGOTIATE_KEY_EXCH | \ NegotiateFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN | \ NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN | \ NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL # Setting the message types based on the ntlm_compatibility level self._set_ntlm_compatibility_flags(self.ntlm_compatibility) self._negotiate_message = None self._challenge_message = None self._authenticate_message = None self._session_security = None @property def mic_present(self): if self._authenticate_message: return bool(self._authenticate_message.mic) return False @property def session_key(self): if self._authenticate_message: return self._authenticate_message.exported_session_key def reset_rc4_state(self, outgoing=True): """ Resets the signing cipher for the incoming or outgoing cipher. For SPNEGO for calculating mechListMIC. """ if self._session_security: self._session_security.reset_rc4_state(outgoing=outgoing) def step(self, input_token=None): if self._negotiate_message is None: self._negotiate_message = NegotiateMessage(self.negotiate_flags, self.domain, self.workstation) return self._negotiate_message.get_data() else: self._challenge_message = ChallengeMessage(input_token) self._authenticate_message = AuthenticateMessage( self.username, self.password, self.domain, self.workstation, self._challenge_message, self.ntlm_compatibility, server_certificate_hash=self._server_certificate_hash, cbt_data=self.cbt_data ) self._authenticate_message.add_mic(self._negotiate_message, self._challenge_message) flag_bytes = self._authenticate_message.negotiate_flags flags = struct.unpack("<I", flag_bytes)[0] if flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL or \ flags & NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN: self._session_security = SessionSecurity( flags, self.session_key ) self.complete = True return self._authenticate_message.get_data() def sign(self, data): return self._session_security.get_signature(data) def verify(self, data, signature): self._session_security.verify_signature(data, signature) def wrap(self, data): if self._session_security is None: raise NoAuthContextError("Cannot wrap data as no security context " "has been established") data, header = self._session_security.wrap(data) return header + data def unwrap(self, data): if self._session_security is None: raise NoAuthContextError("Cannot unwrap data as no security " "context has been established") header = data[0:16] data = data[16:] message = self._session_security.unwrap(data, header) return message def _set_ntlm_compatibility_flags(self, ntlm_compatibility): if (ntlm_compatibility >= 0) and (ntlm_compatibility <= 5): if ntlm_compatibility == 0: self.negotiate_flags |= \ NegotiateFlags.NTLMSSP_NEGOTIATE_NTLM | \ NegotiateFlags.NTLMSSP_NEGOTIATE_LM_KEY elif ntlm_compatibility == 1: self.negotiate_flags |= \ NegotiateFlags.NTLMSSP_NEGOTIATE_NTLM | \ NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY else: self.negotiate_flags |= \ NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY else: raise Exception("Unknown ntlm_compatibility level - " "expecting value between 0 and 5")
def test_invalid_source_param(self): with pytest.raises(ValueError) as exc: SessionSecurity(3791815219, b"\x55" * 16, source="unknown") assert str(exc.value) == "Invalid source parameter unknown, must be " \ "client or server"
def test_invalid_source_param(self): test_flags = ntlmv1_negotiate_flags with self.assertRaises(Exception) as context: SessionSecurity(test_flags, session_base_key, source="unknown") self.assertTrue('Invalid source parameter unknown, must be client or server' in context.exception.args)
def test_ntlm_context(self, monkeypatch): monkeypatch.setattr('os.urandom', lambda s: b"\xaa" * 8) monkeypatch.setattr('ntlm_auth.messages.get_version', lambda s: b"\x05\x01\x28\x0A\x00\x00\x00\x0F") monkeypatch.setattr('ntlm_auth.messages.get_random_export_session_key', lambda: b"\x55" * 16) monkeypatch.setattr('ntlm_auth.compute_response.get_windows_timestamp', lambda: b"\x00" * 8) ch = 'E3CA49271E5089CC48CE82109F1324F41DBEDDC29A777410C738F7868C4FF405' cbt_data = GssChannelBindingsStruct() cbt_data[cbt_data.APPLICATION_DATA] = b"tls-server-end-point:" + \ base64.b16decode(ch) ntlm_context = NtlmContext("User", "Password", "Domain", "COMPUTER", cbt_data=cbt_data) actual_nego = ntlm_context.step() expected_nego = b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" \ b"\x01\x00\x00\x00\x31\xb0\x88\xe2" \ b"\x06\x00\x06\x00\x28\x00\x00\x00" \ b"\x08\x00\x08\x00\x2e\x00\x00\x00" \ b"\x05\x01\x28\x0a\x00\x00\x00\x0f" \ b"\x44\x6f\x6d\x61\x69\x6e\x43\x4f" \ b"\x4d\x50\x55\x54\x45\x52" assert actual_nego == expected_nego assert not ntlm_context.mic_present assert not ntlm_context.complete challenge_msg = b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" \ b"\x02\x00\x00\x00\x2f\x82\x88\xe2" \ b"\x38\x00\x00\x00\x33\x82\x8a\xe2" \ b"\x01\x23\x45\x67\x89\xab\xcd\xef" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" \ b"\x24\x00\x24\x00\x44\x00\x00\x00" \ b"\x06\x00\x70\x17\x00\x00\x00\x0f" \ b"\x53\x00\x65\x00\x72\x00\x76\x00" \ b"\x65\x00\x72\x00\x02\x00\x0c\x00" \ b"\x44\x00\x6f\x00\x6d\x00\x61\x00" \ b"\x69\x00\x6e\x00\x01\x00\x0c\x00" \ b"\x53\x00\x65\x00\x72\x00\x76\x00" \ b"\x65\x00\x72\x00\x00\x00\x00\x00" actual_auth = ntlm_context.step(challenge_msg) expected_auth = b'\x4e\x54\x4c\x4d\x53\x53\x50\x00' \ b'\x03\x00\x00\x00\x18\x00\x18\x00' \ b'\x6c\x00\x00\x00\x68\x00\x68\x00' \ b'\x84\x00\x00\x00\x0c\x00\x0c\x00' \ b'\x48\x00\x00\x00\x08\x00\x08\x00' \ b'\x54\x00\x00\x00\x10\x00\x10\x00' \ b'\x5c\x00\x00\x00\x10\x00\x10\x00' \ b'\xec\x00\x00\x00\x31\x82\x8a\xe2' \ b'\x05\x01\x28\x0a\x00\x00\x00\x0f' \ b'\x44\x00\x6f\x00\x6d\x00\x61\x00' \ b'\x69\x00\x6e\x00\x55\x00\x73\x00' \ b'\x65\x00\x72\x00\x43\x00\x4f\x00' \ b'\x4d\x00\x50\x00\x55\x00\x54\x00' \ b'\x45\x00\x52\x00\x86\xc3\x50\x97' \ b'\xac\x9c\xec\x10\x25\x54\x76\x4a' \ b'\x57\xcc\xcc\x19\xaa\xaa\xaa\xaa' \ b'\xaa\xaa\xaa\xaa\x04\x10\xc4\x7a' \ b'\xcf\x19\x97\x89\xde\x7f\x20\x11' \ b'\x95\x7a\xea\x50\x01\x01\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\xaa\xaa\xaa\xaa' \ b'\xaa\xaa\xaa\xaa\x00\x00\x00\x00' \ b'\x02\x00\x0c\x00\x44\x00\x6f\x00' \ b'\x6d\x00\x61\x00\x69\x00\x6e\x00' \ b'\x01\x00\x0c\x00\x53\x00\x65\x00' \ b'\x72\x00\x76\x00\x65\x00\x72\x00' \ b'\x0a\x00\x10\x00\x6e\xa1\x9d\xf0' \ b'\x66\xda\x46\x22\x05\x1f\x9c\x4f' \ b'\x92\xc6\xdf\x74\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\xe5\x69\x95\x1d' \ b'\x15\xd4\x73\x5f\x49\xe1\x4c\xf9' \ b'\xa7\xd3\xe6\x72' assert actual_auth == expected_auth assert ntlm_context.complete assert not ntlm_context.mic_present request_msg = b"test req" response_msg = b"test res" actual_wrapped = ntlm_context.wrap(request_msg) expected_wrapped = b"\x01\x00\x00\x00\xbc\xe3\x23\xa1" \ b"\x72\x06\x23\x78\x00\x00\x00\x00" \ b"\x70\x80\x1e\x11\xfe\x6b\x3a\xad" assert actual_wrapped == expected_wrapped server_sec = SessionSecurity( ntlm_context._session_security.negotiate_flags, ntlm_context._session_security.exported_session_key, "server") server_unwrap = server_sec.unwrap(actual_wrapped[16:], actual_wrapped[0:16]) assert server_unwrap == request_msg response_wrapped = server_sec.wrap(response_msg) actual_unwrap = ntlm_context.unwrap(response_wrapped[1] + response_wrapped[0]) assert actual_unwrap == response_msg
def test_ntlm_context_with_mic(self, monkeypatch): monkeypatch.setattr('os.urandom', lambda s: b"\xaa" * 8) monkeypatch.setattr('ntlm_auth.messages.get_version', lambda s: b"\x05\x01\x28\x0A\x00\x00\x00\x0F") monkeypatch.setattr('ntlm_auth.messages.get_random_export_session_key', lambda: b"\x55" * 16) monkeypatch.setattr('ntlm_auth.compute_response.get_windows_timestamp', lambda: b"\x00" * 8) ch = 'E3CA49271E5089CC48CE82109F1324F41DBEDDC29A777410C738F7868C4FF405' cbt_data = GssChannelBindingsStruct() cbt_data[cbt_data.APPLICATION_DATA] = b"tls-server-end-point:" + \ base64.b16decode(ch) ntlm_context = NtlmContext("User", "Password", "Domain", "COMPUTER", cbt_data=cbt_data) ntlm_context.reset_rc4_state( ) # Verifies it won't fail when the session security isn't set up. actual_nego = ntlm_context.step() expected_nego = b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" \ b"\x01\x00\x00\x00\x31\xb0\x88\xe2" \ b"\x06\x00\x06\x00\x28\x00\x00\x00" \ b"\x08\x00\x08\x00\x2e\x00\x00\x00" \ b"\x05\x01\x28\x0a\x00\x00\x00\x0f" \ b"\x44\x6f\x6d\x61\x69\x6e\x43\x4f" \ b"\x4d\x50\x55\x54\x45\x52" assert actual_nego == expected_nego assert not ntlm_context.mic_present assert not ntlm_context.complete challenge_msg = b"\x4E\x54\x4C\x4D\x53\x53\x50\x00" \ b"\x02\x00\x00\x00\x00\x00\x00\x00" \ b"\x38\x00\x00\x00\x33\x82\x8A\xE2" \ b"\x01\x23\x45\x67\x89\xAB\xCD\xEF" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" \ b"\x30\x00\x30\x00\x38\x00\x00\x00" \ b"\x06\x01\xB1\x1D\x00\x00\x00\x0F" \ b"\x02\x00\x0C\x00\x44\x00\x6F\x00" \ b"\x6D\x00\x61\x00\x69\x00\x6E\x00" \ b"\x01\x00\x0C\x00\x53\x00\x65\x00" \ b"\x72\x00\x76\x00\x65\x00\x72\x00" \ b"\x07\x00\x08\x00\x00\x00\x00\x00" \ b"\x00\x00\x00\x00\x00\x00\x00\x00" actual_auth = ntlm_context.step(challenge_msg) expected_auth = b'\x4E\x54\x4C\x4D\x53\x53\x50\x00' \ b'\x03\x00\x00\x00\x18\x00\x18\x00' \ b'\x7C\x00\x00\x00\x7C\x00\x7C\x00' \ b'\x94\x00\x00\x00\x0C\x00\x0C\x00' \ b'\x58\x00\x00\x00\x08\x00\x08\x00' \ b'\x64\x00\x00\x00\x10\x00\x10\x00' \ b'\x6C\x00\x00\x00\x10\x00\x10\x00' \ b'\x10\x01\x00\x00\x31\x82\x8A\xE2' \ b'\x05\x01\x28\x0A\x00\x00\x00\x0F' \ b'\xC4\x45\x2C\xF7\xA8\x1E\x4D\x11' \ b'\xD0\x78\x18\x94\x09\x57\x5D\x9E' \ b'\x44\x00\x6F\x00\x6D\x00\x61\x00' \ b'\x69\x00\x6E\x00\x55\x00\x73\x00' \ b'\x65\x00\x72\x00\x43\x00\x4F\x00' \ b'\x4D\x00\x50\x00\x55\x00\x54\x00' \ b'\x45\x00\x52\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\xA1\x3D\x03\x8A' \ b'\xD0\xCA\x02\x64\x33\x89\x7C\x33' \ b'\x5E\x0F\x56\xDF\x01\x01\x00\x00' \ b'\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\xAA\xAA\xAA\xAA' \ b'\xAA\xAA\xAA\xAA\x00\x00\x00\x00' \ b'\x02\x00\x0C\x00\x44\x00\x6F\x00' \ b'\x6D\x00\x61\x00\x69\x00\x6E\x00' \ b'\x01\x00\x0C\x00\x53\x00\x65\x00' \ b'\x72\x00\x76\x00\x65\x00\x72\x00' \ b'\x07\x00\x08\x00\x00\x00\x00\x00' \ b'\x00\x00\x00\x00\x06\x00\x04\x00' \ b'\x02\x00\x00\x00\x0A\x00\x10\x00' \ b'\x6E\xA1\x9D\xF0\x66\xDA\x46\x22' \ b'\x05\x1F\x9C\x4F\x92\xC6\xDF\x74' \ b'\x00\x00\x00\x00\x00\x00\x00\x00' \ b'\x1D\x08\x89\xD1\xA5\xEE\xED\x21' \ b'\x91\x9E\x1A\xB8\x27\xC3\x0B\x17' assert actual_auth == expected_auth assert ntlm_context.complete assert ntlm_context.mic_present request_msg = b"test req" response_msg = b"test res" actual_wrapped = ntlm_context.wrap(request_msg) expected_wrapped = b"\x01\x00\x00\x00\xbc\xe3\x23\xa1" \ b"\x72\x06\x23\x78\x00\x00\x00\x00" \ b"\x70\x80\x1e\x11\xfe\x6b\x3a\xad" assert actual_wrapped == expected_wrapped server_sec = SessionSecurity( ntlm_context._session_security.negotiate_flags, ntlm_context._session_security.exported_session_key, "server") server_unwrap = server_sec.unwrap(actual_wrapped[16:], actual_wrapped[0:16]) assert server_unwrap == request_msg response_wrapped = server_sec.wrap(response_msg) actual_unwrap = ntlm_context.unwrap(response_wrapped[1] + response_wrapped[0]) assert actual_unwrap == response_msg msg = b"Hello" actual_sig1 = ntlm_context.sign(msg) expected_sig1 = b"\x01\x00\x00\x00\x08\xF0\x0D\x86\x34\x05\x1A\x1D\x01\x00\x00\x00" assert actual_sig1 == expected_sig1 server_sec.verify_signature(msg, actual_sig1) actual_sig2 = ntlm_context.sign(msg) expected_sig2 = b"\x01\x00\x00\x00\x07\x64\x0C\x30\x1C\xD7\x76\xF0\x02\x00\x00\x00" assert actual_sig2 == expected_sig2 server_sec.verify_signature(msg, actual_sig2) ntlm_context.reset_rc4_state() actual_sig3 = ntlm_context.sign(msg) expected_sig3 = b"\x01\x00\x00\x00\x1E\xD4\xA3\xE5\xE8\x05\x74\x01\x03\x00\x00\x00" assert actual_sig3 == expected_sig3 server_sec.reset_rc4_state(outgoing=False) server_sec.verify_signature(msg, actual_sig3) server_sig = server_sec.get_signature(msg) ntlm_context.verify(msg, server_sig)