def reset_rc4_state(self, outgoing=True): csk = self._client_sealing_key ssk = self._server_sealing_key if outgoing: self.outgoing_handle = ARC4(csk if self._source == 'client' else ssk) else: self.incoming_handle = ARC4(ssk if self._source == 'client' else csk)
def __init__(self, negotiate_flags, exported_session_key, source="client"): """ Initialises a security session context that can be used by libraries that call ntlm-auth to sign and seal messages send to the server as well as verify and unseal messages that have been received from the server. This is similar to the GSS_Wrap functions specified in the MS-NLMP document which does the same task. :param negotiate_flags: The negotiate flag structure that has been negotiated with the server :param exported_session_key: A 128-bit session key used to derive signing and sealing keys :param source: The source of the message, only used in test scenarios when testing out a server sealing and unsealing """ self.negotiate_flags = negotiate_flags self.exported_session_key = exported_session_key self.outgoing_seq_num = 0 self.incoming_seq_num = 0 client_sealing_key = \ compkeys.get_seal_key(self.negotiate_flags, exported_session_key, SignSealConstants.CLIENT_SEALING) server_sealing_key = \ compkeys.get_seal_key(self.negotiate_flags, exported_session_key, SignSealConstants.SERVER_SEALING) if source == "client": self.outgoing_signing_key = \ compkeys.get_sign_key(exported_session_key, SignSealConstants.CLIENT_SIGNING) self.incoming_signing_key = \ compkeys.get_sign_key(exported_session_key, SignSealConstants.SERVER_SIGNING) self.outgoing_handle = ARC4(client_sealing_key) self.incoming_handle = ARC4(server_sealing_key) elif source == "server": self.outgoing_signing_key = \ compkeys.get_sign_key(exported_session_key, SignSealConstants.SERVER_SIGNING) self.incoming_signing_key = \ compkeys.get_sign_key(exported_session_key, SignSealConstants.CLIENT_SIGNING) self.outgoing_handle = ARC4(server_sealing_key) self.incoming_handle = ARC4(client_sealing_key) else: raise ValueError("Invalid source parameter %s, must be client " "or server" % source)
def test_encrypt_40bit_key(self): test_handle = ARC4(b"\x01\x02\x03\x04\x05") expected1 = b"\xb2\x39\x63\x05\xf0\x3d\xc0\x27" \ b"\xcc\xc3\x52\x4a\x0a\x11\x18\xa8" expected2 = b"\x69\x82\x94\x4f\x18\xfc\x82\xd5" \ b"\x89\xc4\x03\xa4\x7a\x0d\x09\x19" actual1 = test_handle.update(b"\x00" * 16) actual2 = test_handle.update(b"\x00" * 16) assert actual1 == expected1 assert actual2 == expected2
def test_encrypt_56bit_key(self): test_handle = ARC4(b"\x01\x02\x03\x04\x05\x06\x07") expected1 = b"\x29\x3f\x02\xd4\x7f\x37\xc9\xb6" \ b"\x33\xf2\xaf\x52\x85\xfe\xb4\x6b" expected2 = b"\xe6\x20\xf1\x39\x0d\x19\xbd\x84" \ b"\xe2\xe0\xfd\x75\x20\x31\xaf\xc1" actual1 = test_handle.update(b"\x00" * 16) actual2 = test_handle.update(b"\x00" * 16) assert actual1 == expected1 assert actual2 == expected2
def __init__(self, negotiate_flags, exported_session_key, source="client"): self.negotiate_flags = negotiate_flags self.outgoing_seq_num = 0 self.incoming_seq_num = 0 client_sealing_key = compkeys.get_seal_key(self.negotiate_flags, exported_session_key, SignSealConstants.CLIENT_SEALING) server_sealing_key = compkeys.get_seal_key(self.negotiate_flags, exported_session_key, SignSealConstants.SERVER_SEALING) if source == "client": self.outgoing_signing_key = compkeys.get_sign_key(exported_session_key, SignSealConstants.CLIENT_SIGNING) self.incoming_signing_key = compkeys.get_sign_key(exported_session_key, SignSealConstants.SERVER_SIGNING) self.outgoing_handle = ARC4(client_sealing_key) self.incoming_handle = ARC4(server_sealing_key) elif source == "server": self.outgoing_signing_key = compkeys.get_sign_key(exported_session_key, SignSealConstants.SERVER_SIGNING) self.incoming_signing_key = compkeys.get_sign_key(exported_session_key, SignSealConstants.CLIENT_SIGNING) self.outgoing_handle = ARC4(server_sealing_key) self.incoming_handle = ARC4(client_sealing_key) else: raise Exception("Invalid source parameter %s, must be client or server" % source)
def test_encrypt_128bit_key(self): test_handle = ARC4(key_128bit) expected1 = line1_128bit expected2 = line2_128bit actual1 = test_handle.update(input_hex) actual2 = test_handle.update(input_hex) assert actual1 == expected1 assert actual2 == expected2
def test_encrypt_128bit_key(self): test_handle = ARC4(b"\x01\x02\x03\x04\x05\x06\x07\x08" b"\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10") expected1 = b"\x9a\xc7\xcc\x9a\x60\x9d\x1e\xf7" \ b"\xb2\x93\x28\x99\xcd\xe4\x1b\x97" expected2 = b"\x52\x48\xc4\x95\x90\x14\x12\x6a" \ b"\x6e\x8a\x84\xf1\x1d\x1a\x9e\x1c" actual1 = test_handle.update(b"\x00" * 16) actual2 = test_handle.update(b"\x00" * 16) assert actual1 == expected1 assert actual2 == expected2
def test_decrypt_128bit_key(self): test_handle = ARC4(key_128bit) test1 = line1_128bit test2 = line2_128bit expected = input_hex actual1 = test_handle.update(test1) actual2 = test_handle.update(test2) assert actual1 == expected assert actual2 == expected
def __init__(self, user_name, password, nthash, lmhash, domain_name, workstation, challenge_message, ntlm_compatibility, server_certificate_hash): self.signature = NTLM_SIGNATURE self.message_type = struct.pack('<L', MessageTypes.NTLM_AUTHENTICATE) self.negotiate_flags = challenge_message.negotiate_flags self.version = get_version(self.negotiate_flags) self.mic = None if domain_name is None: self.domain_name = '' else: self.domain_name = domain_name if workstation is None: self.workstation = '' else: self.workstation = workstation if self.negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_UNICODE: self.negotiate_flags -= NegotiateFlags.NTLMSSP_NEGOTIATE_OEM encoding_value = 'utf-16-le' else: encoding_value = 'ascii' self.domain_name = self.domain_name.encode(encoding_value) self.user_name = user_name.encode(encoding_value) self.workstation = self.workstation.encode(encoding_value) compute_response = ComputeResponse(user_name, password, nthash, lmhash, domain_name, challenge_message, ntlm_compatibility) self.lm_challenge_response = compute_response.get_lm_challenge_response( ) self.nt_challenge_response, key_exchange_key, target_info = compute_response.get_nt_challenge_response( self.lm_challenge_response, server_certificate_hash) self.target_info = target_info if self.negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_KEY_EXCH: self.exported_session_key = get_random_export_session_key() rc4_handle = ARC4(key_exchange_key) self.encrypted_random_session_key = rc4_handle.update( self.exported_session_key) else: self.exported_session_key = key_exchange_key self.encrypted_random_session_key = b'' self.negotiate_flags = struct.pack('<I', self.negotiate_flags)
def __init__(self, user_name, password, domain_name, workstation, challenge_message, ntlm_compatibility, server_certificate_hash): """ [MS-NLMP] v28.0 2016-07-14 2.2.1.3 AUTHENTICATE_MESSAGE The AUTHENTICATE_MESSAGE defines an NTLM authenticate message that is sent from the client to the server after the CHALLENGE_MESSAGE is processed by the client. :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 challenge_message: A ChallengeMessage object that was received from the server after the negotiate_message :param ntlm_compatibility: The Lan Manager Compatibility Level, used to determine what NTLM auth version to use, see Ntlm in ntlm.py for more details :param server_certificate_hash: The SHA256 hash string of the server certificate (DER encoded) NTLM is authenticating to. This is used to add to the gss_channel_bindings_struct for Channel Binding Tokens support. If none is passed through then ntlm-auth will not use Channel Binding Tokens when authenticating with the server which could cause issues if it is set to only authenticate when these are present. This is only used for NTLMv2 authentication. Message Attributes (Attributes used to compute the message structure): signature: An 8-byte character array that MUST contain the ASCII string 'NTLMSSP\0' message_type: A 32-bit unsigned integer that indicates the message type. This field must be set to 0x00000003 negotiate_flags: A NEGOTIATE strucutre that contains a set of bit flags. These flags are the choices the client has made from the CHALLENGE_MESSAGE options version: Contains the windows version info of the client. It is used only debugging purposes and are only set when NTLMSSP_NEGOTIATE_VERSION flag is set mic: The message integrity for the NEGOTIATE_MESSAGE, CHALLENGE_MESSAGE and AUTHENTICATE_MESSAGE lm_challenge_response: An LM_RESPONSE of LMv2_RESPONSE structure that contains the computed LM response to the challenge nt_challenge_response: An NTLM_RESPONSE or NTLMv2_RESPONSE structure that contains the computed NT response to the challenge domain_name: The domain or computer name hosting the user account, MUST be encoded in the negotiated character set user_name: The name of the user to be authenticated, MUST be encoded in the negotiated character set workstation: The name of the computer to which the user is logged on, MUST Be encoded in the negotiated character set encrypted_random_session_key: The client's encrypted random session key Non-Message Attributes (Attributes not used in auth message): exported_session_key: A randomly generated session key based on other keys, used to derive the SIGNKEY and SEALKEY target_info: The AV_PAIR structure used in the nt response calculation """ self.signature = NTLM_SIGNATURE self.message_type = struct.pack('<L', MessageTypes.NTLM_AUTHENTICATE) self.negotiate_flags = challenge_message.negotiate_flags self.version = get_version(self.negotiate_flags) self.mic = None if domain_name is None: self.domain_name = '' else: self.domain_name = domain_name if workstation is None: self.workstation = '' else: self.workstation = workstation if self.negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_UNICODE: self.negotiate_flags -= NegotiateFlags.NTLMSSP_NEGOTIATE_OEM encoding_value = 'utf-16-le' else: encoding_value = 'ascii' self.domain_name = self.domain_name.encode(encoding_value) self.user_name = user_name.encode(encoding_value) self.workstation = self.workstation.encode(encoding_value) compute_response = ComputeResponse(user_name, password, domain_name, challenge_message, ntlm_compatibility) self.lm_challenge_response = \ compute_response.get_lm_challenge_response() self.nt_challenge_response, key_exchange_key, target_info = \ compute_response.get_nt_challenge_response( self.lm_challenge_response, server_certificate_hash) self.target_info = target_info if self.negotiate_flags & NegotiateFlags.NTLMSSP_NEGOTIATE_KEY_EXCH: self.exported_session_key = get_random_export_session_key() rc4_handle = ARC4(key_exchange_key) self.encrypted_random_session_key = \ rc4_handle.update(self.exported_session_key) else: self.exported_session_key = key_exchange_key self.encrypted_random_session_key = b'' self.negotiate_flags = struct.pack('<I', self.negotiate_flags)