Sunday, April 12, 2015

Dealing with Nmap output

Port scans of multiple hosts typically result in a lot of information that I'd rather have in a spreadsheet. If you use the -oX or -oA Nmap flags, you should theoretically be able to receive XML and use XSLT to transform it into comma-separated variable (CSV) format and load it up in your spreadsheet. When I started using Nmap, I wrote an XSL stylesheet to do that, and recently I've updated it to translate pretty much all the information I'm usually interested in. It goes like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/1999/html">

<xsl:output method="text" indent="no" encoding="UTF-8"/>

<xsl:template match="/nmaprun">

<xsl:text>"Hostname",</xsl:text>
<xsl:text>"RecType",</xsl:text>
<xsl:text>"State",</xsl:text>
<xsl:text>"Proto",</xsl:text>
<xsl:text>"Port",</xsl:text>
<xsl:text>"Service",</xsl:text>
<xsl:text>"State",</xsl:text>
<xsl:text>"Product",</xsl:text>
<xsl:text>"OS Match",</xsl:text>
<xsl:text>"OS Type/Vendor/Family/Gen"</xsl:text>
<xsl:text>&#10;</xsl:text>

<!-- Ports -->
<xsl:for-each select="host/ports/port">
<xsl:text>"</xsl:text>
<xsl:text>","</xsl:text>
<xsl:value-of select="../../hostnames/hostname/@name"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="../../hostnames/hostname/@type"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="../../status/@state"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="@protocol"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="@portid"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="service/@name"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="state/@state"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="service/@product"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="../../os/osmatch/@name"/>
<xsl:text>","</xsl:text>
<xsl:value-of select="../../os/osmatch/osclass/@type"/>
<xsl:text> / </xsl:text>
<xsl:value-of select="../../os/osmatch/osclass/@vendor"/>
<xsl:text> / </xsl:text>
<xsl:value-of select="../../os/osmatch/osclass/@osfamily"/>
<xsl:text> / </xsl:text>
<xsl:value-of select="../../os/osmatch/osclass/@osgen"/>
<xsl:text>"</xsl:text>
<xsl:text>&#10;</xsl:text>
</xsl:for-each>

</xsl:template>

</xsl:stylesheet>


This stylesheet can be used with Microsoft's XSL command-line transformation utility (msxsl.exe), which can be downloaded directly from Microsoft, here:

The msxsl.exe command line for Windows is:

> msxsl.exe portscan.xml nmap_xml_to_csv.xsl -o portscan.csv


Or you can use xsltproc on Linux:

\$ xsltproc nmap_xml_to_csv.xsl portscan.xml --output portscan.csv


Or you can use Python or Perl or whatever. Knock yourself out.

As for how it works, the stylesheet simply uses the xsl:template element to match on the root element of the Nmap output (/nmaprun), the xsl:for-each element to iterate through ports, and Xpath expressions to pull the various host attributes out of the XML. The end result is nice and manageable:

Now you can filter on open ports, sort by host or service, and get a better look at your attack surface.

I haven't tested this with lots and lots of Nmap output, so there could be some corner cases I'm missing, but it's a start. The XSL stylesheet is reproduced in full above, but you can also find it (and other things) on my github:

https://github.com/strictlymike/tools/

For more about XSLT, I actually found w3schools to be pretty helpful:

http://www.w3schools.com/xsl/

Actually, I find pretty much all of their tutorials to be helpful. You should go and read them all. Enjoy!