NTLM: Improved CHALLENGE_MESSAGE implementation

This commit is contained in:
Tal Aloni 2017-09-01 23:13:26 +03:00
parent 7610056a0b
commit 3ce1ca6a30
3 changed files with 35 additions and 24 deletions

View file

@ -13,36 +13,44 @@ namespace SMBLibrary.Authentication.NTLM
{ {
public class AVPairUtils public class AVPairUtils
{ {
public static byte[] GetAVPairSequence(string domainName, string computerName) public static KeyValuePairList<AVPairKey, byte[]> GetAVPairSequence(string domainName, string computerName)
{ {
KeyValuePairList<AVPairKey, byte[]> pairs = new KeyValuePairList<AVPairKey, byte[]>(); KeyValuePairList<AVPairKey, byte[]> pairs = new KeyValuePairList<AVPairKey, byte[]>();
pairs.Add(AVPairKey.NbDomainName, UnicodeEncoding.Unicode.GetBytes(domainName)); pairs.Add(AVPairKey.NbDomainName, UnicodeEncoding.Unicode.GetBytes(domainName));
pairs.Add(AVPairKey.NbComputerName, UnicodeEncoding.Unicode.GetBytes(computerName)); pairs.Add(AVPairKey.NbComputerName, UnicodeEncoding.Unicode.GetBytes(computerName));
return AVPairUtils.GetAVPairSequenceBytes(pairs); return pairs;
} }
public static byte[] GetAVPairSequenceBytes(KeyValuePairList<AVPairKey, byte[]> pairs) public static byte[] GetAVPairSequenceBytes(KeyValuePairList<AVPairKey, byte[]> pairs)
{
int length = GetAVPairSequenceLength(pairs);
byte[] result = new byte[length];
int offset = 0;
WriteAVPairSequence(result, ref offset, pairs);
return result;
}
public static int GetAVPairSequenceLength(KeyValuePairList<AVPairKey, byte[]> pairs)
{ {
int length = 0; int length = 0;
foreach (KeyValuePair<AVPairKey, byte[]> pair in pairs) foreach (KeyValuePair<AVPairKey, byte[]> pair in pairs)
{ {
length += 4 + pair.Value.Length; length += 4 + pair.Value.Length;
} }
length += 4; return length + 4;
}
byte[] result = new byte[length]; public static void WriteAVPairSequence(byte[] buffer, ref int offset, KeyValuePairList<AVPairKey, byte[]> pairs)
int offset = 0; {
foreach (KeyValuePair<AVPairKey, byte[]> pair in pairs) foreach (KeyValuePair<AVPairKey, byte[]> pair in pairs)
{ {
WriteAVPair(result, ref offset, pair.Key, pair.Value); WriteAVPair(buffer, ref offset, pair.Key, pair.Value);
} }
LittleEndianWriter.WriteUInt16(result, ref offset, (ushort)AVPairKey.EOL); LittleEndianWriter.WriteUInt16(buffer, ref offset, (ushort)AVPairKey.EOL);
LittleEndianWriter.WriteUInt16(result, ref offset, 0); LittleEndianWriter.WriteUInt16(buffer, ref offset, 0);
return result;
} }
public static void WriteAVPair(byte[] buffer, ref int offset, AVPairKey key, byte[] value) private static void WriteAVPair(byte[] buffer, ref int offset, AVPairKey key, byte[] value)
{ {
LittleEndianWriter.WriteUInt16(buffer, ref offset, (ushort)key); LittleEndianWriter.WriteUInt16(buffer, ref offset, (ushort)key);
LittleEndianWriter.WriteUInt16(buffer, ref offset, (ushort)value.Length); LittleEndianWriter.WriteUInt16(buffer, ref offset, (ushort)value.Length);
@ -63,7 +71,7 @@ namespace SMBLibrary.Authentication.NTLM
return result; return result;
} }
public static KeyValuePair<AVPairKey, byte[]> ReadAVPair(byte[] buffer, ref int offset) private static KeyValuePair<AVPairKey, byte[]> ReadAVPair(byte[] buffer, ref int offset)
{ {
AVPairKey key = (AVPairKey)LittleEndianReader.ReadUInt16(buffer, ref offset); AVPairKey key = (AVPairKey)LittleEndianReader.ReadUInt16(buffer, ref offset);
ushort length = LittleEndianReader.ReadUInt16(buffer, ref offset); ushort length = LittleEndianReader.ReadUInt16(buffer, ref offset);

View file

@ -22,7 +22,7 @@ namespace SMBLibrary.Authentication.NTLM
public NegotiateFlags NegotiateFlags; public NegotiateFlags NegotiateFlags;
public byte[] ServerChallenge; // 8 bytes public byte[] ServerChallenge; // 8 bytes
// Reserved - 8 bytes // Reserved - 8 bytes
public byte[] TargetInfo; // sequence of AV_PAIR structures public KeyValuePairList<AVPairKey, byte[]> TargetInfo = new KeyValuePairList<AVPairKey,byte[]>();
public NTLMVersion Version; public NTLMVersion Version;
public ChallengeMessage() public ChallengeMessage()
@ -39,7 +39,11 @@ namespace SMBLibrary.Authentication.NTLM
NegotiateFlags = (NegotiateFlags)LittleEndianConverter.ToUInt32(buffer, 20); NegotiateFlags = (NegotiateFlags)LittleEndianConverter.ToUInt32(buffer, 20);
ServerChallenge = ByteReader.ReadBytes(buffer, 24, 8); ServerChallenge = ByteReader.ReadBytes(buffer, 24, 8);
// Reserved // Reserved
TargetInfo = AuthenticationMessageUtils.ReadBufferPointer(buffer, 40); byte[] targetInfoBytes = AuthenticationMessageUtils.ReadBufferPointer(buffer, 40);
if (targetInfoBytes.Length > 0)
{
TargetInfo = AVPairUtils.ReadAVPairSequence(targetInfoBytes, 0);
}
if ((NegotiateFlags & NegotiateFlags.Version) > 0) if ((NegotiateFlags & NegotiateFlags.Version) > 0)
{ {
Version = new NTLMVersion(buffer, 48); Version = new NTLMVersion(buffer, 48);
@ -53,9 +57,10 @@ namespace SMBLibrary.Authentication.NTLM
TargetName = String.Empty; TargetName = String.Empty;
} }
byte[] targetInfoBytes = AVPairUtils.GetAVPairSequenceBytes(TargetInfo);
if ((NegotiateFlags & NegotiateFlags.TargetInfo) == 0) if ((NegotiateFlags & NegotiateFlags.TargetInfo) == 0)
{ {
TargetInfo = new byte[0]; targetInfoBytes = new byte[0];
} }
int fixedLength = 48; int fixedLength = 48;
@ -63,7 +68,7 @@ namespace SMBLibrary.Authentication.NTLM
{ {
fixedLength += 8; fixedLength += 8;
} }
int payloadLength = TargetName.Length * 2 + TargetInfo.Length; int payloadLength = TargetName.Length * 2 + targetInfoBytes.Length;
byte[] buffer = new byte[fixedLength + payloadLength]; byte[] buffer = new byte[fixedLength + payloadLength];
ByteWriter.WriteAnsiString(buffer, 0, AuthenticateMessage.ValidSignature, 8); ByteWriter.WriteAnsiString(buffer, 0, AuthenticateMessage.ValidSignature, 8);
LittleEndianWriter.WriteUInt32(buffer, 8, (uint)MessageType); LittleEndianWriter.WriteUInt32(buffer, 8, (uint)MessageType);
@ -77,8 +82,8 @@ namespace SMBLibrary.Authentication.NTLM
int offset = fixedLength; int offset = fixedLength;
AuthenticationMessageUtils.WriteBufferPointer(buffer, 12, (ushort)(TargetName.Length * 2), (uint)offset); AuthenticationMessageUtils.WriteBufferPointer(buffer, 12, (ushort)(TargetName.Length * 2), (uint)offset);
ByteWriter.WriteUTF16String(buffer, ref offset, TargetName); ByteWriter.WriteUTF16String(buffer, ref offset, TargetName);
AuthenticationMessageUtils.WriteBufferPointer(buffer, 40, (ushort)TargetInfo.Length, (uint)offset); AuthenticationMessageUtils.WriteBufferPointer(buffer, 40, (ushort)targetInfoBytes.Length, (uint)offset);
ByteWriter.WriteBytes(buffer, ref offset, TargetInfo); ByteWriter.WriteBytes(buffer, ref offset, targetInfoBytes);
return buffer; return buffer;
} }

View file

@ -99,8 +99,7 @@ namespace SMBLibrary
message.Version = new NTLMVersion(6, 0, 6000, NTLMVersion.NTLMSSP_REVISION_W2K3); message.Version = new NTLMVersion(6, 0, 6000, NTLMVersion.NTLMSSP_REVISION_W2K3);
message.NegotiateFlags = NegotiateFlags.UnicodeEncoding | NegotiateFlags.OEMEncoding | NegotiateFlags.TargetNameSupplied | NegotiateFlags.Sign | NegotiateFlags.Seal | NegotiateFlags.NTLMSessionSecurity | NegotiateFlags.AlwaysSign | NegotiateFlags.TargetTypeServer | NegotiateFlags.ExtendedSessionSecurity | NegotiateFlags.TargetInfo | NegotiateFlags.Version | NegotiateFlags.Use128BitEncryption | NegotiateFlags.KeyExchange | NegotiateFlags.Use56BitEncryption; message.NegotiateFlags = NegotiateFlags.UnicodeEncoding | NegotiateFlags.OEMEncoding | NegotiateFlags.TargetNameSupplied | NegotiateFlags.Sign | NegotiateFlags.Seal | NegotiateFlags.NTLMSessionSecurity | NegotiateFlags.AlwaysSign | NegotiateFlags.TargetTypeServer | NegotiateFlags.ExtendedSessionSecurity | NegotiateFlags.TargetInfo | NegotiateFlags.Version | NegotiateFlags.Use128BitEncryption | NegotiateFlags.KeyExchange | NegotiateFlags.Use56BitEncryption;
message.TargetName = "Server"; message.TargetName = "Server";
byte[] serverAVPair = AVPairUtils.GetAVPairSequence("Domain", "Server"); message.TargetInfo = AVPairUtils.GetAVPairSequence("Domain", "Server");
message.TargetInfo = serverAVPair;
byte[] messageBytes = message.GetBytes(); byte[] messageBytes = message.GetBytes();
bool success = ByteUtils.AreByteArraysEqual(expected, messageBytes); bool success = ByteUtils.AreByteArraysEqual(expected, messageBytes);
@ -130,7 +129,6 @@ namespace SMBLibrary
byte[] sessionKey = {0xc5, 0xda, 0xd2, 0x54, 0x4f, 0xc9, 0x79, 0x90, 0x94, 0xce, 0x1c, 0xe9, 0x0b, 0xc9, 0xd0, 0x3e}; byte[] sessionKey = {0xc5, 0xda, 0xd2, 0x54, 0x4f, 0xc9, 0x79, 0x90, 0x94, 0xce, 0x1c, 0xe9, 0x0b, 0xc9, 0xd0, 0x3e};
byte[] serverChallenge = new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; byte[] serverChallenge = new byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
byte[] clientChallenge = new byte[] { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa }; byte[] clientChallenge = new byte[] { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa };
byte[] serverAVPair = AVPairUtils.GetAVPairSequence("Domain", "Server");
DateTime time = DateTime.FromFileTimeUtc(0); // same as new byte[8] DateTime time = DateTime.FromFileTimeUtc(0); // same as new byte[8]
NTLMv2ClientChallenge clientChallengeStructure = new NTLMv2ClientChallenge(time, clientChallenge, "Domain", "Server"); NTLMv2ClientChallenge clientChallengeStructure = new NTLMv2ClientChallenge(time, clientChallenge, "Domain", "Server");
byte[] clientChallengeStructurePadded = clientChallengeStructure.GetBytesPadded(); byte[] clientChallengeStructurePadded = clientChallengeStructure.GetBytesPadded();