Parse (grepable) nmap output to print a list of IP\t[all open ports] with text utils like awk

awknmaptext processing

I would like to find a way to print for each IP address found to have at least one open port, to print that IP address, followed by a list of open ports separated by commas. The ports and IP address should be separated by a tab delimiter.

I can do this in an ugly fashion, by grepping only the ip address, writing that to file, then grepping the nmap file again using the ip address result file as the input file, and then trimming down the open ports with cut and sed, writing that to file, and then joining both files. This is an ugly process, and it does not work reliably for fringe situations.

Is there an easy way to do this in one line with awk? I would think I would need to have a function in awk to find all open ports and return them so they could be printed along with the IP address, but I have not found how to do that.

Example source data:

Host: 10.0.0.101 ()Ports: 21/closed/tcp//ftp///, 22/closed/tcp//ssh///, 23/closed/tcp//telnet///, 25/closed/tcp//smtp///, 53/closed/tcp//domain///, 110/closed/tcp//pop3///, 139/open/tcp//netbios-ssn///, 143/closed/tcp//imap///, 445/open/tcp//microsoft-ds///, 3389/closed/tcp//ms-wbt-server///

Expected output data:

10.0.0.101    139,445

Best Answer

This awk program should do it:

$ echo "Host: 10.0.0.101 ()Ports: 21/closed/tcp//ftp///, 22/closed/tcp//ssh///, 23/closed/tcp//telnet///, 25/closed/tcp//smtp///, 53/closed/tcp//domain///, 110/closed/tcp//pop3///, 139/open/tcp//netbios-ssn///, 143/closed/tcp//imap///, 445/open/tcp//microsoft-ds///, 3389/closed/tcp//ms-wbt-server///" |
awk '{printf "%s\t", $2;
      for (i=4;i<=NF;i++) {
        split($i,a,"/");
        if (a[2]=="open") printf ",%s",a[1];}
      print ""}' |
sed -e 's/,//'

10.0.0.101  139,445

Before you edited your question, I had assumed, your output would be from shell nmap, so I had prepared this answer:

$ nmap -sT 127.0.0.1-3 |
  awk '/^Nmap scan report/{cHost=$5;}
       /open/ { split($1,a,"/"); result[cHost][a[1]]=""}
       END {
       for (i in result) {
         printf i;
         for (j in result[i])
           printf ",%s", j ;
         print ""} }' |
  sed -e 's/,/\t/'   

localhost   445,25,139,631,22,80
127.0.0.2   445,139,22,80
127.0.0.3   445,139,22,80

If you need explanations for them, leave a comment. If you can help eliminate the trailing sed calls, or can enhance any of the invocations, please edit.

Related Question