Nmap Security Scanner
*Intro
*Ref Guide
*Install Guide
*Download
*Changelog
*Book
*Docs
Security Lists
*Nmap Hackers
*Nmap Dev
*Bugtraq
*Full Disclosure
*Pen Test
*Basics
*More
Security Tools
*Pass crackers
*Sniffers
*Vuln Scanners
*Web scanners
*Wireless
*Exploitation
*Packet crafters
*More
Site News
Site Search:
Exploit World
Advertising
About/Contact
Credits
Sponsors:
edgeos



Nmap Development: --append_output option with XML files

--append_output option with XML files

From: Duilio J. Protti <dprotti_at_flowgate.net>
Date: Wed, 13 Jul 2005 21:16:15 -0300

Current stable version of nmap (3.81) doesn't handle correctly the --
append_output option when results are logged to XML files.

If you have a file my-run.xml like the following:

<?xml version="1.0" ?>
<?xml-stylesheet ... >
<nmaprun scanner="nmap" args=...>
...
</nmaprun>

and you run:

# nmap -sS -F -oX my-run.xml --append_output $IP

now my-run.xml content is:

<?xml version="1.0" ?>
<?xml-stylesheet ... >
<nmaprun scanner="nmap" args=...>
...
</nmaprun>
<?xml version="1.0" ?>
<?xml-stylesheet ... >
<nmaprun scanner="nmap" args=...>
...
</nmaprun>

Which does not meet the DTD and even worse, it's not well formed XML.

The attached patch solve this issue, avoiding the prologue when the --
append_output is present. However, this not solve all the problems. When
trying to process the generated XML, the XSL transformation will fail,
because there are two root elements (two <nmaprun>).

The problem is that the present DTD for nmap do not take into account
multiple runs. IMHO, the better way to solve this, is enclosing all the
<nmaprun> tags into a new root element, i.e. <nmapruns> (note the 's'),
which will contain a list of <nmaprun> childs.

This way, adding new nmap results to an existing set of previous nmap
runs, is just a matter to add them within a new <nmaprun> child into the
root element. Of course, this requires a change on the current DTD, but
it's a minor change.

I do these changes, because in my job as pentester, I periodically run
surgical nmap tests against one target (not a range of targets), and I
want to keep this results in a one-file-per-ip basis. Previously, I did
this using normal and grepable formats, but since the new XML feature of
nmap is available, I really want to use it, because it's too much
suitable for later report generation.

The attached patch is for version 3.81, and include required changes to
nmap.cc, output.cc, nmap.xsl and nmap.dtd.

I hope this help!

Regards,
Duilio Protti.

--- nmap.cc 2005-07-13 18:51:04.000000000 -0300
+++ nmap.cc 2005-07-13 18:10:30.000000000 -0300
@@ -891,7 +891,9 @@ int nmap_main(int argc, char *argv[]) {
     snprintf(xslline, sizeof(xslline), "<?xml-stylesheet href=\"%s\" type=\"text/xsl\"?>\n", p);
     free(p);
   } else xslline[0] = '\0';
- log_write(LOG_XML, "<?xml version=\"1.0\" ?>\n%s<!-- ", xslline);
+ if (!o.append_output)
+ log_write(LOG_XML, "<?xml version=\"1.0\" ?>\n%s<nmapruns>\n", xslline);
+ log_write(LOG_XML, "<!-- ");
   log_write(LOG_NORMAL|LOG_MACHINE, "# ");
   log_write(LOG_NORMAL|LOG_MACHINE|LOG_XML, "%s %s scan initiated %s as: ", NMAP_NAME, NMAP_VERSION, mytime);
   
--- output.cc 2005-07-13 18:51:17.000000000 -0300
+++ output.cc 2005-07-13 18:44:55.000000000 -0300
@@ -598,7 +598,10 @@ void log_flush_all() {
    it already exists. If the file does not exist, it will be created */
 int log_open(int logt, int append, char *filename)
 {
- int i=0;
+ int i=0,l=logt;
+ long offset;
+ char b[12];
+
   if (logt<=0 || logt>LOG_MASK) return -1;
   while ((logt&1)==0) { i++; logt>>=1; }
   if (o.logfd[i]) fatal("Only one %s output filename allowed",logtypes[i]);
@@ -611,12 +614,35 @@ int log_open(int logt, int append, char
   }
   else
     {
- if (o.append_output)
+ if (append && (l == LOG_XML)) {
+ o.logfd[i] = fopen(filename, "r+");
+ if (o.logfd[i]) {
+ fseek(o.logfd[i],-12L,SEEK_END);
+ fread(b, 1, 11, o.logfd[i]);
+ b[11] = '\0';
+ puts(b);
+ offset = 0;
+ while (1) {
+ offset--;
+ if (fseek(o.logfd[i],offset,SEEK_END) < 0)
+ fatal("Cannot find closing XML tag </nmapruns> on file '%s'", filename);
+ fread(b, 1, 11, o.logfd[i]);
+ if (strstr(b, "</nmapruns>")) {
+ fseek(o.logfd[i],offset,SEEK_END);
+ break;
+ }
+ }
+ }
+ } else if (append)
         o.logfd[i] = fopen(filename, "a");
       else
         o.logfd[i] = fopen(filename, "w");
- if (!o.logfd[i])
- fatal("Failed to open %s output file %s for writing", logtypes[i], filename);
+ if (!o.logfd[i]) {
+ if (append)
+ fatal("Failed to open %s output file '%s' for appending", logtypes[i], filename);
+ else
+ fatal("Failed to open %s output file '%s' for writing", logtypes[i], filename);
+ }
     }
   return 1;
 }
@@ -1179,9 +1205,7 @@ void printfinaloutput(int numhosts_scann
   log_write(LOG_XML, "<!-- Nmap run completed at %s; %d %s (%d %s up) scanned in %.3f seconds -->\n", mytime, numhosts_scanned, (numhosts_scanned == 1)? "IP address" : "IP addresses", numhosts_up, (numhosts_up == 1)? "host" : "hosts", o.TimeSinceStartMS(&tv) / 1000.0 );
   log_write(LOG_NORMAL|LOG_MACHINE, "# Nmap run completed at %s -- %d %s (%d %s up) scanned in %.3f seconds\n", mytime, numhosts_scanned, (numhosts_scanned == 1)? "IP address" : "IP addresses", numhosts_up, (numhosts_up == 1)? "host" : "hosts", o.TimeSinceStartMS(&tv) / 1000.0 );
 
- log_write(LOG_XML, "</runstats></nmaprun>\n");
+ log_write(LOG_XML, "</runstats></nmaprun></nmapruns>\n");
 
 }
 
-
-
--- docs/nmap.xsl 2005-02-05 03:56:11.000000000 -0300
+++ docs/nmap.xsl 2005-07-13 15:14:23.000000000 -0300
@@ -5,6 +5,7 @@
             Benjamin Erb, http://www.benjamin-erb.de
 ==============================================================================
     Copyright (c) 2004 Benjamin Erb
+ Copyright (c) 2005 Duilio Protti
     All rights reserved.
 
     Redistribution and use in source and binary forms, with or without
@@ -36,20 +37,13 @@
 <!-- ............................................................ -->
 <xsl:variable name="nmap_xsl_version">0.9a</xsl:variable>
 <!-- ............................................................ -->
-<xsl:variable name="start"><xsl:value-of select="/nmaprun/@start" /></xsl:variable>
-<xsl:variable name="end"><xsl:value-of select="/nmaprun/runstats/finished/@time" /> </xsl:variable>
-<xsl:variable name="totaltime"><xsl:value-of select="/nmaprun/runstats/finished/@time -/nmaprun/@start" /></xsl:variable>
-<!-- ............................................................ -->
 
 
 <xsl:template match="/">
         <xsl:apply-templates/>
 </xsl:template>
 
-
-<!-- root -->
-<!-- ............................................................ -->
-<xsl:template match="/nmaprun">
+<xsl:template match="nmapruns">
 <html>
 <head>
 
@@ -322,40 +316,73 @@ function timestamp2date(stamp)
 
 <body>
         <div id="container">
+
+ <xsl:apply-templates/>
+
+ </div>
+</body>
+
+</html>
+
+</xsl:template>
+
+<!-- root -->
+<!-- ............................................................ -->
+<xsl:template match="nmaprun">
+
+ <xsl:variable name="start"><xsl:value-of select="@start"/></xsl:variable>
+
     <h1>nmap scan report - scan @
     <xsl:call-template name="timestamp">
- <xsl:with-param name="stamp"><xsl:value-of select="$start" /></xsl:with-param>
+ <xsl:with-param name="stamp"><xsl:value-of select="@start" /></xsl:with-param>
     </xsl:call-template>
     </h1>
     <ul id="menu">
- <li>scan summary</li>
- <li>scan info</li>
-
+ <li>
+ <xsl:element name="a">
+ <xsl:attribute name="href">#scansummary-<xsl:value-of select="@start" /></xsl:attribute>
+ <xsl:attribute name="target">_self</xsl:attribute>
+ scan summary
+ </xsl:element>
+ </li>
+ <li>
+ <xsl:element name="a">
+ <xsl:attribute name="href">#scaninfo-<xsl:value-of select="@start" /></xsl:attribute>
+ <xsl:attribute name="target">_self</xsl:attribute>
+ scan info
+ </xsl:element>
+ </li>
       <xsl:for-each select="host">
       <li>
         <xsl:element name="a">
- <xsl:attribute name="href">#<xsl:value-of select="translate(address/@addr, '.', '_') " /></xsl:attribute>
+ <xsl:attribute name="href">#<xsl:value-of select="translate(address/@addr, '.', '_') " />-<xsl:value-of select="$start"/></xsl:attribute>
             <xsl:attribute name="target">_self</xsl:attribute>
             <xsl:value-of select="address/@addr"/>
         </xsl:element>
       </li>
       </xsl:for-each>
- <li>runstats</li>
+ <li>
+ <xsl:element name="a">
+ <xsl:attribute name="href">#runstats-<xsl:value-of select="@start" /></xsl:attribute>
+ <xsl:attribute name="target">_self</xsl:attribute>
+ runstats
+ </xsl:element>
+ </li>
     </ul>
 
         <xsl:element name="a">
- <xsl:attribute name="name">scansummary</xsl:attribute>
+ <xsl:attribute name="name">scansummary-<xsl:value-of select="@start"/></xsl:attribute>
         </xsl:element>
     <h2>scan summary</h2>
     <p>
         <xsl:value-of select="@scanner"/> was initiated at
         <xsl:call-template name="timestamp">
- <xsl:with-param name="stamp"><xsl:value-of select="$start" /></xsl:with-param>
+ <xsl:with-param name="stamp"><xsl:value-of select="@start" /></xsl:with-param>
     </xsl:call-template> with these arguments:<br/>
     <i><xsl:value-of select="@args" /></i><br/>
     The process stopped at
         <xsl:call-template name="timestamp">
- <xsl:with-param name="stamp"><xsl:value-of select="$end" /></xsl:with-param>
+ <xsl:with-param name="stamp"><xsl:value-of select="@end" /></xsl:with-param>
     </xsl:call-template>.
         <xsl:choose>
         <xsl:when test="debugging/@level = '0'">Debbuging was disabled, </xsl:when>
@@ -364,18 +391,22 @@ function timestamp2date(stamp)
     the verbosing level was <xsl:value-of select="verbose/@level" />.
 
     </p>
- <xsl:apply-templates/>
- </div>
-</body>
-</html>
+
+ <xsl:apply-templates>
+ <xsl:with-param name="start"><xsl:value-of select="@start"/></xsl:with-param>
+ <xsl:with-param name="totaltime"><xsl:value-of select="runstats/finished/@time - @start"/></xsl:with-param>
+ </xsl:apply-templates>
+
 </xsl:template>
 <!-- ............................................................ -->
 
 <!-- scaninfo -->
 <!-- ............................................................ -->
 <xsl:template match="scaninfo">
+ <xsl:param name="start"/>
+
         <xsl:element name="a">
- <xsl:attribute name="name">scaninfo</xsl:attribute>
+ <xsl:attribute name="name">scaninfo-<xsl:value-of select="$start"/></xsl:attribute>
         </xsl:element>
 
         <h2>scan info</h2>
@@ -390,8 +421,11 @@ function timestamp2date(stamp)
 <!-- runstats -->
 <!-- ............................................................ -->
 <xsl:template match="runstats">
+ <xsl:param name="start"/>
+ <xsl:param name="totaltime"/>
+
         <xsl:element name="a">
- <xsl:attribute name="name">runstats</xsl:attribute>
+ <xsl:attribute name="name">runstats-<xsl:value-of select="$start"/></xsl:attribute>
         </xsl:element>
 
         <h2>runstats</h2>
@@ -401,15 +435,17 @@ function timestamp2date(stamp)
         <li><xsl:value-of select="hosts/@up" /> host(s) online</li>
         <li><xsl:value-of select="hosts/@down" /> host(s) offline</li>
         </ul>
- <xsl:apply-templates/>
+ <!--xsl:apply-templates/-->
 </xsl:template>
 <!-- ............................................................ -->
 
 <!-- host -->
 <!-- ............................................................ -->
 <xsl:template match="host">
+ <xsl:param name="start"/>
+
         <xsl:element name="a">
- <xsl:attribute name="name"><xsl:value-of select="translate(address/@addr, '.', '_') " /></xsl:attribute>
+ <xsl:attribute name="name"><xsl:value-of select="translate(address/@addr, '.', '_') " />-<xsl:value-of select="$start"/></xsl:attribute>
         </xsl:element>
 
     <xsl:choose>
--- docs/nmap.dtd 2005-07-13 19:08:00.000000000 -0300
+++ docs/nmap.dtd 2005-07-13 21:01:02.000000000 -0300
@@ -9,7 +9,7 @@
      Now maintained by Fyodor <fyodor_at_insecure.org> as part of Nmap.
 
      To validate using this file, simply add a DOCTYPE line similar to:
- <!DOCTYPE nmaprun SYSTEM "nmap.dtd">
+ <!DOCTYPE nmapruns SYSTEM "nmap.dtd">
      to the nmap output immediately below the prologue (the first line). This
      should allow you to run a validating parser against the output (so long
      as the dtd is in your parser's dtd search path).
@@ -77,6 +77,8 @@
 <!-- This element was started in nmap.c:nmap_main().
      It represents to the topmost element of the output document.
 -->
+<!ELEMENT nmapruns (nmaprun+) >
+
 <!ELEMENT nmaprun (scaninfo?, verbose, debugging, host*, runstats?) >
 <!ATTLIST nmaprun
                         scanner (nmap) #REQUIRED

_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Received on Jul 13 2005

[ Nmap | Sec Tools | Mailing Lists | Site News | About/Contact | Advertising | Privacy ]
edgeos