port POP3
This commit is contained in:
parent
14572887ab
commit
ad6295f421
11
errors.go
11
errors.go
@ -2,4 +2,15 @@ package zgrab2
|
|||||||
|
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
|
// ErrMismatchedFlags is thrown if the flags for one module type are
|
||||||
|
// passed to an incompatible module type.
|
||||||
var ErrMismatchedFlags = errors.New("mismatched flag/module")
|
var ErrMismatchedFlags = errors.New("mismatched flag/module")
|
||||||
|
|
||||||
|
// ErrInvalidArguments is thrown if the command-line arguments invalid.
|
||||||
|
var ErrInvalidArguments = errors.New("invalid arguments")
|
||||||
|
|
||||||
|
// ErrInvalidResponse is returned when the server returns a syntactically-invalid response.
|
||||||
|
var ErrInvalidResponse = errors.New("invalid response")
|
||||||
|
|
||||||
|
// ErrUnexpectedResponse is returned when the server returns a syntactically-valid but unexpected response.
|
||||||
|
var ErrUnexpectedResponse = errors.New("unexpected response")
|
||||||
|
9
integration_tests/pop3/cleanup.sh
Executable file
9
integration_tests/pop3/cleanup.sh
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set +e
|
||||||
|
|
||||||
|
echo "pop3/cleanup: Tests cleanup for pop3"
|
||||||
|
|
||||||
|
CONTAINER_NAME=zgrab_pop3
|
||||||
|
|
||||||
|
docker stop $CONTAINER_NAME
|
9
integration_tests/pop3/container/Dockerfile
Normal file
9
integration_tests/pop3/container/Dockerfile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
FROM zgrab2_service_base:latest
|
||||||
|
|
||||||
|
RUN apt-get install -y popa3d
|
||||||
|
|
||||||
|
WORKDIR /
|
||||||
|
COPY entrypoint.sh .
|
||||||
|
RUN chmod a+x ./entrypoint.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
9
integration_tests/pop3/container/entrypoint.sh
Executable file
9
integration_tests/pop3/container/entrypoint.sh
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -x
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
watch /usr/sbin/popa3d -D
|
||||||
|
echo "popa3d exited unexpectedly. Restarting..."
|
||||||
|
sleep 1
|
||||||
|
done
|
27
integration_tests/pop3/setup.sh
Executable file
27
integration_tests/pop3/setup.sh
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
#############################
|
||||||
|
## FIXME: REMOVE -p 110:110 #
|
||||||
|
#############################
|
||||||
|
|
||||||
|
echo "pop3/setup: Tests setup for pop3"
|
||||||
|
|
||||||
|
CONTAINER_TAG="zgrab_pop3"
|
||||||
|
CONTAINER_NAME="zgrab_pop3"
|
||||||
|
|
||||||
|
# If the container is already running, use it.
|
||||||
|
if docker ps --filter "name=$CONTAINER_NAME" | grep -q $CONTAINER_NAME; then
|
||||||
|
echo "pop3/setup: Container $CONTAINER_NAME already running -- nothing to setup"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If it is not running, try launching it -- on success, use that.
|
||||||
|
echo "pop3/setup: Trying to launch $CONTAINER_NAME..."
|
||||||
|
if ! docker run -p 110:110 --rm --name $CONTAINER_NAME -td $CONTAINER_TAG; then
|
||||||
|
echo "pop3/setup: Building docker image $CONTAINER_TAG..."
|
||||||
|
# If it fails, build it from ./container/Dockerfile
|
||||||
|
docker build -t $CONTAINER_TAG ./container
|
||||||
|
# Try again
|
||||||
|
echo "pop3/setup: Launching $CONTAINER_NAME..."
|
||||||
|
docker run --rm --name $CONTAINER_NAME -p 110:110 -td $CONTAINER_TAG
|
||||||
|
fi
|
41
integration_tests/pop3/test.sh
Executable file
41
integration_tests/pop3/test.sh
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
MODULE_DIR=$(dirname $0)
|
||||||
|
ZGRAB_ROOT=$MODULE_DIR/../..
|
||||||
|
ZGRAB_OUTPUT=$ZGRAB_ROOT/zgrab-output
|
||||||
|
|
||||||
|
mkdir -p $ZGRAB_OUTPUT/pop3
|
||||||
|
|
||||||
|
CONTAINER_NAME=zgrab_pop3
|
||||||
|
|
||||||
|
OUTPUT_ROOT=$ZGRAB_OUTPUT/pop3
|
||||||
|
|
||||||
|
echo "pop3/test: Tests runner for pop3"
|
||||||
|
CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh pop3 > $OUTPUT_ROOT/banner.json
|
||||||
|
CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh pop3 --quit > $OUTPUT_ROOT/banner.quit.json
|
||||||
|
CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh pop3 --help --quit > $OUTPUT_ROOT/help.banner.quit.json
|
||||||
|
CONTAINER_NAME=$CONTAINER_NAME $ZGRAB_ROOT/docker-runner/docker-run.sh pop3 -noop --help --quit > $OUTPUT_ROOT/noop.help.banner.quit.json
|
||||||
|
|
||||||
|
# TODO: the pop3 container does not support STARTTLS; they suggest
|
||||||
|
# wrapping it in stunnel (which would handle the --pop3s case).
|
||||||
|
|
||||||
|
FIELDS="help quit banner noop"
|
||||||
|
status=0
|
||||||
|
for field in $FIELDS; do
|
||||||
|
for file in $(find $OUTPUT_ROOT -iname "*$field*.json"); do
|
||||||
|
echo "check $file for $field"
|
||||||
|
RESULT=$($ZGRAB_ROOT/jp data.pop3.result.$field < $file)
|
||||||
|
if [ "$RESULT" = "null" ]; then
|
||||||
|
echo "Did not find $field in $file [["
|
||||||
|
cat $file
|
||||||
|
echo "]]"
|
||||||
|
status=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
# Dump the docker logs
|
||||||
|
echo "pop3/test: BEGIN docker logs from $CONTAINER_NAME [{("
|
||||||
|
docker logs --tail all $CONTAINER_NAME
|
||||||
|
echo ")}] END docker logs from $CONTAINER_NAME"
|
7
modules/pop3.go
Normal file
7
modules/pop3.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package modules
|
||||||
|
|
||||||
|
import "github.com/zmap/zgrab2/modules/pop3"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
pop3.RegisterModule()
|
||||||
|
}
|
37
modules/pop3/pop3.go
Normal file
37
modules/pop3/pop3.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package pop3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/zmap/zgrab2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is the regex used in zgrab.
|
||||||
|
var pop3EndRegex = regexp.MustCompile(`(?:\r\n\.\r\n$)|(?:\r\n$)`)
|
||||||
|
|
||||||
|
const readBufferSize int = 0x10000
|
||||||
|
|
||||||
|
// Connection wraps the state and access to the SMTP connection.
|
||||||
|
type Connection struct {
|
||||||
|
Conn net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadResponse reads from the connection until it matches the pop3EndRegex. Copied from the original zgrab.
|
||||||
|
// TODO: Catch corner cases, parse out success/error character.
|
||||||
|
func (conn *Connection) ReadResponse() (string, error) {
|
||||||
|
ret := make([]byte, readBufferSize)
|
||||||
|
n, err := zgrab2.ReadUntilRegex(conn.Conn, ret, pop3EndRegex)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return string(ret[0:n]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendCommand sends a command, followed by a CRLF, then wait for / read the server's response.
|
||||||
|
func (conn *Connection) SendCommand(cmd string) (string, error) {
|
||||||
|
if _, err := conn.Conn.Write([]byte(cmd + "\r\n")); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return conn.ReadResponse()
|
||||||
|
}
|
239
modules/pop3/scanner.go
Normal file
239
modules/pop3/scanner.go
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
// Package pop3 provides a zgrab2 module that scans for POP3 mail
|
||||||
|
// servers.
|
||||||
|
// Default Port: 110 (TCP)
|
||||||
|
//
|
||||||
|
// The --send-help and --send-noop flags tell the scanner to send a
|
||||||
|
// HELP or NOOP command and read the response.
|
||||||
|
//
|
||||||
|
// The --pop3s flag tells the scanner to perform a TLS handshake
|
||||||
|
// immediately after connecting, before even attempting to read
|
||||||
|
// the banner.
|
||||||
|
// The --starttls flag tells the scanner to send the STLS command,
|
||||||
|
// and then negotiate a TLS connection.
|
||||||
|
// The scanner uses the standard TLS flags for the handshake.
|
||||||
|
// --pop3s and --starttls are mutually exclusive.
|
||||||
|
// --pop3s does not change the default port number from 110, so
|
||||||
|
// it should usually be coupled with e.g. --port 995.
|
||||||
|
//
|
||||||
|
// The --send-quit flag tells the scanner to send a QUIT command
|
||||||
|
// before disconnecting.
|
||||||
|
//
|
||||||
|
// So, if no flags are specified, the scanner simply reads the banner
|
||||||
|
// returned by the server and disconnects.
|
||||||
|
//
|
||||||
|
// The output contains the banner and the responses to any commands that
|
||||||
|
// were sent, and if or --pop3s --starttls were set, the standard TLS logs.
|
||||||
|
package pop3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/zmap/zgrab2"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// ScanResults instances are returned by the module's Scan function.
|
||||||
|
type ScanResults struct {
|
||||||
|
// Banner is the string sent by the server immediately after connecting.
|
||||||
|
Banner string `json:"banner,omitempty"`
|
||||||
|
|
||||||
|
// NOOP is the server's response to the NOOP command, if one is sent.
|
||||||
|
NOOP string `json:"noop,omitempty"`
|
||||||
|
|
||||||
|
// HELP is the server's response to the HELP command, if it is sent.
|
||||||
|
HELP string `json:"help,omitempty"`
|
||||||
|
|
||||||
|
// StartTLS is the server's response to the STARTTLS command, if it is sent.
|
||||||
|
StartTLS string `json:"starttls,omitempty"`
|
||||||
|
|
||||||
|
// QUIT is the server's response to the QUIT command, if it is sent.
|
||||||
|
QUIT string `json:"quit,omitempty"`
|
||||||
|
|
||||||
|
// TLSLog is the standard TLS log, if --starttls or --pop3s is enabled.
|
||||||
|
TLSLog *zgrab2.TLSLog `json:"tls,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flags holds the command-line configuration for the POP3 scan module.
|
||||||
|
// Populated by the framework.
|
||||||
|
type Flags struct {
|
||||||
|
zgrab2.BaseFlags
|
||||||
|
zgrab2.TLSFlags
|
||||||
|
|
||||||
|
// SendHELP indicates that the client should send the HELP command.
|
||||||
|
SendHELP bool `long:"send-help" description:"Send the HELP command"`
|
||||||
|
|
||||||
|
// SendNOOP indicates that the NOOP command should be sent.
|
||||||
|
SendNOOP bool `long:"send-noop" description:"Send the NOOP command before closing."`
|
||||||
|
|
||||||
|
// SendQUIT indicates that the QUIT command should be sent.
|
||||||
|
SendQUIT bool `long:"send-quit" description:"Send the QUIT command before closing."`
|
||||||
|
|
||||||
|
// POP3Secure indicates that the client should do a TLS handshake immediately after connecting.
|
||||||
|
POP3Secure bool `long:"pop3s" description:"Immediately negotiate a TLS connection"`
|
||||||
|
|
||||||
|
// StartTLS indicates that the client should attempt to update the connection to TLS.
|
||||||
|
StartTLS bool `long:"starttls" description:"Send STLS before negotiating"`
|
||||||
|
|
||||||
|
// Verbose indicates that there should be more verbose logging.
|
||||||
|
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("pop3", "pop3", "Probe for pop3", 110, &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 {
|
||||||
|
if flags.StartTLS && flags.POP3Secure {
|
||||||
|
log.Error("Cannot send both --starttls and --pop3s")
|
||||||
|
return zgrab2.ErrInvalidArguments
|
||||||
|
}
|
||||||
|
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 "pop3"
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPort returns the port being scanned.
|
||||||
|
func (scanner *Scanner) GetPort() uint {
|
||||||
|
return scanner.config.Port
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPOP3Error(response string) error {
|
||||||
|
if !strings.HasPrefix(response, "-") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("POP3 error: %s", response[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// flags.
|
||||||
|
// 3. Read the banner.
|
||||||
|
// 4. If --send-help is sent, send HELP, read the result.
|
||||||
|
// 5. If --send-noop is sent, send NOOP, read the result.
|
||||||
|
// 6. If --starttls is sent, send STLS, read the result, negotiate a
|
||||||
|
// TLS connection using the command-line flags.
|
||||||
|
// 7. If --send-quit is sent, send QUIT and read the result.
|
||||||
|
// 8. Close the connection.
|
||||||
|
func (scanner *Scanner) Scan(target zgrab2.ScanTarget) (zgrab2.ScanStatus, interface{}, error) {
|
||||||
|
c, err := target.Open(&scanner.config.BaseFlags)
|
||||||
|
if err != nil {
|
||||||
|
return zgrab2.TryGetScanStatus(err), nil, err
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
result := &ScanResults{}
|
||||||
|
if scanner.config.POP3Secure {
|
||||||
|
tlsConn, err := scanner.config.TLSFlags.GetTLSConnection(c)
|
||||||
|
if err != nil {
|
||||||
|
return zgrab2.TryGetScanStatus(err), nil, err
|
||||||
|
}
|
||||||
|
result.TLSLog = tlsConn.GetLog()
|
||||||
|
if err := tlsConn.Handshake(); err != nil {
|
||||||
|
return zgrab2.TryGetScanStatus(err), result, err
|
||||||
|
}
|
||||||
|
c = tlsConn
|
||||||
|
}
|
||||||
|
conn := Connection{Conn: c}
|
||||||
|
banner, err := conn.ReadResponse()
|
||||||
|
if err != nil {
|
||||||
|
return zgrab2.TryGetScanStatus(err), nil, err
|
||||||
|
}
|
||||||
|
result.Banner = banner
|
||||||
|
if scanner.config.SendHELP {
|
||||||
|
ret, err := conn.SendCommand("HELP")
|
||||||
|
if err != nil {
|
||||||
|
return zgrab2.TryGetScanStatus(err), result, err
|
||||||
|
}
|
||||||
|
result.HELP = ret
|
||||||
|
}
|
||||||
|
if scanner.config.SendNOOP {
|
||||||
|
ret, err := conn.SendCommand("NOOP")
|
||||||
|
if err != nil {
|
||||||
|
return zgrab2.TryGetScanStatus(err), result, err
|
||||||
|
}
|
||||||
|
result.NOOP = ret
|
||||||
|
}
|
||||||
|
if scanner.config.StartTLS {
|
||||||
|
ret, err := conn.SendCommand("STLS")
|
||||||
|
if err != nil {
|
||||||
|
return zgrab2.TryGetScanStatus(err), result, err
|
||||||
|
}
|
||||||
|
result.StartTLS = ret
|
||||||
|
if err := getPOP3Error(ret); err != nil {
|
||||||
|
return zgrab2.TryGetScanStatus(err), result, err
|
||||||
|
}
|
||||||
|
tlsConn, err := scanner.config.TLSFlags.GetTLSConnection(conn.Conn)
|
||||||
|
if err != nil {
|
||||||
|
return zgrab2.TryGetScanStatus(err), result, err
|
||||||
|
}
|
||||||
|
result.TLSLog = tlsConn.GetLog()
|
||||||
|
if err := tlsConn.Handshake(); err != nil {
|
||||||
|
return zgrab2.TryGetScanStatus(err), result, err
|
||||||
|
}
|
||||||
|
conn.Conn = tlsConn
|
||||||
|
}
|
||||||
|
if scanner.config.SendQUIT {
|
||||||
|
ret, err := conn.SendCommand("QUIT")
|
||||||
|
if err != nil {
|
||||||
|
if err != nil {
|
||||||
|
return zgrab2.TryGetScanStatus(err), nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.QUIT = ret
|
||||||
|
}
|
||||||
|
return zgrab2.SCAN_SUCCESS, result, nil
|
||||||
|
}
|
@ -8,3 +8,4 @@ import schemas.ntp
|
|||||||
import schemas.mssql
|
import schemas.mssql
|
||||||
import schemas.redis
|
import schemas.redis
|
||||||
import schemas.telnet
|
import schemas.telnet
|
||||||
|
import schemas.pop3
|
||||||
|
23
schemas/pop3.py
Normal file
23
schemas/pop3.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# zschema sub-schema for zgrab2's pop3 module
|
||||||
|
# Registers zgrab2-pop3 globally, and pop3 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
|
||||||
|
|
||||||
|
pop3_scan_response = SubRecord({
|
||||||
|
"result": SubRecord({
|
||||||
|
"banner": String(),
|
||||||
|
"noop": String(),
|
||||||
|
"help": String(),
|
||||||
|
"starttls": String(),
|
||||||
|
"quit": String(),
|
||||||
|
"tls": zgrab2.tls_log,
|
||||||
|
})
|
||||||
|
}, extends=zgrab2.base_scan_response)
|
||||||
|
|
||||||
|
zschema.registry.register_schema("zgrab2-pop3", pop3_scan_response)
|
||||||
|
|
||||||
|
zgrab2.register_scan_response_type("pop3", pop3_scan_response)
|
Loading…
Reference in New Issue
Block a user