Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ecubit/fix false positives #262

Merged
merged 12 commits into from Jun 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/weppos/publicsuffix-go v0.4.0 h1:YSnfg3V65LcCFKtIGKGoBhkyKolEd0hlipcXaOjdnQw=
github.com/weppos/publicsuffix-go v0.4.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521 h1:kKCF7VX/wTmdg2ZjEaqlq99Bjsoiz7vH6sFniF/vI4M=
Expand Down
4 changes: 3 additions & 1 deletion modules/dnp3/dnp3.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"encoding/binary"
"io"
"net"
"errors"

"github.com/zmap/zgrab2"
)
Expand Down Expand Up @@ -73,9 +74,10 @@ func GetDNP3Banner(logStruct *DNP3Log, connection net.Conn) (err error) {
if len(data) >= LINK_MIN_HEADER_LENGTH && binary.BigEndian.Uint16(data[0:2]) == LINK_START_FIELD {
logStruct.IsDNP3 = true
logStruct.RawResponse = data
return nil
}

return nil
return zgrab2.NewScanError(zgrab2.SCAN_PROTOCOL_ERROR, errors.New("Invalid response for DNP3"))
}

func makeLinkStatusRequest(dstAddress uint16) []byte {
Expand Down
11 changes: 6 additions & 5 deletions modules/imap/imap.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package imap
import (
"net"
"regexp"
"io"

"github.com/zmap/zgrab2"
)
Expand All @@ -18,14 +19,14 @@ type Connection struct {
}

// ReadResponse reads from the connection until it matches the imapEndRegex. Copied from the original zgrab.
// TODO: Catch corner cases, parse out success/error character.
// TODO: Catch corner cases
func (conn *Connection) ReadResponse() (string, error) {
ret := make([]byte, readBufferSize)
n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, imapStatusEndRegex)
if err != nil {
return "", nil
if err != nil && err != io.EOF && !zgrab2.IsTimeoutError(err) {
return "", err
}
return string(ret[0:n]), nil
return string(ret[:n]), nil
}

// SendCommand sends a command, followed by a CRLF, then wait for / read the server's response.
Expand All @@ -34,4 +35,4 @@ func (conn *Connection) SendCommand(cmd string) (string, error) {
return "", err
}
return conn.ReadResponse()
}
}
32 changes: 31 additions & 1 deletion modules/imap/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ package imap

import (
"fmt"
"errors"

"strings"

Expand Down Expand Up @@ -148,6 +149,29 @@ func getIMAPError(response string) error {
return fmt.Errorf("error: %s", response)
}

// Check the contents of the IMAP banner and return a relevant ScanStatus
func VerifyIMAPContents(banner string) zgrab2.ScanStatus {
lowerBanner := strings.ToLower(banner)
switch {
case strings.HasPrefix(banner, "* NO"),
strings.HasPrefix(banner, "* BAD"):
return zgrab2.SCAN_APPLICATION_ERROR
case strings.HasPrefix(banner, "* OK"),
strings.HasPrefix(banner, "* PREAUTH"),
strings.HasPrefix(banner, "* BYE"),
strings.HasPrefix(banner, "* OKAY"),
strings.Contains(banner, "IMAP"),
strings.Contains(lowerBanner, "blacklist"),
strings.Contains(lowerBanner, "abuse"),
strings.Contains(lowerBanner, "rbl"),
strings.Contains(lowerBanner, "spamhaus"),
strings.Contains(lowerBanner, "relay"):
return zgrab2.SCAN_SUCCESS
default:
return zgrab2.SCAN_PROTOCOL_ERROR
}
}

// Scan performs the IMAP scan.
// 1. Open a TCP connection to the target port (default 143).
// 2. If --imaps is set, perform a TLS handshake using the command-line
Expand Down Expand Up @@ -180,6 +204,12 @@ func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, inter
if err != nil {
return zgrab2.TryGetScanStatus(err), nil, err
}
// Quit early if we didn't get a valid response
// OR save a valid scan result for later
sr := VerifyIMAPContents(banner)
if sr == zgrab2.SCAN_PROTOCOL_ERROR {
return sr, nil, errors.New("Invalid response for IMAP")
}
result.Banner = banner
if scanner.config.StartTLS {
ret, err := conn.SendCommand("a001 STARTTLS")
Expand Down Expand Up @@ -209,5 +239,5 @@ func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, inter
}
result.CLOSE = ret
}
return zgrab2.SCAN_SUCCESS, result, nil
return sr, result, nil
}
15 changes: 10 additions & 5 deletions modules/pop3/pop3.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
// Scanner for POP3 protocol
// https://www.ietf.org/rfc/rfc1939.txt

package pop3

import (
"net"
"regexp"
"io"

"github.com/zmap/zgrab2"
)
Expand All @@ -18,14 +22,15 @@ type Connection struct {
}

// ReadResponse reads from the connection until it matches the pop3EndRegex. Copied from the original zgrab.
// TODO: Catch corner cases, parse out success/error character.
// TODO: Catch corner cases
func (conn *Connection) ReadResponse() (string, error) {
ret := make([]byte, readBufferSize)
n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, pop3EndRegex)
if err != nil {
return "", nil
// Don't quit for timeouts since we might have gotten relevant data still
if err != nil && err != io.EOF && !zgrab2.IsTimeoutError(err) {
return "", err
}
return string(ret[0:n]), nil
return string(ret[:n]), nil
}

// SendCommand sends a command, followed by a CRLF, then wait for / read the server's response.
Expand All @@ -34,4 +39,4 @@ func (conn *Connection) SendCommand(cmd string) (string, error) {
return "", err
}
return conn.ReadResponse()
}
}
31 changes: 29 additions & 2 deletions modules/pop3/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ package pop3

import (
"fmt"

"errors"
"strings"

log "github.com/sirupsen/logrus"
Expand Down Expand Up @@ -163,6 +163,27 @@ func getPOP3Error(response string) error {
return fmt.Errorf("POP3 error: %s", response[1:])
}

// Check the contents of the POP3 header and return a relevant ScanStatus
func VerifyPOP3Contents(banner string) zgrab2.ScanStatus {
lowerBanner := strings.ToLower(banner)
switch {
case strings.HasPrefix(banner, "-ERR "):
return zgrab2.SCAN_APPLICATION_ERROR
case strings.HasPrefix(banner, "+OK "),
strings.Contains(banner, "POP3"),
// These are rare for POP3 if they happen at all,
// But it won't hurt to check just in case as a backup
strings.Contains(lowerBanner, "blacklist"),
strings.Contains(lowerBanner, "abuse"),
strings.Contains(lowerBanner, "rbl"),
strings.Contains(lowerBanner, "spamhaus"),
strings.Contains(lowerBanner, "relay"):
return zgrab2.SCAN_SUCCESS
default:
return zgrab2.SCAN_PROTOCOL_ERROR
}
}

// Scan performs the POP3 scan.
// 1. Open a TCP connection to the target port (default 110).
// 2. If --pop3s is set, perform a TLS handshake using the command-line
Expand Down Expand Up @@ -197,6 +218,12 @@ func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, inter
if err != nil {
return zgrab2.TryGetScanStatus(err), nil, err
}
// Quit early if no valid response
// OR save it to return later
sr := VerifyPOP3Contents(banner)
if sr == zgrab2.SCAN_PROTOCOL_ERROR {
return sr, nil, errors.New("Invalid response for POP3")
}
result.Banner = banner
if scanner.config.SendHELP {
ret, err := conn.SendCommand("HELP")
Expand Down Expand Up @@ -240,5 +267,5 @@ func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, inter
}
result.QUIT = ret
}
return zgrab2.SCAN_SUCCESS, result, nil
return sr, result, nil
}
34 changes: 32 additions & 2 deletions modules/smtp/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import (
)

// ErrInvalidResponse is returned when the server returns an invalid or unexpected response.
var ErrInvalidResponse = errors.New("invalid response")
var ErrInvalidResponse = zgrab2.NewScanError(zgrab2.SCAN_PROTOCOL_ERROR, errors.New("Invalid response for SMTP"))

// ScanResults instances are returned by the module's Scan function.
type ScanResults struct {
Expand Down Expand Up @@ -201,6 +201,27 @@ func getCommand(cmd string, arg string) string {
return cmd + " " + arg
}

// Verify that an SMTP code was returned, and that it is a successful one!
// Return code on SCAN_APPLICATION_ERROR for better info
func VerifySMTPContents(banner string) (zgrab2.ScanStatus, int) {
code, err := getSMTPCode(banner)
lowerBanner := strings.ToLower(banner)
switch {
case err == nil && (code < 200 || code >= 300):
return zgrab2.SCAN_APPLICATION_ERROR, code
case err == nil,
strings.Contains(banner, "STMP"),
strings.Contains(lowerBanner, "blacklist"),
strings.Contains(lowerBanner, "abuse"),
strings.Contains(lowerBanner, "rbl"),
strings.Contains(lowerBanner, "spamhaus"),
strings.Contains(lowerBanner, "relay"):
return zgrab2.SCAN_SUCCESS, 0
default:
return zgrab2.SCAN_PROTOCOL_ERROR, 0
}
}

// Scan performs the SMTP scan.
// 1. Open a TCP connection to the target port (default 25).
// 2. If --smtps is set, perform a TLS handshake.
Expand Down Expand Up @@ -238,6 +259,12 @@ func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, inter
}
return zgrab2.TryGetScanStatus(err), result, err
}
// Quit early if we didn't get a valid response
// OR save response to return later
sr, bannerResponseCode := VerifySMTPContents(banner)
if sr == zgrab2.SCAN_PROTOCOL_ERROR {
return sr, nil, errors.New("Invalid response for SMTP")
}
result.Banner = banner
if scanner.config.SendHELO {
ret, err := conn.SendCommand(getCommand("HELO", scanner.config.HELODomain))
Expand Down Expand Up @@ -292,5 +319,8 @@ func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, inter
}
result.QUIT = ret
}
return zgrab2.SCAN_SUCCESS, result, nil
if sr == zgrab2.SCAN_APPLICATION_ERROR {
return sr, result, fmt.Errorf("SMTP error code %d returned in banner grab", bannerResponseCode)
}
return sr, result, nil
}
9 changes: 5 additions & 4 deletions modules/smtp/smtp.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package smtp
import (
"net"
"regexp"
"io"

"github.com/zmap/zgrab2"
)
Expand All @@ -19,14 +20,14 @@ type Connection struct {
}

// ReadResponse reads from the connection until it matches the smtpEndRegex. Copied from the original zgrab.
// TODO: Catch corner cases, parse out response code.
// TODO: Catch corner cases
func (conn *Connection) ReadResponse() (string, error) {
ret := make([]byte, readBufferSize)
n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, smtpEndRegex)
if err != nil {
return "", nil
if err != nil && err != io.EOF && !zgrab2.IsTimeoutError(err) {
return "", err
}
return string(ret[0:n]), nil
return string(ret[:n]), nil
}

// SendCommand sends a command, followed by a CRLF, then wait for / read the server's response.
Expand Down
4 changes: 4 additions & 0 deletions modules/telnet/telnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ func GetTelnetBanner(logStruct *TelnetLog, conn net.Conn, maxReadSize int) (err
if err != nil && err != io.EOF && !zgrab2.IsTimeoutError(err) {
return err
}
// Make sure it is a telnet banner
if !logStruct.isTelnet() {
return zgrab2.NewScanError(zgrab2.SCAN_PROTOCOL_ERROR, errors.New("Invalid response for Telnet"))
}
return nil
}

Expand Down