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.smtp
|
||||||
import schemas.telnet
|
import schemas.telnet
|
||||||
import schemas.pop3
|
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