前言

在渗透测试过程中,我们有时会使用 Nmap 对相关 IP 资产进行扫描,不论你是使用命令行方式还是 Zenmap-GUI 的图形化方式,我们有时会需要将扫描信息保存下来,不论是自己日后分析还是需要将扫描结果呈现给你的 BOSS 看,都是较为方便的。而 Nmap 它是支持生成扫描报告,然后配合转换模板,可实现自定义转换报告。那么本文就带大家了解一下 Nmap 扫描的 HTML 报告的生成,方便大家能够自主生成美观的报告。

Nmap 扫描报告生成

Nmap 的扫描报告结果一般是 xml 格式,不便于传阅,通常会使用 xsltproc 工具将 xml 格式的文档转换成 html 格式,以便于浏览。其转换原理是根据 Nmap 自带的 xsl 模板(一般位于 /usr/share/nmap/目录下的nmap.xsl文件)渲染转换,不过 Nmap 自带的 xsl 模板已经很老了,在文章的最后分享给大家一个自己在使用的新的 xsl 模板,使得转换后的 html 报告更加漂亮,便于浏览。

废话不多说了,下面教程开始。

由于 xml 格式报告转成 html 格式报告需要用到 xsltproc 工具,您可以提前安装好该工具,在 Linux 系统下该工具的安装命令如下:

  • Redhat系(如 CentOS、Fedora系统):yum install -y libxslt
  • Debian系(如 Ubuntu、Debian 系统):apt install -y xsltproc

接下来,我们在使用 Nmap 扫描时指定参数使其生成一份 xml 格式文件。

命令行

在使用命令行方式扫描时,我们需要使用 -oX 参数将扫描结果保存为一份 xml 格式的文件,例如下方的 nmap 扫描命令:

nmap -A -T4 -Pn -p 1-65535 localhost -oX ./localhost.xml

此时命令行模式下 Nmap 扫描完对应 IP/域名 后,就会在对应目录下生成一份 xml 格式的文件。

Nmap GUI

对于 GUI 图形化,我们可以点击左上方的“扫描--保存扫描”,然后命名一下待保存的 xml 文件以及选择需要保存的文件夹并保存即可。

HTML 报告生成

Nmap 自带 xsl 文件生成

接下来,我们就需要使用到 xsltproc 工具,将 Nmap 扫描后生成的 xml 格式文件,并通过相关 xsl 模板,生成一份可阅读的 HTML 格式的报告。

我们先以 Nmap 自带的 xsl 模板文件生成一份报告为例,相关命令如下:

xsltproc -o ./localhost_nmap.html /usr/share/nmap/nmap.xsl ./localhost.xml

其中,上述命令格式如下:

  • -o :指定待生成 html 报告的文件
  • 倒数第二个参数是指定的 xsl 模板(上述为 Nmap 自带的 xsl 模板,可直接使用)
  • 倒数第一个参数为 Nmap 扫描后生成的 xml 格式文件

通过上述命令后,我们即可在相关目录下生成一份 html 报告了,此时我们使用浏览器打开,即可进行浏览,如图所示:

image-20221127223003491

第三方 xsl 文件生成

由于 Nmap 自带的 xsl 模板文件已经相对而言较为古老了,于是此处自己从网络上搜集了其它 xsl 模板文件并进行了简单修改,然后接着使用上述 xsltproc 工具,修改参数 倒数第二行 使用第三方 xsl 模板文件再生成一份 html 报告,其效果如图所示:

image-20221127223613017

通过两图的对比,可以发现第三方的扫描报告更加美观,且新的 xsl 模板使用了 bootstrapjquerydataTables 等工具包,还支持分页、检索,相比 Nmap 自带的 xsl 模板则实在好太多。

第三方 xsl 模板代码

吃水不忘挖井人,该模板从网络上来,自己将 h2 标签文字与顶栏的空隙距离微调了下,经自己的简单调整优化后再次将该 xsl 模板回馈于大家,其代码如下:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" encoding="utf-8" indent="yes" doctype-system="about:legacy-compat"/>
  <xsl:template match="/">
    <html lang="en">
      <head>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"/>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"/>
        <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/dataTables.bootstrap.min.css"/>
        <script src="https://code.jquery.com/jquery-3.3.1.js"></script>
        <script src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
        <script src="https://cdn.datatables.net/1.10.19/js/dataTables.bootstrap.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
        <style>
          .target:before {
            content: "";
            display: block;
            height: 50px;
            margin: -20px 0 0;
          }
          @media only screen and (min-width:1900px) {
            .container {
              width: 1800px;
              }
          }
          .footer {
            margin-top:60px;
            padding-top:60px;
            width: 100%;
            height: 180px;
            background-color: #f5f5f5;
          }
          h2 {
            margin-top: 40px;
          }
          .navbar-right {
             float: right!important;
              margin-right: -15px;
         }
        </style>
        <title>Nmap 扫描报告</title>
      </head>
      <body>
      <!--导航栏-->
        <nav class="navbar navbar-default navbar-fixed-top">
          <div class="container-fluid">
            <div class="navbar-header">
              <a class="navbar-brand" href="#"><span class="glyphicon glyphicon-home"></span></a>
            </div>
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
              <ul class="nav navbar-nav">
                <li><a href="#summary">概要信息</a></li>
                <li><a href="#scannedhosts">主机信息</a></li>
                <li><a href="#onlinehosts">在线主机</a></li>
                <li><a href="#openservices">服务信息</a></li>
              </ul>              
              <ul class="nav navbar-nav navbar-right">
                <li><a href="https://www.isisy.com">Yang2635</a></li>
              </ul>
            </div>
          </div>
        </nav>

     <!--内容区-->
        <div class="container">        
     <h2 id="summary" class="target">扫描概要</h2>
          <div class="target">
            <p >Nmap 版本:<xsl:value-of select="/nmaprun/@version"/></p>
            <p >Nmap命令:<xsl:value-of select="/nmaprun/@args"/></p>
            <p >开始时间:<xsl:value-of select="/nmaprun/@startstr"/> </p>
            <p >结束时间:<xsl:value-of select="/nmaprun/runstats/finished/@timestr"/></p>
          </div>
          
          <h2 id="scannedhosts" class="target">主机信息<xsl:if test="/nmaprun/runstats/hosts/@down > 1024"><small> (offline hosts are hidden)</small></xsl:if></h2>
          <div class="table-responsive">
            <table id="table-overview" class="table table-striped dataTable" role="grid">
              <thead>
                <tr>
                  <th>状态</th>
                  <th>IP</th>
                  <th>主机名</th>
                  <th>开放TCP端口数</th>
                  <th>开放UDP端口数</th>
                </tr>
              </thead>
              <tbody>
                <xsl:choose>
                  <xsl:when test="/nmaprun/runstats/hosts/@down > 1024">
                    <xsl:for-each select="/nmaprun/host[status/@state='up']">
                      <tr>
                        <td><span class="label label-danger"><xsl:if test="status/@state='up'"><xsl:attribute name="class">label label-success</xsl:attribute></xsl:if><xsl:value-of select="status/@state"/></span></td>
                        <td><xsl:value-of select="address/@addr"/></td>
                        <td><xsl:value-of select="hostnames/hostname/@name"/></td>
                        <td><xsl:value-of select="count(ports/port[state/@state='open' and @protocol='tcp'])"/></td>
                        <td><xsl:value-of select="count(ports/port[state/@state='open' and @protocol='udp'])"/></td>
                      </tr>
                    </xsl:for-each>
                  </xsl:when>
                  <xsl:otherwise>
                    <xsl:for-each select="/nmaprun/host">
                      <tr>
                        <td><span class="label label-danger"><xsl:if test="status/@state='up'"><xsl:attribute name="class">label label-success</xsl:attribute></xsl:if><xsl:value-of select="status/@state"/></span></td>
                        <td><xsl:value-of select="address/@addr"/></td>
                        <td><xsl:value-of select="hostnames/hostname/@name"/></td>
                        <td><xsl:value-of select="count(ports/port[state/@state='open' and @protocol='tcp'])"/></td>
                        <td><xsl:value-of select="count(ports/port[state/@state='open' and @protocol='udp'])"/></td>
                      </tr>
                    </xsl:for-each>
                  </xsl:otherwise>
                </xsl:choose>
              </tbody>
            </table>
          </div>
          <script>
            $(document).ready(function() {              
             $('#table-overview').DataTable({
                 language: {
                     "sProcessing": "处理中...",
                     "sLengthMenu": "显示 _MENU_ 项结果",
                     "sZeroRecords": "没有匹配结果",
                     "sInfo": "显示第 _START_ 至 _END_ 项结果,共 _TOTAL_ 项",
                     "sInfoEmpty": "显示第 0 至 0 项结果,共 0 项",
                     "sInfoFiltered": "(由 _MAX_ 项结果过滤)",
                     "sInfoPostFix": "",
                     "sSearch": "搜索:",
                     "sUrl": "",
                     "sEmptyTable": "表中数据为空",
                     "sLoadingRecords": "载入中...",
                     "sInfoThousands": ",",
                     "oPaginate": {
                     "sFirst": "首页",
                     "sPrevious": "上页",
                     "sNext": "下页",
                     "sLast": "末页"
                     },
                     "oAria": {
                         "sSortAscending": ": 以升序排列此列",
                         "sSortDescending": ": 以降序排列此列"
                     }
                 }
             });              
            });
            
          </script>
          
          <h2 id="onlinehosts" class="target">在线主机</h2>
          <xsl:for-each select="/nmaprun/host[status/@state='up']">
            <div class="panel panel-default">
              <div class="panel-heading">
                <h3 class="panel-title"><xsl:value-of select="address/@addr"/><xsl:if test="count(hostnames/hostname) > 0"> - <xsl:value-of select="hostnames/hostname/@name"/></xsl:if></h3>
              </div>
              <div class="panel-body">
                <xsl:if test="count(hostnames/hostname) > 0">
                  <h4>Hostnames</h4>
                  <ul>
                    <xsl:for-each select="hostnames/hostname">
                      <li><xsl:value-of select="@name"/> (<xsl:value-of select="@type"/>)</li>
                    </xsl:for-each>
                  </ul>
                </xsl:if>
                <h4>端口信息</h4>
                <div class="table-responsive">
                  <table class="table table-bordered">
                    <thead>
                      <tr>
                        <th>端口</th>
                        <th>协议</th>
                        <th>状态</th>
                        <th>探测手段</th>
                        <th>服务</th>
                        <th>组件</th>
                        <th>版本</th>
                        <th>附件信息</th>
                        <th>CPE 信息</th>
                      </tr>
                    </thead>
                    <tbody>
                      <xsl:for-each select="ports/port">
                        <xsl:choose>
                          <xsl:when test="state/@state = 'open'">
                            <tr class="success">
                              <td title="Port"><xsl:value-of select="@portid"/></td>
                              <td title="Protocol"><xsl:value-of select="@protocol"/></td>
                              <td title="State"><xsl:value-of select="state/@state"/></td>
                              <td title="Reason"><xsl:value-of select="state/@reason"/></td>
                              <td title="Service"><xsl:value-of select="service/@name"/></td>
                              <td title="Product"><xsl:value-of select="service/@product"/></td>
                              <td title="Version"><xsl:value-of select="service/@version"/></td>
                              <td title="Extra Info"><xsl:value-of select="service/@extrainfo"/></td>
                              <td title="CPE Info"><xsl:value-of select="service/cpe"/></td>
                            </tr>
                            <xsl:for-each select="script">
                              <tr class="script">
                                <td></td>
                                <td><xsl:value-of select="@id"/> <xsl:text>&#xA0;</xsl:text></td>
                                <td colspan="7">
                                <pre><xsl:value-of select="@output"/> <xsl:text>&#xA0;</xsl:text></pre>
                                </td>
                              </tr>
                            </xsl:for-each>
                              
                          </xsl:when>
                          <xsl:when test="state/@state = 'filtered'">
                            <tr class="warning">
                              <td><xsl:value-of select="@portid"/></td>
                              <td><xsl:value-of select="@protocol"/></td>
                              <td><xsl:value-of select="state/@state"/><br/><xsl:value-of select="state/@reason"/></td>
                              <td><xsl:value-of select="service/@name"/></td>
                              <td><xsl:value-of select="service/@product"/></td>
                              <td><xsl:value-of select="service/@version"/></td>
                              <td><xsl:value-of select="service/@extrainfo"/></td>
                              <td><xsl:value-of select="service/cpe"/></td>
                            </tr>
                          </xsl:when>
                          <xsl:when test="state/@state = 'closed'">
                            <tr class="active">
                              <td><xsl:value-of select="@portid"/></td>
                              <td><xsl:value-of select="@protocol"/></td>
                              <td><xsl:value-of select="state/@state"/><br/><xsl:value-of select="state/@reason"/></td>
                              <td><xsl:value-of select="service/@name"/></td>
                              <td><xsl:value-of select="service/@product"/></td>
                              <td><xsl:value-of select="service/@version"/></td>
                              <td><xsl:value-of select="service/@extrainfo"/></td>
                              <td><xsl:value-of select="service/cpe"/></td>
                            </tr>
                          </xsl:when>
                          <xsl:otherwise>
                            <tr class="info">
                              <td><xsl:value-of select="@portid"/></td>
                              <td><xsl:value-of select="@protocol"/></td>
                              <td><xsl:value-of select="state/@state"/><br/><xsl:value-of select="state/@reason"/></td>
                              <td><xsl:value-of select="service/@name"/></td>
                              <td><xsl:value-of select="service/@product"/></td>
                              <td><xsl:value-of select="service/@version"/></td>
                              <td><xsl:value-of select="service/@extrainfo"/></td>
                              <td><xsl:value-of select="service/cpe"/></td>
                            </tr>
                          </xsl:otherwise>
                        </xsl:choose>
                      </xsl:for-each>
                    </tbody>
                  </table>
                </div>
                
                <xsl:if test="count(hostscript/script) > 0">
                  <h4>主机 脚本</h4>
                </xsl:if>
                <xsl:for-each select="hostscript/script">
                  <h5><xsl:value-of select="@id"/></h5>
                  <pre style="white-space:pre-wrap; word-wrap:break-word;"><xsl:value-of select="@output"/></pre>
                </xsl:for-each>
                
              </div>
            </div>
          </xsl:for-each>  
          
          <h2 id="openservices" class="target">服务信息</h2>
          <div class="table-responsive">
            <table id="table-services" class="table table-striped dataTable" role="grid">
              <thead>
                <tr>
                  <th>IP</th>
                  <th>端口</th>
                  <th>协议</th>
                  <th>服务</th>
                  <th>组件</th>
                  <th>版本</th>
                  <th>CPE</th>
                  <th>附加信息</th>
                </tr>
              </thead>
              <tbody>
                <xsl:for-each select="/nmaprun/host">
                  <xsl:for-each select="ports/port[state/@state='open']">
                    <tr>
                      <td><xsl:value-of select="../../address/@addr"/><xsl:if test="count(../../hostnames/hostname) > 0"> - <xsl:value-of select="../../hostnames/hostname/@name"/></xsl:if></td>
                      <td><xsl:value-of select="@portid"/></td>
                      <td><xsl:value-of select="@protocol"/></td>
                      <td><xsl:value-of select="service/@name"/></td>
                      <td><xsl:value-of select="service/@product"/></td>
                      <td><xsl:value-of select="service/@version"/></td>
                      <td><xsl:value-of select="service/cpe"/></td>
                      <td><xsl:value-of select="service/@extrainfo"/></td>
                    </tr>
                  </xsl:for-each>
                </xsl:for-each>
              </tbody>
            </table>
          </div>
          <script>        
            $(document).ready(function() {              
             $('#table-services').DataTable({
                 language: {
                     "sProcessing": "处理中...",
                     "sLengthMenu": "显示 _MENU_ 项结果",
                     "sZeroRecords": "没有匹配结果",
                     "sInfo": "显示第 _START_ 至 _END_ 项结果,共 _TOTAL_ 项",
                     "sInfoEmpty": "显示第 0 至 0 项结果,共 0 项",
                     "sInfoFiltered": "(由 _MAX_ 项结果过滤)",
                     "sInfoPostFix": "",
                     "sSearch": "搜索:",
                     "sUrl": "",
                     "sEmptyTable": "表中数据为空",
                     "sLoadingRecords": "载入中...",
                     "sInfoThousands": ",",
                     "oPaginate": {
                     "sFirst": "首页",
                     "sPrevious": "上页",
                     "sNext": "下页",
                     "sLast": "末页"
                     },
                     "oAria": {
                         "sSortAscending": ": 以升序排列此列",
                         "sSortDescending": ": 以降序排列此列"
                     }
                 }
             });              
            });            
          </script>
        </div>
     
     <!-- 页脚 -->
        <footer class="footer">
          <div class="container">
            <p class="text-muted">
              This Report Was Generated By <a href='https://www.isisy.com'>Yang2635</a>.<br/>
            </p>
          </div>
        </footer>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>
End

本文标题:如何优雅的给领导呈现Nmap扫描报告

本文链接:https://www.isisy.com/1413.html

除非另有说明,本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

声明:转载请注明文章来源。

最后修改:2022 年 11 月 28 日
如果觉得我的文章对你有用,请随意赞赏