Idiomatic nmap library for go developers

nmap

PkgGoDev github.com/Ullaakut/nmap/v2

This library aims at providing idiomatic nmap bindings for go developers, in order to make it easier to write security audit tools using golang.

What is nmap

Nmap (Network Mapper) is a free and open-source network scanner created by Gordon Lyon. Nmap is used to discover hosts and services on a computer network by sending packets and analyzing the responses.

Nmap provides a number of features for probing computer networks, including host discovery and service and operating system detection. These features are extensible by scripts that provide more advanced service detection, vulnerability detection, and other features. Nmap can adapt to network conditions including latency and congestion during a scan.

Why use go for penetration testing

Most pentest tools are currently written using Python and not Go, because it is easy to quickly write scripts, lots of libraries are available, and it's a simple language to use. However, for writing robust and reliable applications, Go is the better tool. It is statically compiled, has a static type system, much better performance, it is also a very simple language to use and goroutines are awesome... But I might be slighly biased, so feel free to disagree.

Supported features

  • All of nmap's native options.
  • Additional idiomatic go filters for filtering hosts and ports.
  • Cancellable contexts support.
  • Helpful enums for nmap commands. (time templates, os families, port states, etc.)
  • Complete documentation of each option, mostly insipred from nmap's documentation.

TODO

  • Add asynchronous scan, send scan progress percentage and time estimation through channel

Simple example

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/Ullaakut/nmap/v2"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
    defer cancel()

    // Equivalent to `/usr/local/bin/nmap -p 80,443,843 google.com facebook.com youtube.com`,
    // with a 5 minute timeout.
    scanner, err := nmap.NewScanner(
        nmap.WithTargets("google.com", "facebook.com", "youtube.com"),
        nmap.WithPorts("80,443,843"),
        nmap.WithContext(ctx),
    )
    if err != nil {
        log.Fatalf("unable to create nmap scanner: %v", err)
    }

    result, warnings, err := scanner.Run()
    if err != nil {
        log.Fatalf("unable to run nmap scan: %v", err)
    }

    if warnings != nil {
        log.Printf("Warnings: \n %v", warnings)
    }

    // Use the results to print an example output
    for _, host := range result.Hosts {
        if len(host.Ports) == 0 || len(host.Addresses) == 0 {
            continue
        }

        fmt.Printf("Host %q:\n", host.Addresses[0])

        for _, port := range host.Ports {
            fmt.Printf("\tPort %d/%s %s %s\n", port.ID, port.Protocol, port.State, port.Service.Name)
        }
    }

    fmt.Printf("Nmap done: %d hosts up scanned in %3f seconds\n", len(result.Hosts), result.Stats.Finished.Elapsed)
}

The program above outputs:

Host "172.217.16.46":
    Port 80/tcp open http
    Port 443/tcp open https
    Port 843/tcp filtered unknown
Host "31.13.81.36":
    Port 80/tcp open http
    Port 443/tcp open https
    Port 843/tcp open unknown
Host "216.58.215.110":
    Port 80/tcp open http
    Port 443/tcp open https
    Port 843/tcp filtered unknown
Nmap done: 3 hosts up scanned in 1.29 seconds

Advanced example

Cameradar already uses this library at its core to communicate with nmap, discover RTSP streams and access them remotely.

More examples:

External resources

Owner
Brendan Le Glaunec
Software Engineer @traefik & previously R&D Software Engineer @EtixLabs / Go Contractor @fresh8
Brendan Le Glaunec
Comments
  • Wrong detect if port is open/close

    Wrong detect if port is open/close

    package main
    
    import (
    	"fmt"
    	"log"
    
    	"github.com/Ullaakut/nmap/v2"
    )
    
    func main() {
    	scanner, err := nmap.NewScanner(
    		nmap.WithTargets("127.0.0.1"),
    		nmap.WithPorts("1", "3389", "3390", "9040", "9050"),
    		nmap.WithServiceInfo(),
    		nmap.WithTimingTemplate(nmap.TimingAggressive),
    		nmap.WithFilterHost(func(h nmap.Host) bool {
    			for idx := range h.Ports {
    				if h.Ports[idx].Status() == "open" {
    					return true
    				}
    			}
    			return false
    		}),
    	)
    	if err != nil {
    		log.Fatalf("unable to create nmap scanner: %v", err)
    	}
    
    	result, _, err := scanner.Run()
    	if err != nil {
    		log.Fatalf("nmap scan failed: %v", err)
    	}
    	for _, host := range result.Hosts {
    		fmt.Printf("Host %s\n", host.Addresses[0])
    		for _, port := range host.Ports {
    			fmt.Printf("\tPort %d open, service %s\n", port.ID, port.Service.Name)
    		}
    	}
    }
    

    Result

    Host 127.0.0.1
            Port 1 open, service tcpmux
            Port 3389 open, service ms-wbt-server
            Port 3390 open, service dsc
            Port 9040 open, service tcpwrapped
            Port 9050 open, service tor-socks
    

    And nmap scan

    nmap -A -sV -sC -Pn -T4 -p1,3389,3390,9040,9050 127.0.0.1
    Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
    Starting Nmap 7.91 ( https://nmap.org ) at 2020-11-16 16:53 MSK
    Nmap scan report for localhost (127.0.0.1)
    Host is up (0.00012s latency).
    
    PORT     STATE  SERVICE       VERSION
    1/tcp    closed tcpmux
    3389/tcp closed ms-wbt-server
    3390/tcp closed dsc
    9040/tcp open   tcpwrapped
    9050/tcp open   tor-socks     Tor SOCKS proxy
    | socks-auth-info: 
    |   No authentication
    |_  Username and password
    | socks-open-proxy: 
    |   status: open
    |   versions: 
    |_    socks4
    
    Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
    Nmap done: 1 IP address (1 host up) scanned in 17.96 seconds
    
  • Broken module version management

    Broken module version management

    Hi, I recently had many issues with go complaining about checksum mismatches when trying to "go get" this module with version v2 or greater.

    I think I found the issue: Go got the checksum mismatch because v2.0.0 does not contain a go.mod file and then got "incompatible" https://stackoverflow.com/questions/57355929/what-does-incompatible-in-go-mod-mean-will-it-cause-harm

    So I ask you to set the tag v2.0.0 to a commit where the go.mod is correct. You may set tag v2.0.0 to the same commit as v2.0.1.

  • Unresolvable hosts prevent remaining results

    Unresolvable hosts prevent remaining results

    Hi@

    when a scan finishes before timeout you firstly check if something went to stderr.

    Lets assume the following:

    nmap -p 80,443 asdasasdasdasasdaasdassad.com google.de

    First host goes to stderr with message Failed to resolve "asdasasdasdasasdaasdassad.com". and the second one finishes just fine.

    The early error exit (currently https://github.com/Ullaakut/nmap/blob/master/nmap.go#L87) from your done channel prevents access to any other successful results if just one fails :/

  • Fixing

    Fixing "Error handling difficult and not reliable #38"

    Here comes the implementation of my suggestion to fix https://github.com/Ullaakut/nmap/issues/38. Please have a look and see how this works for you. I tested it against some scan targets and am still running it against even more. You are probably using different Nmap arguments than me, so I'm curious about your experiences.

    Result processing would be like:

    1. Check if error is returned (includes package errors and nmap errors). If error is returned, something went most probably wrong and you can abort. Warning's might give you more details/flexibility. Most returned errors are defined as error constants in errors.go. Have a look there to see what issues might come back to handle them individually
    2. OptionA: Check warnings for things that might be an issue for you and abort or proceed OptionB: Ignore warnings at all and just work with the returned XML. It already contains a lot of details

    Here is sample usage code:

    // Prepare Nmap scan
    proc, err := nmap.NewScanner(options...)
    if err != nil {
        fmt.Println("Could not initialize Nmap")
        return
    }
    
    // Execute Nmap scan
    result, warnings, err := proc.Run()
    
    // Check for nmap errors and optionally handle them individually
    if err != nil {
    	switch err {
    		case nmap.ErrScanTimeout:
            	fmt.Println("ERROR: Nmap scan ran into timeout")
        	case nmap.ErrParseOutput:
            	fmt.Printf("ERROR: Nmap output could not be parsed: %w\n", warnings[len(warnings)-1])
        	case nmap.ErrExcludeList:
            	fmt.Println("ERROR: Nmap could not resolve host(s) on exclude list")
        	default:
            	fmt.Printf("ERROR: Nmap scan failed with unexpected error: %w\n", err)
    	}
    	return
    }
    
    // Check for nmap warnings that might be relevant for you
    for _, warning := range warnings {
        switch {
     		case strings.Contains(warning, "Failed to resolve"):
            	fmt.Println("A target could not be resolved.") 
        	case strings.Contains(warning, "No targets were specified")
    			fmt.Println("The only target is blacklisted.")
        	case strings.Contains(warning, "TTVAR has grown")
            	fmt.Println("Just some TTVAR change notification.")
        	default:
            	fmt.Println(fmt.Sprintf("Other warning: %s", warning))
        }
    }
    
    // Process result
    fmt.Println(result)
    
  • Can't see to get the output of default script.

    Can't see to get the output of default script.

    Hello.

    nmap.WithDefaultScript()
    

    I can get all methods from port.* etc but cant see to get the output of the Script. example:

    for _, host := range result.Hosts {
                    fmt.Printf("Host %s\n", host.Addresses[0])
    
                    for _, port := range host.Ports {
                            fmt.Printf("\tPort %d/%s %s %s %s %s %s\n", port.ID, port.Protocol, port.State, port.Service.Name, port.Service.Product, port.Service.Version, port.Service.ExtraInfo)
    
                            for _, script := range port.Scripts {
                                    fmt.Printf("%s", script.Output)
                            }
                    }
            }
    

    trying to get the script.Output from the scripts slice. Im sure im doing something wrong.

  • github.com/Ullaakut/nmap/v2/pkg/osfamilies: not found

    github.com/Ullaakut/nmap/v2/pkg/osfamilies: not found

    I'm creating a custom build script for Pentoo using standard Gentoo ebuild and golang-vcs-snapshot eclass.

    Currently, Cameradar fails to build due to the following error:

    WORK=/var/tmp/portage/net-analyzer/cameradar-5.0.1-r1/temp/go-build4077822569
    src/github.com/ullaakut/cameradar/vendor/github.com/Ullaakut/nmap/xml.go:11:2: cannot find package "github.com/Ullaakut/nmap/v2/pkg/osfamilies" in any of:
            /var/tmp/portage/net-analyzer/cameradar-5.0.1-r1/work/cameradar-5.0.1/src/github.com/ullaakut/cameradar/vendor/github.com/Ullaakut/nmap/v2/pkg/osfamilies (vendor tree)
            /usr/lib/go/src/github.com/Ullaakut/nmap/v2/pkg/osfamilies (from $GOROOT)
            /var/tmp/portage/net-analyzer/cameradar-5.0.1-r1/work/cameradar-5.0.1/src/github.com/Ullaakut/nmap/v2/pkg/osfamilies (from $GOPATH)
            /usr/lib/go-gentoo/src/github.com/Ullaakut/nmap/v2/pkg/osfamilies
    src/github.com/ullaakut/cameradar/cmd/cameradar/cameradar.go:10:2: cannot find package "github.com/Ullaakut/cameradar/v5" in any of:
            /var/tmp/portage/net-analyzer/cameradar-5.0.1-r1/work/cameradar-5.0.1/src/github.com/ullaakut/cameradar/vendor/github.com/Ullaakut/cameradar/v5 (vendor tree)
            /usr/lib/go/src/github.com/Ullaakut/cameradar/v5 (from $GOROOT)
            /var/tmp/portage/net-analyzer/cameradar-5.0.1-r1/work/cameradar-5.0.1/src/github.com/Ullaakut/cameradar/v5 (from $GOPATH)
            /usr/lib/go-gentoo/src/github.com/Ullaakut/cameradar/v5
     * ERROR: net-analyzer/cameradar-5.0.1-r1::pentoo failed (compile phase):
    
    

    There are few references which seems broken: https://github.com/Ullaakut/nmap/blob/master/xml.go#L11 https://github.com/Ullaakut/nmap/blob/master/go.mod#L1

    can you create both directories (v2/pkg/osfamilies) or remove v2 and v5 completely?

  • Nmap error handling

    Nmap error handling

    Added a NmapErrors Struct for holding errors encountered during the Nmap Scan. Since these errors are specific to nmap, we shouldn't cancel an entire multi target scan just because one host is unresolvable or causing issues.

    Additionally, added a utils file that holds a RemoveDuplicates to remove Duplicate nmap errors. Nmap can under certain circumstances return duplicate errors.

    This could potentially be further improved to remove duplicates of specific errors.

    Example Usage:

    package main
    
    import (
    	"context"
    	"fmt"
    	"log"
    	"time"
    
    	"github.com/theseceng/nmap"
    )
    
    func main() {
    	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
    	defer cancel()
    
    	// Equivalent to `/usr/local/bin/nmap -p 80,443,843 google.com facebook.com youtube.com`,
    	// with a 5 minute timeout.
    	scanner, err := nmap.NewScanner(
    		nmap.WithTargets("facebook.com", "Bad.Domain.Com.Dsads"),
    		nmap.WithPorts("80"),
    		nmap.WithContext(ctx),
    	)
    	if err != nil {
    		log.Fatalf("unable to create nmap scanner: %v", err)
    	}
    
    	result, err := scanner.Run()
    	if err != nil {
    		fmt.Println("Errors encountered")
    	}
    
    	for _, e := range result.NmapErrors {
    		fmt.Printf("[ ERROR ] - %v\n", e.Error)
    	}
    
    	// Use the results to print an example output
    	for _, host := range result.Hosts {
    		if len(host.Ports) == 0 || len(host.Addresses) == 0 {
    			continue
    		}
    
    		fmt.Printf("[ SUCCESS ] - Host %q:\n", host.Addresses[0])
    
    		for _, port := range host.Ports {
    			fmt.Printf("\t\tPort %d/%s %s %s\n", port.ID, port.Protocol, port.State, port.Service.Name)
    		}
    	}
    
    	fmt.Printf("Nmap done: %d hosts up scanned in %.2f seconds\n", len(result.Hosts), result.Stats.Finished.Elapsed)
    }
    
  • Add RunAsync() method

    Add RunAsync() method

    Allowing the users to run nmap asnchronously makes it possible to parse the live nmap output, for example for building a UI on top, or computing a progress bar.

    I'm not sure if many people have this use-case though, so I won't prioritize this for now, but I find it really cool.

    • [ ] Add a RunAsync() method with the following signature func (s *Scanner) RunAsync() (stdout chan <-[]byte, stderr chan <-[]byte, error)
      • [ ] Make this method return one channel for stdout, one for stderr, and an error if the nmap process could not be started.
      • [ ] Have goroutine to kill nmap if context is done or times out.
      • [ ] Store output in private attribute to expose a nmap.Result structure once the scan is done
  • Showing up all hosts up in privileged mode

    Showing up all hosts up in privileged mode

    If we try to scan for example a /24, the nmap package will tell that every host is up, even if they not. If this can help, I think it's because you fallback on a "up" status if you can't find any "status" tag in the XML, because nmap doesn't put any info about the host in the XML if it is down.

  • Feature/async channels with Progress

    Feature/async channels with Progress

    Experimental, how can we improve upon the Progress function to not require an argument? Ideally I believe it should be done in this fashion

    go scanner.Progress()
    

    or

    go scanner.Progress(nmapResult Channel)
    

    and the console would stream the progress asynchronously without further code requirements.

  • Detect ICMP response

    Detect ICMP response

    Hi, is there a way of using the library to detect whether a host responds to ICMP? At the moment I have something along the lines of:

    	switch mode {
    	case "tcp":
    		typeScanner = []func(*nmap.Scanner){
    			nmap.WithServiceInfo(),
    			nmap.WithSYNScan(),
    			nmap.WithOSDetection(),
    			nmap.WithOSScanGuess(),
    			nmap.WithOSScanLimit(),
    		}
    		break
    	case "udp":
    		typeScanner = []func(*nmap.Scanner){
    			nmap.WithServiceInfo(),
    			nmap.WithUDPScan(),
    		}
    		break
    	case "icmp":
    		typeScanner = []func(*nmap.Scanner){
    			nmap.WithServiceInfo(),
    			nmap.WithICMPEchoDiscovery(),
    			nmap.WithOSDetection(),
    			nmap.WithOSScanGuess(),
    		}
    		break
    	default:
    		return errors.New("unknown scan mode: " + mode)
    	}
    
    ...
    			nmap.WithContext(ctx),
    			nmap.WithTargets(ip.String()),
    			nmap.WithDisabledDNSResolution(),
    			nmap.WithPrivileged(),
    			nmap.WithTimingTemplate(nmap.TimingAggressive),
    

    But not sure if ServiceInfo and OSDetection do anything at the ICMP scanning phase.

  • RunWithProgress goes from 100% to 0% and back again

    RunWithProgress goes from 100% to 0% and back again

    When the IP range is < /26 the progress is not accurate as it goes to 100% and then back to 0% multiple times:

    ✗ go run main.go
    Progress: 27.25 %
    Progress: 43.55 %
    Progress: 54.1 %
    Progress: 75.49 %
    Progress: 75.49 %
    Progress: 89.06 %
    Progress: 95.51 %
    Progress: 97.75 %
    Progress: 32.3 % <------
    Progress: 56.64 %
    Progress: 56.64 %
    Progress: 84.96 %
    Host "172.217.169.0":
    	Port 80/tcp open http
    Host "172.217.169.1":
    	Port 80/tcp open http
    Host "172.217.169.2":
    	Port 80/tcp open http
    Host "172.217.169.3":
    	Port 80/tcp open http
    
    package main
    
    import (
    	"context"
    	"fmt"
    	"github.com/Ullaakut/nmap/v2"
    	"log"
    	"time"
    )
    
    func main() {
    	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
    	defer cancel()
    
    	scanner, err := nmap.NewScanner(
    		nmap.WithTargets("172.217.169.0/24"),
    		nmap.WithPorts("80"),
    		nmap.WithContext(ctx),
    		nmap.WithVerbosity(3),
    	)
    	if err != nil {
    		log.Fatalf("unable to create nmap scanner: %v", err)
    	}
    
    	progress := make(chan float32, 1)
    
    	// Function to listen and print the progress
    	go func() {
    		for p := range progress {
    			fmt.Printf("Progress: %v %%\n", p)
    		}
    	}()
    
    	result, warnings, err := scanner.RunWithProgress(progress)
    	if err != nil {
    		log.Fatalf("unable to run nmap scan: %v", err)
    	}
    
    	if warnings != nil {
    		log.Printf("Warnings: \n %v", warnings)
    	}
    	// Use the results to print an example output
    	for _, host := range result.Hosts {
    		if len(host.Ports) == 0 || len(host.Addresses) == 0 {
    			continue
    		}
    
    		fmt.Printf("Host %q:\n", host.Addresses[0])
    
    		for _, port := range host.Ports {
    			fmt.Printf("\tPort %d/%s %s %s\n", port.ID, port.Protocol, port.State, port.Service.Name)
    		}
    	}
    
    	fmt.Printf("Nmap done: %d hosts up scanned in %3f seconds\n", len(result.Hosts), result.Stats.Finished.Elapsed)
    }
    
  • Crash after long running scan ( /24 and /26 with -p-)

    Crash after long running scan ( /24 and /26 with -p-)

    I get reproducible crashes after scanning a large number of hosts, for example a /24 network. I'm using -sSV -p- so the scan will take a while. After ~ 1 hour the application simply exits with the following errors:

    goroutine 91 [IO wait, 317 minutes]:
    internal/poll.runtime_pollWait(0x7fa9fa9c8ea0, 0x72, 0xc00003ece8)
            /usr/lib/go-1.11/src/runtime/netpoll.go:173 +0x66
    internal/poll.(*pollDesc).wait(0xc001942798, 0x72, 0xffffffffffffff01, 0x542a60, 0x5ed140)
            /usr/lib/go-1.11/src/internal/poll/fd_poll_runtime.go:85 +0x9a
    internal/poll.(*pollDesc).waitRead(0xc001942798, 0xc00013c401, 0x200, 0x200)
            /usr/lib/go-1.11/src/internal/poll/fd_poll_runtime.go:90 +0x3d
    internal/poll.(*FD).Read(0xc001942780, 0xc00013c400, 0x200, 0x200, 0x0, 0x0, 0x0)
            /usr/lib/go-1.11/src/internal/poll/fd_unix.go:169 +0x179
    os.(*File).read(0xc0001720b8, 0xc00013c400, 0x200, 0x200, 0xc00013c400, 0x0, 0x0)
            /usr/lib/go-1.11/src/os/file_unix.go:249 +0x4e
    os.(*File).Read(0xc0001720b8, 0xc00013c400, 0x200, 0x200, 0xc00003ee58, 0x49b31c, 0xc00003ee60)
            /usr/lib/go-1.11/src/os/file.go:108 +0x69
    bytes.(*Buffer).ReadFrom(0xc0001c63f0, 0x542900, 0xc0001720b8, 0x7fa9fa984020, 0xc0001c63f0, 0x1)
            /usr/lib/go-1.11/src/bytes/buffer.go:206 +0xbd
    io.copyBuffer(0x542780, 0xc0001c63f0, 0x542900, 0xc0001720b8, 0x0, 0x0, 0x0, 0xc00d2a6660, 0x0, 0x0)
            /usr/lib/go-1.11/src/io/io.go:388 +0x303
    io.Copy(0x542780, 0xc0001c63f0, 0x542900, 0xc0001720b8, 0x404a75, 0xc00d2a67e0, 0xc00003efb0)
            /usr/lib/go-1.11/src/io/io.go:364 +0x5a
    os/exec.(*Cmd).writerDescriptor.func1(0xc00d2a67e0, 0xc00003efb0)
            /usr/lib/go-1.11/src/os/exec/exec.go:279 +0x4d
    os/exec.(*Cmd).Start.func1(0xc003427a20, 0xc00220bc80)
            /usr/lib/go-1.11/src/os/exec/exec.go:400 +0x27
    created by os/exec.(*Cmd).Start
            /usr/lib/go-1.11/src/os/exec/exec.go:399 +0x5af
    
    goroutine 90 [IO wait]:
    internal/poll.runtime_pollWait(0x7fa9fa9c8820, 0x72, 0xc0000a1ce8)
            /usr/lib/go-1.11/src/runtime/netpoll.go:173 +0x66
    internal/poll.(*pollDesc).wait(0xc0019426d8, 0x72, 0xffffffffffffff01, 0x542a60, 0x5ed140)
            /usr/lib/go-1.11/src/internal/poll/fd_poll_runtime.go:85 +0x9a
    internal/poll.(*pollDesc).waitRead(0xc0019426d8, 0xc0fdd84b01, 0xd209, 0xd209)
            /usr/lib/go-1.11/src/internal/poll/fd_poll_runtime.go:90 +0x3d
    internal/poll.(*FD).Read(0xc0019426c0, 0xc0fdd84bf7, 0xd209, 0xd209, 0x0, 0x0, 0x0)
            /usr/lib/go-1.11/src/internal/poll/fd_unix.go:169 +0x179
    os.(*File).read(0xc0001720a0, 0xc0fdd84bf7, 0xd209, 0xd209, 0x6c, 0x0, 0x0)
            /usr/lib/go-1.11/src/os/file_unix.go:249 +0x4e
    os.(*File).Read(0xc0001720a0, 0xc0fdd84bf7, 0xd209, 0xd209, 0x6c, 0x0, 0x0)
            /usr/lib/go-1.11/src/os/file.go:108 +0x69
    bytes.(*Buffer).ReadFrom(0xc0001c6380, 0x542900, 0xc0001720a0, 0x7fa9fa984020, 0xc0001c6380, 0xc00d2dc901)
            /usr/lib/go-1.11/src/bytes/buffer.go:206 +0xbd
    io.copyBuffer(0x542780, 0xc0001c6380, 0x542900, 0xc0001720a0, 0x0, 0x0, 0x0, 0xc00d2a6720, 0x0, 0x0)
            /usr/lib/go-1.11/src/io/io.go:388 +0x303
    io.Copy(0x542780, 0xc0001c6380, 0x542900, 0xc0001720a0, 0x404a75, 0xc00d2a67e0, 0xc0004c3fb0)
            /usr/lib/go-1.11/src/io/io.go:364 +0x5a
    os/exec.(*Cmd).writerDescriptor.func1(0xc00d2a67e0, 0xc0004c3fb0)
            /usr/lib/go-1.11/src/os/exec/exec.go:279 +0x4d
    os/exec.(*Cmd).Start.func1(0xc003427a20, 0xc00220bc40)
            /usr/lib/go-1.11/src/os/exec/exec.go:400 +0x27
    created by os/exec.(*Cmd).Start
            /usr/lib/go-1.11/src/os/exec/exec.go:399 +0x5af
    
    goroutine 89 [chan receive]:
    main.scan_host.func1(0xc00015a000, 0xc00220bc00, 0x1)
            main.go:139 +0x3f
    created by main.scan_host
            main.go:138 +0x432
    exit status 2
    

    main.go:139 does contain the progress function.

    The code (the progress if statement is not the most elegant but shouldn't be the cause of the error I guess :)):

            s, err := nmap.NewScanner(
                    nmap.WithTargets(host),
                    nmap.WithTimingTemplate(nmap.TimingAggressive),
                    nmap.WithServiceInfo(),
                    nmap.WithSYNScan(),
                    nmap.WithPorts("-"),
                    nmap.WithVerbosity(3),
                    //nmap.WithFastMode(),
            )
    
            if err != nil {
                    log.Fatalf("[worker_%v] unable to create nmap scanner: %v", id, err)
            }
    
            progress := make(chan float32, 1)
            ts := time.Now()
            go func() {
                    for p := range progress {
                            if time.Now().After(ts.Add(60 * time.Second)) {
                                    ts = time.Now()
                                    log.Printf("[worker_%v] portscan progress: %v %%", id, p)
                            }
                    }
            }()
    
            result, w, e := s.RunWithProgress(progress)
    

    I'm not sure if this is a bug in the library, a frozen nmap process or anything else... Doing a fast-mode scan with the same settings works fine (which is obviously faster ;-)).

    I would appreciate any hints for debugging this further because I guess the pasted errors are not that helpful and I have no experience debugging these kind of errors in the go ecosystem.

  • Issues with count_hosts_by_os example

    Issues with count_hosts_by_os example

    I'm seeing a few issues with the count_hosts_by_os example in this repository.

    I get this error when I run the program without sudo. Does this example require to be ran with sudo or root?

    $ ./count_hosts_by_os
    2021/05/23 07:03:44 nmap scan failed: unable to parse nmap output, see warnings for details
    

    When I run the example, the number of Linux hosts is more than the number of hosts seen on the network.

    $ sudo ./count_hosts_by_os
    Discovered 25 linux hosts and 0 windows hosts out of 18 total up hosts.
    
  • Add Progress function to easily stream progress updates from AsyncRun method

    Add Progress function to easily stream progress updates from AsyncRun method

    • I'm submitting a ...

      • [ ] bug report
      • [x] feature request
      • [ ] support request
    • What is the current behavior? AsyncRun returns bufio reader, allowing users to manipulate the data to have it perform in several different manners. However some users might only want to use AsyncRun for being able to stream progress. It would be nice to have a Progress function that would allow streaming progress without having a bunch of code to accompany it.

    • What is the expected behavior? Call range Progress(content) {} and have progress updates printed to screen

    • What is the motivation / use case for changing the behavior? Allow an easier to use Progress implementation allowing straight forward implementation of parsing Nmap xml bytes from the stream and getting progress status.

  • Review Async implementation vs Channels Implementation.

    Review Async implementation vs Channels Implementation.

    Review current Bufio Async implementation vs Async channel implementation.

    The below is fully working AsyncRun with Channels

    // RunAsync runs nmap asynchronously and returns error.
    func (s *Scanner) RunAsync() (<-chan []byte, <-chan []byte, error) {
    	stdoutChannel := make(chan []byte)
    	stderrChannel := make(chan []byte)
    
    	// Enable XML output.
    	s.args = append(s.args, "-oX")
    
    	// Get XML output in stdout instead of writing it in a file.
    	s.args = append(s.args, "-")
    	s.cmd = exec.Command(s.binaryPath, s.args...)
    
    	// Get CMD Stderr Pipe
    	stderr, err := s.cmd.StderrPipe()
    	if err != nil {
    		return nil, nil, fmt.Errorf("unable to get error output from asynchronous nmap run: %v", err)
    	}
    
    	// Get CMD Stdout Pipe
    	stdout, err := s.cmd.StdoutPipe()
    	if err != nil {
    		return nil, nil, fmt.Errorf("unable to get standard output from asynchronous nmap run: %v", err)
    	}
    
    	if err := s.cmd.Start(); err != nil {
    		return nil, nil, fmt.Errorf("unable to execute asynchronous nmap run: %v", err)
    	}
    
    	// Stream stdout to the stdoutChannel
    	go func() {
    		defer close(stdoutChannel)
    		for {
    			buf := make([]byte, 1024)
    			n, err := stdout.Read(buf)
    			if err != nil {
    				if err != io.EOF {
    					log.Fatal(err)
    				}
    				if n == 0 {
    					break
    				}
    			}
    			stdoutChannel <- buf[:n]
    		}
    	}()
    
    	// Stream stderr to the stderrChannel
    	go func() {
    		defer close(stderrChannel)
    		for {
    			buf := make([]byte, 2048)
    			n, err := stderr.Read(buf)
    			if err != nil {
    				if err != io.EOF {
    					log.Fatal(err)
    				}
    				if n == 0 {
    					break
    				}
    			}
    			stderrChannel <- buf[:n]
    		}
    	}()
    
    	go func() {
    		<-s.ctx.Done()
    		_ = s.cmd.Process.Kill()
    	}()
    
    	return stdoutChannel, stderrChannel, nil
    }
    
The dynamic infrastructure framework for everybody! Distribute the workload of many different scanning tools with ease, including nmap, ffuf, masscan, nuclei, meg and many more!
The dynamic infrastructure framework for everybody! Distribute the workload of many different scanning tools with ease, including nmap, ffuf, masscan, nuclei, meg and many more!

Axiom is a dynamic infrastructure framework to efficiently work with multi-cloud environments, build and deploy repeatable infrastructure focussed on

Dec 30, 2022
A fully self-contained Nmap like parallel port scanning module in pure Golang that supports SYN-ACK (Silent Scans)

gomap What is gomap? Gomap is a fully self-contained nmap like module for Golang. Unlike other projects which provide nmap C bindings or rely on other

Dec 10, 2022
🗺 Allows quick generation of basic network plans based on nmap and scan6 output.

NPlan Transforms nmap XML into intermediate JSON and generates a basic network plan in the DrawIO XML format. Installation Just run go install github.

Mar 10, 2022
Simple PKI for developers.

SimpleCA Have you ever been working with a technology and needed TLS certificates quickly? Perhaps you wanted to set up a PKI infrastructure for testi

Mar 31, 2022
Libdns-exoscale - A template for developers to use when creating new libdns provider implementations

DEVELOPER INSTRUCTIONS: This repo is a template for developers to use when creat

Jan 18, 2022
Let's Encrypt client and ACME library written in Go
Let's Encrypt client and ACME library written in Go

Let's Encrypt client and ACME library written in Go. Features ACME v2 RFC 8555 Register with CA Obtain certificates, both from scratch or with an exis

Dec 27, 2022
:key: Idiotproof golang password validation library inspired by Python's passlib

passlib for go Python's passlib is quite an amazing library. I'm not sure there's a password library in existence with more thought put into it, or wi

Dec 30, 2022
A convenience library for generating, comparing and inspecting password hashes using the scrypt KDF in Go 🔑

simple-scrypt simple-scrypt provides a convenience wrapper around Go's existing scrypt package that makes it easier to securely derive strong keys ("h

Dec 22, 2022
An authorization library that supports access control models like ACL, RBAC, ABAC in Golang
An authorization library that supports access control models like ACL, RBAC, ABAC in Golang

Casbin News: still worry about how to write the correct Casbin policy? Casbin online editor is coming to help! Try it at: https://casbin.org/editor/ C

Jan 6, 2023
Let's Encrypt client and ACME library written in Go
Let's Encrypt client and ACME library written in Go

Let's Encrypt client and ACME library written in Go. Features ACME v2 RFC 8555 Register with CA Obtain certificates, both from scratch or with an exis

Dec 30, 2022
Official library of StackPulse steps

StackPulse Steps This repository is the official library of StackPulse steps. StackPulse steps are containerized applications that can be composed tog

Jun 14, 2021
Secure Remote Password library for Go

go-srp NOTE: This is a port of node-srp to Go. I recommend reading their README for general information about the use of SRP. Installation go get gith

Aug 8, 2022
A simple, modern and secure encryption tool (and Go library) with small explicit keys, no config options, and UNIX-style composability.

age age is a simple, modern and secure file encryption tool, format, and library. It features small explicit keys, no config options, and UNIX-style c

Dec 28, 2022
A Go Library For Generating Random, Rule Based Passwords. Many Random, Much Secure.
A Go Library For Generating Random, Rule Based Passwords. Many Random, Much Secure.

Can Haz Password? A Go library for generating random, rule based passwords. Many random, much secure. Features Randomized password length (bounded). T

Dec 6, 2021
Coraza WAF is a golang modsecurity compatible web application firewall library
Coraza WAF is a golang modsecurity compatible web application firewall library

Coraza Web Application Firewall, this project is a Golang port of ModSecurity with the goal to become the first enterprise-grade Open Source Web Application Firewall, flexible and powerful enough to serve as the baseline for many projects.

Jan 9, 2023
golang users friendly linux hacking library.
golang users friendly linux hacking library.

go-cheat users friendly linux hacking library

Nov 9, 2022
Golang library to make sandboxing AppImages easy

aisap AppImage SAndboxing Project: a Golang library to help sandbox AppImages with bwrap What is it? aisap intends to be a simple way to implement And

Nov 16, 2022