diff --git a/lib/smb/smb/session.go b/lib/smb/smb/session.go index d0fe1fe..fb4f3dd 100644 --- a/lib/smb/smb/session.go +++ b/lib/smb/smb/session.go @@ -381,6 +381,7 @@ func (s *Session) send(req interface{}) (res []byte, err error) { switch string(protID) { default: return nil, errors.New("Protocol Not Implemented") + case ProtocolSmb: case ProtocolSmb2: } diff --git a/lib/smb/smb/smb.go b/lib/smb/smb/smb.go index ba924e6..7100a2b 100644 --- a/lib/smb/smb/smb.go +++ b/lib/smb/smb/smb.go @@ -33,6 +33,8 @@ const DialectSmb_3_0_2 = 0x0302 const DialectSmb_3_1_1 = 0x0311 const DialectSmb2_ALL = 0x02FF +const DialectSmb_1_0 = "\x02NT LM 0.12\x00" + const ( CommandNegotiate uint16 = iota CommandSessionSetup @@ -93,6 +95,21 @@ const ( ShareCapAsymmetric uint32 = 0x00000080 ) +type HeaderV1 struct { + ProtocolID []byte `smb:"fixed:4"` + Command uint8 + Status uint32 + Flags uint8 + Flags2 uint16 + PIDHigh uint16 + SecurityFeatures []byte `smb:"fixed:8"` + Reserved uint16 + TID uint16 + PIDLow uint16 + UID uint16 + MID uint16 +} + type Header struct { ProtocolID []byte `smb:"fixed:4"` StructureSize uint16 @@ -109,6 +126,31 @@ type Header struct { Signature []byte `smb:"fixed:16"` } +type NegotiateReqV1 struct { + HeaderV1 + WordCount uint8 + ByteCount uint16 // hardcoded to 14 + Dialects []uint8 `smb:"fixed:12"` +} + +type NegotiateResV1 struct { + HeaderV1 + WordCount uint8 + DialectIndex uint16 + SecurityMode uint8 + MaxMpxCount uint16 + MaxNumberVcs uint16 + MaxBufferSize uint32 + MaxRawSize uint32 + SessionKey uint32 + Capabilities uint32 + SystemTime uint64 + ServerTimezon uint16 + ChallengeLength uint8 + ByteCount uint16 + // variable data afterwords that we don't care about +} + type NegotiateReq struct { Header StructureSize uint16 @@ -215,6 +257,12 @@ type TreeDisconnectRes struct { Reserved uint16 } +func newHeaderV1() HeaderV1 { + return HeaderV1{ + ProtocolID: []byte(ProtocolSmb), + } +} + func newHeader() Header { return Header{ ProtocolID: []byte(ProtocolSmb2), @@ -233,6 +281,17 @@ func newHeader() Header { } } +func (s *Session) NewNegotiateReqV1() NegotiateReqV1 { + header := newHeaderV1() + header.Command = 0x72 // SMB1 Negotiate + return NegotiateReqV1{ + HeaderV1: header, + WordCount: 0, + ByteCount: 14, + Dialects: []uint8(DialectSmb_1_0), + } +} + func (s *Session) NewNegotiateReq() NegotiateReq { header := newHeader() header.Command = CommandNegotiate diff --git a/lib/smb/smb/zgrab.go b/lib/smb/smb/zgrab.go index baa1eed..a9fce8e 100644 --- a/lib/smb/smb/zgrab.go +++ b/lib/smb/smb/zgrab.go @@ -221,11 +221,57 @@ func wstring(input []byte) string { return string(utf16.Decode(u16)) } +// Temporary placeholder to detect SMB v1 by sending a simple v1 +// header with an invalid command; the response with be an error +// code, but with a v1 ProtocolID +// TODO: Parse the unmarshaled results. +func (ls *LoggedSession) LoggedNegotiateProtocolv1(setup bool) error { + s := &ls.Session + + negReq := s.NewNegotiateReqV1() + s.Debug("Sending LoggedNegotiateProtocolV1 request", nil) + buf, err := s.send(negReq) + if err != nil { + s.Debug("", err) + return err + } + + logStruct := new(SMBLog) + ls.Log = logStruct + + // s.send() will return error if buf size is < 4. + // Check the for the protocol identifier here, so that we at least + // log that this is an SMB1 server even if the full unmarshal fails. + if string(buf[0:4]) == ProtocolSmb { + ls.Log.SupportV1 = true + ls.Log.Version = &SMBVersions{Major: 1, + Minor: 0, + Revision: 0, + VerString: "SMB 1.0"} + } else { + return fmt.Errorf("Invalid v1 Protocol ID\n") + } + + negRes := NegotiateResV1{} + // TODO: Unmarshal struct depends on the CIF dialect response field. + if err := encoder.Unmarshal(buf, &negRes); err != nil { + s.Debug("Raw:\n"+hex.Dump(buf), err) + // Not returning error here, because the NegotiationResV1 is + // only valid for the extended NT LM 0.12 dialect of SMB1. + } + + // TODO: Parse capabilities and return those results + + return nil +} + // LoggedNegotiateProtocol performs the same operations as // Session.NegotiateProtocol() up to the point where user credentials would be // required, and logs the server's responses. // If setup is false, stop after reading the response to Negotiate. // If setup is true, send a SessionSetup1 request. +// +// Note: This supports SMB2 only. func (ls *LoggedSession) LoggedNegotiateProtocol(setup bool) error { s := &ls.Session negReq := s.NewNegotiateReq()