Dealing with Nmap grepable Output

Recently I had to deal with Nmap grepable output. Of course you can say: Why not using XML and the Nmap Parsers of your favorite scripting language?

Well, if it’s not there you can’t 😉

I needed to get one line per IP and open port, so an nmap line of

192.168.1.64 (server.home.inc) Ports: 21/open/tcp//ftp//FileZilla ftpd 0.9.37 beta/, 80/open/tcp//http//Microsoft IIS httpd 7.5/, 443/open/tcp//https?///, 445/filtered/tcp//microsoft-ds// Ignored State: filtered (65529) Seq Index: 263 IP ID Seq: Incremental

should result in

192.168.1.64,server.home.inc,21
192.168.1.64,server.home.inc,80
192.168.1.64,server.home.inc,443

Therefore I created the following little perl script where one can use regex filters:

#!/usr/bin/perl

use warnings;
use strict;

# note: you have to take the Nmap port output into account for the regex, e.g.
# “open/.+/(ssl|https)” to find open ssl ports
if (@ARGV < 2) {
print “usage: csv-ports.pl <nmap grepable> <regex service filter>\n”;
exit 1;
}

my $file = $ARGV[0];
my $regex = $ARGV[1];

# input format: nmap grepable output
# output:
#             ip1,hostname1,port1
#             ip1,hostname1,port2
#             …

open FILE, “<$file” || die “Cannot open $file”;

while(<FILE>) {
next if /^\n$|^#.*$/; # ignore comments and blank lines
next if /Status/; # ignore status lines
chomp;
# match line
# $1 IP
# $2 hostname
# $3 ports in nmap notation
next if not /^Host: ([\d\.]+) \((.*)\)\tPorts: (.+)(\t.*)*$/;

my $ip = $1;
my $host = $2;
my @ports = split /, /, $3;

foreach my $port (@ports) {
next if $port !~ /$regex/i; # use given regex as filter for ports
$port =~ /^(\d+).*$/;
print “$ip,$host,$1\n”;
} # foreach
} # while

close FILE;

So if you want to get only open ports from the output you simply call

perl csv-ports.pl nmap.gnmap “open/”

Note the trailing slash in the regex, omitting it may result in wrong output if there is “open” included in the version string or such in nmap’s output.

It’s not perfect but it does its job – at least for me 🙂