zgrab2/modules/mssql/scanner.go

164 lines
5.1 KiB
Go

// Package mssql provides the zgrab2 scanner module for the MSSQL protocol.
// Default Port: 1433 (TCP)
//
// The --encrypt-mode flag allows setting an explicit client encryption mode
// (the default is ENCRYPT_ON). Note: only ENCRYPT_NOT_SUP will skip the TLS
// handshake, since even ENCRYPT_OFF uses TLS for the login step.
//
// The scan performs a PRELOGIN and if possible does a TLS handshake.
//
// The output is the the server version and instance name, and if applicable the
// TLS output.
package mssql
import (
log "github.com/sirupsen/logrus"
"github.com/zmap/zgrab2"
)
// ScanResults contains detailed information about each step of the
// MySQL handshake, and can be encoded to JSON.
type ScanResults struct {
// Version is the version returned by the server in the PRELOGIN response.
// Its format is "MAJOR.MINOR.BUILD_NUMBER".
Version string `json:"version,omitempty"`
// InstanceName is the value of the INSTANCE field returned by the server
// in the PRELOGIN response.
InstanceName string `json:"instance_name,omitempty"`
// PreloginOptions are the raw key-value pairs returned by the server in
// response to the PRELOGIN call. Debug only.
PreloginOptions *PreloginOptions `json:"prelogin_options,omitempty" zgrab:"debug"`
// TLSLog is the shared TLS handshake/scan log.
TLSLog *zgrab2.TLSLog `json:"tls,omitempty"`
}
// Flags defines the command-line configuration options for the module.
type Flags struct {
zgrab2.BaseFlags
zgrab2.TLSFlags
EncryptMode string `long:"encrypt-mode" description:"The type of encryption to request in the pre-login step. One of ENCRYPT_ON, ENCRYPT_OFF, ENCRYPT_NOT_SUP." default:"ENCRYPT_ON"`
Verbose bool `long:"verbose" description:"More verbose logging, include debug fields in the scan results"`
}
// Module is the implementation of zgrab2.Module for the MSSQL protocol.
type Module struct {
}
// Scanner is the implementation of zgrab2.Scanner for the MSSQL protocol.
type Scanner struct {
config *Flags
}
// NewFlags returns a default Flags instance to be populated by the command
// line flags.
func (module *Module) NewFlags() interface{} {
return new(Flags)
}
// NewScanner returns a new Scanner instance.
func (module *Module) NewScanner() zgrab2.Scanner {
return new(Scanner)
}
// Validate does nothing in this module.
func (flags *Flags) Validate(args []string) error {
return nil
}
// Help returns the help string for this module.
func (flags *Flags) Help() string {
return ""
}
// Init initializes the Scanner instance with the given command-line flags.
func (scanner *Scanner) Init(flags zgrab2.ScanFlags) error {
f, _ := flags.(*Flags)
scanner.config = f
return nil
}
// InitPerSender does nothing in this module.
func (scanner *Scanner) InitPerSender(senderID int) error {
return nil
}
// Protocol returns the protocol identifer for the scanner.
func (s *Scanner) Protocol() string {
return "mssql"
}
// GetName returns the configured scanner name.
func (scanner *Scanner) GetName() string {
return scanner.config.Name
}
// GetPort returns the configured scanner port.
func (scanner *Scanner) GetPort() uint {
return scanner.config.Port
}
// Scan performs the MSSQL scan.
// 1. Open a TCP connection to the target port (default 1433).
// 2. Send a PRELOGIN packet to the server.
// 3. Read the PRELOGIN response from the server.
// 4. If the server encrypt mode is EncryptModeNotSupported, break.
// 5. Perform a TLS handshake, with the packets wrapped in TDS headers.
// 6. Decode the Version and InstanceName from the PRELOGIN response
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
}
sql := NewConnection(conn)
defer sql.Close()
result := &ScanResults{}
_, err = sql.Handshake(scanner.config)
if sql.tlsConn != nil {
result.TLSLog = sql.tlsConn.GetLog()
}
if sql.PreloginOptions != nil {
result.PreloginOptions = sql.PreloginOptions
version := sql.PreloginOptions.GetVersion()
if version != nil {
result.Version = version.String()
}
result.InstanceName = string((*sql.PreloginOptions)[PreloginInstance])
}
if err != nil {
if sql.PreloginOptions == nil && sql.readValidTDSPacket == false {
// If we received no PreloginOptions and none of the packets we've
// read appeared to be a valid TDS header, then the inference is
// that we found no MSSQL service on the target.
// NOTE: In the case where PreloginOptions == nil but
// readValidTDSPacket == true, the result will be empty, but not
// nil.
result = nil
}
switch err {
case ErrNoServerEncryption:
return zgrab2.SCAN_APPLICATION_ERROR, result, err
case ErrServerRequiresEncryption:
return zgrab2.SCAN_APPLICATION_ERROR, result, err
default:
return zgrab2.TryGetScanStatus(err), result, err
}
}
return zgrab2.SCAN_SUCCESS, result, nil
}
// RegisterModule is called by modules/mssql.go's init()
func RegisterModule() {
var module Module
_, err := zgrab2.AddCommand("mssql", "MSSQL", "Grab a mssql handshake", 1433, &module)
log.SetLevel(log.DebugLevel)
if err != nil {
log.Fatal(err)
}
}