def get_cbt_data(response): """ Create Channel Binding for TLS data See: - https://tools.ietf.org/html/rfc5929 - https://github.com/jborean93/ntlm-auth#ntlmv2 - https://github.com/requests/requests-ntlm/pull/116#discussion_r325961121 - https://support.microsoft.com/en-za/help/976918/authentication-failure-from-non-windows-ntlm-or-kerberos-servers # noqa :param response: HTTP Response object """ cert_hash_bytes = get_server_cert(response) if not cert_hash_bytes: logger.debug("server cert not found, channel binding tokens (CBT) wont be used") return None channel_binding_type = b"tls-server-end-point" # https://tools.ietf.org/html/rfc5929#section-4 data_type = GssChannelBindingsStruct.APPLICATION_DATA cbt_data = GssChannelBindingsStruct() cbt_data[data_type] = b":".join([channel_binding_type, cert_hash_bytes]) logger.debug("cbt data: %s", cbt_data.get_data()) return cbt_data
def _get_channel_bindings_value(server_certificate_hash): """ Get's the MD5 hash of the gss_channel_bindings_struct to add to the AV_PAIR MSV_AV_CHANNEL_BINDINGS. This method takes in the SHA256 hash (Hash of the DER encoded certificate of the server we are connecting to) and add's it to the gss_channel_bindings_struct. It then gets the MD5 hash and converts this to a byte array in preparation of adding it to the AV_PAIR structure. :param server_certificate_hash: The SHA256 hash of the server certificate (DER encoded) NTLM is authenticated to :return channel_bindings: An MD5 hash of the gss_channel_bindings_struct to add to the AV_PAIR MsvChannelBindings """ # Channel Binding Tokens support, used for NTLMv2 # Decode the SHA256 certificate hash certificate_digest = base64.b16decode(server_certificate_hash) # Initialise the GssChannelBindingsStruct and add the # certificate_digest to the application_data field gss_channel_bindings = GssChannelBindingsStruct() gss_channel_bindings[gss_channel_bindings.APPLICATION_DATA] = \ b'tls-server-end-point:' + certificate_digest # Get the gss_channel_bindings_struct and create an MD5 hash channel_bindings_struct_data = gss_channel_bindings.get_data() channel_bindings = hashlib.md5(channel_bindings_struct_data).digest() return channel_bindings
def _get_channel_bindings_value(server_certificate_hash): """ https://msdn.microsoft.com/en-us/library/windows/desktop/dd919963%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 https://blogs.msdn.microsoft.com/openspecification/2013/03/26/ntlm-and-channel-binding-hash-aka-extended-protection-for-authentication/ Get's the MD5 hash of the gss_channel_bindings_struct to add to the AV_PAIR MSV_AV_CHANNEL_BINDINGS. This method takes in the SHA256 hash (Hash of the DER encoded certificate of the server we are connecting to) and add's it to the gss_channel_bindings_struct. It then gets the MD5 hash and converts this to a byte array in preparation of adding it to the AV_PAIR structure. :param server_certificate_hash: The SHA256 hash of the server certificate (DER encoded) NTLM is authenticated to :return channel_bindings: An MD5 hash of the gss_channel_bindings_struct to add to the AV_PAIR MsvChannelBindings """ # Channel Binding Tokens support, used for NTLMv2 # Decode the SHA256 certificate hash certificate_digest = base64.b16decode(server_certificate_hash) # Initialise the GssChannelBindingsStruct and add the certificate_digest to the application_data field gss_channel_bindings = GssChannelBindingsStruct() gss_channel_bindings[gss_channel_bindings.APPLICATION_DATA] = 'tls-server-end-point:'.encode() + certificate_digest # Get the gss_channel_bindings_struct and create an MD5 hash channel_bindings_struct_data = gss_channel_bindings.get_data() channel_bindings_hash = hashlib.md5(channel_bindings_struct_data).hexdigest() try: cbt_value = bytearray.fromhex(channel_bindings_hash) except TypeError: # Work-around for Python 2.6 bug cbt_value = bytearray.fromhex(unicode(channel_bindings_hash)) channel_bindings = bytes(cbt_value) return channel_bindings
def test_application_data(self): struct = GssChannelBindingsStruct() struct[GssChannelBindingsStruct.APPLICATION_DATA] = b"abc" expected = b"\x00\x00\x00\x00" \ b"\x00\x00\x00\x00" \ b"\x00\x00\x00\x00" \ b"\x00\x00\x00\x00" \ b"\x03\x00\x00\x00" \ b"\x61\x62\x63" actual = struct.get_data() assert actual == expected
def test_full_data(self): struct = GssChannelBindingsStruct() struct[GssChannelBindingsStruct.INITIATOR_ADDTYPE] = 2 struct[GssChannelBindingsStruct.INITIATOR_ADDRESS] = b"abc" struct[GssChannelBindingsStruct.ACCEPTOR_ADDRTYPE] = 4 struct[GssChannelBindingsStruct.ACCEPTOR_ADDRESS] = b"def" struct[GssChannelBindingsStruct.APPLICATION_DATA] = b"ghi" expected = b"\x02\x00\x00\x00" \ b"\x03\x00\x00\x00" \ b"\x61\x62\x63" \ b"\x04\x00\x00\x00" \ b"\x03\x00\x00\x00" \ b"\x64\x65\x66" \ b"\x03\x00\x00\x00" \ b"\x67\x68\x69" actual = struct.get_data() assert actual == expected
def _get_channel_bindings_value(server_certificate_hash): """ https://msdn.microsoft.com/en-us/library/windows/desktop/dd919963%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 https://blogs.msdn.microsoft.com/openspecification/2013/03/26/ntlm-and-channel-binding-hash-aka-extended-protection-for-authentication/ Get's the MD5 hash of the gss_channel_bindings_struct to add to the AV_PAIR MSV_AV_CHANNEL_BINDINGS. This method takes in the SHA256 hash (Hash of the DER encoded certificate of the server we are connecting to) and add's it to the gss_channel_bindings_struct. It then gets the MD5 hash and converts this to a byte array in preparation of adding it to the AV_PAIR structure. :param server_certificate_hash: The SHA256 hash of the server certificate (DER encoded) NTLM is authenticated to :return channel_bindings: An MD5 hash of the gss_channel_bindings_struct to add to the AV_PAIR MsvChannelBindings """ # Channel Binding Tokens support, used for NTLMv2 # Decode the SHA256 certificate hash certificate_digest = base64.b16decode(server_certificate_hash) # Initialise the GssChannelBindingsStruct and add the certificate_digest to the application_data field gss_channel_bindings = GssChannelBindingsStruct() gss_channel_bindings[ gss_channel_bindings. APPLICATION_DATA] = 'tls-server-end-point:'.encode( ) + certificate_digest # Get the gss_channel_bindings_struct and create an MD5 hash channel_bindings_struct_data = gss_channel_bindings.get_data() channel_bindings_hash = hashlib.md5( channel_bindings_struct_data).hexdigest() try: cbt_value = bytearray.fromhex(channel_bindings_hash) except TypeError: # Work-around for Python 2.6 bug cbt_value = bytearray.fromhex(unicode(channel_bindings_hash)) channel_bindings = bytes(cbt_value) return channel_bindings
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