I can reproduce this issue against a proper domain setup but a simple reproducer for this issue is
import gssapi
import os
import tempfile
from gssapi.raw import acquire_cred_with_password, inquire_sec_context_by_oid
NTLM = gssapi.OID.from_int_seq('1.3.6.1.4.1.311.2.2.10')
GSS_C_INQ_SSPI_SESSION_KEY = gssapi.OID.from_int_seq("1.2.840.113554.1.2.2.5.5")
with tempfile.NamedTemporaryFile() as ntlm_file:
with open(ntlm_file.name, mode='wb') as fd:
fd.write(b"domain:username:password")
os.environ['NTLM_USER_FILE'] = ntlm_file.name
c = gssapi.SecurityContext(
creds=acquire_cred_with_password(gssapi.Name(base='username', name_type=gssapi.NameType.user),
b'password', usage='initiate', mechs=[NTLM]).creds,
usage='initiate',
name=gssapi.Name('host@domain', name_type=gssapi.NameType.hostbased_service),
mech=NTLM,
)
s = gssapi.SecurityContext(
creds=acquire_cred_with_password(gssapi.Name(base='username', name_type=gssapi.NameType.hostbased_service),
b'password', usage='accept', mechs=[NTLM]).creds,
usage='accept')
in_token = None
while not c.complete:
out_token = c.step(in_token)
in_token = s.step(out_token)
# Verifies wrapping is actually working which would mean there is a session key available
c_enc_msg = c.wrap(b"Hello world", encrypt=True).message
s_dec_msg = s.unwrap(c_enc_msg).message
s_enc_msg = s.wrap(s_dec_msg, encrypt=True).message
c_dec_msg = c.unwrap(s_enc_msg).message
assert c_dec_msg == b"Hello world"
# Both fail with the GSSError I shared above
c_session_key = inquire_sec_context_by_oid(c, GSS_C_INQ_SSPI_SESSION_KEY)[0]
s_session_key = inquire_sec_context_by_oid(s, GSS_C_INQ_SSPI_SESSION_KEY)[0]
I've double checked that the OID I'm using matches up with what is documented for GSS_C_INQ_SSPI_SESSION_KEY in MIT krb5 and this same code works fine for a Kerberos negotiated context.
On a side note manually parsing the NTLM 3 msg with the known password I am able to generate the session key myself but because NTLMSSP_NEGOTIATE_KEY_EXCH
is not a negotiated flag it isn't sending the encrypted session key across the wire. Is this by choice as the docs indicate that this "SHOULD" be used under the V entry for the flags
Has this been omitted on purpose due to the age of RC4 encryption that it used here or is there another reason?
(ansible-py37) jborean:~/dev/pyspnego$ bin/pyspnego-parse --password password --token TlRMTVNTUAADAAAAAAAAAEAAAADCAMIAQAAAAAAAAAAAAAAAEAAQAAIBAAAaABoAEgEAABAAEAAsAQAAFYKKoAgDDMe4rJ/5XPH/v/d/Kw0BAQAAAAAAAOC3ZQP5GdYBHdWOyE5G9/YAAAAAAQAaAEoAQgBPAFIARQBBAE4ALQBMAEkATgBVAFgAAgAWAFcATwBSAEsAUwBUAEEAVABJAE8ATgADABoAagBiAG8AcgBlAGEAbgAtAGwAaQBuAHUAeAAGAAQAAAAAAAcACADgt2UD+RnWAQkADABkAG8AbQBhAGkAbgAKABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdQBzAGUAcgBuAGEAbQBlAEoAQgBPAFIARQBBAE4ALQBMAEkATgBVAFgAAAAAAAAAAAAAAAAAAAAAAA==
{
"MessageType": "NtlmAuthenticate (3)",
"Signature": "NTLMSSP\u0000",
"Data": {
"NegotiateFlagsRaw": 2693431829,
"NegotiateFlags": [
"NTLMSSP_NEGOTIATE_56 (2147483648)",
"NTLMSSP_NEGOTIATE_128 (536870912)",
"NTLMSSP_NEGOTIATE_TARGET_INFO (8388608)",
"NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY (524288)",
"NTLMSSP_TARGET_TYPE_SERVER (131072)",
"NTLMSSP_NEGOTIATE_ALWAYS_SIGN (32768)",
"NTLMSSP_NEGOTIATE_NTLM (512)",
"NTLMSSP_NEGOTIATE_SIGN (16)",
"NTLMSSP_REQUEST_TARGET (4)",
"NTLMSSP_NEGOTIATE_UNICODE (1)"
],
"LmChallengeResponse": null,
"NtChallengeResponse": {
"ResponseType": "NTLMv2",
"NTProofStr": [
"08030CC7B8AC9FF95CF1FFBFF77F2B0D"
],
"ClientChallenge": {
"RespType": 1,
"HiRespType": 1,
"Reserved1": 0,
"Reserved2": 0,
"TimeStamp": "2020-04-24T05:27:14.855728",
"ChallengeFromClient": "1DD58EC84E46F7F6",
"Reserved3": 0,
"AvPairs": [
{
"AvId": "MSV_AV_NB_COMPUTER_NAME (1)",
"Value": "JBOREAN-LINUX"
},
{
"AvId": "MSV_AV_NB_DOMAIN_NAME (2)",
"Value": "WORKSTATION"
},
{
"AvId": "MSV_AV_DNS_COMPUTER_NAME (3)",
"Value": "jborean-linux"
},
{
"AvId": "MSV_AV_FLAGS (6)",
"Value": []
},
{
"AvId": "MSV_AV_TIMESTAMP (7)",
"Value": "2020-04-24T05:27:14.855728"
},
{
"AvId": "MSV_AV_TARGET_NAME (9)",
"Value": "domain"
},
{
"AvId": "MSV_AV_CHANNEL_BINDINGS (10)",
"Value": "00000000000000000000000000000000"
},
{
"AvId": "MSV_AV_EOL (0)",
"Value": null
}
],
"Reserved4": 0
}
},
"DomainName": null,
"UserName": "username",
"Workstation": "JBOREAN-LINUX",
"EncryptedRandomSessionKey": null,
"Version": null,
"MIC": "5CF1FFBFF77F2B0D0101000000000000",
"SessionKey": "D4271F9C44E73E7C2D89A69C73198304"
},
"RawData": "4E544C4D53535000030000000000000040000000C200C20040000000000000000000000010001000020100001A001A0012010000100010002C01000015828AA008030CC7B8AC9FF95CF1FFBFF77F2B0D0101000000000000E0B76503F919D6011DD58EC84E46F7F60000000001001A004A0042004F005200450041004E002D004C0049004E00550058000200160057004F0052004B00530054004100540049004F004E0003001A006A0062006F007200650061006E002D006C0069006E0075007800060004000000000007000800E0B76503F919D60109000C0064006F006D00610069006E000A00100000000000000000000000000000000000000000000000000075007300650072006E0061006D0065004A0042004F005200450041004E002D004C0049004E005500580000000000000000000000000000000000"
}