SMB1 Client: Added Login implementation using NTLM authentication

This commit is contained in:
Tal Aloni 2017-09-02 18:00:07 +03:00
parent 2b7e10db07
commit b6280748c1
4 changed files with 366 additions and 11 deletions

View file

@ -0,0 +1,10 @@
namespace SMBLibrary.Client
{
public enum AuthenticationMethod
{
NTLMv1,
NTLMv1ExtendedSessionSecurity,
NTLMv2,
}
}

View file

@ -0,0 +1,236 @@
/* Copyright (C) 2017 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.Security.Cryptography;
using SMBLibrary.Authentication.GSSAPI;
using SMBLibrary.Authentication.NTLM;
using Utilities;
namespace SMBLibrary.Client
{
public class NTLMAuthenticationHelper
{
public static byte[] GetNegotiateMessage(byte[] securityBlob, string domainName, AuthenticationMethod authenticationMethod)
{
bool useGSSAPI = false;
if (securityBlob.Length > 0)
{
SimpleProtectedNegotiationTokenInit inputToken = null;
try
{
inputToken = SimpleProtectedNegotiationToken.ReadToken(securityBlob, 0) as SimpleProtectedNegotiationTokenInit;
}
catch
{
}
if (inputToken == null || !ContainsMechanism(inputToken, GSSProvider.NTLMSSPIdentifier))
{
return null;
}
useGSSAPI = true;
}
NegotiateMessage negotiateMessage = new NegotiateMessage();
negotiateMessage.NegotiateFlags = NegotiateFlags.UnicodeEncoding |
NegotiateFlags.OEMEncoding |
NegotiateFlags.Sign |
NegotiateFlags.NTLMSessionSecurity |
NegotiateFlags.DomainNameSupplied |
NegotiateFlags.WorkstationNameSupplied |
NegotiateFlags.AlwaysSign |
NegotiateFlags.Version |
NegotiateFlags.Use128BitEncryption |
NegotiateFlags.KeyExchange |
NegotiateFlags.Use56BitEncryption;
if (authenticationMethod == AuthenticationMethod.NTLMv1)
{
negotiateMessage.NegotiateFlags |= NegotiateFlags.LanManagerSessionKey;
}
else
{
negotiateMessage.NegotiateFlags |= NegotiateFlags.ExtendedSessionSecurity;
}
negotiateMessage.Version = NTLMVersion.Server2003;
negotiateMessage.DomainName = domainName;
negotiateMessage.Workstation = Environment.MachineName;
if (useGSSAPI)
{
SimpleProtectedNegotiationTokenInit outputToken = new SimpleProtectedNegotiationTokenInit();
outputToken.MechanismTypeList = new List<byte[]>();
outputToken.MechanismTypeList.Add(GSSProvider.NTLMSSPIdentifier);
outputToken.MechanismToken = negotiateMessage.GetBytes();
return outputToken.GetBytes();
}
else
{
return negotiateMessage.GetBytes();
}
}
public static byte[] GetAuthenticateMessage(byte[] securityBlob, string domainName, string userName, string password, AuthenticationMethod authenticationMethod, out byte[] sessionKey)
{
sessionKey = null;
bool useGSSAPI = false;
SimpleProtectedNegotiationTokenResponse inputToken = null;
try
{
inputToken = SimpleProtectedNegotiationToken.ReadToken(securityBlob, 0) as SimpleProtectedNegotiationTokenResponse;
}
catch
{
}
ChallengeMessage challengeMessage;
if (inputToken != null)
{
challengeMessage = GetChallengeMessage(inputToken.ResponseToken);
useGSSAPI = true;
}
else
{
challengeMessage = GetChallengeMessage(securityBlob);
}
if (challengeMessage == null)
{
return null;
}
DateTime time = DateTime.UtcNow;
byte[] clientChallenge = new byte[8];
new Random().NextBytes(clientChallenge);
AuthenticateMessage authenticateMessage = new AuthenticateMessage();
// https://msdn.microsoft.com/en-us/library/cc236676.aspx
authenticateMessage.NegotiateFlags = NegotiateFlags.Sign |
NegotiateFlags.NTLMSessionSecurity |
NegotiateFlags.AlwaysSign |
NegotiateFlags.Version |
NegotiateFlags.Use128BitEncryption |
NegotiateFlags.Use56BitEncryption;
if ((challengeMessage.NegotiateFlags & NegotiateFlags.UnicodeEncoding) > 0)
{
authenticateMessage.NegotiateFlags |= NegotiateFlags.UnicodeEncoding;
}
else
{
authenticateMessage.NegotiateFlags |= NegotiateFlags.OEMEncoding;
}
if ((challengeMessage.NegotiateFlags & NegotiateFlags.KeyExchange) > 0)
{
authenticateMessage.NegotiateFlags |= NegotiateFlags.KeyExchange;
}
if (authenticationMethod == AuthenticationMethod.NTLMv1)
{
authenticateMessage.NegotiateFlags |= NegotiateFlags.LanManagerSessionKey;
}
else
{
authenticateMessage.NegotiateFlags |= NegotiateFlags.ExtendedSessionSecurity;
}
authenticateMessage.UserName = userName;
authenticateMessage.DomainName = domainName;
authenticateMessage.WorkStation = Environment.MachineName;
byte[] sessionBaseKey;
byte[] keyExchangeKey;
if (authenticationMethod == AuthenticationMethod.NTLMv1 || authenticationMethod == AuthenticationMethod.NTLMv1ExtendedSessionSecurity)
{
if (authenticationMethod == AuthenticationMethod.NTLMv1)
{
authenticateMessage.LmChallengeResponse = NTLMCryptography.ComputeLMv1Response(challengeMessage.ServerChallenge, password);
authenticateMessage.NtChallengeResponse = NTLMCryptography.ComputeNTLMv1Response(challengeMessage.ServerChallenge, password);
}
else // NTLMv1ExtendedSessionSecurity
{
authenticateMessage.LmChallengeResponse = ByteUtils.Concatenate(clientChallenge, new byte[16]);
authenticateMessage.NtChallengeResponse = NTLMCryptography.ComputeNTLMv1ExtendedSessionSecurityResponse(challengeMessage.ServerChallenge, clientChallenge, password);
}
// https://msdn.microsoft.com/en-us/library/cc236699.aspx
sessionBaseKey = new MD4().GetByteHashFromBytes(NTLMCryptography.NTOWFv1(password));
byte[] lmowf = NTLMCryptography.LMOWFv1(password);
keyExchangeKey = NTLMCryptography.KXKey(sessionBaseKey, authenticateMessage.NegotiateFlags, authenticateMessage.LmChallengeResponse, challengeMessage.ServerChallenge, lmowf);
}
else // NTLMv2
{
NTLMv2ClientChallenge clientChallengeStructure = new NTLMv2ClientChallenge(time, clientChallenge, challengeMessage.TargetInfo);
byte[] clientChallengeStructurePadded = clientChallengeStructure.GetBytesPadded();
byte[] ntProofStr = NTLMCryptography.ComputeNTLMv2Proof(challengeMessage.ServerChallenge, clientChallengeStructurePadded, password, userName, domainName);
authenticateMessage.LmChallengeResponse = NTLMCryptography.ComputeLMv2Response(challengeMessage.ServerChallenge, clientChallenge, password, userName, challengeMessage.TargetName);
authenticateMessage.NtChallengeResponse = ByteUtils.Concatenate(ntProofStr, clientChallengeStructurePadded);
// https://msdn.microsoft.com/en-us/library/cc236700.aspx
byte[] responseKeyNT = NTLMCryptography.NTOWFv2(password, userName, domainName);
sessionBaseKey = new HMACMD5(responseKeyNT).ComputeHash(ntProofStr);
keyExchangeKey = sessionBaseKey;
}
authenticateMessage.Version = NTLMVersion.Server2003;
// https://msdn.microsoft.com/en-us/library/cc236676.aspx
if ((challengeMessage.NegotiateFlags & NegotiateFlags.KeyExchange) > 0)
{
sessionKey = new byte[8];
new Random().NextBytes(sessionKey);
authenticateMessage.EncryptedRandomSessionKey = RC4.Encrypt(keyExchangeKey, sessionKey);
}
else
{
sessionKey = keyExchangeKey;
}
if (useGSSAPI)
{
SimpleProtectedNegotiationTokenResponse outputToken = new SimpleProtectedNegotiationTokenResponse();
outputToken.ResponseToken = authenticateMessage.GetBytes();
return outputToken.GetBytes();
}
else
{
return authenticateMessage.GetBytes();
}
}
private static ChallengeMessage GetChallengeMessage(byte[] messageBytes)
{
if (AuthenticationMessageUtils.IsSignatureValid(messageBytes))
{
MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(messageBytes);
if (messageType == MessageTypeName.Challenge)
{
try
{
return new ChallengeMessage(messageBytes);
}
catch
{
return null;
}
}
}
return null;
}
private static bool ContainsMechanism(SimpleProtectedNegotiationTokenInit token, byte[] mechanismIdentifier)
{
for (int index = 0; index < token.MechanismTypeList.Count; index++)
{
if (ByteUtils.AreByteArraysEqual(token.MechanismTypeList[index], GSSProvider.NTLMSSPIdentifier))
{
return true;
}
}
return false;
}
}
}

View file

@ -10,6 +10,7 @@ using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using SMBLibrary.Authentication.NTLM;
using SMBLibrary.NetBios;
using SMBLibrary.SMB1;
using Utilities;
@ -21,25 +22,40 @@ namespace SMBLibrary.Client
public const int NetBiosOverTCPPort = 139;
public const int DirectTCPPort = 445;
public const string NTLanManagerDialect = "NT LM 0.12";
public const int MaxBufferSize = 65535; // Valid range: 512 - 65535
public const int MaxMpxCount = 1;
private SMBTransportType m_transport;
private bool m_isConnected;
private Socket m_clientSocket;
private IAsyncResult m_currentAsyncResult;
private bool m_forceExtendedSecurity;
private object m_incomingQueueLock = new object();
private List<SMB1Message> m_incomingQueue = new List<SMB1Message>();
private EventWaitHandle m_incomingQueueEventHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
private ushort m_userID;
private byte[] m_serverChallenge;
private byte[] m_securityBlob;
private byte[] m_sessionKey;
public SMB1Client()
{
}
public bool Connect(IPAddress serverAddress, SMBTransportType transport)
{
return Connect(serverAddress, transport, true);
}
public bool Connect(IPAddress serverAddress, SMBTransportType transport, bool forceExtendedSecurity)
{
m_transport = transport;
if (!m_isConnected)
{
m_forceExtendedSecurity = forceExtendedSecurity;
m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
int port;
if (transport == SMBTransportType.DirectTCPTransport)
@ -62,7 +78,7 @@ namespace SMBLibrary.Client
ConnectionState state = new ConnectionState();
NBTConnectionReceiveBuffer buffer = state.ReceiveBuffer;
m_currentAsyncResult = m_clientSocket.BeginReceive(buffer.Buffer, buffer.WriteOffset, buffer.AvailableLength, SocketFlags.None, new AsyncCallback(OnServerSocketReceive), state);
bool supportsCIFS = NegotiateNTLanManagerDialect();
bool supportsCIFS = NegotiateNTLanManagerDialect(m_forceExtendedSecurity);
if (!supportsCIFS)
{
m_clientSocket.Close();
@ -75,6 +91,91 @@ namespace SMBLibrary.Client
return m_isConnected;
}
public bool Login(string domainName, string userName, string password)
{
return Login(domainName, userName, password, AuthenticationMethod.NTLMv2);
}
public bool Login(string domainName, string userName, string password, AuthenticationMethod authenticationMethod)
{
if (!m_isConnected)
{
return false;
}
if (m_serverChallenge != null)
{
SessionSetupAndXRequest request = new SessionSetupAndXRequest();
request.MaxBufferSize = MaxBufferSize;
request.MaxMpxCount = MaxMpxCount;
request.Capabilities = ServerCapabilities.Unicode | ServerCapabilities.NTStatusCode;
request.AccountName = userName;
request.PrimaryDomain = domainName;
byte[] clientChallenge = new byte[8];
new Random().NextBytes(clientChallenge);
if (authenticationMethod == AuthenticationMethod.NTLMv1)
{
request.OEMPassword = NTLMCryptography.ComputeLMv1Response(m_serverChallenge, password);
request.UnicodePassword = NTLMCryptography.ComputeNTLMv1Response(m_serverChallenge, password);
}
else if (authenticationMethod == AuthenticationMethod.NTLMv1ExtendedSessionSecurity)
{
// [MS-CIFS] CIFS does not support Extended Session Security because there is no mechanism in CIFS to negotiate Extended Session Security
throw new ArgumentException("SMB Extended Security must be negotiated in order for NTLMv1 Extended Session Security to be used");
}
else // NTLMv2
{
// Note: NTLMv2 over non-extended security session setup is not supported under Windows Vista and later which will return STATUS_INVALID_PARAMETER.
// https://msdn.microsoft.com/en-us/library/ee441701.aspx
// https://msdn.microsoft.com/en-us/library/cc236700.aspx
request.OEMPassword = NTLMCryptography.ComputeLMv2Response(m_serverChallenge, clientChallenge, password, userName, domainName);
NTLMv2ClientChallenge clientChallengeStructure = new NTLMv2ClientChallenge(DateTime.UtcNow, clientChallenge, AVPairUtils.GetAVPairSequence(domainName, Environment.MachineName));
byte[] temp = clientChallengeStructure.GetBytesPadded();
byte[] proofStr = NTLMCryptography.ComputeNTLMv2Proof(m_serverChallenge, temp, password, userName, domainName);
request.UnicodePassword = ByteUtils.Concatenate(proofStr, temp);
}
TrySendMessage(m_clientSocket, request);
SMB1Message reply = WaitForMessage(CommandName.SMB_COM_SESSION_SETUP_ANDX);
if (reply != null && reply.Header.Status == NTStatus.STATUS_SUCCESS)
{
return true;
}
return false;
}
else if (m_securityBlob != null)
{
SessionSetupAndXRequestExtended request = new SessionSetupAndXRequestExtended();
request.MaxBufferSize = MaxBufferSize;
request.MaxMpxCount = MaxMpxCount;
request.Capabilities = ServerCapabilities.Unicode | ServerCapabilities.NTStatusCode;
request.SecurityBlob = NTLMAuthenticationHelper.GetNegotiateMessage(m_securityBlob, domainName, authenticationMethod);
TrySendMessage(m_clientSocket, request);
SMB1Message reply = WaitForMessage(CommandName.SMB_COM_SESSION_SETUP_ANDX);
if (reply != null && reply.Header.Status == NTStatus.STATUS_MORE_PROCESSING_REQUIRED && reply.Commands[0] is SessionSetupAndXResponseExtended)
{
SessionSetupAndXResponseExtended response = (SessionSetupAndXResponseExtended)reply.Commands[0];
m_userID = reply.Header.UID;
request = new SessionSetupAndXRequestExtended();
request.MaxBufferSize = MaxBufferSize;
request.MaxMpxCount = MaxMpxCount;
request.Capabilities = ServerCapabilities.Unicode | ServerCapabilities.NTStatusCode | ServerCapabilities.ExtendedSecurity;
request.SecurityBlob = NTLMAuthenticationHelper.GetAuthenticateMessage(response.SecurityBlob, domainName, userName, password, authenticationMethod, out m_sessionKey);
TrySendMessage(m_clientSocket, request);
reply = WaitForMessage(CommandName.SMB_COM_SESSION_SETUP_ANDX);
if (reply != null && reply.Header.Status == NTStatus.STATUS_SUCCESS)
{
return true;
}
}
}
return false;
}
public void Disconnect()
{
if (m_isConnected)
@ -84,7 +185,7 @@ namespace SMBLibrary.Client
}
}
private bool NegotiateNTLanManagerDialect()
private bool NegotiateNTLanManagerDialect(bool forceExtendedSecurity)
{
if (m_transport == SMBTransportType.NetBiosOverTCP)
{
@ -95,6 +196,7 @@ namespace SMBLibrary.Client
}
NegotiateRequest request = new NegotiateRequest();
request.Dialects.Add(NTLanManagerDialect);
TrySendMessage(m_clientSocket, request);
SMB1Message reply = WaitForMessage(CommandName.SMB_COM_NEGOTIATE);
if (reply == null)
@ -102,14 +204,16 @@ namespace SMBLibrary.Client
return false;
}
if (reply.Commands[0] is NegotiateResponse)
if (reply.Commands[0] is NegotiateResponse && !forceExtendedSecurity)
{
NegotiateResponse response = (NegotiateResponse)reply.Commands[0];
m_serverChallenge = response.Challenge;
return true;
}
else if (reply.Commands[0] is NegotiateResponseExtended)
{
NegotiateResponseExtended response = (NegotiateResponseExtended)reply.Commands[0];
m_securityBlob = response.SecurityBlob;
return true;
}
else
@ -200,7 +304,6 @@ namespace SMBLibrary.Client
}
}
private void ProcessPacket(SessionPacket packet, ConnectionState state)
{
if (packet is SessionKeepAlivePacket && m_transport == SMBTransportType.NetBiosOverTCP)
@ -263,30 +366,34 @@ namespace SMBLibrary.Client
return null;
}
public void Log(string message)
private void Log(string message)
{
System.Diagnostics.Debug.Print(message);
}
public static void TrySendMessage(Socket serverSocket, SMB1Command request)
public void TrySendMessage(Socket socket, SMB1Command request)
{
SMB1Message message = new SMB1Message();
message.Header.UnicodeFlag = true;
message.Header.ExtendedSecurityFlag = m_forceExtendedSecurity;
message.Header.Flags2 |= HeaderFlags2.LongNamesAllowed | HeaderFlags2.LongNameUsed | HeaderFlags2.NTStatusCode;
message.Header.UID = m_userID;
message.Commands.Add(request);
TrySendMessage(serverSocket, message);
TrySendMessage(socket, message);
}
public static void TrySendMessage(Socket serverSocket, SMB1Message message)
public static void TrySendMessage(Socket socket, SMB1Message message)
{
SessionMessagePacket packet = new SessionMessagePacket();
packet.Trailer = message.GetBytes();
TrySendPacket(serverSocket, packet);
TrySendPacket(socket, packet);
}
public static void TrySendPacket(Socket serverSocket, SessionPacket response)
public static void TrySendPacket(Socket socket, SessionPacket response)
{
try
{
serverSocket.Send(response.GetBytes());
socket.Send(response.GetBytes());
}
catch (SocketException)
{

View file

@ -55,6 +55,8 @@
<Compile Include="Authentication\NTLM\Structures\NTLMv2ClientChallenge.cs" />
<Compile Include="Authentication\NTLM\Structures\NTLMVersion.cs" />
<Compile Include="Client\ConnectionState.cs" />
<Compile Include="Client\Enums\AuthenticationMethod.cs" />
<Compile Include="Client\NTLMAuthenticationHelper.cs" />
<Compile Include="Client\SMB1Client.cs" />
<Compile Include="Enums\NTStatus.cs" />
<Compile Include="Enums\SMBTransportType.cs" />