Port SMB (???) scanner from ZGrab
This commit is contained in:
parent
85f4b8f06a
commit
70314ce92b
490
lib/smb/encoder/encoder.go
Normal file
490
lib/smb/encoder/encoder.go
Normal file
@ -0,0 +1,490 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 stacktitan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type BinaryMarshallable interface {
|
||||
MarshalBinary(*Metadata) ([]byte, error)
|
||||
UnmarshalBinary([]byte, *Metadata) error
|
||||
}
|
||||
|
||||
type Metadata struct {
|
||||
Tags *TagMap
|
||||
Lens map[string]uint64
|
||||
Offsets map[string]uint64
|
||||
Parent interface{}
|
||||
ParentBuf []byte
|
||||
CurrOffset uint64
|
||||
CurrField string
|
||||
}
|
||||
|
||||
type TagMap struct {
|
||||
m map[string]interface{}
|
||||
has map[string]bool
|
||||
}
|
||||
|
||||
func (t TagMap) Has(key string) bool {
|
||||
return t.has[key]
|
||||
}
|
||||
|
||||
func (t TagMap) Set(key string, val interface{}) {
|
||||
t.m[key] = val
|
||||
t.has[key] = true
|
||||
}
|
||||
|
||||
func (t TagMap) Get(key string) interface{} {
|
||||
return t.m[key]
|
||||
}
|
||||
|
||||
func (t TagMap) GetInt(key string) (int, error) {
|
||||
if !t.Has(key) {
|
||||
return 0, errors.New("Key does not exist in tag")
|
||||
}
|
||||
return t.Get(key).(int), nil
|
||||
}
|
||||
|
||||
func (t TagMap) GetString(key string) (string, error) {
|
||||
if !t.Has(key) {
|
||||
return "", errors.New("Key does not exist in tag")
|
||||
}
|
||||
return t.Get(key).(string), nil
|
||||
}
|
||||
|
||||
func parseTags(sf reflect.StructField) (*TagMap, error) {
|
||||
ret := &TagMap{
|
||||
m: make(map[string]interface{}),
|
||||
has: make(map[string]bool),
|
||||
}
|
||||
tag := sf.Tag.Get("smb")
|
||||
smbTags := strings.Split(tag, ",")
|
||||
for _, smbTag := range smbTags {
|
||||
tokens := strings.Split(smbTag, ":")
|
||||
switch tokens[0] {
|
||||
case "len", "offset", "count":
|
||||
if len(tokens) != 2 {
|
||||
return nil, errors.New("Missing required tag data. Expecting key:val")
|
||||
}
|
||||
ret.Set(tokens[0], tokens[1])
|
||||
case "fixed":
|
||||
if len(tokens) != 2 {
|
||||
return nil, errors.New("Missing required tag data. Expecting key:val")
|
||||
}
|
||||
i, err := strconv.Atoi(tokens[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.Set(tokens[0], i)
|
||||
case "asn1":
|
||||
ret.Set(tokens[0], true)
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getOffsetByFieldName(fieldName string, meta *Metadata) (uint64, error) {
|
||||
if meta == nil || meta.Tags == nil || meta.Parent == nil || meta.Lens == nil {
|
||||
return 0, errors.New("Cannot determine field offset. Missing required metadata")
|
||||
}
|
||||
var ret uint64
|
||||
var found bool
|
||||
parentvf := reflect.Indirect(reflect.ValueOf(meta.Parent))
|
||||
// To determine offset, we loop through all fields of the struct, summing lengths of previous elements
|
||||
// until we reach our field
|
||||
for i := 0; i < parentvf.NumField(); i++ {
|
||||
tf := parentvf.Type().Field(i)
|
||||
if tf.Name == fieldName {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
if l, ok := meta.Lens[tf.Name]; ok {
|
||||
// Length of field is in cache
|
||||
ret += l
|
||||
} else {
|
||||
// Not in cache. Must marshal field to determine length. Add to cache after
|
||||
buf, err := Marshal(parentvf.Field(i).Interface())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
l := uint64(len(buf))
|
||||
meta.Lens[tf.Name] = l
|
||||
ret += l
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return 0, errors.New("Cannot find field name within struct: " + fieldName)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getFieldLengthByName(fieldName string, meta *Metadata) (uint64, error) {
|
||||
var ret uint64
|
||||
if meta == nil || meta.Tags == nil || meta.Parent == nil || meta.Lens == nil {
|
||||
return 0, errors.New("Cannot determine field length. Missing required metadata")
|
||||
}
|
||||
|
||||
// Check if length is stored in field length cache
|
||||
if val, ok := meta.Lens[fieldName]; ok {
|
||||
return uint64(val), nil
|
||||
}
|
||||
|
||||
parentvf := reflect.Indirect(reflect.ValueOf(meta.Parent))
|
||||
|
||||
field := parentvf.FieldByName(fieldName)
|
||||
if !field.IsValid() {
|
||||
return 0, errors.New("Invalid field. Cannot determine length.")
|
||||
}
|
||||
|
||||
bm, ok := field.Interface().(BinaryMarshallable)
|
||||
if ok {
|
||||
// Custom marshallable interface found.
|
||||
buf, err := bm.(BinaryMarshallable).MarshalBinary(meta)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint64(len(buf)), nil
|
||||
}
|
||||
|
||||
if field.Kind() == reflect.Ptr {
|
||||
field = field.Elem()
|
||||
}
|
||||
|
||||
switch field.Kind() {
|
||||
case reflect.Struct:
|
||||
buf, err := Marshal(field.Interface())
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ret = uint64(len(buf))
|
||||
case reflect.Interface:
|
||||
return 0, errors.New("Interface length calculation not implemented")
|
||||
case reflect.Slice, reflect.Array:
|
||||
switch field.Type().Elem().Kind() {
|
||||
case reflect.Uint8:
|
||||
ret = uint64(len(field.Interface().([]byte)))
|
||||
default:
|
||||
return 0, errors.New("Cannot calculate the length of unknown slice type for " + fieldName)
|
||||
}
|
||||
case reflect.Uint8:
|
||||
ret = uint64(binary.Size(field.Interface().(uint8)))
|
||||
case reflect.Uint16:
|
||||
ret = uint64(binary.Size(field.Interface().(uint16)))
|
||||
case reflect.Uint32:
|
||||
ret = uint64(binary.Size(field.Interface().(uint32)))
|
||||
case reflect.Uint64:
|
||||
ret = uint64(binary.Size(field.Interface().(uint64)))
|
||||
default:
|
||||
return 0, errors.New("Cannot calculate the length of unknown kind for field " + fieldName)
|
||||
}
|
||||
meta.Lens[fieldName] = ret
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
return marshal(v, nil)
|
||||
}
|
||||
|
||||
func marshal(v interface{}, meta *Metadata) ([]byte, error) {
|
||||
var ret []byte
|
||||
tf := reflect.TypeOf(v)
|
||||
vf := reflect.ValueOf(v)
|
||||
|
||||
bm, ok := v.(BinaryMarshallable)
|
||||
if ok {
|
||||
// Custom marshallable interface found.
|
||||
buf, err := bm.MarshalBinary(meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
if tf.Kind() == reflect.Ptr {
|
||||
vf = reflect.Indirect(reflect.ValueOf(v))
|
||||
tf = vf.Type()
|
||||
}
|
||||
|
||||
w := bytes.NewBuffer(ret)
|
||||
switch tf.Kind() {
|
||||
case reflect.Struct:
|
||||
m := &Metadata{
|
||||
Tags: &TagMap{},
|
||||
Lens: make(map[string]uint64),
|
||||
Parent: v,
|
||||
}
|
||||
for j := 0; j < vf.NumField(); j++ {
|
||||
tags, err := parseTags(tf.Field(j))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.Tags = tags
|
||||
buf, err := marshal(vf.Field(j).Interface(), m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.Lens[tf.Field(j).Name] = uint64(len(buf))
|
||||
if err := binary.Write(w, binary.LittleEndian, buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case reflect.Slice, reflect.Array:
|
||||
switch tf.Elem().Kind() {
|
||||
case reflect.Uint8:
|
||||
if err := binary.Write(w, binary.LittleEndian, v.([]uint8)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case reflect.Uint16:
|
||||
if err := binary.Write(w, binary.LittleEndian, v.([]uint16)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case reflect.Uint8:
|
||||
if err := binary.Write(w, binary.LittleEndian, vf.Interface().(uint8)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case reflect.Uint16:
|
||||
data := vf.Interface().(uint16)
|
||||
if meta != nil && meta.Tags.Has("len") {
|
||||
fieldName, err := meta.Tags.GetString("len")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l, err := getFieldLengthByName(fieldName, meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = uint16(l)
|
||||
}
|
||||
if meta != nil && meta.Tags.Has("offset") {
|
||||
fieldName, err := meta.Tags.GetString("offset")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l, err := getOffsetByFieldName(fieldName, meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = uint16(l)
|
||||
}
|
||||
if err := binary.Write(w, binary.LittleEndian, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case reflect.Uint32:
|
||||
data := vf.Interface().(uint32)
|
||||
if meta != nil && meta.Tags.Has("len") {
|
||||
fieldName, err := meta.Tags.GetString("len")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l, err := getFieldLengthByName(fieldName, meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = uint32(l)
|
||||
}
|
||||
if meta != nil && meta.Tags.Has("offset") {
|
||||
fieldName, err := meta.Tags.GetString("offset")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l, err := getOffsetByFieldName(fieldName, meta)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = uint32(l)
|
||||
}
|
||||
if err := binary.Write(w, binary.LittleEndian, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case reflect.Uint64:
|
||||
if err := binary.Write(w, binary.LittleEndian, vf.Interface().(uint64)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("Marshal not implemented for kind: %s", tf.Kind()))
|
||||
}
|
||||
return w.Bytes(), nil
|
||||
}
|
||||
|
||||
func unmarshal(buf []byte, v interface{}, meta *Metadata) (interface{}, error) {
|
||||
tf := reflect.TypeOf(v)
|
||||
vf := reflect.ValueOf(v)
|
||||
|
||||
bm, ok := v.(BinaryMarshallable)
|
||||
if ok {
|
||||
// Custom marshallable interface found.
|
||||
if err := bm.UnmarshalBinary(buf, meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bm, nil
|
||||
}
|
||||
|
||||
if tf.Kind() == reflect.Ptr {
|
||||
vf = reflect.ValueOf(v).Elem()
|
||||
tf = vf.Type()
|
||||
}
|
||||
|
||||
if meta == nil {
|
||||
meta = &Metadata{
|
||||
Tags: &TagMap{},
|
||||
Lens: make(map[string]uint64),
|
||||
Parent: v,
|
||||
ParentBuf: buf,
|
||||
Offsets: make(map[string]uint64),
|
||||
CurrOffset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
r := bytes.NewBuffer(buf)
|
||||
switch tf.Kind() {
|
||||
case reflect.Struct:
|
||||
m := &Metadata{
|
||||
Tags: &TagMap{},
|
||||
Lens: make(map[string]uint64),
|
||||
Parent: v,
|
||||
ParentBuf: buf,
|
||||
Offsets: make(map[string]uint64),
|
||||
CurrOffset: 0,
|
||||
}
|
||||
for i := 0; i < tf.NumField(); i++ {
|
||||
m.CurrField = tf.Field(i).Name
|
||||
tags, err := parseTags(tf.Field(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.Tags = tags
|
||||
var data interface{}
|
||||
switch tf.Field(i).Type.Kind() {
|
||||
case reflect.Struct:
|
||||
data, err = unmarshal(buf[m.CurrOffset:], vf.Field(i).Addr().Interface(), m)
|
||||
default:
|
||||
data, err = unmarshal(buf[m.CurrOffset:], vf.Field(i).Interface(), m)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vf.Field(i).Set(reflect.ValueOf(data))
|
||||
}
|
||||
v = reflect.Indirect(reflect.ValueOf(v)).Interface()
|
||||
meta.CurrOffset += m.CurrOffset
|
||||
return v, nil
|
||||
case reflect.Uint8:
|
||||
var ret uint8
|
||||
if err := binary.Read(r, binary.LittleEndian, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
meta.CurrOffset += uint64(binary.Size(ret))
|
||||
return ret, nil
|
||||
case reflect.Uint16:
|
||||
var ret uint16
|
||||
if err := binary.Read(r, binary.LittleEndian, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if meta.Tags.Has("len") {
|
||||
ref, err := meta.Tags.GetString("len")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
meta.Lens[ref] = uint64(ret)
|
||||
}
|
||||
meta.CurrOffset += uint64(binary.Size(ret))
|
||||
return ret, nil
|
||||
case reflect.Uint32:
|
||||
var ret uint32
|
||||
if err := binary.Read(r, binary.LittleEndian, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if meta.Tags.Has("offset") {
|
||||
ref, err := meta.Tags.GetString("offset")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
meta.Offsets[ref] = uint64(ret)
|
||||
}
|
||||
meta.CurrOffset += uint64(binary.Size(ret))
|
||||
return ret, nil
|
||||
case reflect.Uint64:
|
||||
var ret uint64
|
||||
if err := binary.Read(r, binary.LittleEndian, &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
meta.CurrOffset += uint64(binary.Size(ret))
|
||||
return ret, nil
|
||||
case reflect.Slice, reflect.Array:
|
||||
switch tf.Elem().Kind() {
|
||||
case reflect.Uint8:
|
||||
var l, o int
|
||||
var err error
|
||||
if meta.Tags.Has("fixed") {
|
||||
if l, err = meta.Tags.GetInt("fixed"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Fixed length fields advance current offset
|
||||
meta.CurrOffset += uint64(l)
|
||||
} else {
|
||||
if val, ok := meta.Lens[meta.CurrField]; ok {
|
||||
l = int(val)
|
||||
} else {
|
||||
return nil, errors.New("Variable length field missing length reference in struct: " + meta.CurrField)
|
||||
}
|
||||
if val, ok := meta.Offsets[meta.CurrField]; ok {
|
||||
o = int(val)
|
||||
} else {
|
||||
// No offset found in map. Use current offset
|
||||
o = int(meta.CurrOffset)
|
||||
}
|
||||
// Variable length data is relative to parent/outer struct. Reset reader to point to beginning of data
|
||||
r = bytes.NewBuffer(meta.ParentBuf[o : o+l])
|
||||
// Variable length data fields do NOT advance current offset.
|
||||
}
|
||||
data := make([]byte, l)
|
||||
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
case reflect.Uint16:
|
||||
return errors.New("Unmarshal not implemented for slice kind:" + tf.Kind().String()), nil
|
||||
}
|
||||
default:
|
||||
return errors.New("Unmarshal not implemented for kind:" + tf.Kind().String()), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
|
||||
}
|
||||
|
||||
func Unmarshal(buf []byte, v interface{}) error {
|
||||
_, err := unmarshal(buf, v, nil)
|
||||
return err
|
||||
}
|
53
lib/smb/encoder/unicode.go
Normal file
53
lib/smb/encoder/unicode.go
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 stacktitan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package encoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
func FromUnicode(d []byte) (string, error) {
|
||||
// Credit to https://github.com/Azure/go-ntlmssp/blob/master/unicode.go for logic
|
||||
if len(d)%2 > 0 {
|
||||
return "", errors.New("Unicode (UTF 16 LE) specified, but uneven data length")
|
||||
}
|
||||
s := make([]uint16, len(d)/2)
|
||||
err := binary.Read(bytes.NewReader(d), binary.LittleEndian, &s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(utf16.Decode(s)), nil
|
||||
}
|
||||
|
||||
func ToUnicode(s string) []byte {
|
||||
// Credit to https://github.com/Azure/go-ntlmssp/blob/master/unicode.go for logic
|
||||
uints := utf16.Encode([]rune(s))
|
||||
b := bytes.Buffer{}
|
||||
binary.Write(&b, binary.LittleEndian, &uints)
|
||||
return b.Bytes()
|
||||
}
|
136
lib/smb/gss/gss.go
Normal file
136
lib/smb/gss/gss.go
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 stacktitan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package gss
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"log"
|
||||
|
||||
"github.com/zmap/zgrab2/lib/smb/encoder"
|
||||
)
|
||||
|
||||
const SpnegoOid = "1.3.6.1.5.5.2"
|
||||
const NtLmSSPMechTypeOid = "1.3.6.1.4.1.311.2.2.10"
|
||||
|
||||
const GssStateAcceptCompleted = 0
|
||||
const GssStateAcceptIncomplete = 1
|
||||
const GssStateReject = 2
|
||||
const GssStateRequestMic = 3
|
||||
|
||||
type NegTokenInitData struct {
|
||||
MechTypes []asn1.ObjectIdentifier `asn1:"explicit,tag:0"`
|
||||
ReqFlags asn1.BitString `asn1:"explicit,optional,omitempty,tag:1"`
|
||||
MechToken []byte `asn1:"explicit,optional,omitempty,tag:2"`
|
||||
MechTokenMIC []byte `asn1:"explicit,optional,omitempty,tag:3"`
|
||||
}
|
||||
|
||||
type NegTokenInit struct {
|
||||
OID asn1.ObjectIdentifier
|
||||
Data NegTokenInitData `asn1:"explicit"`
|
||||
}
|
||||
|
||||
type NegTokenResp struct {
|
||||
State asn1.Enumerated `asn1:"explicit,optional,omitempty,tag:0"`
|
||||
SupportedMech asn1.ObjectIdentifier `asn1:"explicit,optional,omitempty,tag:1"`
|
||||
ResponseToken []byte `asn1:"explicit,optional,omitempty,tag:2"`
|
||||
MechListMIC []byte `asn1:"explicit,optional,omitempty,tag:3"`
|
||||
}
|
||||
|
||||
// gsswrapped used to force ASN1 encoding to include explicit sequence tags
|
||||
// Type does not fulfill the BinaryMarshallable interface and is used only as a
|
||||
// helper to marshal a NegTokenResp
|
||||
type gsswrapped struct{ G interface{} }
|
||||
|
||||
func NewNegTokenInit() (NegTokenInit, error) {
|
||||
oid, err := ObjectIDStrToInt(SpnegoOid)
|
||||
if err != nil {
|
||||
return NegTokenInit{}, err
|
||||
}
|
||||
ntlmoid, err := ObjectIDStrToInt(NtLmSSPMechTypeOid)
|
||||
if err != nil {
|
||||
return NegTokenInit{}, err
|
||||
}
|
||||
return NegTokenInit{
|
||||
OID: oid,
|
||||
Data: NegTokenInitData{
|
||||
MechTypes: []asn1.ObjectIdentifier{ntlmoid},
|
||||
ReqFlags: asn1.BitString{},
|
||||
MechToken: []byte{},
|
||||
MechTokenMIC: []byte{},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewNegTokenResp() (NegTokenResp, error) {
|
||||
return NegTokenResp{}, nil
|
||||
}
|
||||
|
||||
func (n *NegTokenInit) MarshalBinary(meta *encoder.Metadata) ([]byte, error) {
|
||||
buf, err := asn1.Marshal(*n)
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// When marshalling struct, asn1 uses 30 (sequence) tag by default.
|
||||
// Override to set 60 (application) to remain consistent with GSS/SMB
|
||||
buf[0] = 0x60
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (n *NegTokenInit) UnmarshalBinary(buf []byte, meta *encoder.Metadata) error {
|
||||
data := NegTokenInit{}
|
||||
if _, err := asn1.UnmarshalWithParams(buf, &data, "application"); err != nil {
|
||||
return err
|
||||
}
|
||||
*n = data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *NegTokenResp) MarshalBinary(meta *encoder.Metadata) ([]byte, error) {
|
||||
// Oddities in Go's ASN1 package vs SMB encoding mean we have to wrap our
|
||||
// struct in another struct to ensure proper tags and lengths are added
|
||||
// to encoded data
|
||||
wrapped := &gsswrapped{*r}
|
||||
return wrapped.MarshalBinary(meta)
|
||||
}
|
||||
|
||||
func (r *NegTokenResp) UnmarshalBinary(buf []byte, meta *encoder.Metadata) error {
|
||||
data := NegTokenResp{}
|
||||
if _, err := asn1.UnmarshalWithParams(buf, &data, "explicit,tag:1"); err != nil {
|
||||
return err
|
||||
}
|
||||
*r = data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *gsswrapped) MarshalBinary(meta *encoder.Metadata) ([]byte, error) {
|
||||
buf, err := asn1.Marshal(*g)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf[0] = 0xa1
|
||||
return buf, nil
|
||||
}
|
42
lib/smb/gss/oid.go
Normal file
42
lib/smb/gss/oid.go
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 stacktitan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package gss
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ObjectIDStrToInt(oid string) ([]int, error) {
|
||||
ret := []int{}
|
||||
tokens := strings.Split(oid, ".")
|
||||
for _, token := range tokens {
|
||||
i, err := strconv.Atoi(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret = append(ret, i)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
5
lib/smb/log.go
Normal file
5
lib/smb/log.go
Normal file
@ -0,0 +1,5 @@
|
||||
package smb
|
||||
|
||||
type SMBLog struct {
|
||||
SupportV1 bool `json:"smbv1_support"`
|
||||
}
|
161
lib/smb/session.go
Normal file
161
lib/smb/session.go
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 stacktitan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package smb
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/zmap/zgrab2/lib/smb/encoder"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
IsSigningRequired bool
|
||||
IsAuthenticated bool
|
||||
debug bool
|
||||
securityMode uint16
|
||||
messageID uint64
|
||||
sessionID uint64
|
||||
conn net.Conn
|
||||
dialect uint16
|
||||
options Options
|
||||
trees map[string]uint32
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Host string
|
||||
Port int
|
||||
Workstation string
|
||||
Domain string
|
||||
User string
|
||||
Password string
|
||||
Hash string
|
||||
}
|
||||
|
||||
func GetSMBBanner(logStruct *SMBLog, conn net.Conn) (err error) {
|
||||
opt := Options{}
|
||||
|
||||
s := &Session{
|
||||
IsSigningRequired: false,
|
||||
IsAuthenticated: false,
|
||||
debug: false,
|
||||
securityMode: 0,
|
||||
messageID: 0,
|
||||
sessionID: 0,
|
||||
dialect: 0,
|
||||
conn: conn,
|
||||
options: opt,
|
||||
trees: make(map[string]uint32),
|
||||
}
|
||||
|
||||
err = s.NegotiateProtocol(logStruct)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
func (s *Session) NegotiateProtocol(logStruct *SMBLog) error {
|
||||
negReq := s.NewNegotiateReq()
|
||||
buf, err := s.send(negReq, logStruct)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
negRes := NewNegotiateRes()
|
||||
if err := encoder.Unmarshal(buf, &negRes); err != nil {
|
||||
s.Debug("Raw:\n"+hex.Dump(buf), err)
|
||||
}
|
||||
|
||||
if negRes.Header.Status != StatusOk {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.conn.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Session) send(req interface{}, logStruct *SMBLog) (res []byte, err error) {
|
||||
buf, err := encoder.Marshal(req)
|
||||
if err != nil {
|
||||
s.Debug("", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
if err = binary.Write(b, binary.BigEndian, uint32(len(buf))); err != nil {
|
||||
s.Debug("", err)
|
||||
return
|
||||
}
|
||||
|
||||
rw := bufio.NewReadWriter(bufio.NewReader(s.conn), bufio.NewWriter(s.conn))
|
||||
if _, err = rw.Write(append(b.Bytes(), buf...)); err != nil {
|
||||
s.Debug("", err)
|
||||
return
|
||||
}
|
||||
rw.Flush()
|
||||
|
||||
var size uint32
|
||||
if err = binary.Read(rw, binary.BigEndian, &size); err != nil {
|
||||
s.Debug("", err)
|
||||
return
|
||||
}
|
||||
if size > 0x00FFFFFF {
|
||||
return nil, errors.New("invalid NetBIOS Session message")
|
||||
}
|
||||
|
||||
data := make([]byte, size)
|
||||
l, err := io.ReadFull(rw, data)
|
||||
if err != nil {
|
||||
s.Debug("", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if uint32(l) != size || len(data) < 4 {
|
||||
return nil, errors.New("message size invalid")
|
||||
}
|
||||
|
||||
//First four bytes contain protocol ID
|
||||
protID := data[0:4]
|
||||
switch string(protID) {
|
||||
default:
|
||||
return nil, errors.New("protocol not implemented")
|
||||
case ProtocolSmb:
|
||||
logStruct.SupportV1 = true
|
||||
}
|
||||
|
||||
s.messageID++
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (s *Session) Debug(msg string, err error) {
|
||||
if s.debug {
|
||||
log.Println("[ DEBUG ] ", msg)
|
||||
}
|
||||
}
|
221
lib/smb/smb.go
Normal file
221
lib/smb/smb.go
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 stacktitan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package smb
|
||||
|
||||
import (
|
||||
"github.com/zmap/zgrab2/lib/smb/gss"
|
||||
)
|
||||
|
||||
const ProtocolSmb = "\xFFSMB"
|
||||
const ProtocolSmb2 = "\xFESMB"
|
||||
|
||||
const StatusOk = 0x00000000
|
||||
const StatusMoreProcessingRequired = 0xc0000016
|
||||
const StatusInvalidParameter = 0xc000000d
|
||||
const StatusLogonFailure = 0xc000006d
|
||||
const StatusUserSessionDeleted = 0xc0000203
|
||||
|
||||
var StatusMap = map[uint32]string{
|
||||
StatusOk: "OK",
|
||||
StatusMoreProcessingRequired: "More Processing Required",
|
||||
StatusInvalidParameter: "Invalid Parameter",
|
||||
StatusLogonFailure: "Logon failed",
|
||||
StatusUserSessionDeleted: "User session deleted",
|
||||
}
|
||||
|
||||
const DialectSmb_2_0_2 = 0x0202
|
||||
const DialectSmb_2_1 = 0x0210
|
||||
const DialectSmb_3_0 = 0x0300
|
||||
const DialectSmb_3_0_2 = 0x0302
|
||||
const DialectSmb_3_1_1 = 0x0311
|
||||
const DialectSmb2_ALL = 0x02FF
|
||||
|
||||
const (
|
||||
CommandNegotiate uint16 = iota
|
||||
CommandSessionSetup
|
||||
CommandLogoff
|
||||
CommandTreeConnect
|
||||
CommandTreeDisconnect
|
||||
CommandCreate
|
||||
CommandClose
|
||||
CommandFlush
|
||||
CommandRead
|
||||
CommandWrite
|
||||
CommandLock
|
||||
CommandIOCtl
|
||||
CommandCancel
|
||||
CommandEcho
|
||||
CommandQueryDirectory
|
||||
CommandChangeNotify
|
||||
CommandQueryInfo
|
||||
CommandSetInfo
|
||||
CommandOplockBreak
|
||||
)
|
||||
|
||||
const (
|
||||
_ uint16 = iota
|
||||
SecurityModeSigningEnabled
|
||||
SecurityModeSigningRequired
|
||||
)
|
||||
|
||||
const (
|
||||
_ byte = iota
|
||||
ShareTypeDisk
|
||||
ShareTypePipe
|
||||
ShareTypePrint
|
||||
)
|
||||
|
||||
const (
|
||||
ShareFlagManualCaching uint32 = 0x00000000
|
||||
ShareFlagAutoCaching uint32 = 0x00000010
|
||||
ShareFlagVDOCaching uint32 = 0x00000020
|
||||
ShareFlagNoCaching uint32 = 0x00000030
|
||||
ShareFlagDFS uint32 = 0x00000001
|
||||
ShareFlagDFSRoot uint32 = 0x00000002
|
||||
ShareFlagRestriceExclusiveOpens uint32 = 0x00000100
|
||||
ShareFlagForceSharedDelete uint32 = 0x00000200
|
||||
ShareFlagAllowNamespaceCaching uint32 = 0x00000400
|
||||
ShareFlagAccessBasedDirectoryEnum uint32 = 0x00000800
|
||||
ShareFlagForceLevelIIOplock uint32 = 0x00001000
|
||||
ShareFlagEnableHashV1 uint32 = 0x00002000
|
||||
ShareFlagEnableHashV2 uint32 = 0x00004000
|
||||
ShareFlagEncryptData uint32 = 0x00008000
|
||||
)
|
||||
|
||||
const (
|
||||
ShareCapDFS uint32 = 0x00000008
|
||||
ShareCapContinuousAvailability uint32 = 0x00000010
|
||||
ShareCapScaleout uint32 = 0x00000020
|
||||
ShareCapCluster uint32 = 0x00000040
|
||||
ShareCapAsymmetric uint32 = 0x00000080
|
||||
)
|
||||
|
||||
type Header struct {
|
||||
ProtocolID []byte `smb:"fixed:4"`
|
||||
StructureSize uint16
|
||||
CreditCharge uint16
|
||||
Status uint32
|
||||
Command uint16
|
||||
Credits uint16
|
||||
Flags uint32
|
||||
NextCommand uint32
|
||||
MessageID uint64
|
||||
Reserved uint32
|
||||
TreeID uint32
|
||||
SessionID uint64
|
||||
Signature []byte `smb:"fixed:16"`
|
||||
}
|
||||
|
||||
type NegotiateReq struct {
|
||||
Header
|
||||
StructureSize uint16
|
||||
DialectCount uint16 `smb:"count:Dialects"`
|
||||
SecurityMode uint16
|
||||
Reserved uint16
|
||||
Capabilities uint32
|
||||
ClientGuid []byte `smb:"fixed:16"`
|
||||
ClientStartTime uint64
|
||||
Dialects []uint16
|
||||
}
|
||||
|
||||
type NegotiateRes struct {
|
||||
Header
|
||||
StructureSize uint16
|
||||
SecurityMode uint16
|
||||
DialectRevision uint16
|
||||
Reserved uint16
|
||||
ServerGuid []byte `smb:"fixed:16"`
|
||||
Capabilities uint32
|
||||
MaxTransactSize uint32
|
||||
MaxReadSize uint32
|
||||
MaxWriteSize uint32
|
||||
SystemTime uint64
|
||||
ServerStartTime uint64
|
||||
SecurityBufferOffset uint16 `smb:"offset:SecurityBlob"`
|
||||
SecurityBufferLength uint16 `smb:"len:SecurityBlob"`
|
||||
Reserved2 uint32
|
||||
SecurityBlob *gss.NegTokenInit
|
||||
}
|
||||
|
||||
func newHeader() Header {
|
||||
return Header{
|
||||
ProtocolID: []byte(ProtocolSmb),
|
||||
StructureSize: 64,
|
||||
CreditCharge: 0,
|
||||
Status: 0,
|
||||
Command: 0,
|
||||
Credits: 0,
|
||||
Flags: 0,
|
||||
NextCommand: 0,
|
||||
MessageID: 0,
|
||||
Reserved: 0,
|
||||
TreeID: 0,
|
||||
SessionID: 0,
|
||||
Signature: make([]byte, 16),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) NewNegotiateReq() NegotiateReq {
|
||||
header := newHeader()
|
||||
header.Command = CommandNegotiate
|
||||
header.CreditCharge = 1
|
||||
header.MessageID = s.messageID
|
||||
|
||||
dialects := []uint16{
|
||||
uint16(DialectSmb_2_1),
|
||||
}
|
||||
return NegotiateReq{
|
||||
Header: header,
|
||||
StructureSize: 36,
|
||||
DialectCount: uint16(len(dialects)),
|
||||
SecurityMode: SecurityModeSigningEnabled,
|
||||
Reserved: 0,
|
||||
Capabilities: 0,
|
||||
ClientGuid: make([]byte, 16),
|
||||
ClientStartTime: 0,
|
||||
Dialects: dialects,
|
||||
}
|
||||
}
|
||||
|
||||
func NewNegotiateRes() NegotiateRes {
|
||||
return NegotiateRes{
|
||||
Header: newHeader(),
|
||||
StructureSize: 0,
|
||||
SecurityMode: 0,
|
||||
DialectRevision: 0,
|
||||
Reserved: 0,
|
||||
ServerGuid: make([]byte, 16),
|
||||
Capabilities: 0,
|
||||
MaxTransactSize: 0,
|
||||
MaxReadSize: 0,
|
||||
MaxWriteSize: 0,
|
||||
SystemTime: 0,
|
||||
ServerStartTime: 0,
|
||||
SecurityBufferOffset: 0,
|
||||
SecurityBufferLength: 0,
|
||||
Reserved2: 0,
|
||||
SecurityBlob: &gss.NegTokenInit{},
|
||||
}
|
||||
}
|
7
modules/smb.go
Normal file
7
modules/smb.go
Normal file
@ -0,0 +1,7 @@
|
||||
package modules
|
||||
|
||||
import "github.com/zmap/zgrab2/modules/smb"
|
||||
|
||||
func init() {
|
||||
smb.RegisterModule()
|
||||
}
|
106
modules/smb/scanner.go
Normal file
106
modules/smb/scanner.go
Normal file
@ -0,0 +1,106 @@
|
||||
// Package smb provides a zgrab2 module that scans for smb.
|
||||
// This was ported directly from zgrab.
|
||||
package smb
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/zmap/zgrab2"
|
||||
"github.com/zmap/zgrab2/lib/smb"
|
||||
)
|
||||
|
||||
// ScanResults instances are returned by the module's Scan function.
|
||||
type ScanResults struct {
|
||||
smb.SMBLog
|
||||
}
|
||||
|
||||
// Flags holds the command-line configuration for the smb scan module.
|
||||
// Populated by the framework.
|
||||
type Flags struct {
|
||||
zgrab2.BaseFlags
|
||||
Verbose bool `long:"verbose" description:"More verbose logging, include debug fields in the scan results"`
|
||||
}
|
||||
|
||||
// Module implements the zgrab2.Module interface.
|
||||
type Module struct {
|
||||
}
|
||||
|
||||
// Scanner implements the zgrab2.Scanner interface.
|
||||
type Scanner struct {
|
||||
config *Flags
|
||||
}
|
||||
|
||||
// RegisterModule registers the zgrab2 module.
|
||||
func RegisterModule() {
|
||||
var module Module
|
||||
_, err := zgrab2.AddCommand("smb", "smb", "Probe for smb", 445, &module)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// NewFlags returns a default Flags object.
|
||||
func (module *Module) NewFlags() interface{} {
|
||||
return new(Flags)
|
||||
}
|
||||
|
||||
// NewScanner returns a new Scanner instance.
|
||||
func (module *Module) NewScanner() zgrab2.Scanner {
|
||||
return new(Scanner)
|
||||
}
|
||||
|
||||
// Validate checks that the flags are valid.
|
||||
// On success, returns nil.
|
||||
// On failure, returns an error instance describing the error.
|
||||
func (flags *Flags) Validate(args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Help returns the module's help string.
|
||||
func (flags *Flags) Help() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Init initializes the Scanner.
|
||||
func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error {
|
||||
f, _ := flags.(*Flags)
|
||||
scanner.config = f
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitPerSender initializes the scanner for a given sender.
|
||||
func (scanner *Scanner) InitPerSender(senderID int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetName returns the Scanner name defined in the Flags.
|
||||
func (scanner *Scanner) GetName() string {
|
||||
return scanner.config.Name
|
||||
}
|
||||
|
||||
// Protocol returns the protocol identifier of the scan.
|
||||
func (scanner *Scanner) Protocol() string {
|
||||
return "smb"
|
||||
}
|
||||
|
||||
// GetPort returns the port being scanned.
|
||||
func (scanner *Scanner) GetPort() uint {
|
||||
return scanner.config.Port
|
||||
}
|
||||
|
||||
// Scan performs the following:
|
||||
// 1. Connect to the TCP port (default 445).
|
||||
// 2. Call smb.GetSMBBanner() on the connection
|
||||
// 3. Return the result.
|
||||
func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) {
|
||||
conn, err:= target.Open(&scanner.config.BaseFlags)
|
||||
if err != nil {
|
||||
return zgrab2.TryGetScanStatus(err), nil, err
|
||||
}
|
||||
smbLog := smb.SMBLog{}
|
||||
|
||||
if err := smb.GetSMBBanner(&smbLog, conn); err != nil {
|
||||
return zgrab2.TryGetScanStatus(err), nil, err
|
||||
}
|
||||
ret := ScanResults{SMBLog: smbLog}
|
||||
return zgrab2.SCAN_SUCCESS, ret, nil
|
||||
}
|
@ -10,3 +10,4 @@ import schemas.redis
|
||||
import schemas.smtp
|
||||
import schemas.telnet
|
||||
import schemas.pop3
|
||||
import schemas.smb
|
||||
|
18
schemas/smb.py
Normal file
18
schemas/smb.py
Normal file
@ -0,0 +1,18 @@
|
||||
# zschema sub-schema for zgrab2's smb module
|
||||
# Registers zgrab2-smb globally, and smb with the main zgrab2 schema.
|
||||
from zschema.leaves import *
|
||||
from zschema.compounds import *
|
||||
import zschema.registry
|
||||
|
||||
import schemas.zcrypto as zcrypto
|
||||
import schemas.zgrab2 as zgrab2
|
||||
|
||||
smb_scan_response = SubRecord({
|
||||
"result": SubRecord({
|
||||
"smbv1_support": Boolean(),
|
||||
})
|
||||
}, extends=zgrab2.base_scan_response)
|
||||
|
||||
zschema.registry.register_schema("zgrab2-smb", smb_scan_response)
|
||||
|
||||
zgrab2.register_scan_response_type("smb", smb_scan_response)
|
Loading…
Reference in New Issue
Block a user