Skip to content

Use as a library

Vsevolod Djagilev edited this page Sep 28, 2023 · 4 revisions

It's possible to write golang code that allows to parse nmap xml and convert it to any format you want, or use parsed information further:

Convert to JSON using golang

package main

import (
	"encoding/xml"
	"os"

	"github.com/vdjagilev/nmap-formatter/v2/formatter"
)

func main() {
	var nmap formatter.NMAPRun
	var config formatter.Config = formatter.Config{}

	// Read XML file that was produced by nmap (with -oX option)
	content, err := os.ReadFile("example.xml")
	if err != nil {
		panic(err)
	}
	// Unmarshal XML and map structure(s) fields accordingly
	if err = xml.Unmarshal(content, &nmap); err != nil {
		panic(err)
	}

	// Output data to console stdout
	// You can use any other io.Writer implementation
	// for example: os.OpenFile("file.json", os.O_CREATE|os.O_EXCL|os.O_WRONLY, os.ModePerm)
	config.Writer = os.Stdout
	// Formatting data to JSON, you can use:
	// CSVOutput, MarkdownOutput, HTMLOutput as well
	config.OutputFormat = formatter.JSONOutput

	// Setting formatter data/options
	templateData := formatter.TemplateData{
		NMAPRun: nmap, // NMAP output data itself
		OutputOptions: formatter.OutputOptions{
			JSONOptions: formatter.JSONOutputOptions{
				PrettyPrint: true, // Additional option to prettify JSON
			},
		},
	}

	// New formatter instance
	formatter := formatter.New(&config)
	if formatter == nil {
		// Not json/markdown/html/csv
		panic("wrong formatter provided")
	}

	// Attempt to format the data
	if err = formatter.Format(&templateData, "" /* no template content for JSON */); err != nil {
		// html template could not be parsed or some other issue occured
		panic(err)
	}
}

Use NMAPRun struct

In this example we parse example.xml, go through all hosts that are online, filtering out only those ports that have http services running. After that we attempt to make a request to that service via http, if that fails there is another attempt made to make a request via https, if that still fails we print those errors and continue to the next request.

package main

import (
	"encoding/xml"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/vdjagilev/nmap-formatter/v2/formatter"
)

func sendRequest(address string, port int, https bool) (*http.Response, error) {
	schema := "http"
	if https {
		schema = "https"
	}
	resp, err := http.Get(fmt.Sprintf("%s://%s:%d", schema, address, port))
	if err != nil {
		// I guess we fail
		return nil, err
	}
	return resp, err
}

func main() {
	var nmap formatter.NMAPRun
	// var config formatter.Config = formatter.Config{}

	// Read XML file that was produced by nmap (with -oX option)
	content, err := os.ReadFile("example.xml")
	if err != nil {
		panic(err)
	}
	// Unmarshal XML and map structure(s) fields accordingly
	if err = xml.Unmarshal(content, &nmap); err != nil {
		panic(err)
	}

	for _, host := range nmap.Host {
		// If host is up and receiving connections
		if host.Status.State != "up" {
			continue
		}

		// Iterating over all ports on that host
		for _, port := range host.Port {
			// If this port is has http service on it
			if port.Service.Name == "http" {
				for _, addr := range host.HostAddress {
					resp, err := sendRequest(addr.Address, port.PortID, false)
					if err != nil {
						log.Printf("Fail http: %s:%d: %v", addr.Address, port.PortID, err)
						// Attempt https then
						resp, err = sendRequest(addr.Address, port.PortID, true)
						if err != nil {
							// Still fail, continue to the next port, if any
							log.Printf("Fail https: %s:%d: %v", addr.Address, port.PortID, err)
							continue
						}
					}
					defer resp.Body.Close()

					log.Printf("Req: %s:%d --->", addr.Address, port.PortID)
					log.Printf("Res: %s:%d: %d <---", addr.Address, port.PortID, resp.StatusCode)
				}
			}
		}
	}
}

Use NMAP to create simplest CSV

package main

import (
	"encoding/xml"
	"fmt"
	"os"
	"strings"

	"github.com/vdjagilev/nmap-formatter/v2/formatter"
)

func main() {

	// usage: go run use_as_csv.go <path_to_nmap_xml_file>

	var nmap formatter.NMAPRun
	content, err := os.ReadFile(os.Args[1])
	if err != nil {
		panic(err)
	}
	// Unmarshal XML and map structure(s) fields accordingly
	if err = xml.Unmarshal(content, &nmap); err != nil {
		panic(err)
	}

	// Print CSV headers for host address and joined ports
	fmt.Println("address;ports")

	// Use as CSV: Iterate over hosts and print their addresses and joined ports
	for _, host := range nmap.Host {
		if host.Status.State != "up" {
			continue
		}

		// Print host address
		fmt.Printf("%s;", host.JoinedAddresses(","))
		// Print joined ports
		for i, port := range host.Port {
			if i != 0 {
				fmt.Print(",")
			}
			fmt.Printf("%d/%s", port.PortID, strings.ToLower(port.Protocol))
		}
		fmt.Println()
	}
}
Clone this wiki locally