Skip to content

Commit

Permalink
Merge pull request #13 from Dualog/update-from-origin
Browse files Browse the repository at this point in the history
Update from origin
  • Loading branch information
legendarymember authored Aug 27, 2024
2 parents 19d7ffb + bb67d6d commit 50fd8ae
Show file tree
Hide file tree
Showing 51 changed files with 1,219 additions and 338 deletions.
16 changes: 8 additions & 8 deletions ClientExamples.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Login and list shares:
======================
```
```cs
SMB1Client client = new SMB1Client(); // SMB2Client can be used as well
bool isConnected = client.Connect(IPAddress.Parse("192.168.1.11"), SMBTransportType.DirectTCPTransport);
if (isConnected)
Expand All @@ -17,7 +17,7 @@ if (isConnected)

Connect to share and list files and directories - SMB1:
=======================================================
```
```cs
ISMBFileStore fileStore = client.TreeConnect("Shared", out status);
if (status == NTStatus.STATUS_SUCCESS)
{
Expand All @@ -36,7 +36,7 @@ status = fileStore.Disconnect();

Connect to share and list files and directories - SMB2:
=======================================================
```
```cs
ISMBFileStore fileStore = client.TreeConnect("Shared", out status);
if (status == NTStatus.STATUS_SUCCESS)
{
Expand All @@ -55,7 +55,7 @@ status = fileStore.Disconnect();

Read large file to its end:
===========================
```
```cs
ISMBFileStore fileStore = client.TreeConnect("Shared", out status);
object fileHandle;
FileStatus fileStatus;
Expand Down Expand Up @@ -93,7 +93,7 @@ status = fileStore.Disconnect();

Create a file and write to it:
==============================
```
```cs
ISMBFileStore fileStore = client.TreeConnect("Shared", out status);
string filePath = "NewFile.txt";
if (fileStore is SMB1FileStore)
Expand All @@ -119,7 +119,7 @@ status = fileStore.Disconnect();

Write a large file:
===================
```
```cs
ISMBFileStore fileStore = client.TreeConnect("Shared", out status);
if (status != NTStatus.STATUS_SUCCESS)
{
Expand Down Expand Up @@ -161,7 +161,7 @@ status = fileStore.Disconnect();

Delete file:
============
```
```cs
ISMBFileStore fileStore = client.TreeConnect("Shared", out status);
string filePath = "DeleteMe.txt";
if (fileStore is SMB1FileStore)
Expand All @@ -181,4 +181,4 @@ if (status == NTStatus.STATUS_SUCCESS)
status = fileStore.CloseFile(fileHandle);
}
status = fileStore.Disconnect();
```
```
6 changes: 6 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ NuGet Packages:
[SMBLibrary.Win32](https://www.nuget.org/packages/SMBLibrary.Win32/) - Allows utilizing Integrated Windows Authentication and/or the Windows storage subsystem on a Windows host.
[SMBLibrary.Adapters](https://www.nuget.org/packages/SMBLibrary.Adapters/) - IFileSystem to INTFileStore adapter for SMBLibrary.

Licensing:
==========
A commercial license of SMBLibrary is available for a fee.
This is intended for companies who are unable to use the LGPL version.
Please contact me for additional details.

Contact:
========
If you have any question, feel free to contact me.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
/* Copyright (C) 2017-2024 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
Expand Down Expand Up @@ -102,16 +102,7 @@ public override byte[] GetBytes()

protected virtual int GetTokenFieldsLength()
{
int result = 0;
if (MechanismTypeList != null)
{
int typeListSequenceLength = GetMechanismTypeListSequenceLength(MechanismTypeList);
int typeListSequenceLengthFieldSize = DerEncodingHelper.GetLengthFieldSize(typeListSequenceLength);
int typeListConstructionLength = 1 + typeListSequenceLengthFieldSize + typeListSequenceLength;
int typeListConstructionLengthFieldSize = DerEncodingHelper.GetLengthFieldSize(typeListConstructionLength);
int entryLength = 1 + typeListConstructionLengthFieldSize + 1 + typeListSequenceLengthFieldSize + typeListSequenceLength;
result += entryLength;
}
int result = GetEncodedMechanismTypeListLength(MechanismTypeList);
if (MechanismToken != null)
{
int mechanismTokenLengthFieldSize = DerEncodingHelper.GetLengthFieldSize(MechanismToken.Length);
Expand Down Expand Up @@ -200,6 +191,11 @@ protected static void WriteMechanismTypeList(byte[] buffer, ref int offset, List
int constructionLength = 1 + sequenceLengthFieldSize + sequenceLength;
ByteWriter.WriteByte(buffer, ref offset, MechanismTypeListTag);
DerEncodingHelper.WriteLength(buffer, ref offset, constructionLength);
WriteMechanismTypeListSequence(buffer, ref offset, mechanismTypeList, sequenceLength);
}

protected static void WriteMechanismTypeListSequence(byte[] buffer, ref int offset, List<byte[]> mechanismTypeList, int sequenceLength)
{
ByteWriter.WriteByte(buffer, ref offset, (byte)DerEncodingTag.Sequence);
DerEncodingHelper.WriteLength(buffer, ref offset, sequenceLength);
foreach (byte[] mechanismType in mechanismTypeList)
Expand Down Expand Up @@ -229,5 +225,32 @@ protected static void WriteMechanismListMIC(byte[] buffer, ref int offset, byte[
DerEncodingHelper.WriteLength(buffer, ref offset, mechanismListMIC.Length);
ByteWriter.WriteBytes(buffer, ref offset, mechanismListMIC);
}

public static byte[] GetMechanismTypeListBytes(List<byte[]> mechanismTypeList)
{
int sequenceLength = GetMechanismTypeListSequenceLength(mechanismTypeList);
int sequenceLengthFieldSize = DerEncodingHelper.GetLengthFieldSize(sequenceLength);
int constructionLength = 1 + sequenceLengthFieldSize + sequenceLength;
byte[] buffer = new byte[constructionLength];
int offset = 0;
WriteMechanismTypeListSequence(buffer, ref offset, mechanismTypeList, sequenceLength);
return buffer;
}

private static int GetEncodedMechanismTypeListLength(List<byte[]> mechanismTypeList)
{
if (mechanismTypeList == null)
{
return 0;
}
else
{
int typeListSequenceLength = GetMechanismTypeListSequenceLength(mechanismTypeList);
int typeListSequenceLengthFieldSize = DerEncodingHelper.GetLengthFieldSize(typeListSequenceLength);
int typeListConstructionLength = 1 + typeListSequenceLengthFieldSize + typeListSequenceLength;
int typeListConstructionLengthFieldSize = DerEncodingHelper.GetLengthFieldSize(typeListConstructionLength);
return 1 + typeListConstructionLengthFieldSize + 1 + typeListSequenceLengthFieldSize + typeListSequenceLength;
}
}
}
}
125 changes: 118 additions & 7 deletions SMBLibrary/Authentication/NTLM/Helpers/NTLMCryptography.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
/* Copyright (C) 2014-2024 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Security.Cryptography;
Expand Down Expand Up @@ -78,11 +77,26 @@ public static ICryptoTransform CreateWeakDesEncryptor(CipherMode mode, byte[] rg
{
DES des = DES.Create();
des.Mode = mode;
DESCryptoServiceProvider sm = des as DESCryptoServiceProvider;
MethodInfo mi = sm.GetType().GetMethod("_NewEncryptor", BindingFlags.NonPublic | BindingFlags.Instance);
object[] Par = { rgbKey, mode, rgbIV, sm.FeedbackSize, 0 };
ICryptoTransform trans = mi.Invoke(sm, Par) as ICryptoTransform;
return trans;
ICryptoTransform transform;
if (DES.IsWeakKey(rgbKey) || DES.IsSemiWeakKey(rgbKey))
{
#if NETSTANDARD2_0
MethodInfo getTransformCoreMethodInfo = des.GetType().GetMethod("CreateTransformCore", BindingFlags.NonPublic | BindingFlags.Static);
object[] getTransformCoreParameters = { mode, des.Padding, rgbKey, rgbIV, des.BlockSize / 8 , des.FeedbackSize / 8, des.BlockSize / 8, true };
transform = getTransformCoreMethodInfo.Invoke(null, getTransformCoreParameters) as ICryptoTransform;
#else
DESCryptoServiceProvider desServiceProvider = des as DESCryptoServiceProvider;
MethodInfo newEncryptorMethodInfo = desServiceProvider.GetType().GetMethod("_NewEncryptor", BindingFlags.NonPublic | BindingFlags.Instance);
object[] encryptorParameters = { rgbKey, mode, rgbIV, desServiceProvider.FeedbackSize, 0 };
transform = newEncryptorMethodInfo.Invoke(desServiceProvider, encryptorParameters) as ICryptoTransform;
#endif
}
else
{
transform = des.CreateEncryptor(rgbKey, rgbIV);
}

return transform;
}

/// <summary>
Expand Down Expand Up @@ -123,7 +137,11 @@ public static byte[] DesLongEncrypt(byte[] key, byte[] plainText)

public static Encoding GetOEMEncoding()
{
#if NETSTANDARD2_0
return ASCIIEncoding.GetEncoding(28591);
#else
return Encoding.GetEncoding(CultureInfo.CurrentCulture.TextInfo.OEMCodePage);
#endif
}

/// <summary>
Expand Down Expand Up @@ -239,5 +257,98 @@ public static byte[] KXKey(byte[] sessionBaseKey, NegotiateFlags negotiateFlags,
return keyExchangeKey;
}
}

/// <remarks>
/// Caller must verify that the authenticate message has MIC before calling this method
/// </remarks>
public static bool ValidateAuthenticateMessageMIC(byte[] exportedSessionKey, byte[] negotiateMessageBytes, byte[] challengeMessageBytes, byte[] authenticateMessageBytes)
{
// https://msdn.microsoft.com/en-us/library/cc236695.aspx
int micFieldOffset = AuthenticateMessage.GetMicFieldOffset(authenticateMessageBytes);
byte[] expectedMic = ByteReader.ReadBytes(authenticateMessageBytes, micFieldOffset, AuthenticateMessage.MicFieldLenght);

ByteWriter.WriteBytes(authenticateMessageBytes, micFieldOffset, new byte[AuthenticateMessage.MicFieldLenght]);
byte[] temp = ByteUtils.Concatenate(ByteUtils.Concatenate(negotiateMessageBytes, challengeMessageBytes), authenticateMessageBytes);
byte[] mic = new HMACMD5(exportedSessionKey).ComputeHash(temp);

return ByteUtils.AreByteArraysEqual(mic, expectedMic);
}

public static byte[] ComputeClientSignKey(byte[] exportedSessionKey)
{
return ComputeSignKey(exportedSessionKey, true);
}

public static byte[] ComputeServerSignKey(byte[] exportedSessionKey)
{
return ComputeSignKey(exportedSessionKey, false);
}

private static byte[] ComputeSignKey(byte[] exportedSessionKey, bool isClient)
{
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/524cdccb-563e-4793-92b0-7bc321fce096
string str;
if (isClient)
{
str = "session key to client-to-server signing key magic constant";
}
else
{
str = "session key to server-to-client signing key magic constant";
}
byte[] encodedString = Encoding.GetEncoding(28591).GetBytes(str);
byte[] nullTerminatedEncodedString = ByteUtils.Concatenate(encodedString, new byte[1]);
byte[] concatendated = ByteUtils.Concatenate(exportedSessionKey, nullTerminatedEncodedString);
return MD5.Create().ComputeHash(concatendated);
}

public static byte[] ComputeClientSealKey(byte[] exportedSessionKey)
{
return ComputeSealKey(exportedSessionKey, true);
}

public static byte[] ComputeServerSealKey(byte[] exportedSessionKey)
{
return ComputeSealKey(exportedSessionKey, false);
}

private static byte[] ComputeSealKey(byte[] exportedSessionKey, bool isClient)
{
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/524cdccb-563e-4793-92b0-7bc321fce096
string str;
if (isClient)
{
str = "session key to client-to-server sealing key magic constant";
}
else
{
str = "session key to server-to-client sealing key magic constant";
}
byte[] encodedString = Encoding.GetEncoding(28591).GetBytes(str);
byte[] nullTerminatedEncodedString = ByteUtils.Concatenate(encodedString, new byte[1]);
byte[] concatendated = ByteUtils.Concatenate(exportedSessionKey, nullTerminatedEncodedString);
return MD5.Create().ComputeHash(concatendated);
}

public static byte[] ComputeMechListMIC(byte[] exportedSessionKey, byte[] message)
{
return ComputeMechListMIC(exportedSessionKey, message, 0);
}

public static byte[] ComputeMechListMIC(byte[] exportedSessionKey, byte[] message, int seqNum)
{
// [MS-NLMP] 3.4.4.2
byte[] signKey = ComputeClientSignKey(exportedSessionKey);
byte[] sequenceNumberBytes = LittleEndianConverter.GetBytes(seqNum);
byte[] concatendated = ByteUtils.Concatenate(sequenceNumberBytes, message);
byte[] fullHash = new HMACMD5(signKey).ComputeHash(concatendated);
byte[] hash = ByteReader.ReadBytes(fullHash, 0, 8);

byte[] sealKey = ComputeClientSealKey(exportedSessionKey);
byte[] encryptedHash = RC4.Encrypt(sealKey, hash);

byte[] version = new byte[] { 0x01, 0x00, 0x00, 0x00 };
return ByteUtils.Concatenate(ByteUtils.Concatenate(version, encryptedHash), sequenceNumberBytes);
}
}
}
28 changes: 24 additions & 4 deletions SMBLibrary/Authentication/NTLM/Structures/AuthenticateMessage.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
/* Copyright (C) 2014-2020 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
/* Copyright (C) 2014-2023 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using Utilities;

namespace SMBLibrary.Authentication.NTLM
Expand All @@ -17,6 +16,7 @@ namespace SMBLibrary.Authentication.NTLM
public class AuthenticateMessage
{
public const string ValidSignature = "NTLMSSP\0";
public const int MicFieldLenght = 16;

public string Signature; // 8 bytes
public MessageTypeName MessageType;
Expand Down Expand Up @@ -59,7 +59,7 @@ public AuthenticateMessage(byte[] buffer)
}
if (HasMicField())
{
MIC = ByteReader.ReadBytes(buffer, offset, 16);
MIC = ByteReader.ReadBytes(buffer, offset, MicFieldLenght);
}
}

Expand Down Expand Up @@ -142,5 +142,25 @@ public byte[] GetBytes()

return buffer;
}

public void CalculateMIC(byte[] sessionKey, byte[] negotiateMessage, byte[] challengeMessage)
{
MIC = new byte[MicFieldLenght];
byte[] authenticateMessageBytes = GetBytes();
byte[] temp = ByteUtils.Concatenate(ByteUtils.Concatenate(negotiateMessage, challengeMessage), authenticateMessageBytes);
MIC = new HMACMD5(sessionKey).ComputeHash(temp);
}

public static int GetMicFieldOffset(byte[] authenticateMessageBytes)
{
NegotiateFlags negotiateFlags = (NegotiateFlags)LittleEndianConverter.ToUInt32(authenticateMessageBytes, 60);
int offset = 64;
if ((negotiateFlags & NegotiateFlags.Version) > 0)
{
offset += NTLMVersion.Length;
}

return offset;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,21 @@ public NTLMv2ClientChallenge(DateTime timeStamp, byte[] clientChallenge, string
}

public NTLMv2ClientChallenge(DateTime timeStamp, byte[] clientChallenge, KeyValuePairList<AVPairKey, byte[]> targetInfo)
: this(timeStamp, clientChallenge, targetInfo, null)
{
}

public NTLMv2ClientChallenge(DateTime timeStamp, byte[] clientChallenge, KeyValuePairList<AVPairKey, byte[]> targetInfo, string spn)
{
CurrentVersion = StructureVersion;
MaximumSupportedVersion = StructureVersion;
TimeStamp = timeStamp;
ClientChallenge = clientChallenge;
AVPairs = targetInfo;
if (!string.IsNullOrEmpty(spn))
{
AVPairs.Add(AVPairKey.TargetName, UnicodeEncoding.Unicode.GetBytes(spn));
}
}

public NTLMv2ClientChallenge(byte[] buffer) : this(buffer, 0)
Expand Down
Loading

0 comments on commit 50fd8ae

Please sign in to comment.