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 test_ntowfv1_hash(): lm_hash = os.urandom(16) nt_hash = os.urandom(16) ntlm_hash = to_text(b"%s:%s" % (base64.b16encode(lm_hash), base64.b16encode(nt_hash))) actual = crypto.ntowfv1(ntlm_hash) assert actual == nt_hash
def __init__(self, domain=None, username=None, password=None): if password: self.domain = domain self.username = username self.lm_hash = lmowfv1(password) self.nt_hash = ntowfv1(password) self._store = 'explicit' else: self._store = _get_credential_file() self.domain, self.username, self.lm_hash, self.nt_hash = _get_credential( self._store, domain, username)
def test_compute_response_v1_session_security(): actual_nt, actual_lm, actual_kek = crypto.compute_response_v1( TEST_NTLMV1_CLIENT_CHALLENGE_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/becc1601-9c97-4553-aef7-7053d3db5883 assert actual_nt == b"\x75\x37\xF8\x03\xAE\x36\x71\x28\xCA\x45\x82\x04\xBD\xE7\xCA\xF8" \ b"\x1E\x97\xED\x26\x83\x26\x72\x32" # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/63725b2b-21fc-4977-9b56-2b3e65f7be76 assert actual_lm == TEST_CLIENT_CHALLENGE + b"\x00" * 16 # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/321bb3da-c27a-4a5d-91f9-4f23705e4029 assert actual_kek == b"\xEB\x93\x42\x9A\x8B\xD9\x52\xF8\xB8\x9C\x55\xB8\x7F\x47\x5E\xDC"
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_seal_ntlmv1_with_ess(): # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/052aef59-b55b-4800-b4a8-e93eca1600d6 key_exchange_key = compute_response_v1(TEST_NTLMV1_CLIENT_CHALLENGE_FLAGS, ntowfv1(TEST_PASSWD), lmowfv1(TEST_PASSWD), TEST_SERVER_CHALLENGE, TEST_CLIENT_CHALLENGE)[2] seal_key = sealkey(TEST_NTLMV1_CLIENT_CHALLENGE_FLAGS, key_exchange_key, usage='initiate') seal_handle = rc4init(seal_key) sign_key = signkey(TEST_NTLMV1_CLIENT_CHALLENGE_FLAGS, key_exchange_key, usage='initiate') b_data = to_bytes(u"Plaintext", encoding='utf-16-le') actual_msg, actual_signature = seal(TEST_NTLMV1_CLIENT_CHALLENGE_FLAGS, seal_handle, sign_key, 0, b_data) assert actual_msg == b"\xA0\x23\x72\xF6\x53\x02\x73\xF3\xAA\x1E\xB9\x01\x90\xCE\x52\x00" \ b"\xC9\x9D" assert actual_signature == b"\x01\x00\x00\x00\xFF\x2A\xEB\x52\xF6\x81\x79\x3A\x00\x00\x00\x00"
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 test_ntowfv1(): # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/0fb94d19-16d2-481d-9121-112defbaac0b actual = crypto.ntowfv1(TEST_PASSWD) assert actual == b"\xA4\xF4\x9C\x40\x65\x10\xBD\xCA\xB6\x82\x4E\xE7\xC3\x0F\xD8\x52"
def test_ntowfv2(): actual = crypto.ntowfv2(TEST_USER, crypto.ntowfv1(TEST_PASSWD), TEST_USER_DOM) # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/7795bd0e-fd5e-43ec-bd9c-994704d8ee26 assert actual == b"\x0C\x86\x8A\x40\x3B\xFD\x7A\x93\xA3\x00\x1E\xF2\x2E\xF0\x2E\x3F"
def _get_credential(store, domain=None, username=None): # type: (text_type, Optional[text_type], Optional[text_type]) -> Tuple[text_type, text_type, bytes, bytes] """Look up NTLM credentials from the common flat file. Retrieves the LM and NT hash for use with authentication or validating a credential from an initiator. Each line in the store can be in the Heimdal format `DOMAIN:USER:PASSWORD` like:: testdom:testuser:Password01 :[email protected]:Password01 Or it can use the `smbpasswd`_ file format `USERNAME:UID:LM_HASH:NT_HASH:ACCT_FLAGS:TIMESTAMP` like:: testuser:1000:278623D830DABE161104594F8C2EF12B:C3C6F4FD8A02A6C1268F1A8074B6E7E0:[U]:LCT-1589398321 TESTDOM\testuser:1000:4588C64B89437893AAD3B435B51404EE:65202355FA01AEF26B89B19E00F52679:[U]:LCT-1589398321 [email protected]:1000:00000000000000000000000000000000:8ADB9B997580D69E69CAA2BBB68F4697:[U]:LCT-1589398321 While only the `USERNAME`, `LM_HASH`, and `NT_HASH` fields are used, the colons are still required to differentiate between the 2 formats. See `ntlm hash generator`_ for ways to generate the `LM_HASH` and `NT_HASH`. The username is case insensitive but the format of the domain and user part must match up with the value used as the username specified by the caller. While each line can use a different format, it is recommended to stick to 1 throughout the file. The same env var and format can also be read with gss-ntlmssp. Args: store: The credential store to lookup the credential from. domain: The domain for the user to get the credentials for. Should be `None` for a user in the UPN form. username: The username to get the credentials for. If omitted then the first entry in the store is used. Returns: Tuple[text_type, text_type, bytes, bytes]: The domain, username, LM, and NT hash of the user specified. .. _smbpasswd: https://www.samba.org/samba/docs/current/man-html/smbpasswd.5.html .. _ntlm hash generator: https://asecuritysite.com/encryption/lmhash """ if not store: raise OperationNotAvailableError( context_msg= "Retrieving NTLM store without NTLM_USER_FILE set to a filepath") domain = domain or u"" def store_lines(text): for line in text.splitlines(): line_split = line.split(':') if len(line_split) == 3: yield line_split[0], line_split[1], line_split[2], None, None elif len(line_split) == 6: domain_entry, user_entry = split_username(line_split[0]) lm_entry = base64.b16decode(line_split[2].upper()) nt_entry = base64.b16decode(line_split[3].upper()) yield domain_entry or u"", user_entry, None, lm_entry, nt_entry with open(store, mode='rb') as fd: cred_text = to_text(fd.read()) for line_domain, line_user, line_password, lm_hash, nt_hash in store_lines( cred_text): if not username or (username.upper() == line_user.upper() and domain.upper() == line_domain.upper()): # The Heimdal format uses the password so if the LM or NT hash isn't set generate it ourselves. if not lm_hash: lm_hash = lmowfv1(line_password) if not nt_hash: nt_hash = ntowfv1(line_password) # Favour the explicit username/password value, otherwise use what was in the credential file. if not username: username = line_user if not domain: domain = line_domain or None return domain, username, lm_hash, nt_hash else: raise SpnegoError( ErrorCode.failure, context_msg="Failed to find any matching credential in " "NTLM_USER_FILE credential store.")