def test_create_negotiate_message(self): test_ntlm_context = Ntlm() expected = 'TlRMTVNTUAABAAAAMrCI4gYABgAoAAAAEAAQAC4AAAAGAbEdAAAAD0RvbWFpbkMATwBNAFAAVQBUAEUAUgA=' actual = test_ntlm_context.create_negotiate_message( domain_name, workstation_name).decode() assert actual == expected
def connect(self, host_port): (host, port) = host_port ntlm_context = Ntlm(ntlm_compatibility=5) negotiate_message = ntlm_context.create_negotiate_message(self._domain, self._workstation).decode() resp = None try: self._sock.connect((self._proxy_ip, self._proxy_port)) self._sock.send(NtlmProxyContext.negotiate_request.format(host, str(port), negotiate_message)) resp = self._sock.recv(4096) except socket.error as (code, msg): logger.error("Caught socket error trying to establish connection to proxy. Code {0}. Msg {1}".format(code, msg)) raise
def test_parse_challenge_message(self): test_target_info = TargetInfo() test_target_info[AvId.MSV_AV_NB_DOMAIN_NAME] = \ "Domain".encode('utf-16-le') test_target_info[AvId.MSV_AV_NB_COMPUTER_NAME] = \ "Server".encode('utf-16-le') test_challenge_string = base64.b64encode( b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" b"\x02\x00\x00\x00\x03\x00\x0c\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"\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" ) test_ntlm_context = Ntlm() test_ntlm_context.parse_challenge_message(test_challenge_string) expected_message_type = MessageTypes.NTLM_CHALLENGE expected_negotiate_flags = 3800728115 expected_server_challenge = b"\x01\x23\x45\x67\x89\xab\xcd\xef" expected_signature = NTLM_SIGNATURE expected_target_info = test_target_info.pack() expected_target_name = None expected_version = 1080863910962135046 actual = test_ntlm_context.challenge_message actual_message_type = actual.message_type actual_negotiate_flags = actual.negotiate_flags actual_server_challenge = actual.server_challenge actual_signature = actual.signature actual_target_info = actual.target_info.pack() actual_target_name = actual.target_name actual_version = actual.version assert actual_message_type == expected_message_type assert actual_negotiate_flags == expected_negotiate_flags assert actual_server_challenge == expected_server_challenge assert actual_signature == expected_signature assert actual_target_info == expected_target_info assert actual_target_name == expected_target_name assert actual_version == expected_version
def test_initialise_with_illegal_ntlm_compatibility_low(self): with self.assertRaises(Exception) as context: Ntlm(ntlm_compatibility=-1) self.assertTrue( 'Unknown ntlm_compatibility level - expecting value between 0 and 5' in context.exception.args)
def test_create_authenticate_message_without_security( self, random_function, version_function, session_key_function, timestamp_function): test_challenge_string = base64.b64encode(ntlmv2_challenge_message) test_ntlm_context = Ntlm() test_ntlm_context.create_negotiate_message(domain_name, workstation_name) test_ntlm_context.parse_challenge_message(test_challenge_string) # Need to override the sign and seal flags so they don't return a security context test_ntlm_context.negotiate_flags -= NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN test_ntlm_context.negotiate_flags -= NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL # Need to override the flags in the challenge message to match the expectation, these flags are inconsequential and are done manualy for sanity test_ntlm_context.challenge_message.negotiate_flags -= NegotiateFlags.NTLMSSP_TARGET_TYPE_SERVER test_ntlm_context.challenge_message.negotiate_flags |= NegotiateFlags.NTLMSSP_REQUEST_TARGET expected_message = base64.b64encode( ntlmv2_authenticate_message).decode() actual_message = test_ntlm_context.create_authenticate_message( user_name, password, domain_name, "COMPUTER").decode() actual_session_security = test_ntlm_context.session_security assert actual_message == expected_message assert actual_session_security is None
def __init__(self, username, password): if username is None: raise SMBAuthenticationError("The username must be set when using " "NTLM authentication") if password is None: raise SMBAuthenticationError("The password must be set when using " "NTLM authentication") # try and get the domain part from the username log.info("Setting up NTLM Security Context for user %s" % username) try: self.domain, self.username = username.split("\\", 1) except ValueError: self.username = username self.domain = '' self.password = password self.context = Ntlm() self.in_token = None
def test_initialise_with_ntlm0(self): ntlm_context = Ntlm(ntlm_compatibility=0) expected_flags = default_negotiate_flags | \ NegotiateFlags.NTLMSSP_NEGOTIATE_NTLM | \ NegotiateFlags.NTLMSSP_NEGOTIATE_LM_KEY expected_ntlm_compatibility = 0 actual_flags = ntlm_context.negotiate_flags actual_ntlm_compatibility = ntlm_context.ntlm_compatibility assert actual_flags == expected_flags assert actual_ntlm_compatibility == expected_ntlm_compatibility
def test_initialise_with_ntlm5(self): ntlm_context = Ntlm(ntlm_compatibility=5) expected_flags = \ default_negotiate_flags |\ NegotiateFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY expected_ntlm_compatibility = 5 actual_flags = ntlm_context.negotiate_flags actual_ntlm_compatibility = ntlm_context.ntlm_compatibility assert actual_flags == expected_flags assert actual_ntlm_compatibility == expected_ntlm_compatibility
def __init__(self, username, password): if username is None: raise SMBAuthenticationError("The username must be set when using " "NTLM authentication") if password is None: raise SMBAuthenticationError("The password must be set when using " "NTLM authentication") log.info("Setting up NTLM Security Context for user %s" % username) self.domain, self.username = _split_username_and_domain(username) self.context = Ntlm(self.username, password, domain=self.domain)
class NtlmContext(object): def __init__(self, username, password): if username is None: raise SMBAuthenticationError("The username must be set when using " "NTLM authentication") if password is None: raise SMBAuthenticationError("The password must be set when using " "NTLM authentication") # try and get the domain part from the username log.info("Setting up NTLM Security Context for user %s" % username) try: self.domain, self.username = username.split("\\", 1) except ValueError: self.username = username self.domain = '' self.password = password self.context = Ntlm() self.in_token = None def step(self): log.info("NTLM: Generating Negotiate message") msg1 = self.context.create_negotiate_message(self.domain) msg1 = base64.b64decode(msg1) log.debug("NTLM: Negotiate message: %s" % _bytes_to_hex(msg1)) yield msg1 log.info("NTLM: Parsing Challenge message") msg2 = base64.b64encode(self.in_token) log.debug("NTLM: Challenge message: %s" % _bytes_to_hex(self.in_token)) self.context.parse_challenge_message(msg2) log.info("NTLM: Generating Authenticate message") msg3 = self.context.create_authenticate_message( user_name=self.username, password=self.password, domain_name=self.domain) yield base64.b64decode(msg3) def get_session_key(self): return self.context.authenticate_message.exported_session_key
def test_parse_challenge_message(self): test_target_info = TargetInfo() test_target_info[ TargetInfo.MSV_AV_NB_DOMAIN_NAME] = ntlmv2_netbios_domain_name test_target_info[ TargetInfo.MSV_AV_NB_COMPUTER_NAME] = ntlmv2_netbios_server_name test_challenge_string = base64.b64encode(ntlmv2_challenge_message) test_ntlm_context = Ntlm() test_ntlm_context.parse_challenge_message(test_challenge_string) expected_message_type = MessageTypes.NTLM_CHALLENGE expected_negotiate_flags = ntlmv2_negotiate_flags expected_server_challenge = server_challenge expected_signature = NTLM_SIGNATURE expected_target_info = test_target_info.get_data() expected_target_name = None expected_version = struct.unpack( "<q", HexToByte('06 00 70 17 00 00 00 0f'))[0] actual = test_ntlm_context.challenge_message actual_message_type = actual.message_type actual_negotiate_flags = actual.negotiate_flags actual_server_challenge = actual.server_challenge actual_signature = actual.signature actual_target_info = actual.target_info.get_data() actual_target_name = actual.target_name actual_version = actual.version assert actual_message_type == expected_message_type assert actual_negotiate_flags == expected_negotiate_flags assert actual_server_challenge == expected_server_challenge assert actual_signature == expected_signature assert actual_target_info == expected_target_info assert actual_target_name == expected_target_name assert actual_version == expected_version
def retry_with_ntlm_auth_legacy(self, auth_header_field, auth_header, response, auth_type, args): try: cert_hash = self._get_server_cert(response) except Exception: cert_hash = None context = Ntlm(ntlm_compatibility=self.ntlm_compatibility) # Consume the original response contents and release the connection for # later response.content response.raw.release_conn() # Create the negotiate request msg1_req = response.request.copy() msg1 = context.create_negotiate_message(self.domain) msg1_header = "%s %s" % (auth_type, msg1.decode('ascii')) msg1_req.headers[auth_header] = msg1_header # Send the negotiate request and receive the challenge message disable_stream_args = dict(args, stream=False) msg2_resp = response.connection.send(msg1_req, **disable_stream_args) msg2_resp.content msg2_resp.raw.release_conn() # Parse the challenge response in the ntlm_context msg2_header = msg2_resp.headers[auth_header_field] msg2 = msg2_header.replace(auth_type + ' ', '') context.parse_challenge_message(msg2) # Create the authenticate request msg3_req = msg2_resp.request.copy() msg3 = context.create_authenticate_message( self.username, self.password, self.domain, server_certificate_hash=cert_hash) msg3_header = auth_type + ' ' + msg3.decode('ascii') msg3_req.headers[auth_header] = msg3_header # Send the authenticate request final_response = msg2_resp.connection.send(msg3_req, **args) final_response.history.append(response) final_response.history.append(msg2_resp) return final_response
def connect(self, host_port): (host, port) = host_port ntlm_context = Ntlm(ntlm_compatibility=5) negotiate_message = ntlm_context.create_negotiate_message( self._domain, self._workstation).decode() resp = None try: self._sock.connect((self._proxy_ip, self._proxy_port)) self._sock.send( NtlmProxyContext.negotiate_request.format( host, str(port), negotiate_message)) resp = self._sock.recv(4096) except socket.error: print( "Caught socket error trying to establish connection to proxy. Code {0}. Msg {1}" .format(code, msg)) raise try: chal_msg = NtlmProxyContext.get_challenge(resp) ntlm_context.parse_challenge_message(chal_msg) except TypeError: print("Couldn't parse proxy challenge. Code {0}. Msg {1}".format( code, msg)) if resp is not None: print("Challenge contents: {0}".format(resp)) else: print("Challenge contents is 'None'") self._sock.close() authenticate_message = ntlm_context.create_authenticate_message( user_name=self._username, domain_name=self._domain, password=self._password, nthash=self._nthash, lmhash=self._lmhash).decode() resp = None try: self._sock.send( NtlmProxyContext.authenticate_request.format( host, str(port), authenticate_message)) resp = self._sock.recv(4096) except socket.error: print( "Caught socket error trying to send challenge response connection to proxy. Code {0}. Msg {1}" .format(code, msg)) self._sock.close() raise if resp is None: print("Received an empty response to the challenge response") self._sock.close() if 'HTTP/1.1 200 Connection established' in resp: #logger.info('Ntlm proxy established connection') print(resp) elif 'HTTP/1.1 503 Service Unavailable' in resp: #print('Ntlm proxy response: Service Unavailable') print(resp) self._sock.close() elif 'HTTP/1.1 407 Proxy Authentication Required' in resp: #print('Ntlm proxy authentication failed') print(resp) self._sock.close() sys.exit(1) else: print('Ntlm proxy unknown error') print(resp) self._sock.close()
def __init__(self, domain, username, password, ntlm_compatibility): self.username = username self.domain = domain.upper() self.password = password self.context = Ntlm(ntlm_compatibility=ntlm_compatibility)
class NtlmAuth(AuthBase): def __init__(self, domain, username, password, ntlm_compatibility): self.username = username self.domain = domain.upper() self.password = password self.context = Ntlm(ntlm_compatibility=ntlm_compatibility) def __call__(self, response): response.headers['Connection'] = 'Keep-Alive' response.register_hook('response', self.hook) return response def hook(self, response, **kwargs): if response.status_code == 401: return self.retry_with_ntlm_auth('www-authenticate', 'Authorization', response, 'NTLM', kwargs) else: return response def retry_with_ntlm_auth(self, auth_header_field, auth_header, response, auth_type, args): try: cert_hash = self._get_server_cert(response) except Exception: cert_hash = None # Consume the original response contents and release the connection for # later response.content response.raw.release_conn() # Create the negotiate request msg1_req = response.request.copy() msg1 = self.context.create_negotiate_message(self.domain) msg1_header = "%s %s" % (auth_type, msg1.decode('ascii')) msg1_req.headers[auth_header] = msg1_header # Send the negotiate request and receive the challenge message disable_stream_args = dict(args, stream=False) msg2_resp = response.connection.send(msg1_req, **disable_stream_args) msg2_resp.content msg2_resp.raw.release_conn() # Parse the challenge response in the ntlm_context msg2_header = msg2_resp.headers[auth_header_field] msg2 = msg2_header.replace(auth_type + ' ', '') self.context.parse_challenge_message(msg2) # Create the authenticate request msg3_req = msg2_resp.request.copy() msg3 = self.context.create_authenticate_message( self.username, self.password, self.domain, server_certificate_hash=cert_hash ) msg3_header = auth_type + ' ' + msg3.decode('ascii') msg3_req.headers[auth_header] = msg3_header # Send the authenticate request final_response = msg2_resp.connection.send(msg3_req, **args) final_response.history.append(response) final_response.history.append(msg2_resp) return final_response def _get_server_cert(self, response): if sys.version_info > (3, 0): socket = response.raw._fp.fp.raw._sock else: socket = response.raw._fp.fp._sock server_certificate = socket.getpeercert(True) hash_object = hashlib.sha256(server_certificate) server_certificate_hash = hash_object.hexdigest().upper() return server_certificate_hash
def test_create_authenticate_message_without_security(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) test_challenge_string = base64.b64encode( b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" b"\x02\x00\x00\x00\x03\x00\x0c\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"\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" ) test_ntlm_context = Ntlm() test_ntlm_context.create_negotiate_message("Domain", "COMPUTER") test_ntlm_context.parse_challenge_message(test_challenge_string) # Need to override the sign and seal flags so they don't return a # security context test_ntlm_context.negotiate_flags -= \ NegotiateFlags.NTLMSSP_NEGOTIATE_SIGN test_ntlm_context.negotiate_flags -=\ NegotiateFlags.NTLMSSP_NEGOTIATE_SEAL # Need to override the flags in the challenge message to match the # expectation, these flags are inconsequential and are done manualy for # sanity test_ntlm_context.challenge_message.negotiate_flags -= \ NegotiateFlags.NTLMSSP_TARGET_TYPE_SERVER test_ntlm_context.challenge_message.negotiate_flags |= \ NegotiateFlags.NTLMSSP_REQUEST_TARGET expected_message = base64.b64encode( b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" b"\x03\x00\x00\x00\x18\x00\x18\x00" b"\x6c\x00\x00\x00\x54\x00\x54\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"\xd8\x00\x00\x00\x35\x82\x88\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\x68\xcd\x0a\xb8" b"\x51\xe5\x1c\x96\xaa\xbc\x92\x7b" b"\xeb\xef\x6a\x1c\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"\x00\x00\x00\x00\x00\x00\x00\x00" b"\xc5\xda\xd2\x54\x4f\xc9\x79\x90" b"\x94\xce\x1c\xe9\x0b\xc9\xd0\x3e" ) actual_message = \ test_ntlm_context.create_authenticate_message("User", "Password", "Domain", "COMPUTER") actual_session_security = test_ntlm_context.session_security assert actual_message == expected_message assert actual_session_security is None
def test_create_negotiate_message(self): ntlm_context = Ntlm() expected = b'TlRMTVNTUAABAAAAMrCI4gYABgAoAAAACAAIAC4AAAA' \ b'GAbEdAAAAD0RvbWFpbkNPTVBVVEVS' actual = ntlm_context.create_negotiate_message("Domain", "COMPUTER") assert actual == expected
def test_initialise_with_illegal_ntlm_compatibility_low(self): with pytest.raises(Exception) as exc: Ntlm(ntlm_compatibility=-1) assert str(exc.value) == "Unknown ntlm_compatibility level - " \ "expecting value between 0 and 5"
def test_create_authenticate_message_without_security(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) test_challenge_string = base64.b64encode( b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" b"\x02\x00\x00\x00\x03\x00\x0c\x00" b"\x38\x00\x00\x00\x03\x92\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") test_ntlm_context = Ntlm() test_ntlm_context.create_negotiate_message("Domain", "COMPUTER") test_ntlm_context.parse_challenge_message(test_challenge_string) expected_message = base64.b64encode( b"\x4e\x54\x4c\x4d\x53\x53\x50\x00" b"\x03\x00\x00\x00\x18\x00\x18\x00" b"\x6c\x00\x00\x00\x54\x00\x54\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"\xd8\x00\x00\x00\x01\x92\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\x68\xcd\x0a\xb8" b"\x51\xe5\x1c\x96\xaa\xbc\x92\x7b" b"\xeb\xef\x6a\x1c\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"\x00\x00\x00\x00\x00\x00\x00\x00" b"\xc5\xda\xd2\x54\x4f\xc9\x79\x90" b"\x94\xce\x1c\xe9\x0b\xc9\xd0\x3e") actual_message = \ test_ntlm_context.create_authenticate_message("User", "Password", "Domain", "COMPUTER") actual_session_security = test_ntlm_context.session_security assert actual_message == expected_message assert actual_session_security is None # now test the properties map up the the correct NtlmContext ones assert test_ntlm_context.authenticate_message == \ test_ntlm_context._context._authenticate_message test_ntlm_context.authenticate_message = b"1" assert test_ntlm_context._context._authenticate_message == b"1" assert test_ntlm_context.challenge_message == \ test_ntlm_context._context._challenge_message test_ntlm_context.challenge_message = b"2" assert test_ntlm_context._context._challenge_message == b"2" assert test_ntlm_context.negotiate_flags == \ test_ntlm_context._context.negotiate_flags test_ntlm_context.negotiate_flags = 1 assert test_ntlm_context._context.negotiate_flags == 1 assert test_ntlm_context.negotiate_message == \ test_ntlm_context._context._negotiate_message test_ntlm_context.negotiate_message = b"3" assert test_ntlm_context._context._negotiate_message == b"3" assert test_ntlm_context.ntlm_compatibility == \ test_ntlm_context._context.ntlm_compatibility test_ntlm_context.ntlm_compatibility = 2 assert test_ntlm_context._context.ntlm_compatibility == 2 assert test_ntlm_context.session_security == \ test_ntlm_context._context._session_security test_ntlm_context.session_security = b"4" assert test_ntlm_context._context._session_security == b"4"