Monday, March 5, 2012

Converting Nmap XML output to CSV (Excel) Script

Ever ran a Nmap scan and wished that there was a way to easily get the data into Excel? You could want the data in Excel for a number of reasons... I personally like to have it in Excel sometimes so it is easier to track information about the host, OS, ports, services, vulnerabilities, etc.

The closest output that Nmap has is its built in XML format (-oX flag). Using this XML output I have created a perl script that easily converts the XML output into a CSV file for use in Excel. Luckily, a guy named Anthony Persaud created an Nmap Parser module for perl that we can use (

We need to ensure we take care of a couple of dependencies first:

  1. The first step is obviously to install Perl 5+ if you haven't already. 
  2. Then you need to install the Nmap Parser module. You can do this with the following command: perl -MCPAN -e 'install Nmap::Parser'
  3. That's it! Not too shabby.

Here is the actual perl script. Just copy/paste it and run. It takes two arguments. The first is the name of the Nmap XML file and the second is the name of your output file. 


# Author: Travis Lee
# Date Modified: 3-5-12
# Description: Script to convert a Nmap scan XML file to CSV
# Usage: <nmap xml file.xml> <output file.csv>

use Nmap::Parser;
$base = new Nmap::Parser;

if (!$ARGV[0] || !$ARGV[1])
print "Usage: <nmap xml file.xml> <output file.csv>\n\n";

$base_file = $ARGV[0]; #baseline scan filename from command line
$out_file = $ARGV[1]; #output filename from command line

$base->parsefile($base_file); #load baseline scan file

open (OUTFILE, ">$out_file");

$session = $base->get_session;
print OUTFILE $session->scan_args."  --  Scan finished on: ".$session->time_str;

print OUTFILE "\n\nIP,Hostname,MAC,OS,Port,Proto,State,Service,Version\n";

#loop through all the IPs in the current scan file
for my $ip ($base->get_ips) 
        #get host object for the current IP
        $ip_base = $base->get_host($ip);

#populate arrays with tcp/udp ports
my @tcpports = $ip_base->tcp_ports;
my @udpports = $ip_base->udp_ports;

print OUTFILE $ip_base->ipv4_addr.",";
print OUTFILE $ip_base->hostname.",";
print OUTFILE $ip_base->mac_addr.",";

#get os object for the current IP
my $os = $ip_base->os_sig;

#if smb-os-discrovery script was run, use this value for os. more accurate
if ($ip_base->hostscripts("smb-os-discovery"))
my @os_split1 = split(/\n/, $ip_base->hostscripts("smb-os-discovery"));
my @os_split2 = split("OS: ", $os_split1[1]);
print OUTFILE $os_split2[1].",";
else { print OUTFILE $os->name.","; } #else use what was discovered with os fingerprint

$first = 1;

&portout(\@tcpports, "tcp");
&portout(\@udpports, "udp");

print OUTFILE "\n";
} #end for loop

close (OUTFILE);
print "\n\nConversion complete!\n\n";

sub portout
my @ports = @{$_[0]};
my $proto = $_[1];

for my $port (@ports)
#get service object for the given port
if ($proto eq "tcp")
$svc = $ip_base->tcp_service($port);
elsif ($proto eq "udp")
$svc = $ip_base->udp_service($port);

if ($first == 1) { $first = 0; }
else { print OUTFILE ",,,,"; }

print OUTFILE $port.",";
print OUTFILE $proto.",";

if ($proto eq "tcp")
print OUTFILE $ip_base->tcp_port_state($port).",";
elsif ($proto eq "udp")
print OUTFILE $ip_base->udp_port_state($port).",";

print OUTFILE $svc->name.",";
print OUTFILE $svc->product."\n";

} #end for loop

} #end sub

I am not a professional perl ninja so it may not be the most efficient, but it works. If you have any suggestions on improvements to the script, please let me know! I would love to make this better and more helpful for anyone that needs it.

And of course there is also the obligatory disclaimer to use this at your own risk. :) Enjoy!


  1. it could be useful

  2. I don't know if you still do work on this, but your script generates the nmap command line as a row, which tends to jack up importing into a spreadsheet.

  3. Thanks for publishing your script Travis, definitely a time-saver!

    For what it's worth, on a CentOS 6.2 system with nmap 5.21 and perl-Nmap-Parser 1.19, the script threw this error:
    [Nmap-Parser] method ->hostscripts() not defined!

    Since I'm not using SMB-OS-discovery, I commented out that if-else block and re-added the line:
    print OUTFILE $os->name.",";