From aa5580ff1a84efda4ce4a8d2d7b8b418916c0163 Mon Sep 17 00:00:00 2001 From: Alex Halderman Date: Thu, 29 Aug 2013 15:00:34 -0400 Subject: [PATCH 1/6] Added experimental banner grabber implemented in Go. (Caution: This is my first attempt at writing Go.) --- examples/banner-grab-go/README | 36 +++++++ examples/banner-grab-go/banner.go | 160 ++++++++++++++++++++++++++++++ examples/banner-grab-go/http-req | 3 + 3 files changed, 199 insertions(+) create mode 100644 examples/banner-grab-go/README create mode 100644 examples/banner-grab-go/banner.go create mode 100644 examples/banner-grab-go/http-req diff --git a/examples/banner-grab-go/README b/examples/banner-grab-go/README new file mode 100644 index 0000000..4930dbf --- /dev/null +++ b/examples/banner-grab-go/README @@ -0,0 +1,36 @@ +TCP banner grabber implemented in Go (experimental) +====== + +This program will make TCP connections to IP addresses provide on +stdin, optionally send them a short message, and wait for their +responses. Each response is printed to stdout, along with the +responding host's IP address. Status messages appear on stderr. + +USING: +----- +go build banner.go +zmap -p 80 -N 1000 -o - | ./banner -port 80 -concurrent 100 -data http-req > banners.out + + +OPTIONS: +----- +-concurrent Number of connections that can be going on at once. + This, combined with timeouts, will decide the maximum + rate at which banners are grabbed. If this value + is set higher than 1000, you should use + `ulimit -SHn 1000000` and `ulimit -SSn 1000000` to + avoid running out of file descriptors (typically capped + at 1024). Default: 100. + +-port The port which to connect to hosts on. Default: 80. + +-timeout Connection timeout (seconds). Give up on a host if grabber + has not completed by this time. Default: 4 seconds. + +-format Format to output banner responses. One of 'hex', 'ascii', + or 'base64'. Default: ascii. + +-d, --data Optional data file. This data will be sent to each host + upon successful connection. Occurrences of the + string '%s' will be replaced with the current + target host's address. diff --git a/examples/banner-grab-go/banner.go b/examples/banner-grab-go/banner.go new file mode 100644 index 0000000..d50ed05 --- /dev/null +++ b/examples/banner-grab-go/banner.go @@ -0,0 +1,160 @@ +/* +TCP banner grabber, implemented in go + +This program will make TCP connections to IP addresses provide on +stdin, optionally send them a short message, and wait for their +responses. Each response is printed to stdout, along with the +responding host's IP address. Status messages appear on stderr. +*/ + +package main + +/* + * banner.go Copyright 2013 Regents of the University of Michigan + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + */ + +import ( + "bufio" + "encoding/base64" + "encoding/hex" + "flag" + "fmt" + "io" + "net" + "os" + "strings" + "syscall" + "time" +) + +var ( + nConnectFlag = flag.Int("concurrent", 100, "Number of concurrent connections") + portFlag = flag.String("port", "80", "Destination port") + formatFlag = flag.String("format", "ascii", "Output format for responses ('ascii', 'hex', or 'base64')") + timeoutFlag = flag.Int("timeout", 4, "Seconds to wait for each host to respond") + dataFileFlag = flag.String("data", "", "File containing message to send to responsive hosts ('%s' will be replaced with host IP)") +) + +var messageData = make([]byte, 0) // data read from file specified with dataFile flag + +// Before running main, parse flags and load message data, if applicable +func init() { + flag.Parse() + if *dataFileFlag != "" { + fi, err := os.Open(*dataFileFlag) + if err != nil { + panic(err) + } + buf := make([]byte, 1024) + n, err := fi.Read(buf) + messageData = buf[0:n] + if err != nil && err != io.EOF { + panic(err) + } + fi.Close() + } + // Increase file descriptor limit + rlimit := syscall.Rlimit{Max: uint64(*nConnectFlag + 4), Cur: uint64(*nConnectFlag + 4)} + if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlimit); err != nil { + fmt.Fprintf(os.Stderr, "Error setting rlimit: %s", err) + } +} + +type resultStruct struct { + addr string // address of remote host + data []byte // data returned from the host, if successful + err error // error, if any +} + +// Read addresses from addrChan and grab banners from these hosts. +// Sends resultStructs to resultChan. Writes to doneChan when complete. +func grabber(addrChan chan string, resultChan chan resultStruct, doneChan chan int) { + for addr := range addrChan { + deadline := time.Now().Add(time.Duration(*timeoutFlag) * time.Second) + dialer := net.Dialer{Deadline: deadline} + conn, err := dialer.Dial("tcp", net.JoinHostPort(addr, *portFlag)) + if err != nil { + resultChan <- resultStruct{addr, nil, err} + continue + } + conn.SetDeadline(deadline) + if len(messageData) > 0 { + s := strings.Replace(string(messageData), "%s", addr, -1) + if _, err := conn.Write([]byte(s)); err != nil { + conn.Close() + resultChan <- resultStruct{addr, nil, err} + continue + } + } + var buf [1024]byte + n, err := conn.Read(buf[:]) + conn.Close() + if err != nil && (err != io.EOF || n == 0) { + resultChan <- resultStruct{addr, nil, err} + continue + } + resultChan <- resultStruct{addr, buf[0:n], nil} + } + doneChan <- 1 +} + +// Read resultStructs from resultChan, print output, and maintain +// status counters. Writes to doneChan when complete. +func output(resultChan chan resultStruct, doneChan chan int) { + ok, timeout, error := 0, 0, 0 + for result := range resultChan { + if result.err == nil { + switch *formatFlag { + case "hex": + fmt.Printf("%s: %s\n", result.addr, + hex.EncodeToString(result.data)) + case "base64": + fmt.Printf("%s: %s\n", result.addr, + base64.StdEncoding.EncodeToString(result.data)) + default: + fmt.Printf("%s: %s\n", result.addr, + string(result.data)) + } + ok++ + } else if nerr, ok := result.err.(net.Error); ok && nerr.Timeout() { + fmt.Fprintf(os.Stderr, "%s: Timeout\n", result.addr) + timeout++ + } else { + fmt.Fprintf(os.Stderr, "%s: Error %s\n", result.addr, result.err) + error++ + } + } + fmt.Fprintf(os.Stderr, "Complete (OK=%d, timeout=%d, error=%d)\n", + ok, timeout, error) + doneChan <- 1 +} + +func main() { + addrChan := make(chan string, *nConnectFlag) // pass addresses to grabbers + resultChan := make(chan resultStruct, *nConnectFlag) // grabbers send results to output + doneChan := make(chan int, *nConnectFlag) // let grabbers signal completion + + // Start grabbers and output thread + go output(resultChan, doneChan) + for i := 0; i < *nConnectFlag; i++ { + go grabber(addrChan, resultChan, doneChan) + } + + // Read addresses from stdin and pass to grabbers + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + addrChan <- scanner.Text() + } + close(addrChan) + + // Wait for completion + for i := 0; i < *nConnectFlag; i++ { + <-doneChan + } + close(resultChan) + <-doneChan +} diff --git a/examples/banner-grab-go/http-req b/examples/banner-grab-go/http-req new file mode 100644 index 0000000..8370bb1 --- /dev/null +++ b/examples/banner-grab-go/http-req @@ -0,0 +1,3 @@ +GET / HTTP/1.1 +Host: %s + From fb8509a2f3c67b0e45b04407ba7315aa3dc5a6cd Mon Sep 17 00:00:00 2001 From: Alex Halderman Date: Thu, 29 Aug 2013 15:11:18 -0400 Subject: [PATCH 2/6] Correct README --- examples/banner-grab-go/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/banner-grab-go/README b/examples/banner-grab-go/README index 4930dbf..b487b22 100644 --- a/examples/banner-grab-go/README +++ b/examples/banner-grab-go/README @@ -30,7 +30,7 @@ OPTIONS: -format Format to output banner responses. One of 'hex', 'ascii', or 'base64'. Default: ascii. --d, --data Optional data file. This data will be sent to each host +--data Optional data file. This data will be sent to each host upon successful connection. Occurrences of the string '%s' will be replaced with the current target host's address. From 227ca3c9bb7d78334e5d105e7830450b636e2b33 Mon Sep 17 00:00:00 2001 From: Alex Halderman Date: Thu, 29 Aug 2013 15:21:12 -0400 Subject: [PATCH 3/6] Fix description --- examples/banner-grab-go/README | 8 ++++---- examples/banner-grab-go/banner.go | 16 ++++------------ 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/examples/banner-grab-go/README b/examples/banner-grab-go/README index b487b22..1f7eb3a 100644 --- a/examples/banner-grab-go/README +++ b/examples/banner-grab-go/README @@ -1,10 +1,10 @@ TCP banner grabber implemented in Go (experimental) ====== -This program will make TCP connections to IP addresses provide on -stdin, optionally send them a short message, and wait for their -responses. Each response is printed to stdout, along with the -responding host's IP address. Status messages appear on stderr. +This program will make TCP connections to IP addresses provided on +stdin, optionally send a short message, and wait for responses. Each +response is printed to stdout, along with the responding host's IP +address. Status messages appear on stderr. USING: ----- diff --git a/examples/banner-grab-go/banner.go b/examples/banner-grab-go/banner.go index d50ed05..d3c5026 100644 --- a/examples/banner-grab-go/banner.go +++ b/examples/banner-grab-go/banner.go @@ -1,22 +1,14 @@ /* TCP banner grabber, implemented in go -This program will make TCP connections to IP addresses provide on -stdin, optionally send them a short message, and wait for their -responses. Each response is printed to stdout, along with the -responding host's IP address. Status messages appear on stderr. +This program will make TCP connections to IP addresses provided on +stdin, optionally send a short message, and wait for responses. Each +response is printed to stdout, along with the responding host's IP +address. Status messages appear on stderr. */ package main -/* - * banner.go Copyright 2013 Regents of the University of Michigan - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at http://www.apache.org/licenses/LICENSE-2.0 - */ - import ( "bufio" "encoding/base64" From 48f5299c532f7e1c35591980b9e7a6e7a199723b Mon Sep 17 00:00:00 2001 From: Alex Halderman Date: Thu, 29 Aug 2013 15:21:47 -0400 Subject: [PATCH 4/6] Fix README --- examples/banner-grab-go/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/banner-grab-go/README b/examples/banner-grab-go/README index 1f7eb3a..5a5261d 100644 --- a/examples/banner-grab-go/README +++ b/examples/banner-grab-go/README @@ -30,7 +30,7 @@ OPTIONS: -format Format to output banner responses. One of 'hex', 'ascii', or 'base64'. Default: ascii. ---data Optional data file. This data will be sent to each host +-data Optional data file. This data will be sent to each host upon successful connection. Occurrences of the string '%s' will be replaced with the current target host's address. From e4025464b092b1c09705d5c7b6f616ed08d6babb Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 29 Aug 2013 18:05:06 -0400 Subject: [PATCH 5/6] must initialize cmdline_parser before we read args --- src/zmap.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/zmap.c b/src/zmap.c index 598c9d1..04d5191 100644 --- a/src/zmap.c +++ b/src/zmap.c @@ -341,13 +341,14 @@ int main(int argc, char *argv[]) params->override = 0; params->check_required = 0; + if (cmdline_parser_ext(argc, argv, &args, params) != 0) { + exit(EXIT_SUCCESS); + } + zconf.log_level = args.verbosity_arg; log_init(stderr, zconf.log_level); log_trace("zmap", "zmap main thread started"); - if (cmdline_parser_ext(argc, argv, &args, params) != 0) { - exit(EXIT_SUCCESS); - } if (args.help_given) { cmdline_parser_print_help(); exit(EXIT_SUCCESS); From 2aeb368f37ed9cbc5ae3a65c9f7c2bb874c0cad6 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 29 Aug 2013 18:05:41 -0400 Subject: [PATCH 6/6] remove old man if it is there --- src/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Makefile b/src/Makefile index 6f9f98f..b3b767f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -8,6 +8,7 @@ PREFIX=/usr/local INSTALL=install INSTALLDATA=install -m 644 mandir=$(PREFIX)/man/man1/ +oldmanfile=/usr/share/man/man1/zmap.1 bindir=$(PREFIX)/sbin # Hardening and warnings for building with gcc @@ -56,6 +57,7 @@ zopt.c zopt.h: zopt.ggo install: zmap $(INSTALL) zmap $(bindir)/zmap test -d /etc/zmap || (mkdir /etc/zmap && $(INSTALLDATA) ../conf/* /etc/zmap/) + test -f $(oldmanfile) && rm -f $(oldmanfile) && mandb -f $(oldmanfile) || /bin/true # remove old man page if it's there test -d $(mandir) || mkdir -p $(mandir) $(INSTALLDATA) ./zmap.1 $(mandir) @echo "\n**************\nSuccess! ZMap is installed. Try running (as root):\nzmap -p 80 -N 10 -B 1M -o -\n**************"