8

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.

Nick
  • 93
  • 1
  • 5
  • What is the output you are currently recieving? – l'L'l Aug 15 '18 at 20:27
  • Currently, this is what I'm getting domain http microsoft-ds netbios-ssn ssh svrloc – Nick Aug 15 '18 at 20:30
  • You're going to have to describe your requirements more fully/clearly as there's several ways to get that output from that input but I suspect only one of them is what you are actually trying to do. – Ed Morton Aug 15 '18 at 20:44
  • why don't you show the other `tcp.*open` matches, where do you filter only ssh or 22? – karakfa Aug 15 '18 at 20:45
  • I'm purely trying to just get the "22/ssh" \n "======" \n echo $ips. I'm attempting to use a "for" loop for this, and doing research on it as well. It's not a lack of effort, but more of a lack of understanding. Even an explanation or pointer in the right direction is appreciated. I'm not here trying to leech, but learn. Thanks again! For the requirements, as log as I get a list of the IPs under that port and/or service (please reference my above post for an example. – Nick Aug 15 '18 at 20:52
  • 1
    [edit] your question to contain all relevant information, don't spread it out across comments where it can get missed. Some things that aren't clear, for example - why only ssh instead of separate lists per service type? what if one IP only had http? Why only port 22 instead of lists per port and service. etc., etc... – Ed Morton Aug 15 '18 at 20:53
  • Oh, my apologies. Yes, I want it to be listed by service. For example, IPs using HTTP, IPs using SSH, IPs with RPC, etc. I hope this clarifies what I'm asking assistance with. – Nick Aug 15 '18 at 21:05

2 Answers2

5

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
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • Thanks a ton! I appreciate it. If I learn perl, I can at least call that in *nix systems. I never thought to use perl. Cheers! – Nick Aug 15 '18 at 21:40
1
$ 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.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
  • 1
    I read the man page. Must admit, it seems like awk is going to be my go to. It's seems a lot more readable (for revisiting code) than perl is. But please correct me if I'm wrong as I'm going to attempt to interpret what your suggestion is doing. 1. Uses the BEGIN and END portions of awk to evaluate/format before it takes input from the file to be read. 2. Removes the () from around the IPs and substitutes it with nothing and sets that to the IP variable. 3. Looks for the 'tcp.*open' regex and formats the "key" to number/service. 4. Iterates through each section and prints out if matching – Nick Aug 15 '18 at 22:43
  • You would not be alone in feeling that way about perl syntax (see https://www.zoitz.com/comics/perl_small.png). Also awk comes as standard on all UNIX systems, perl doesn't. Yup that's about it except, to be clear, BEGIN executes before any input file is opened and END executes after all input files have been read. The stuff in between is executed in an implicit `while read` loop that's reading each line one at a time and populating the `ips[]` array as it goes so that we can loop on it's indices in the END section and print the associated contents (the IP addresses for each port/service pair) – Ed Morton Aug 16 '18 at 04:00