NTLMAuthenticationHelper.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
  2. *
  3. * You can redistribute this program and/or modify it under the terms of
  4. * the GNU Lesser Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Security.Cryptography;
  10. using SMBLibrary.Authentication.GSSAPI;
  11. using SMBLibrary.Authentication.NTLM;
  12. using Utilities;
  13. namespace SMBLibrary.Client
  14. {
  15. public class NTLMAuthenticationHelper
  16. {
  17. public static byte[] GetNegotiateMessage(byte[] securityBlob, string domainName, AuthenticationMethod authenticationMethod)
  18. {
  19. bool useGSSAPI = false;
  20. if (securityBlob.Length > 0)
  21. {
  22. SimpleProtectedNegotiationTokenInit inputToken = null;
  23. try
  24. {
  25. inputToken = SimpleProtectedNegotiationToken.ReadToken(securityBlob, 0) as SimpleProtectedNegotiationTokenInit;
  26. }
  27. catch
  28. {
  29. }
  30. if (inputToken == null || !ContainsMechanism(inputToken, GSSProvider.NTLMSSPIdentifier))
  31. {
  32. return null;
  33. }
  34. useGSSAPI = true;
  35. }
  36. NegotiateMessage negotiateMessage = new NegotiateMessage();
  37. negotiateMessage.NegotiateFlags = NegotiateFlags.UnicodeEncoding |
  38. NegotiateFlags.OEMEncoding |
  39. NegotiateFlags.Sign |
  40. NegotiateFlags.NTLMSessionSecurity |
  41. NegotiateFlags.DomainNameSupplied |
  42. NegotiateFlags.WorkstationNameSupplied |
  43. NegotiateFlags.AlwaysSign |
  44. NegotiateFlags.Version |
  45. NegotiateFlags.Use128BitEncryption |
  46. NegotiateFlags.KeyExchange |
  47. NegotiateFlags.Use56BitEncryption;
  48. if (authenticationMethod == AuthenticationMethod.NTLMv1)
  49. {
  50. negotiateMessage.NegotiateFlags |= NegotiateFlags.LanManagerSessionKey;
  51. }
  52. else
  53. {
  54. negotiateMessage.NegotiateFlags |= NegotiateFlags.ExtendedSessionSecurity;
  55. }
  56. negotiateMessage.Version = NTLMVersion.Server2003;
  57. negotiateMessage.DomainName = domainName;
  58. negotiateMessage.Workstation = Environment.MachineName;
  59. if (useGSSAPI)
  60. {
  61. SimpleProtectedNegotiationTokenInit outputToken = new SimpleProtectedNegotiationTokenInit();
  62. outputToken.MechanismTypeList = new List<byte[]>();
  63. outputToken.MechanismTypeList.Add(GSSProvider.NTLMSSPIdentifier);
  64. outputToken.MechanismToken = negotiateMessage.GetBytes();
  65. return outputToken.GetBytes();
  66. }
  67. else
  68. {
  69. return negotiateMessage.GetBytes();
  70. }
  71. }
  72. public static byte[] GetAuthenticateMessage(byte[] securityBlob, string domainName, string userName, string password, AuthenticationMethod authenticationMethod, out byte[] sessionKey)
  73. {
  74. sessionKey = null;
  75. bool useGSSAPI = false;
  76. SimpleProtectedNegotiationTokenResponse inputToken = null;
  77. try
  78. {
  79. inputToken = SimpleProtectedNegotiationToken.ReadToken(securityBlob, 0) as SimpleProtectedNegotiationTokenResponse;
  80. }
  81. catch
  82. {
  83. }
  84. ChallengeMessage challengeMessage;
  85. if (inputToken != null)
  86. {
  87. challengeMessage = GetChallengeMessage(inputToken.ResponseToken);
  88. useGSSAPI = true;
  89. }
  90. else
  91. {
  92. challengeMessage = GetChallengeMessage(securityBlob);
  93. }
  94. if (challengeMessage == null)
  95. {
  96. return null;
  97. }
  98. DateTime time = DateTime.UtcNow;
  99. byte[] clientChallenge = new byte[8];
  100. new Random().NextBytes(clientChallenge);
  101. AuthenticateMessage authenticateMessage = new AuthenticateMessage();
  102. // https://msdn.microsoft.com/en-us/library/cc236676.aspx
  103. authenticateMessage.NegotiateFlags = NegotiateFlags.Sign |
  104. NegotiateFlags.NTLMSessionSecurity |
  105. NegotiateFlags.AlwaysSign |
  106. NegotiateFlags.Version |
  107. NegotiateFlags.Use128BitEncryption |
  108. NegotiateFlags.Use56BitEncryption;
  109. if ((challengeMessage.NegotiateFlags & NegotiateFlags.UnicodeEncoding) > 0)
  110. {
  111. authenticateMessage.NegotiateFlags |= NegotiateFlags.UnicodeEncoding;
  112. }
  113. else
  114. {
  115. authenticateMessage.NegotiateFlags |= NegotiateFlags.OEMEncoding;
  116. }
  117. if ((challengeMessage.NegotiateFlags & NegotiateFlags.KeyExchange) > 0)
  118. {
  119. authenticateMessage.NegotiateFlags |= NegotiateFlags.KeyExchange;
  120. }
  121. if (authenticationMethod == AuthenticationMethod.NTLMv1)
  122. {
  123. authenticateMessage.NegotiateFlags |= NegotiateFlags.LanManagerSessionKey;
  124. }
  125. else
  126. {
  127. authenticateMessage.NegotiateFlags |= NegotiateFlags.ExtendedSessionSecurity;
  128. }
  129. authenticateMessage.UserName = userName;
  130. authenticateMessage.DomainName = domainName;
  131. authenticateMessage.WorkStation = Environment.MachineName;
  132. byte[] sessionBaseKey;
  133. byte[] keyExchangeKey;
  134. if (authenticationMethod == AuthenticationMethod.NTLMv1 || authenticationMethod == AuthenticationMethod.NTLMv1ExtendedSessionSecurity)
  135. {
  136. if (authenticationMethod == AuthenticationMethod.NTLMv1)
  137. {
  138. authenticateMessage.LmChallengeResponse = NTLMCryptography.ComputeLMv1Response(challengeMessage.ServerChallenge, password);
  139. authenticateMessage.NtChallengeResponse = NTLMCryptography.ComputeNTLMv1Response(challengeMessage.ServerChallenge, password);
  140. }
  141. else // NTLMv1ExtendedSessionSecurity
  142. {
  143. authenticateMessage.LmChallengeResponse = ByteUtils.Concatenate(clientChallenge, new byte[16]);
  144. authenticateMessage.NtChallengeResponse = NTLMCryptography.ComputeNTLMv1ExtendedSessionSecurityResponse(challengeMessage.ServerChallenge, clientChallenge, password);
  145. }
  146. // https://msdn.microsoft.com/en-us/library/cc236699.aspx
  147. sessionBaseKey = new MD4().GetByteHashFromBytes(NTLMCryptography.NTOWFv1(password));
  148. byte[] lmowf = NTLMCryptography.LMOWFv1(password);
  149. keyExchangeKey = NTLMCryptography.KXKey(sessionBaseKey, authenticateMessage.NegotiateFlags, authenticateMessage.LmChallengeResponse, challengeMessage.ServerChallenge, lmowf);
  150. }
  151. else // NTLMv2
  152. {
  153. NTLMv2ClientChallenge clientChallengeStructure = new NTLMv2ClientChallenge(time, clientChallenge, challengeMessage.TargetInfo);
  154. byte[] clientChallengeStructurePadded = clientChallengeStructure.GetBytesPadded();
  155. byte[] ntProofStr = NTLMCryptography.ComputeNTLMv2Proof(challengeMessage.ServerChallenge, clientChallengeStructurePadded, password, userName, domainName);
  156. authenticateMessage.LmChallengeResponse = NTLMCryptography.ComputeLMv2Response(challengeMessage.ServerChallenge, clientChallenge, password, userName, challengeMessage.TargetName);
  157. authenticateMessage.NtChallengeResponse = ByteUtils.Concatenate(ntProofStr, clientChallengeStructurePadded);
  158. // https://msdn.microsoft.com/en-us/library/cc236700.aspx
  159. byte[] responseKeyNT = NTLMCryptography.NTOWFv2(password, userName, domainName);
  160. sessionBaseKey = new HMACMD5(responseKeyNT).ComputeHash(ntProofStr);
  161. keyExchangeKey = sessionBaseKey;
  162. }
  163. authenticateMessage.Version = NTLMVersion.Server2003;
  164. // https://msdn.microsoft.com/en-us/library/cc236676.aspx
  165. if ((challengeMessage.NegotiateFlags & NegotiateFlags.KeyExchange) > 0)
  166. {
  167. sessionKey = new byte[8];
  168. new Random().NextBytes(sessionKey);
  169. authenticateMessage.EncryptedRandomSessionKey = RC4.Encrypt(keyExchangeKey, sessionKey);
  170. }
  171. else
  172. {
  173. sessionKey = keyExchangeKey;
  174. }
  175. if (useGSSAPI)
  176. {
  177. SimpleProtectedNegotiationTokenResponse outputToken = new SimpleProtectedNegotiationTokenResponse();
  178. outputToken.ResponseToken = authenticateMessage.GetBytes();
  179. return outputToken.GetBytes();
  180. }
  181. else
  182. {
  183. return authenticateMessage.GetBytes();
  184. }
  185. }
  186. private static ChallengeMessage GetChallengeMessage(byte[] messageBytes)
  187. {
  188. if (AuthenticationMessageUtils.IsSignatureValid(messageBytes))
  189. {
  190. MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(messageBytes);
  191. if (messageType == MessageTypeName.Challenge)
  192. {
  193. try
  194. {
  195. return new ChallengeMessage(messageBytes);
  196. }
  197. catch
  198. {
  199. return null;
  200. }
  201. }
  202. }
  203. return null;
  204. }
  205. private static bool ContainsMechanism(SimpleProtectedNegotiationTokenInit token, byte[] mechanismIdentifier)
  206. {
  207. for (int index = 0; index < token.MechanismTypeList.Count; index++)
  208. {
  209. if (ByteUtils.AreByteArraysEqual(token.MechanismTypeList[index], GSSProvider.NTLMSSPIdentifier))
  210. {
  211. return true;
  212. }
  213. }
  214. return false;
  215. }
  216. }
  217. }