zgrab2/processing.go

220 lines
6.0 KiB
Go
Raw Normal View History

package zgrab2
2017-07-14 20:44:36 +00:00
import (
"encoding/json"
Implements postgres zgrab2 module (#30) * remove unnecessary indirection on net.Conn * Ignore *.pyc * fix NPE on nil handshake * refactoring -- move status to status.go; add Open() methods for ScanTarget * cherry-pick .gitignore fix * pull in TLS fix * status.go comments * trim over-generalizations * use /usr/bin/env bash instead of absolute path * remove debug tcpwrap * add integration tests for postgres * hack for cleanup.sh to work on mingw -- use //var/lib instead of /var/lib * cleanup should actually stop the process though * comments / rearrange * Bump up timeout in postgres tests; only pass user if explicitly requested to do so * add schema stubs to new.sh * Integration test fixes -- use /usr/bin/env bash; log all validation failures * add postgres schemas * fill out zcrypto.client_hello schema * handle early get of TLSLog * postgres: return SCAN_SUCCESS on success * cleanup * fix new.sh * fix typo * postgres container cleanup * build.sh docs * standardize container/image names * add not to check for success * shift mysql's connection management to ScanTarget.Open(); wrap Read/Write methods returned by ScanTarget.Open() to enforce timeouts * catch schematically-valid but non-successful scans * postgres: clean up output format; more scanning * cleanup; better error handling; get detailed protocol version error * refactor modules * clean up dangling connections * split gigantic postgres.go * remove unused * ServerParams gets its own type * refactor integration tests: run zgrab2 in its own container, which is linked to the service containers, so that we don't need to keep track of unique ports on the host any more * rename entrypoint; remove duplicate postgres tests * comments for postgres schema * Use param expansion to check for env variable [minor] This is a *very* minor change to `docker-runner/docker-run.sh` checks to see if the environment variable required to run the script has been set to a non-empty string. If not, the script exits with a non-zero status code and displays a default message: ``` ❯ docker-runner/docker-run.sh docker-runner/docker-run.sh: line 7: CONTAINER_NAME: parameter null or not set ``` This was the behavior before, but just uses a one-liner declarative bash idiom. For further reading on parameter expansion, see https://stackoverflow.com/a/307735. @justinbastress can tell me if I did something wrong and broke the intent of the script :-) * Add integration_test targets to makefile; use makefile instead of directly calling go build everywhere; run postgres schema through PEP8 linter * use make in docker-runner entrypoint * add .integration_test_setup to .gitignore * more .gitignore items * Makefile updates: Windows support; add docker-runner target; better cleanup. * docker-runner Dockerfile: start from zgrab2_runner_base image * cleanup postgres setup * make travis use make * add .gitattributes, try to prevent it from overriding lfs with crlfs in shell scripts at least * fix folder name in Makefile * update go (one of our dependencies now works only with >= 1.9) * From travis: `I don't have any idea what to do with '1.9.0'.` * explicit clean make * fix dep order * fix build.sh location * popd * use make to ensure zgrab2_runner exists * Make docker-runner an order-dependency for integration-test-cleanup; don't do a cleanup after each integration test * use explicit tag name for zgrab2_runner * Add container-clean target to Makefile, to remove cyclic dependency on docker; use .id files to track docker images; add servce-base image; use Make to build / track images * use LF in Makefiles; update .gitignore; use zgrab_service_base image in ssh container; fix line endings (?) * remove overzealous cleanup * let setup continue even if some containers are already running * zgrab depends on *.go * docker-runner depends on zgrab2 binary * clean output before running integration tests
2018-01-15 19:24:57 +00:00
"fmt"
2017-08-04 20:20:32 +00:00
"net"
2017-07-14 20:44:36 +00:00
"sync"
log "github.com/sirupsen/logrus"
2018-03-30 20:28:24 +00:00
"github.com/zmap/zgrab2/lib/output"
2017-07-14 20:44:36 +00:00
)
2017-10-04 03:57:56 +00:00
// Grab contains all scan responses for a single host
type Grab struct {
2017-09-26 18:02:27 +00:00
IP string `json:"ip,omitempty"`
Domain string `json:"domain,omitempty"`
Data map[string]ScanResponse `json:"data,omitempty"`
2017-07-14 20:44:36 +00:00
}
2017-10-04 03:57:56 +00:00
// ScanTarget is the host that will be scanned
2017-09-26 18:02:27 +00:00
type ScanTarget struct {
2017-08-04 20:20:32 +00:00
IP net.IP
Domain string
2018-06-26 17:51:10 +00:00
Tag string
Port *uint
2017-08-04 20:20:32 +00:00
}
2018-02-07 17:25:46 +00:00
func (target ScanTarget) String() string {
if target.IP == nil && target.Domain == "" {
2018-01-31 15:14:52 +00:00
return "<empty target>"
2018-06-26 17:51:10 +00:00
}
res := ""
if target.IP != nil && target.Domain != "" {
res = target.Domain + "(" + target.IP.String() + ")"
2018-02-07 17:25:46 +00:00
} else if target.IP != nil {
2018-06-26 17:51:10 +00:00
res = target.IP.String()
} else {
res = target.Domain
2018-01-31 15:14:52 +00:00
}
2018-06-26 17:51:10 +00:00
if target.Tag != "" {
res += " tag:" + target.Tag
2018-06-26 17:51:10 +00:00
}
return res
2018-01-31 15:14:52 +00:00
}
// Host gets the host identifier as a string: the IP address if it is available,
// or the domain if not.
func (target *ScanTarget) Host() string {
if target.IP != nil {
return target.IP.String()
} else if target.Domain != "" {
return target.Domain
}
log.Fatalf("Bad target %s: no IP/Domain", target.String())
panic("unreachable")
}
2018-02-07 17:25:46 +00:00
// Open connects to the ScanTarget using the configured flags, and returns a net.Conn that uses the configured timeouts for Read/Write operations.
func (target *ScanTarget) Open(flags *BaseFlags) (net.Conn, error) {
var port uint
// If the port is supplied in ScanTarget, let that override the cmdline option
if target.Port != nil {
port = *target.Port
} else {
port = flags.Port
}
address := net.JoinHostPort(target.Host(), fmt.Sprintf("%d", port))
return DialTimeoutConnection("tcp", address, flags.Timeout, flags.BytesReadLimit)
Implements postgres zgrab2 module (#30) * remove unnecessary indirection on net.Conn * Ignore *.pyc * fix NPE on nil handshake * refactoring -- move status to status.go; add Open() methods for ScanTarget * cherry-pick .gitignore fix * pull in TLS fix * status.go comments * trim over-generalizations * use /usr/bin/env bash instead of absolute path * remove debug tcpwrap * add integration tests for postgres * hack for cleanup.sh to work on mingw -- use //var/lib instead of /var/lib * cleanup should actually stop the process though * comments / rearrange * Bump up timeout in postgres tests; only pass user if explicitly requested to do so * add schema stubs to new.sh * Integration test fixes -- use /usr/bin/env bash; log all validation failures * add postgres schemas * fill out zcrypto.client_hello schema * handle early get of TLSLog * postgres: return SCAN_SUCCESS on success * cleanup * fix new.sh * fix typo * postgres container cleanup * build.sh docs * standardize container/image names * add not to check for success * shift mysql's connection management to ScanTarget.Open(); wrap Read/Write methods returned by ScanTarget.Open() to enforce timeouts * catch schematically-valid but non-successful scans * postgres: clean up output format; more scanning * cleanup; better error handling; get detailed protocol version error * refactor modules * clean up dangling connections * split gigantic postgres.go * remove unused * ServerParams gets its own type * refactor integration tests: run zgrab2 in its own container, which is linked to the service containers, so that we don't need to keep track of unique ports on the host any more * rename entrypoint; remove duplicate postgres tests * comments for postgres schema * Use param expansion to check for env variable [minor] This is a *very* minor change to `docker-runner/docker-run.sh` checks to see if the environment variable required to run the script has been set to a non-empty string. If not, the script exits with a non-zero status code and displays a default message: ``` ❯ docker-runner/docker-run.sh docker-runner/docker-run.sh: line 7: CONTAINER_NAME: parameter null or not set ``` This was the behavior before, but just uses a one-liner declarative bash idiom. For further reading on parameter expansion, see https://stackoverflow.com/a/307735. @justinbastress can tell me if I did something wrong and broke the intent of the script :-) * Add integration_test targets to makefile; use makefile instead of directly calling go build everywhere; run postgres schema through PEP8 linter * use make in docker-runner entrypoint * add .integration_test_setup to .gitignore * more .gitignore items * Makefile updates: Windows support; add docker-runner target; better cleanup. * docker-runner Dockerfile: start from zgrab2_runner_base image * cleanup postgres setup * make travis use make * add .gitattributes, try to prevent it from overriding lfs with crlfs in shell scripts at least * fix folder name in Makefile * update go (one of our dependencies now works only with >= 1.9) * From travis: `I don't have any idea what to do with '1.9.0'.` * explicit clean make * fix dep order * fix build.sh location * popd * use make to ensure zgrab2_runner exists * Make docker-runner an order-dependency for integration-test-cleanup; don't do a cleanup after each integration test * use explicit tag name for zgrab2_runner * Add container-clean target to Makefile, to remove cyclic dependency on docker; use .id files to track docker images; add servce-base image; use Make to build / track images * use LF in Makefiles; update .gitignore; use zgrab_service_base image in ssh container; fix line endings (?) * remove overzealous cleanup * let setup continue even if some containers are already running * zgrab depends on *.go * docker-runner depends on zgrab2 binary * clean output before running integration tests
2018-01-15 19:24:57 +00:00
}
// OpenTLS connects to the ScanTarget using the configured flags, then performs
// the TLS handshake. On success error is nil, but the connection can be non-nil
// even if there is an error (this allows fetching the handshake log).
func (target *ScanTarget) OpenTLS(baseFlags *BaseFlags, tlsFlags *TLSFlags) (*TLSConnection, error) {
conn, err := tlsFlags.Connect(target, baseFlags)
if err != nil {
return conn, err
}
err = conn.Handshake()
return conn, err
}
2018-02-07 17:25:46 +00:00
// OpenUDP connects to the ScanTarget using the configured flags, and returns a net.Conn that uses the configured timeouts for Read/Write operations.
// Note that the UDP "connection" does not have an associated timeout.
func (target *ScanTarget) OpenUDP(flags *BaseFlags, udp *UDPFlags) (net.Conn, error) {
var port uint
// If the port is supplied in ScanTarget, let that override the cmdline option
if target.Port != nil {
port = *target.Port
} else {
port = flags.Port
}
address := net.JoinHostPort(target.Host(), fmt.Sprintf("%d", port))
2018-02-07 17:25:46 +00:00
var local *net.UDPAddr
if udp != nil && (udp.LocalAddress != "" || udp.LocalPort != 0) {
local = &net.UDPAddr{}
if udp.LocalAddress != "" && udp.LocalAddress != "*" {
local.IP = net.ParseIP(udp.LocalAddress)
}
if udp.LocalPort != 0 {
local.Port = int(udp.LocalPort)
Implements postgres zgrab2 module (#30) * remove unnecessary indirection on net.Conn * Ignore *.pyc * fix NPE on nil handshake * refactoring -- move status to status.go; add Open() methods for ScanTarget * cherry-pick .gitignore fix * pull in TLS fix * status.go comments * trim over-generalizations * use /usr/bin/env bash instead of absolute path * remove debug tcpwrap * add integration tests for postgres * hack for cleanup.sh to work on mingw -- use //var/lib instead of /var/lib * cleanup should actually stop the process though * comments / rearrange * Bump up timeout in postgres tests; only pass user if explicitly requested to do so * add schema stubs to new.sh * Integration test fixes -- use /usr/bin/env bash; log all validation failures * add postgres schemas * fill out zcrypto.client_hello schema * handle early get of TLSLog * postgres: return SCAN_SUCCESS on success * cleanup * fix new.sh * fix typo * postgres container cleanup * build.sh docs * standardize container/image names * add not to check for success * shift mysql's connection management to ScanTarget.Open(); wrap Read/Write methods returned by ScanTarget.Open() to enforce timeouts * catch schematically-valid but non-successful scans * postgres: clean up output format; more scanning * cleanup; better error handling; get detailed protocol version error * refactor modules * clean up dangling connections * split gigantic postgres.go * remove unused * ServerParams gets its own type * refactor integration tests: run zgrab2 in its own container, which is linked to the service containers, so that we don't need to keep track of unique ports on the host any more * rename entrypoint; remove duplicate postgres tests * comments for postgres schema * Use param expansion to check for env variable [minor] This is a *very* minor change to `docker-runner/docker-run.sh` checks to see if the environment variable required to run the script has been set to a non-empty string. If not, the script exits with a non-zero status code and displays a default message: ``` ❯ docker-runner/docker-run.sh docker-runner/docker-run.sh: line 7: CONTAINER_NAME: parameter null or not set ``` This was the behavior before, but just uses a one-liner declarative bash idiom. For further reading on parameter expansion, see https://stackoverflow.com/a/307735. @justinbastress can tell me if I did something wrong and broke the intent of the script :-) * Add integration_test targets to makefile; use makefile instead of directly calling go build everywhere; run postgres schema through PEP8 linter * use make in docker-runner entrypoint * add .integration_test_setup to .gitignore * more .gitignore items * Makefile updates: Windows support; add docker-runner target; better cleanup. * docker-runner Dockerfile: start from zgrab2_runner_base image * cleanup postgres setup * make travis use make * add .gitattributes, try to prevent it from overriding lfs with crlfs in shell scripts at least * fix folder name in Makefile * update go (one of our dependencies now works only with >= 1.9) * From travis: `I don't have any idea what to do with '1.9.0'.` * explicit clean make * fix dep order * fix build.sh location * popd * use make to ensure zgrab2_runner exists * Make docker-runner an order-dependency for integration-test-cleanup; don't do a cleanup after each integration test * use explicit tag name for zgrab2_runner * Add container-clean target to Makefile, to remove cyclic dependency on docker; use .id files to track docker images; add servce-base image; use Make to build / track images * use LF in Makefiles; update .gitignore; use zgrab_service_base image in ssh container; fix line endings (?) * remove overzealous cleanup * let setup continue even if some containers are already running * zgrab depends on *.go * docker-runner depends on zgrab2 binary * clean output before running integration tests
2018-01-15 19:24:57 +00:00
}
}
2018-02-07 17:25:46 +00:00
remote, err := net.ResolveUDPAddr("udp", address)
if err != nil {
return nil, err
}
conn, err := net.DialUDP("udp", local, remote)
if err != nil {
return nil, err
}
return NewTimeoutConnection(nil, conn, flags.Timeout, 0, 0, flags.BytesReadLimit), nil
Implements postgres zgrab2 module (#30) * remove unnecessary indirection on net.Conn * Ignore *.pyc * fix NPE on nil handshake * refactoring -- move status to status.go; add Open() methods for ScanTarget * cherry-pick .gitignore fix * pull in TLS fix * status.go comments * trim over-generalizations * use /usr/bin/env bash instead of absolute path * remove debug tcpwrap * add integration tests for postgres * hack for cleanup.sh to work on mingw -- use //var/lib instead of /var/lib * cleanup should actually stop the process though * comments / rearrange * Bump up timeout in postgres tests; only pass user if explicitly requested to do so * add schema stubs to new.sh * Integration test fixes -- use /usr/bin/env bash; log all validation failures * add postgres schemas * fill out zcrypto.client_hello schema * handle early get of TLSLog * postgres: return SCAN_SUCCESS on success * cleanup * fix new.sh * fix typo * postgres container cleanup * build.sh docs * standardize container/image names * add not to check for success * shift mysql's connection management to ScanTarget.Open(); wrap Read/Write methods returned by ScanTarget.Open() to enforce timeouts * catch schematically-valid but non-successful scans * postgres: clean up output format; more scanning * cleanup; better error handling; get detailed protocol version error * refactor modules * clean up dangling connections * split gigantic postgres.go * remove unused * ServerParams gets its own type * refactor integration tests: run zgrab2 in its own container, which is linked to the service containers, so that we don't need to keep track of unique ports on the host any more * rename entrypoint; remove duplicate postgres tests * comments for postgres schema * Use param expansion to check for env variable [minor] This is a *very* minor change to `docker-runner/docker-run.sh` checks to see if the environment variable required to run the script has been set to a non-empty string. If not, the script exits with a non-zero status code and displays a default message: ``` ❯ docker-runner/docker-run.sh docker-runner/docker-run.sh: line 7: CONTAINER_NAME: parameter null or not set ``` This was the behavior before, but just uses a one-liner declarative bash idiom. For further reading on parameter expansion, see https://stackoverflow.com/a/307735. @justinbastress can tell me if I did something wrong and broke the intent of the script :-) * Add integration_test targets to makefile; use makefile instead of directly calling go build everywhere; run postgres schema through PEP8 linter * use make in docker-runner entrypoint * add .integration_test_setup to .gitignore * more .gitignore items * Makefile updates: Windows support; add docker-runner target; better cleanup. * docker-runner Dockerfile: start from zgrab2_runner_base image * cleanup postgres setup * make travis use make * add .gitattributes, try to prevent it from overriding lfs with crlfs in shell scripts at least * fix folder name in Makefile * update go (one of our dependencies now works only with >= 1.9) * From travis: `I don't have any idea what to do with '1.9.0'.` * explicit clean make * fix dep order * fix build.sh location * popd * use make to ensure zgrab2_runner exists * Make docker-runner an order-dependency for integration-test-cleanup; don't do a cleanup after each integration test * use explicit tag name for zgrab2_runner * Add container-clean target to Makefile, to remove cyclic dependency on docker; use .id files to track docker images; add servce-base image; use Make to build / track images * use LF in Makefiles; update .gitignore; use zgrab_service_base image in ssh container; fix line endings (?) * remove overzealous cleanup * let setup continue even if some containers are already running * zgrab depends on *.go * docker-runner depends on zgrab2 binary * clean output before running integration tests
2018-01-15 19:24:57 +00:00
}
2017-08-30 19:39:25 +00:00
// grabTarget calls handler for each action
2017-09-26 18:02:27 +00:00
func grabTarget(input ScanTarget, m *Monitor) []byte {
moduleResult := make(map[string]ScanResponse)
2017-09-26 18:02:27 +00:00
for _, scannerName := range orderedScanners {
2018-06-26 17:51:10 +00:00
scanner := scanners[scannerName]
trigger := (*scanner).GetTrigger()
2018-06-27 18:07:07 +00:00
if input.Tag != trigger {
2018-06-26 17:51:10 +00:00
continue
}
2018-01-31 15:14:52 +00:00
defer func(name string) {
if e := recover(); e != nil {
2018-05-11 14:57:27 +00:00
log.Errorf("Panic on scanner %s when scanning target %s: %#v", scannerName, input.String(), e)
2018-01-31 15:14:52 +00:00
// Bubble out original error (with original stack) in lieu of explicitly logging the stack / error
panic(e)
}
}(scannerName)
2017-10-04 15:32:42 +00:00
name, res := RunScanner(*scanner, m, input)
2017-08-09 20:13:18 +00:00
moduleResult[name] = res
if res.Error != nil && !config.Multiple.ContinueOnError {
break
2017-07-14 20:44:36 +00:00
}
if res.Status == SCAN_SUCCESS && config.Multiple.BreakOnSuccess {
break
}
2017-07-14 20:44:36 +00:00
}
2017-08-30 19:39:25 +00:00
var ipstr string
if input.IP == nil {
ipstr = ""
2017-08-04 20:20:32 +00:00
} else {
s := input.IP.String()
2017-08-30 19:39:25 +00:00
ipstr = s
2017-08-04 20:20:32 +00:00
}
raw := Grab{IP: ipstr, Domain: input.Domain, Data: moduleResult}
var outputData interface{} = raw
if !includeDebugOutput() {
// If the caller doesn't explicitly request debug data, strip it out.
// Take advantage of the fact that we can skip the (expensive) call to
// process if debug output is included (TODO: until Process does anything else)
processor := output.Processor{Verbose: false}
stripped, err := processor.Process(raw)
if err != nil {
log.Debugf("Error processing results: %v", err)
stripped = raw
}
outputData = stripped
}
2018-03-30 20:28:24 +00:00
result, err := json.Marshal(outputData)
2017-08-04 20:20:32 +00:00
if err != nil {
2017-08-30 19:39:25 +00:00
log.Fatalf("unable to marshal data: %s", err)
2017-08-04 20:20:32 +00:00
}
2017-07-14 20:44:36 +00:00
return result
}
// Process sets up an output encoder, input reader, and starts grab workers.
func Process(mon *Monitor) {
workers := config.Senders
2017-09-26 18:02:27 +00:00
processQueue := make(chan ScanTarget, workers*4)
outputQueue := make(chan []byte, workers*4)
2017-07-14 20:44:36 +00:00
//Create wait groups
var workerDone sync.WaitGroup
var outputDone sync.WaitGroup
workerDone.Add(int(workers))
outputDone.Add(1)
// Start the output encoder
go func() {
defer outputDone.Done()
if err := config.outputResults(outputQueue); err != nil {
log.Fatal(err)
2017-07-14 20:44:36 +00:00
}
}()
//Start all the workers
for i := 0; i < workers; i++ {
go func(i int) {
for _, scannerName := range orderedScanners {
scanner := *scanners[scannerName]
scanner.InitPerSender(i)
}
2017-07-14 20:44:36 +00:00
for obj := range processQueue {
2017-09-05 02:31:44 +00:00
for run := uint(0); run < uint(config.ConnectionsPerHost); run++ {
2017-09-04 22:44:58 +00:00
result := grabTarget(obj, mon)
outputQueue <- result
}
2017-07-14 20:44:36 +00:00
}
workerDone.Done()
}(i)
2017-07-14 20:44:36 +00:00
}
if err := config.inputTargets(processQueue); err != nil {
log.Fatal(err)
2017-07-14 20:44:36 +00:00
}
close(processQueue)
workerDone.Wait()
close(outputQueue)
outputDone.Wait()
}