ntlmssp.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. package ntlmssp
  2. import (
  3. "bytes"
  4. "crypto/hmac"
  5. "crypto/md5"
  6. "crypto/rand"
  7. "encoding/binary"
  8. "errors"
  9. "fmt"
  10. "time"
  11. "imuslab.com/smb/driver/mod/smb/encoder"
  12. )
  13. const Signature = "NTLMSSP\x00"
  14. const (
  15. _ uint32 = iota
  16. TypeNtLmNegotiate
  17. TypeNtLmChallenge
  18. TypeNtLmAuthenticate
  19. )
  20. const (
  21. FlgNegUnicode uint32 = 1 << iota
  22. FlgNegOEM
  23. FlgNegRequestTarget
  24. FlgNegReserved10
  25. FlgNegSign
  26. FlgNegSeal
  27. FlgNegDatagram
  28. FlgNegLmKey
  29. FlgNegReserved9
  30. FlgNegNtLm
  31. FlgNegReserved8
  32. FlgNegAnonymous
  33. FlgNegOEMDomainSupplied
  34. FlgNegOEMWorkstationSupplied
  35. FlgNegReserved7
  36. FlgNegAlwaysSign
  37. FlgNegTargetTypeDomain
  38. FlgNegTargetTypeServer
  39. FlgNegReserved6
  40. FlgNegExtendedSessionSecurity // requests usage of the NTLM v2 session security.
  41. FlgNegIdentify
  42. FlgNegReserved5
  43. FlgNegRequestNonNtSessionKey
  44. FlgNegTargetInfo
  45. FlgNegReserved4
  46. FlgNegVersion
  47. FlgNegReserved3
  48. FlgNegReserved2
  49. FlgNegReserved1
  50. FlgNeg128
  51. FlgNegKeyExch
  52. FlgNeg56
  53. )
  54. const (
  55. MsvAvEOL uint16 = iota
  56. MsvAvNbComputerName
  57. MsvAvNbDomainName
  58. MsvAvDnsComputerName
  59. MsvAvDnsDomainName
  60. MsvAvDnsTreeName
  61. MsvAvFlags
  62. MsvAvTimestamp
  63. MsvAvSingleHost
  64. MsvAvTargetName
  65. MsvChannelBindings
  66. )
  67. type Header struct {
  68. Signature []byte `smb:"fixed:8"`
  69. MessageType uint32
  70. }
  71. //https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NLMP/%5bMS-NLMP%5d-210625.pdf
  72. type Negotiate struct {
  73. Header
  74. NegotiateFlags uint32
  75. DomainNameLen uint16 `smb:"len:DomainName"`
  76. DomainNameMaxLen uint16 `smb:"len:DomainName"`
  77. DomainNameBufferOffset uint32 `smb:"offset:DomainName"`
  78. WorkstationLen uint16 `smb:"len:Workstation"`
  79. WorkstationMaxLen uint16 `smb:"len:Workstation"`
  80. WorkstationBufferOffset uint32 `smb:"offset:Workstation"`
  81. DomainName []byte
  82. Workstation []byte
  83. }
  84. type Challenge struct {
  85. Header
  86. TargetNameLen uint16 `smb:"len:TargetName"`
  87. TargetNameMaxLen uint16 `smb:"len:TargetName"`
  88. TargetNameBufferOffset uint32 `smb:"offset:TargetName"`
  89. NegotiateFlags uint32
  90. ServerChallenge uint64
  91. Reserved uint64
  92. TargetInfoLen uint16 `smb:"len:TargetInfo"`
  93. TargetInfoMaxLen uint16 `smb:"len:TargetInfo"`
  94. TargetInfoBufferOffset uint32 `smb:"offset:TargetInfo"`
  95. Version []byte `smb:"fixed:8"` // uint64
  96. TargetName []byte
  97. TargetInfo *AvPairSlice
  98. }
  99. type Authenticate struct {
  100. Header
  101. LmChallengeResponseLen uint16 `smb:"len:LmChallengeResponse"`
  102. LmChallengeResponseMaxLen uint16 `smb:"len:LmChallengeResponse"`
  103. LmChallengeResponseBufferOffset uint32 `smb:"offset:LmChallengeResponse"`
  104. NtChallengeResponseLen uint16 `smb:"len:NtChallengeResponse"`
  105. NtChallengeResponseMaxLen uint16 `smb:"len:NtChallengeResponse"`
  106. NtChallengResponseBufferOffset uint32 `smb:"offset:NtChallengeResponse"`
  107. DomainNameLen uint16 `smb:"len:DomainName"`
  108. DomainNameMaxLen uint16 `smb:"len:DomainName"`
  109. DomainNameBufferOffset uint32 `smb:"offset:DomainName"`
  110. UserNameLen uint16 `smb:"len:UserName"`
  111. UserNameMaxLen uint16 `smb:"len:UserName"`
  112. UserNameBufferOffset uint32 `smb:"offset:UserName"`
  113. WorkstationLen uint16 `smb:"len:Workstation"`
  114. WorkstationMaxLen uint16 `smb:"len:Workstation"`
  115. WorkstationBufferOffset uint32 `smb:"offset:Workstation"`
  116. EncryptedRandomSessionKeyLen uint16 `smb:"len:EncryptedRandomSessionKey"`
  117. EncryptedRandomSessionKeyMaxLen uint16 `smb:"len:EncryptedRandomSessionKey"`
  118. EncryptedRandomSessionKeyBufferOffset uint32 `smb:"offset:EncryptedRandomSessionKey"`
  119. NegotiateFlags uint32
  120. Version []byte `smb:"fixed:8"` // uint64
  121. DomainName []byte //`smb:"unicode"`
  122. UserName []byte //`smb:"unicode"`
  123. Workstation []byte //`smb:"unicode"`
  124. EncryptedRandomSessionKey []byte
  125. LmChallengeResponse []byte
  126. NtChallengeResponse []byte
  127. }
  128. type AvPair struct {
  129. AvID uint16
  130. AvLen uint16 `smb:"len:Value"`
  131. Value []byte
  132. }
  133. type AvPairSlice []AvPair
  134. func (p AvPair) Size() uint64 {
  135. return uint64(binary.Size(p.AvID) + binary.Size(p.AvLen) + int(p.AvLen))
  136. }
  137. var _ encoder.BinaryMarshallable = (*AvPairSlice)(nil)
  138. func (s *AvPairSlice) MarshalBinary(meta *encoder.Metadata) ([]byte, error) {
  139. var ret []byte
  140. w := bytes.NewBuffer(ret)
  141. for _, pair := range *s {
  142. buf, err := encoder.Marshal(pair)
  143. if err != nil {
  144. return nil, err
  145. }
  146. if err := binary.Write(w, binary.LittleEndian, buf); err != nil {
  147. return nil, err
  148. }
  149. }
  150. return w.Bytes(), nil
  151. }
  152. func (s *AvPairSlice) UnmarshalBinary(buf []byte, meta *encoder.Metadata) (interface{}, error) {
  153. slice := []AvPair{}
  154. l, ok := meta.Lens[meta.CurrField]
  155. if !ok {
  156. return nil, errors.New(fmt.Sprintf("Cannot unmarshal field '%s'. Missing length\n", meta.CurrField))
  157. }
  158. o, ok := meta.Offsets[meta.CurrField]
  159. if !ok {
  160. return nil, errors.New(fmt.Sprintf("Cannot unmarshal field '%s'. Missing offset\n", meta.CurrField))
  161. }
  162. for i := l; i > 0; {
  163. var avPair AvPair
  164. err := encoder.Unmarshal(meta.ParentBuf[o:o+i], &avPair)
  165. if err != nil {
  166. return nil, err
  167. }
  168. slice = append(slice, avPair)
  169. size := avPair.Size()
  170. o += size
  171. i -= size
  172. }
  173. *s = slice
  174. return nil, nil
  175. }
  176. func NewNegotiate(domainName, workstation string) Negotiate {
  177. return Negotiate{
  178. Header: Header{
  179. Signature: []byte(Signature),
  180. MessageType: TypeNtLmNegotiate,
  181. },
  182. NegotiateFlags: FlgNeg56 |
  183. FlgNeg128 |
  184. FlgNegTargetInfo |
  185. FlgNegExtendedSessionSecurity |
  186. FlgNegOEMDomainSupplied |
  187. FlgNegNtLm |
  188. FlgNegRequestTarget |
  189. FlgNegUnicode,
  190. DomainNameLen: 0,
  191. DomainNameMaxLen: 0,
  192. DomainNameBufferOffset: 0,
  193. WorkstationLen: 0,
  194. WorkstationMaxLen: 0,
  195. WorkstationBufferOffset: 0,
  196. DomainName: []byte(domainName),
  197. Workstation: []byte(workstation),
  198. }
  199. }
  200. // var ServerChallenge uint64 = 0
  201. func NewChallenge(ServerChallenge uint64) Challenge {
  202. return Challenge{
  203. Header: Header{
  204. Signature: []byte(Signature),
  205. MessageType: TypeNtLmChallenge,
  206. },
  207. TargetNameLen: 0,
  208. TargetNameMaxLen: 0,
  209. TargetNameBufferOffset: 0,
  210. NegotiateFlags: FlgNeg56 |
  211. FlgNeg128 |
  212. FlgNegVersion |
  213. FlgNegTargetInfo |
  214. FlgNegExtendedSessionSecurity |
  215. FlgNegTargetTypeServer |
  216. FlgNegNtLm |
  217. FlgNegRequestTarget |
  218. FlgNegUnicode,
  219. ServerChallenge: ServerChallenge, // atomic.AddUint64(&ServerChallenge, 1),
  220. Reserved: 0,
  221. TargetInfoLen: 0,
  222. TargetInfoMaxLen: 0,
  223. TargetInfoBufferOffset: 0,
  224. Version: []byte{},
  225. TargetName: []byte{},
  226. TargetInfo: new(AvPairSlice),
  227. }
  228. }
  229. func NewAuthenticatePass(domain, user, workstation, password string, c Challenge) Authenticate {
  230. // Assumes domain, user, and workstation are not unicode
  231. nthash := Ntowfv2(password, user, domain)
  232. lmhash := Lmowfv2(password, user, domain)
  233. return newAuthenticate(domain, user, workstation, nthash, lmhash, c)
  234. }
  235. // func NewAuthenticateHash(domain, user, workstation, hash string, c Challenge) Authenticate {
  236. // // Assumes domain, user, and workstation are not unicode
  237. // buf := make([]byte, len(hash)/2)
  238. // hex.Decode(buf, []byte(hash))
  239. // return newAuthenticate(domain, user, workstation, buf, buf, c)
  240. // }
  241. func newAuthenticate(domain, user, workstation string, nthash, lmhash []byte, c Challenge) Authenticate {
  242. // Assumes domain, user, and workstation are not unicode
  243. var timestamp []byte
  244. for k, av := range *c.TargetInfo {
  245. if av.AvID == MsvAvTimestamp {
  246. timestamp = (*c.TargetInfo)[k].Value
  247. }
  248. }
  249. if timestamp == nil {
  250. // Credit to https://github.com/Azure/go-ntlmssp/blob/master/unicode.go for logic
  251. ft := uint64(time.Now().UnixNano()) / 100
  252. ft += 116444736000000000 // add time between unix & windows offset
  253. timestamp = make([]byte, 8)
  254. binary.LittleEndian.PutUint64(timestamp, ft)
  255. }
  256. clientChallenge := make([]byte, 8)
  257. rand.Reader.Read(clientChallenge)
  258. serverChallenge := make([]byte, 8)
  259. w := bytes.NewBuffer(make([]byte, 0))
  260. binary.Write(w, binary.LittleEndian, c.ServerChallenge)
  261. serverChallenge = w.Bytes()
  262. w = bytes.NewBuffer(make([]byte, 0))
  263. for _, av := range *c.TargetInfo {
  264. binary.Write(w, binary.LittleEndian, av.AvID)
  265. binary.Write(w, binary.LittleEndian, av.AvLen)
  266. binary.Write(w, binary.LittleEndian, av.Value)
  267. }
  268. response := ComputeResponseNTLMv2(nthash, lmhash, clientChallenge, serverChallenge, timestamp, w.Bytes())
  269. h := hmac.New(md5.New, lmhash)
  270. h.Write(append(serverChallenge, clientChallenge...))
  271. lmChallengeResponse := h.Sum(nil)
  272. lmChallengeResponse = append(lmChallengeResponse, clientChallenge...)
  273. return Authenticate{
  274. Header: Header{
  275. Signature: []byte(Signature),
  276. MessageType: TypeNtLmAuthenticate,
  277. },
  278. DomainName: encoder.ToUnicode(domain),
  279. UserName: encoder.ToUnicode(user),
  280. Workstation: encoder.ToUnicode(workstation),
  281. NegotiateFlags: FlgNeg56 |
  282. FlgNeg128 |
  283. FlgNegTargetInfo |
  284. FlgNegExtendedSessionSecurity |
  285. FlgNegOEMDomainSupplied |
  286. FlgNegNtLm |
  287. FlgNegRequestTarget |
  288. FlgNegUnicode,
  289. NtChallengeResponse: response,
  290. LmChallengeResponse: lmChallengeResponse,
  291. }
  292. }
  293. type Version struct {
  294. ProductMajorVersion uint8
  295. ProductMinorVersion uint8
  296. ProductBuild uint16
  297. _ [3]byte
  298. NTLMRevisionCurrent uint8
  299. }
  300. // DefaultVersion returns a Version with "sensible" defaults (Windows 7)
  301. func DefaultVersion() Version {
  302. return Version{
  303. ProductMajorVersion: 6,
  304. ProductMinorVersion: 1,
  305. ProductBuild: 7601,
  306. NTLMRevisionCurrent: 15,
  307. }
  308. }