Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing nmap output

A project to familiarize myself that I'm working on is to parse through an nmap result.

(I know of the -oG option, but I'm working with grep, awk, for, and while loops here).

Below is what I'm trying to parse through:

Starting Nmap 7.60 ( https://nmap.org ) at 2017-12-05 11:26 EST
Nmap scan report for house.router.nick (192.168.1.1)


Host is up (0.00059s latency).
Not shown: 995 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
53/tcp   open  domain
427/tcp  open  svrloc
1900/tcp open  upnp

MAC Address: 50:C7:BF:A8:CF:C8 (Tp-link Technologies)


Nmap scan report for 192.168.1.2


Host is up (0.00034s latency).
Not shown: 996 closed ports
PORT      STATE SERVICE
22/tcp    open  ssh
80/tcp    open  http
139/tcp   open  netbios-ssn
445/tcp   open  microsoft-ds

MAC Address: 48:F8:B3:C9:AE:BB (Cisco-Linksys)

What I want to get is this:

22/ssh
====
192.168.1.1
192.168.1.2

http
===
192.168.1.2

So far, I have this:

grep -E "tcp.*open" nmap.txt | awk '{ print $3 }' | sort | uniq

For the life of me, I can't figure out how to get this into a loop of sorts and get the desired output from above.

Can you please help me learn and explain why you came to the solution you did? No point in getting a potential solution if I can't understand the logic behind it.

like image 268
Nick Avatar asked Aug 15 '18 19:08

Nick


2 Answers

You'd be better served using a general purpose programming language (python, perl, awk) where you can capture the IP address when you see the "scan report for" keywords, and then maintain a data structure mapping the service names to the list of IP addresses running those services.

For example, given that output, this perl one-liner (offered without explanation)

perl -ne '
    if (/Nmap scan report for .*?(\d+\.\d+\.\d+\.\d+)/) {
        $ip = $1
    }
    elsif (/^(\d+)\/tcp\s+open\s+(.*)/) {
        push @{$services{"$1/$2"}}, $ip
    }
    END {
        for $svc (sort {$a <=> $b} keys %services) {
            printf "%s\n%s\n\n", $svc, join("\n", @{$services{$svc}})
        }
    }
'

produces

22/ssh
192.168.1.1
192.168.1.2

53/domain
192.168.1.1

80/http
192.168.1.2

139/netbios-ssn
192.168.1.2

427/svrloc
192.168.1.1

445/microsoft-ds
192.168.1.2

1900/upnp
192.168.1.1
like image 70
2 revs Avatar answered Oct 22 '22 15:10

2 revs


$ cat tst.awk
BEGIN { FS="[[:space:]/]+" }

/Nmap scan report/ {
    ip = $NF
    gsub(/[()]/,"",ip)
}

/tcp.*open/ {
    key = $1 "/" $4
    ips[key] = (key in ips ? ips[key] ORS : "") ip
}

END {
    for (key in ips) {
        print key
        print "===="
        print ips[key]
        print ""
    }
}

.

$ awk -f tst.awk file
80/http
====
192.168.1.2

1900/upnp
====
192.168.1.1

427/svrloc
====
192.168.1.1

445/microsoft-ds
====
192.168.1.2

53/domain
====
192.168.1.1

22/ssh
====
192.168.1.1
192.168.1.2

139/netbios-ssn
====
192.168.1.2

I think it's extremely obvious what it's doing but if you have any questions after maybe a few glances at the awk man page then feel free to ask.

The above will work using any awk in any shell on every UNIX system.

like image 37
Ed Morton Avatar answered Oct 22 '22 14:10

Ed Morton