add some tighter bounds checking in MSSQL scanner, and if there is an uncaught panic, log the body that caused it

This commit is contained in:
Justin Bastress 2018-10-01 11:08:26 -04:00
parent e7e7be1f6f
commit 6618920234
2 changed files with 35 additions and 2 deletions

@ -11,7 +11,7 @@ import (
"strings" "strings"
"time" "time"
logrus "github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/zmap/zgrab2" "github.com/zmap/zgrab2"
) )
@ -401,6 +401,9 @@ func (options PreloginOptions) Encode() ([]byte, error) {
for _, ik := range sortedKeys { for _, ik := range sortedKeys {
k := PreloginOptionToken(ik) k := PreloginOptionToken(ik)
v := options[k] v := options[k]
if len(cursor) < 5 {
return nil, fmt.Errorf("encode: size mismatch (options.Size()=%d)", options.Size())
}
cursor[0] = byte(k) cursor[0] = byte(k)
if offset > 0xffff { if offset > 0xffff {
return nil, ErrTooLarge return nil, ErrTooLarge
@ -411,6 +414,9 @@ func (options PreloginOptions) Encode() ([]byte, error) {
offset += len(v) offset += len(v)
cursor = cursor[5:] cursor = cursor[5:]
} }
if len(cursor) < 1 {
return nil, fmt.Errorf("encode: size mismatch (options.Size()=%d, len(sortedKeys)=%d)", options.Size(), len(sortedKeys))
}
// Write the terminator after the last PL_OPTION header // Write the terminator after the last PL_OPTION header
// (and just before the first value) // (and just before the first value)
cursor[0] = 0xff cursor[0] = 0xff
@ -421,6 +427,9 @@ func (options PreloginOptions) Encode() ([]byte, error) {
// returned in rest. // returned in rest.
// If body can't be decoded as a PRELOGIN body, returns nil, nil, ErrInvalidData // If body can't be decoded as a PRELOGIN body, returns nil, nil, ErrInvalidData
func decodePreloginOptions(body []byte) (result *PreloginOptions, rest []byte, err error) { func decodePreloginOptions(body []byte) (result *PreloginOptions, rest []byte, err error) {
if len(body) < 1 {
return nil, nil, ErrInvalidData
}
cursor := body[:] cursor := body[:]
options := make(PreloginOptions) options := make(PreloginOptions)
max := 0 max := 0
@ -506,7 +515,13 @@ func (options PreloginOptions) MarshalJSON() ([]byte, error) {
fedAuthRequired, hasFedAuthRequired := opts[PreloginFedAuthRequired] fedAuthRequired, hasFedAuthRequired := opts[PreloginFedAuthRequired]
if hasFedAuthRequired { if hasFedAuthRequired {
aux.FedAuthRequired = &fedAuthRequired[0] temp := uint8(0)
if len(fedAuthRequired) > 0 {
temp = fedAuthRequired[0]
} else {
logrus.Debugf("fedAuthRequired was present but empty (options=%#v)", options)
}
aux.FedAuthRequired = &temp
} }
nonce, hasNonce := opts[PreloginNonce] nonce, hasNonce := opts[PreloginNonce]
@ -632,6 +647,7 @@ func (connection *Connection) readPreloginPacket() (*TDSPacket, *PreloginOptions
if packet.Type != TDSPacketTypeTabularResult { if packet.Type != TDSPacketTypeTabularResult {
return packet, nil, &zgrab2.ScanError{Status: zgrab2.SCAN_APPLICATION_ERROR, Err: err} return packet, nil, &zgrab2.ScanError{Status: zgrab2.SCAN_APPLICATION_ERROR, Err: err}
} }
defer zgrab2.LogPanic("Error decoding Prelogin packet %#v", packet.Body)
plOptions, rest, err := decodePreloginOptions(packet.Body) plOptions, rest, err := decodePreloginOptions(packet.Body)
if err != nil { if err != nil {
return packet, nil, err return packet, nil, err

@ -10,6 +10,8 @@ import (
"time" "time"
"github.com/zmap/zflags" "github.com/zmap/zflags"
"github.com/sirupsen/logrus"
"runtime/debug"
) )
var parser *flags.Parser var parser *flags.Parser
@ -209,3 +211,18 @@ func IsTimeoutError(err error) bool {
return false return false
} }
// LogPanic is intended to be called from within defer -- if there was no panic, it returns without
// doing anything. Otherwise, it logs the stacktrace, the panic error, and the provided message
// before re-raising the original panic.
// Example:
// defer zgrab2.LogPanic("Error decoding body '%x'", body)
func LogPanic(format string, args...interface{}) {
err := recover()
if err == nil {
return
}
logrus.Errorf("Uncaught panic at %s: %v", string(debug.Stack()), err)
logrus.Errorf(format, args...)
panic(err)
}