Skip to content

Commit

Permalink
Add NativeOS, NTLM, and GroupName to SMBv1 results (#286)
Browse files Browse the repository at this point in the history
* add smbv1 session setup scan

* remove unused values

* rename os_name to native_os to match smb documentation

* remove superfluous comment

* update zschema to include new SMB fields

* improve clarity on bounds checking for SMBv1 requests
  • Loading branch information
Elliot Cubit authored Mar 15, 2021
1 parent d3d2a37 commit 17a5257
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 5 deletions.
18 changes: 18 additions & 0 deletions lib/smb/smb/encoder/unicode.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,21 @@ func ToUnicode(s string) []byte {
binary.Write(&b, binary.LittleEndian, &uints)
return b.Bytes()
}

func ToSmbString(s string) []byte {
res := ToUnicode(s)
res = append(res, 0x0, 0x0)
return res
}

func FromSmbString(d []byte) (string, error) {
res, err := FromUnicode(d)
if err != nil {
return "", err
}
if len(res) == 0 {
return "", nil
}
// Trim null terminator
return res[:len(res)-1], nil
}
52 changes: 49 additions & 3 deletions lib/smb/smb/smb.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ const (
ShareCapAsymmetric uint32 = 0x00000080
)

const (
SmbHeaderV1Length = 32
)

type HeaderV1 struct {
ProtocolID []byte `smb:"fixed:4"`
Command uint8
Expand Down Expand Up @@ -133,6 +137,24 @@ type NegotiateReqV1 struct {
Dialects []uint8 `smb:"fixed:12"`
}

type SessionSetupV1Req struct {
HeaderV1
WordCount uint8
AndCommand uint8
Reserved1 uint8
AndOffset uint16
MaxBuffer uint16
MaxMPXCount uint16
VCNumber uint16
SessionKey uint32
OEMPasswordLength uint16
UnicodePasswordLength uint16
Reserved2 uint32
Capabilities uint32
ByteCount uint16
VarData []byte
}

type NegotiateResV1 struct {
HeaderV1
WordCount uint8
Expand All @@ -147,8 +169,8 @@ type NegotiateResV1 struct {
SystemTime uint64
ServerTimezon uint16
ChallengeLength uint8
ByteCount uint16
// variable data afterwords that we don't care about
ByteCount uint16 `smb:"len:VarData"`
VarData []byte
}

type NegotiateReq struct {
Expand Down Expand Up @@ -260,6 +282,17 @@ type TreeDisconnectRes struct {
func newHeaderV1() HeaderV1 {
return HeaderV1{
ProtocolID: []byte(ProtocolSmb),
Status: 0,
Flags: 0x18,
Flags2: 0xc843,
PIDHigh: 0,
// These bytes must be explicit here
SecurityFeatures: []byte{0, 0, 0, 0, 0, 0, 0, 0},
Reserved: 0,
TID: 0xffff,
PIDLow: 0xfeff,
UID: 0,
MID: 0,
}
}

Expand Down Expand Up @@ -287,11 +320,24 @@ func (s *Session) NewNegotiateReqV1() NegotiateReqV1 {
return NegotiateReqV1{
HeaderV1: header,
WordCount: 0,
ByteCount: 14,
ByteCount: 12,
Dialects: []uint8(DialectSmb_1_0),
}
}

func (s *Session) NewSessionSetupV1Req() SessionSetupV1Req {
header := newHeaderV1()
header.Command = 0x73 // SMB1 Session Setup
return SessionSetupV1Req{
HeaderV1: header,
WordCount: 0xd,
AndCommand: 0xff,
MaxBuffer: 0x1111,
MaxMPXCount: 0xa,
VarData: []byte{},
}
}

func (s *Session) NewNegotiateReq() NegotiateReq {
header := newHeader()
header.Command = CommandNegotiate
Expand Down
69 changes: 68 additions & 1 deletion lib/smb/smb/zgrab.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"net"
"strings"

"unicode/utf16"

Expand Down Expand Up @@ -130,6 +131,12 @@ type SMBLog struct {

Version *SMBVersions `json:"smb_version,omitempty"`

// If present, represent the NativeOS, NTLM, and GroupName fields of SMBv1 Session Setup Negotiation
// An empty string for these values indicate the data was not available
NativeOs string `json:"native_os"`
NTLM string `json:"ntlm"`
GroupName string `json:"group_name"`

// While the NegotiationLogs and SessionSetupLog each have their own
// Capabilties field, we are ignoring the SessionsSetupLog capability
// when decoding, and only representing the server capabilties based
Expand Down Expand Up @@ -208,7 +215,10 @@ func GetSMBLog(conn net.Conn, session bool, v1 bool, debug bool) (smbLog *SMBLog
}

if v1 {
err = s.LoggedNegotiateProtocolv1(session)
err := s.LoggedNegotiateProtocolv1(session)
if err == nil && session {
s.LoggedSessionSetupV1()
}
} else {
err = s.LoggedNegotiateProtocol(session)
}
Expand Down Expand Up @@ -269,6 +279,63 @@ func (ls *LoggedSession) LoggedNegotiateProtocolv1(setup bool) error {
return nil
}

func (ls *LoggedSession) LoggedSessionSetupV1() (err error) {
s := &ls.Session
var buf []byte

req := s.NewSessionSetupV1Req()
s.Debug("Sending LoggedSessionSetupV1 Request", nil)
buf, err = s.send(req)
if err != nil {
s.Debug("No response to SMBv1 cleartext SessionSetup", nil)
return nil
}

// Safely trim down everything except the payload
if len(buf) < SmbHeaderV1Length {
return nil
}
// When using unicode, a padding byte will exist after the header
paddingLength := int((buf[11] >> 7) & 1)
// Skip header
buf = buf[SmbHeaderV1Length:]
// The byte after the header holds the number of words remaining in uint16s
// words + 3 bytes for wordlength & bytecount + potential unicode padding
claimedRemainingSize := int(buf[0])*2 + 3 + paddingLength
if len(buf) < claimedRemainingSize {
return nil
}
buf = buf[claimedRemainingSize:]

var decoded string
if paddingLength == 1 {
// Unicode string
decoded, err = encoder.FromSmbString(buf)
if err != nil {
s.Debug("Error encountered while decoding SMB string", err)
return nil
}
} else {
// ASCII string
decoded = string(buf)
}

// We expect 3 null-terminated strings in this order;
// These fields are technically all optional, but guaranteed to be in this order
fields := strings.Split(decoded, "\000")
if len(fields) > 0 {
ls.Log.NativeOs = fields[0]
}
if len(fields) > 1 {
ls.Log.NTLM = fields[1]
}
if len(fields) > 2 {
ls.Log.GroupName = fields[2]
}

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.
Expand Down
5 changes: 4 additions & 1 deletion zgrab2_schemas/zgrab2/smb.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ def extended(base, new):
"revision": Unsigned8BitInteger(doc="Protocol Revision"),
"version_string": String(doc="Full SMB Version String"),
}),
"native_os": String(doc="Operating system claimed by server"),
"ntlm": String(doc="Native LAN Manager"),
"group_name": String(doc="Group name"),
"smb_capabilities": SubRecord({
"smb_dfs_support": Boolean(doc="Server supports Distributed File System"),
"smb_leasing_support": Boolean(doc="Server supports Leasing"),
Expand All @@ -62,7 +65,7 @@ def extended(base, new):
"smb_encryption_support": Boolean(doc="Server supports encryption"),
}, doc="Capabilities flags for the connection. See [MS-SMB2] Sect. 2.2.4."),
'negotiation_log': negotiate_log,
'has_ntlm': Boolean(),
'has_ntlm': Boolean(doc="Server supports the NTLM authentication method"),
'session_setup_log': session_setup_log,
})
}, extends=zgrab2.base_scan_response)
Expand Down

0 comments on commit 17a5257

Please sign in to comment.