| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- package ntlmssp
- import (
- "bytes"
- "crypto/hmac"
- "crypto/md5"
- "crypto/rand"
- "encoding/binary"
- "errors"
- "fmt"
- "time"
- "imuslab.com/smb/driver/mod/smb/encoder"
- )
- const Signature = "NTLMSSP\x00"
- const (
- _ uint32 = iota
- TypeNtLmNegotiate
- TypeNtLmChallenge
- TypeNtLmAuthenticate
- )
- const (
- FlgNegUnicode uint32 = 1 << iota
- FlgNegOEM
- FlgNegRequestTarget
- FlgNegReserved10
- FlgNegSign
- FlgNegSeal
- FlgNegDatagram
- FlgNegLmKey
- FlgNegReserved9
- FlgNegNtLm
- FlgNegReserved8
- FlgNegAnonymous
- FlgNegOEMDomainSupplied
- FlgNegOEMWorkstationSupplied
- FlgNegReserved7
- FlgNegAlwaysSign
- FlgNegTargetTypeDomain
- FlgNegTargetTypeServer
- FlgNegReserved6
- FlgNegExtendedSessionSecurity // requests usage of the NTLM v2 session security.
- FlgNegIdentify
- FlgNegReserved5
- FlgNegRequestNonNtSessionKey
- FlgNegTargetInfo
- FlgNegReserved4
- FlgNegVersion
- FlgNegReserved3
- FlgNegReserved2
- FlgNegReserved1
- FlgNeg128
- FlgNegKeyExch
- FlgNeg56
- )
- const (
- MsvAvEOL uint16 = iota
- MsvAvNbComputerName
- MsvAvNbDomainName
- MsvAvDnsComputerName
- MsvAvDnsDomainName
- MsvAvDnsTreeName
- MsvAvFlags
- MsvAvTimestamp
- MsvAvSingleHost
- MsvAvTargetName
- MsvChannelBindings
- )
- type Header struct {
- Signature []byte `smb:"fixed:8"`
- MessageType uint32
- }
- //https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-NLMP/%5bMS-NLMP%5d-210625.pdf
- type Negotiate struct {
- Header
- NegotiateFlags uint32
- DomainNameLen uint16 `smb:"len:DomainName"`
- DomainNameMaxLen uint16 `smb:"len:DomainName"`
- DomainNameBufferOffset uint32 `smb:"offset:DomainName"`
- WorkstationLen uint16 `smb:"len:Workstation"`
- WorkstationMaxLen uint16 `smb:"len:Workstation"`
- WorkstationBufferOffset uint32 `smb:"offset:Workstation"`
- DomainName []byte
- Workstation []byte
- }
- type Challenge struct {
- Header
- TargetNameLen uint16 `smb:"len:TargetName"`
- TargetNameMaxLen uint16 `smb:"len:TargetName"`
- TargetNameBufferOffset uint32 `smb:"offset:TargetName"`
- NegotiateFlags uint32
- ServerChallenge uint64
- Reserved uint64
- TargetInfoLen uint16 `smb:"len:TargetInfo"`
- TargetInfoMaxLen uint16 `smb:"len:TargetInfo"`
- TargetInfoBufferOffset uint32 `smb:"offset:TargetInfo"`
- Version []byte `smb:"fixed:8"` // uint64
- TargetName []byte
- TargetInfo *AvPairSlice
- }
- type Authenticate struct {
- Header
- LmChallengeResponseLen uint16 `smb:"len:LmChallengeResponse"`
- LmChallengeResponseMaxLen uint16 `smb:"len:LmChallengeResponse"`
- LmChallengeResponseBufferOffset uint32 `smb:"offset:LmChallengeResponse"`
- NtChallengeResponseLen uint16 `smb:"len:NtChallengeResponse"`
- NtChallengeResponseMaxLen uint16 `smb:"len:NtChallengeResponse"`
- NtChallengResponseBufferOffset uint32 `smb:"offset:NtChallengeResponse"`
- DomainNameLen uint16 `smb:"len:DomainName"`
- DomainNameMaxLen uint16 `smb:"len:DomainName"`
- DomainNameBufferOffset uint32 `smb:"offset:DomainName"`
- UserNameLen uint16 `smb:"len:UserName"`
- UserNameMaxLen uint16 `smb:"len:UserName"`
- UserNameBufferOffset uint32 `smb:"offset:UserName"`
- WorkstationLen uint16 `smb:"len:Workstation"`
- WorkstationMaxLen uint16 `smb:"len:Workstation"`
- WorkstationBufferOffset uint32 `smb:"offset:Workstation"`
- EncryptedRandomSessionKeyLen uint16 `smb:"len:EncryptedRandomSessionKey"`
- EncryptedRandomSessionKeyMaxLen uint16 `smb:"len:EncryptedRandomSessionKey"`
- EncryptedRandomSessionKeyBufferOffset uint32 `smb:"offset:EncryptedRandomSessionKey"`
- NegotiateFlags uint32
- Version []byte `smb:"fixed:8"` // uint64
- DomainName []byte //`smb:"unicode"`
- UserName []byte //`smb:"unicode"`
- Workstation []byte //`smb:"unicode"`
- EncryptedRandomSessionKey []byte
- LmChallengeResponse []byte
- NtChallengeResponse []byte
- }
- type AvPair struct {
- AvID uint16
- AvLen uint16 `smb:"len:Value"`
- Value []byte
- }
- type AvPairSlice []AvPair
- func (p AvPair) Size() uint64 {
- return uint64(binary.Size(p.AvID) + binary.Size(p.AvLen) + int(p.AvLen))
- }
- var _ encoder.BinaryMarshallable = (*AvPairSlice)(nil)
- func (s *AvPairSlice) MarshalBinary(meta *encoder.Metadata) ([]byte, error) {
- var ret []byte
- w := bytes.NewBuffer(ret)
- for _, pair := range *s {
- buf, err := encoder.Marshal(pair)
- if err != nil {
- return nil, err
- }
- if err := binary.Write(w, binary.LittleEndian, buf); err != nil {
- return nil, err
- }
- }
- return w.Bytes(), nil
- }
- func (s *AvPairSlice) UnmarshalBinary(buf []byte, meta *encoder.Metadata) (interface{}, error) {
- slice := []AvPair{}
- l, ok := meta.Lens[meta.CurrField]
- if !ok {
- return nil, errors.New(fmt.Sprintf("Cannot unmarshal field '%s'. Missing length\n", meta.CurrField))
- }
- o, ok := meta.Offsets[meta.CurrField]
- if !ok {
- return nil, errors.New(fmt.Sprintf("Cannot unmarshal field '%s'. Missing offset\n", meta.CurrField))
- }
- for i := l; i > 0; {
- var avPair AvPair
- err := encoder.Unmarshal(meta.ParentBuf[o:o+i], &avPair)
- if err != nil {
- return nil, err
- }
- slice = append(slice, avPair)
- size := avPair.Size()
- o += size
- i -= size
- }
- *s = slice
- return nil, nil
- }
- func NewNegotiate(domainName, workstation string) Negotiate {
- return Negotiate{
- Header: Header{
- Signature: []byte(Signature),
- MessageType: TypeNtLmNegotiate,
- },
- NegotiateFlags: FlgNeg56 |
- FlgNeg128 |
- FlgNegTargetInfo |
- FlgNegExtendedSessionSecurity |
- FlgNegOEMDomainSupplied |
- FlgNegNtLm |
- FlgNegRequestTarget |
- FlgNegUnicode,
- DomainNameLen: 0,
- DomainNameMaxLen: 0,
- DomainNameBufferOffset: 0,
- WorkstationLen: 0,
- WorkstationMaxLen: 0,
- WorkstationBufferOffset: 0,
- DomainName: []byte(domainName),
- Workstation: []byte(workstation),
- }
- }
- // var ServerChallenge uint64 = 0
- func NewChallenge(ServerChallenge uint64) Challenge {
- return Challenge{
- Header: Header{
- Signature: []byte(Signature),
- MessageType: TypeNtLmChallenge,
- },
- TargetNameLen: 0,
- TargetNameMaxLen: 0,
- TargetNameBufferOffset: 0,
- NegotiateFlags: FlgNeg56 |
- FlgNeg128 |
- FlgNegVersion |
- FlgNegTargetInfo |
- FlgNegExtendedSessionSecurity |
- FlgNegTargetTypeServer |
- FlgNegNtLm |
- FlgNegRequestTarget |
- FlgNegUnicode,
- ServerChallenge: ServerChallenge, // atomic.AddUint64(&ServerChallenge, 1),
- Reserved: 0,
- TargetInfoLen: 0,
- TargetInfoMaxLen: 0,
- TargetInfoBufferOffset: 0,
- Version: []byte{},
- TargetName: []byte{},
- TargetInfo: new(AvPairSlice),
- }
- }
- func NewAuthenticatePass(domain, user, workstation, password string, c Challenge) Authenticate {
- // Assumes domain, user, and workstation are not unicode
- nthash := Ntowfv2(password, user, domain)
- lmhash := Lmowfv2(password, user, domain)
- return newAuthenticate(domain, user, workstation, nthash, lmhash, c)
- }
- // func NewAuthenticateHash(domain, user, workstation, hash string, c Challenge) Authenticate {
- // // Assumes domain, user, and workstation are not unicode
- // buf := make([]byte, len(hash)/2)
- // hex.Decode(buf, []byte(hash))
- // return newAuthenticate(domain, user, workstation, buf, buf, c)
- // }
- func newAuthenticate(domain, user, workstation string, nthash, lmhash []byte, c Challenge) Authenticate {
- // Assumes domain, user, and workstation are not unicode
- var timestamp []byte
- for k, av := range *c.TargetInfo {
- if av.AvID == MsvAvTimestamp {
- timestamp = (*c.TargetInfo)[k].Value
- }
- }
- if timestamp == nil {
- // Credit to https://github.com/Azure/go-ntlmssp/blob/master/unicode.go for logic
- ft := uint64(time.Now().UnixNano()) / 100
- ft += 116444736000000000 // add time between unix & windows offset
- timestamp = make([]byte, 8)
- binary.LittleEndian.PutUint64(timestamp, ft)
- }
- clientChallenge := make([]byte, 8)
- rand.Reader.Read(clientChallenge)
- serverChallenge := make([]byte, 8)
- w := bytes.NewBuffer(make([]byte, 0))
- binary.Write(w, binary.LittleEndian, c.ServerChallenge)
- serverChallenge = w.Bytes()
- w = bytes.NewBuffer(make([]byte, 0))
- for _, av := range *c.TargetInfo {
- binary.Write(w, binary.LittleEndian, av.AvID)
- binary.Write(w, binary.LittleEndian, av.AvLen)
- binary.Write(w, binary.LittleEndian, av.Value)
- }
- response := ComputeResponseNTLMv2(nthash, lmhash, clientChallenge, serverChallenge, timestamp, w.Bytes())
- h := hmac.New(md5.New, lmhash)
- h.Write(append(serverChallenge, clientChallenge...))
- lmChallengeResponse := h.Sum(nil)
- lmChallengeResponse = append(lmChallengeResponse, clientChallenge...)
- return Authenticate{
- Header: Header{
- Signature: []byte(Signature),
- MessageType: TypeNtLmAuthenticate,
- },
- DomainName: encoder.ToUnicode(domain),
- UserName: encoder.ToUnicode(user),
- Workstation: encoder.ToUnicode(workstation),
- NegotiateFlags: FlgNeg56 |
- FlgNeg128 |
- FlgNegTargetInfo |
- FlgNegExtendedSessionSecurity |
- FlgNegOEMDomainSupplied |
- FlgNegNtLm |
- FlgNegRequestTarget |
- FlgNegUnicode,
- NtChallengeResponse: response,
- LmChallengeResponse: lmChallengeResponse,
- }
- }
- type Version struct {
- ProductMajorVersion uint8
- ProductMinorVersion uint8
- ProductBuild uint16
- _ [3]byte
- NTLMRevisionCurrent uint8
- }
- // DefaultVersion returns a Version with "sensible" defaults (Windows 7)
- func DefaultVersion() Version {
- return Version{
- ProductMajorVersion: 6,
- ProductMinorVersion: 1,
- ProductBuild: 7601,
- NTLMRevisionCurrent: 15,
- }
- }
|