private bool NegotiateDialect() { NegotiateRequest request = new NegotiateRequest(); request.SecurityMode = SecurityMode.SigningEnabled; request.ClientGuid = Guid.NewGuid(); request.ClientStartTime = DateTime.Now; request.Dialects.Add(SMB2Dialect.SMB202); request.Dialects.Add(SMB2Dialect.SMB210); TrySendCommand(request); NegotiateResponse response = WaitForCommand(SMB2CommandName.Negotiate) as NegotiateResponse; if (response != null && response.Header.Status == NTStatus.STATUS_SUCCESS) { m_dialect = response.DialectRevision; m_signingRequired = (response.SecurityMode & SecurityMode.SigningRequired) > 0; m_maxTransactSize = Math.Min(response.MaxTransactSize, ClientMaxTransactSize); m_maxReadSize = Math.Min(response.MaxReadSize, ClientMaxReadSize); m_maxWriteSize = Math.Min(response.MaxWriteSize, ClientMaxWriteSize); m_securityBlob = response.SecurityBuffer; return(true); } return(false); }
private static void EnqueueResponseChain(ConnectionState state, List <SMB2Command> responseChain) { byte[] signingKey = null; if (state is SMB2ConnectionState) { // Note: multiple sessions MAY be multiplexed on the same connection, so theoretically // we could have compounding unrelated requests from different sessions. // In practice however this is not a real problem. ulong sessionID = responseChain[0].Header.SessionID; if (sessionID != 0) { SMB2Session session = ((SMB2ConnectionState)state).GetSession(sessionID); if (session != null) { signingKey = session.SigningKey; } } } SessionMessagePacket packet = new SessionMessagePacket(); SMB2Dialect smb2Dialect = (signingKey != null) ? ToSMB2Dialect(state.Dialect) : SMB2Dialect.SMB2xx; packet.Trailer = SMB2Command.GetCommandChainBytes(responseChain, signingKey, smb2Dialect); state.SendQueue.Enqueue(packet); state.LogToServer(Severity.Verbose, "SMB2 response chain queued: Response count: {0}, First response: {1}, Packet length: {2}", responseChain.Count, responseChain[0].CommandName.ToString(), packet.Length); }
public static byte[] CalculateSignature(byte[] signingKey, SMB2Dialect dialect, byte[] buffer, int offset, int paddedLength) { if (dialect != SMB2Dialect.SMB202 && dialect != SMB2Dialect.SMB210) { return(AesCmac.CalculateAesCmac(signingKey, buffer, offset, paddedLength)); } using HMACSHA256 sha256 = new HMACSHA256(signingKey); return(sha256.ComputeHash(buffer, offset, paddedLength)); }
public static byte[] CalculateSignature(byte[] signingKey, SMB2Dialect dialect, byte[] buffer, int offset, int paddedLength) { if (dialect == SMB2Dialect.SMB202 || dialect == SMB2Dialect.SMB210) { return(new HMACSHA256(signingKey).ComputeHash(buffer, offset, paddedLength)); } else { return(AesCmac.CalculateAesCmac(signingKey, buffer, offset, paddedLength)); } }
public static byte[] GenerateClientDecryptionKey(byte[] sessionKey, SMB2Dialect dialect, byte[] preauthIntegrityHashValue) { if (dialect == SMB2Dialect.SMB311 && preauthIntegrityHashValue == null) { throw new ArgumentNullException(nameof(preauthIntegrityHashValue)); } string labelString = (dialect == SMB2Dialect.SMB311) ? "SMBS2CCipherKey" : "SMB2AESCCM"; byte[] label = GetNullTerminatedAnsiString(labelString); byte[] context = (dialect == SMB2Dialect.SMB311) ? preauthIntegrityHashValue : GetNullTerminatedAnsiString("ServerOut"); using HMACSHA256 hmac = new HMACSHA256(sessionKey); return(SP800_1008.DeriveKey(hmac, label, context, 128)); }
public override void WriteCommandBytes(byte[] buffer, int offset) { LittleEndianWriter.WriteUInt16(buffer, offset + 0, StructureSize); LittleEndianWriter.WriteUInt16(buffer, offset + 2, (ushort)Dialects.Count); LittleEndianWriter.WriteUInt16(buffer, offset + 4, (ushort)SecurityMode); LittleEndianWriter.WriteUInt16(buffer, offset + 6, Reserved); LittleEndianWriter.WriteUInt32(buffer, offset + 8, (uint)Capabilities); LittleEndianWriter.WriteGuid(buffer, offset + 12, ClientGuid); LittleEndianWriter.WriteInt64(buffer, offset + 28, ClientStartTime.ToFileTimeUtc()); for (int index = 0; index < Dialects.Count; index++) { SMB2Dialect dialect = Dialects[index]; LittleEndianWriter.WriteUInt16(buffer, offset + 36 + index * 2, (ushort)dialect); } }
public NegotiateRequest(byte[] buffer, int offset) : base(buffer, offset) { StructureSize = LittleEndianConverter.ToUInt16(buffer, offset + SMB2Header.Length + 0); DialectCount = LittleEndianConverter.ToUInt16(buffer, offset + SMB2Header.Length + 2); SecurityMode = (SecurityMode)LittleEndianConverter.ToUInt16(buffer, offset + SMB2Header.Length + 4); Reserved = LittleEndianConverter.ToUInt16(buffer, offset + SMB2Header.Length + 6); Capabilities = (ServerCapabilities)LittleEndianConverter.ToUInt32(buffer, offset + SMB2Header.Length + 8); ClientGuid = LittleEndianConverter.ToGuid(buffer, offset + SMB2Header.Length + 12); ClientStartTime = DateTime.FromFileTimeUtc(LittleEndianConverter.ToInt64(buffer, offset + SMB2Header.Length + 28)); for (int index = 0; index < DialectCount; index++) { SMB2Dialect dialect = (SMB2Dialect)LittleEndianConverter.ToUInt16(buffer, offset + SMB2Header.Length + 36 + index * 2); Dialects.Add(dialect); } }
public NegotiateResponse(byte[] buffer, int offset) : base(buffer, offset) { StructureSize = LittleEndianConverter.ToUInt16(buffer, offset + SMB2Header.Length + 0); SecurityMode = (SecurityMode)LittleEndianConverter.ToUInt16(buffer, offset + SMB2Header.Length + 2); DialectRevision = (SMB2Dialect)LittleEndianConverter.ToUInt16(buffer, offset + SMB2Header.Length + 4); NegotiateContextCount = LittleEndianConverter.ToUInt16(buffer, offset + SMB2Header.Length + 6); ServerGuid = LittleEndianConverter.ToGuid(buffer, offset + SMB2Header.Length + 8); Capabilities = (Capabilities)LittleEndianConverter.ToUInt32(buffer, offset + SMB2Header.Length + 24); MaxTransactSize = LittleEndianConverter.ToUInt32(buffer, offset + SMB2Header.Length + 28); MaxReadSize = LittleEndianConverter.ToUInt32(buffer, offset + SMB2Header.Length + 32); MaxWriteSize = LittleEndianConverter.ToUInt32(buffer, offset + SMB2Header.Length + 36); SystemTime = DateTime.FromFileTimeUtc(LittleEndianConverter.ToInt64(buffer, offset + SMB2Header.Length + 40)); ServerStartTime = DateTime.FromFileTimeUtc(LittleEndianConverter.ToInt64(buffer, offset + SMB2Header.Length + 48)); SecurityBufferOffset = LittleEndianConverter.ToUInt16(buffer, offset + SMB2Header.Length + 56); SecurityBufferLength = LittleEndianConverter.ToUInt16(buffer, offset + SMB2Header.Length + 58); NegotiateContextOffset = LittleEndianConverter.ToUInt32(buffer, offset + SMB2Header.Length + 60); SecurityBuffer = ByteReader.ReadBytes(buffer, offset + SecurityBufferOffset, SecurityBufferLength); NegotiateContextList = NegotiateContext.ReadNegotiateContextList(buffer, (int)NegotiateContextOffset, NegotiateContextCount); }
public static byte[] GenerateSigningKey(byte[] sessionKey, SMB2Dialect dialect, byte[]?preauthIntegrityHashValue) { if (dialect == SMB2Dialect.SMB202 || dialect == SMB2Dialect.SMB210) { return(sessionKey); } if (dialect == SMB2Dialect.SMB311 && preauthIntegrityHashValue == null) { throw new ArgumentNullException(nameof(preauthIntegrityHashValue)); } string labelString = (dialect == SMB2Dialect.SMB311) ? "SMBSigningKey" : "SMB2AESCMAC"; byte[] label = GetNullTerminatedAnsiString(labelString); byte[] context = (dialect == SMB2Dialect.SMB311) ? preauthIntegrityHashValue ! : GetNullTerminatedAnsiString("SmbSign"); using HMACSHA256 hmac = new HMACSHA256(sessionKey); return(SP800_1008.DeriveKey(hmac, label, context, 128)); }
internal static SMB2Command GetSessionSetupResponse(SessionSetupRequest request, GSSProvider securityProvider, SMB2ConnectionState state) { // [MS-SMB2] Windows [..] will also accept raw Kerberos messages and implicit NTLM messages as part of GSS authentication. SessionSetupResponse response = new SessionSetupResponse(); byte[] outputToken; NTStatus status = securityProvider.AcceptSecurityContext(ref state.AuthenticationContext, request.SecurityBuffer, out outputToken); if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.SEC_I_CONTINUE_NEEDED) { string userName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.UserName) as string; string domainName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.DomainName) as string; string machineName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.MachineName) as string; string osVersion = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.OSVersion) as string; state.LogToServer(Severity.Information, "Session Setup: User '{0}' failed authentication (Domain: '{1}', Workstation: '{2}', OS version: '{3}'), NTStatus: {4}", userName, domainName, machineName, osVersion, status); return(new ErrorResponse(request.CommandName, status)); } if (outputToken != null) { response.SecurityBuffer = outputToken; } // According to [MS-SMB2] 3.3.5.5.3, response.Header.SessionID must be allocated if the server returns STATUS_MORE_PROCESSING_REQUIRED if (request.Header.SessionID == 0) { ulong?sessionID = state.AllocateSessionID(); if (!sessionID.HasValue) { return(new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_SESSIONS)); } response.Header.SessionID = sessionID.Value; } if (status == NTStatus.SEC_I_CONTINUE_NEEDED) { response.Header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED; } else // status == STATUS_SUCCESS { string userName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.UserName) as string; string domainName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.DomainName) as string; string machineName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.MachineName) as string; string osVersion = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.OSVersion) as string; byte[] sessionKey = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.SessionKey) as byte[]; object accessToken = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.AccessToken); bool? isGuest = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.IsGuest) as bool?; if (!isGuest.HasValue || !isGuest.Value) { state.LogToServer(Severity.Information, "Session Setup: User '{0}' authenticated successfully (Domain: '{1}', Workstation: '{2}', OS version: '{3}').", userName, domainName, machineName, osVersion); bool signingRequired = (request.SecurityMode & SecurityMode.SigningRequired) > 0; SMB2Dialect smb2Dialect = SMBServer.ToSMB2Dialect(state.Dialect); byte[] signingKey = SMB2Cryptography.GenerateSigningKey(sessionKey, smb2Dialect, null); state.CreateSession(request.Header.SessionID, userName, machineName, sessionKey, accessToken, signingRequired, signingKey); } else { state.LogToServer(Severity.Information, "Session Setup: User '{0}' failed authentication (Domain: '{1}', Workstation: '{2}', OS version: '{3}'), logged in as guest.", userName, domainName, machineName, osVersion); state.CreateSession(request.Header.SessionID, "Guest", machineName, sessionKey, accessToken, false, null); response.SessionFlags = SessionFlags.IsGuest; } } return(response); }
/// <param name="signingKey"> /// Message will be signed using this key if (not null and) SMB2_FLAGS_SIGNED is set. /// </param> /// <param name="dialect"> /// Used for signature calculation when applicable. /// </param> public static byte[] GetCommandChainBytes(List <SMB2Command> commands, byte[] signingKey, SMB2Dialect dialect) { int totalLength = 0; for (int index = 0; index < commands.Count; index++) { // Any subsequent SMB2 header MUST be 8-byte aligned int length = commands[index].Length; if (index < commands.Count - 1) { int paddedLength = (int)Math.Ceiling((double)length / 8) * 8; totalLength += paddedLength; } else { totalLength += length; } } byte[] buffer = new byte[totalLength]; int offset = 0; for (int index = 0; index < commands.Count; index++) { SMB2Command command = commands[index]; int commandLength = command.Length; int paddedLength; if (index < commands.Count - 1) { paddedLength = (int)Math.Ceiling((double)commandLength / 8) * 8; command.Header.NextCommand = (uint)paddedLength; } else { paddedLength = commandLength; } command.WriteBytes(buffer, offset); if (command.Header.IsSigned && signingKey != null) { // [MS-SMB2] Any padding at the end of the message MUST be used in the hash computation. byte[] signature = SMB2Cryptography.CalculateSignature(signingKey, dialect, buffer, offset, paddedLength); // [MS-SMB2] The first 16 bytes of the hash MUST be copied into the 16-byte signature field of the SMB2 Header. ByteWriter.WriteBytes(buffer, offset + SMB2Header.SignatureOffset, signature, 16); } offset += paddedLength; } return(buffer); }