void Decode(byte[] message, int startIndex, int length) { ValidateArguments(message, startIndex, length); Flags = (NtlmFlags)BitConverterLE.ToUInt32(message, startIndex + 20); Buffer.BlockCopy(message, startIndex + 24, serverChallenge, 0, 8); var targetNameLength = BitConverterLE.ToUInt16(message, startIndex + 12); var targetNameOffset = BitConverterLE.ToUInt16(message, startIndex + 16); if (targetNameLength > 0) { var encoding = (Flags & NtlmFlags.NegotiateUnicode) != 0 ? Encoding.Unicode : Encoding.UTF8; TargetName = encoding.GetString(message, startIndex + targetNameOffset, targetNameLength); } if ((Flags & NtlmFlags.NegotiateVersion) != 0 && length >= 56) { // decode the OS Version int major = message[startIndex + 48]; int minor = message[startIndex + 49]; int build = BitConverterLE.ToUInt16(message, startIndex + 50); OSVersion = new Version(major, minor, build); } // The Target Info block is optional. if (length >= 48 && targetNameOffset >= 48) { var targetInfoLength = BitConverterLE.ToUInt16(message, startIndex + 40); var targetInfoOffset = BitConverterLE.ToUInt16(message, startIndex + 44); if (targetInfoLength > 0 && targetInfoOffset < length && targetInfoLength <= (length - targetInfoOffset)) { TargetInfo = new NtlmTargetInfo(message, startIndex + targetInfoOffset, targetInfoLength, (Flags & NtlmFlags.NegotiateUnicode) != 0); } } }
/// <summary> /// Copy the attribute value pairs to another TargetInfo. /// </summary> /// <remarks> /// Copies the attribute value pairs to another TargetInfo. /// </remarks> public void CopyTo(NtlmTargetInfo targetInfo) { targetInfo.attributes.Clear(); foreach (var attribute in attributes) { if (attribute is NtlmAttributeTimestampValuePair timestamp) { targetInfo.attributes.Add(new NtlmAttributeTimestampValuePair(timestamp.Attribute, timestamp.Value, timestamp.Size)); } else if (attribute is NtlmAttributeFlagsValuePair flags) { targetInfo.attributes.Add(new NtlmAttributeFlagsValuePair(flags.Attribute, flags.Value, flags.Size)); } else if (attribute is NtlmAttributeByteArrayValuePair array) { targetInfo.attributes.Add(new NtlmAttributeByteArrayValuePair(array.Attribute, array.Value)); } else if (attribute is NtlmAttributeStringValuePair str) { targetInfo.attributes.Add(new NtlmAttributeStringValuePair(str.Attribute, str.Value)); } } }
public void ComputeNtlmV2(string targetName, bool unverifiedTargetName, byte[] channelBinding) { var targetInfo = new NtlmTargetInfo(); int avFlags = 0; // If the CHALLENGE_MESSAGE contains a TargetInfo field if (challenge.TargetInfo != null) { challenge.TargetInfo.CopyTo(targetInfo); if (targetInfo.Flags.HasValue) { avFlags = targetInfo.Flags.Value; } // If the CHALLENGE_MESSAGE TargetInfo field (section 2.2.1.2) has an MsvAvTimestamp present, the client SHOULD provide a MIC. if (challenge.TargetInfo?.Timestamp != null) { // If there is an AV_PAIR structure (section 2.2.2.1) with the AvId field set to MsvAvFlags, then in the Value field, set bit 0x2 to 1. // Else add an AV_PAIR structure and set the AvId field to MsvAvFlags and the Value field bit 0x2 to 1. targetInfo.Flags = avFlags |= 0x2; // Temporarily set the MIC to Z16. Mic = Z16; } // If ClientSuppliedTargetName (section 3.1.1.2) is not NULL if (targetName != null) { // If UnverifiedTargetName (section 3.1.1.2) is TRUE, then in AvId field = MsvAvFlags set 0x00000004 bit. if (unverifiedTargetName) { targetInfo.Flags = avFlags |= 0x4; } // Add an AV_PAIR structure and set the AvId field to MsvAvTargetName and the Value field to ClientSuppliedTargetName without // terminating NULL. targetInfo.TargetName = targetName; } else { // Else add an AV_PAIR structure and set the AvId field to MsvAvTargetName and the Value field to an empty string without terminating NULL. targetInfo.TargetName = string.Empty; } // The client SHOULD send the channel binding AV_PAIR: // If the ClientChannelBindingsUnhashed (section 3.1.1.2) is not NULL if (channelBinding != null) { // Add an AV_PAIR structure and set the AvId field to MsvAvChannelBindings and the Value field to MD5_HASH(ClientChannelBindingsUnhashed). targetInfo.ChannelBinding = NtlmUtils.MD5(channelBinding); } else { // Else add an AV_PAIR structure and set the AvId field to MsvAvChannelBindings and the Value field to Z(16). targetInfo.ChannelBinding = Z16; } } var encodedTargetInfo = targetInfo.Encode((Flags & NtlmFlags.NegotiateUnicode) != 0); // Note: For NTLMv2, the sessionBaseKey is the same as the keyExchangeKey. NtlmUtils.ComputeNtlmV2(challenge, Domain, UserName, Password, encodedTargetInfo, clientChallenge, Timestamp, out var ntChallengeResponse, out var lmChallengeResponse, out var keyExchangeKey); NtChallengeResponse = ntChallengeResponse; LmChallengeResponse = lmChallengeResponse; if ((Flags & NtlmFlags.NegotiateKeyExchange) != 0 && (Flags & (NtlmFlags.NegotiateSign | NtlmFlags.NegotiateSeal)) != 0) { ExportedSessionKey = NtlmUtils.NONCE(16); EncryptedRandomSessionKey = NtlmUtils.RC4K(keyExchangeKey, ExportedSessionKey); } else { ExportedSessionKey = keyExchangeKey; EncryptedRandomSessionKey = null; } // If the CHALLENGE_MESSAGE TargetInfo field (section 2.2.1.2) has an MsvAvTimestamp present, the client SHOULD provide a MIC. if ((avFlags & 0x2) != 0) { Mic = NtlmUtils.HMACMD5(ExportedSessionKey, NtlmUtils.ConcatenationOf(negotiate.Encode(), challenge.Encode(), Encode())); } }