现在我们已经介绍了 NSE 是如何工作的,现在是学习如何编写第一个 Nmap 脚本的时候了。由于编写 Nmap 脚本的通用性和极为定制的特性,有几种不同的方法来生成执行各种功能的脚本,也有许多优点和缺点来编写自己的脚本。
虽然从头开始创建 Nmap 脚本可能并不总是完成任务的最快方式(因为几乎总是有一个脚本已经存在,用于您可能需要的任何目的),在某些情况下,利用 Nmap 脚本引擎强大的内置功能可以完全正确地编写自己的脚本。
在本章中,我们将介绍以下主题:
- Nmap 脚本的剖析
- 编写 Nmap 脚本的头
- 创建规则
- 定义脚本的操作
- 调试 Nmap 脚本
Nmap 脚本由几个独特的部分组成,每个部分定义了脚本要执行的不同区域,或 Nmap 解释预期输出的不同区域。为了确保脚本有效运行(并且 Nmap 能够理解如何解释数据),我们必须在创建的任何脚本中始终包含几个主要方面。
尽管 Nmap 脚本是用 Lua(一种解释性编程语言)编写的,但重要的是要记住,这些脚本不是可以独立运行的独立可执行文件。与其运行一个需要 Nmap 的脚本,不如将 Nmap 脚本看作是一种独特的 Nmap 编程语言的简单指令集。
Nmap 脚本由三个独特的部分组成:
- 头:Nmap 脚本的部分包括脚本的文档和分类,以便 Nmap 和 NSE 数据库能够成功地将脚本分类到适当的区域。
- 规则:脚本的部分准确定义了 Nmap 脚本的执行位置和方式。因为脚本在运行时利用了 Nmap 扫描的数据,所以某些元素可以触发脚本运行。这实际上是一个触发器,用于评估脚本是否应执行。
- 动作:最后,Nmap 脚本的部分就是动作发生的地方(你猜对了!)。这是脚本中执行大量处理的部分,在头部定义了脚本并且规则触发了操作之后。
现在我们已经了解了 Nmap 脚本是如何编写的,是时候开始工作并开始编写一个了。因为每一个 Nmap 脚本都是如此独特,我们将重新创建一个已经运行的脚本,但它显示了 NSE 的强大功能。
我们对该脚本的案例研究将是编写一个简单易用的 Nmap 脚本,该脚本使用 Nmap 的内置功能(结合 NSE 的功能)来确定 web 服务器是否有robots.txt
文件。robots.txt
文件表明网站的哪些区域应该(也不应该)被网络爬虫和搜索引擎索引,并且通常会列出敏感目录,并指示不为其索引。出于这个原因,它们对于安全专业人员和渗透测试人员来说非常有趣,因为我们正在寻找的正是那些敏感文件和目录!
每个 Nmap 脚本必须使用在脚本开头定义的特定必需变量创建。成功执行所需的任何 Nmap 先决条件、脚本分类的定义(例如,脚本是否具有侵入性、安全性、是否包含漏洞等)以及许可证也是标头中必需的内容之一。
前面的屏幕截图说明了 Nmap 脚本所需的各个部分,每个部分对程序的成功执行都至关重要。让我们浏览一下这些元素,以确定脚本作者正在做什么。
首先,定义几个变量(由本地前缀定义)。为了确保适当地包括每个 Nmap 元素,提出了若干要求。
接下来,创建一个较长的变量来描述。这是一个多行 Lua 变量,封装在[[
和]]
括号中。该区域应该包括 Nmap 脚本的基本描述,以便在以编程方式运行时,可以选择正确的脚本。
description 变量下面是一个完全注释的文本块,用于定义脚本的用法。在 Lua 中,--
序言注释掉了那行代码,使其在脚本执行时不运行。您可以很容易地看到@usage
块是如何格式化的,只需显示脚本应该如何运行,以及它可能接受的任何参数,以及@output
块是如何格式化的。这些块显示了如何正确运行脚本,如何在命令行上传递参数(如果需要),以及您应该从相关脚本获得什么输出。
在注释的 out 块下面是 Nmap 解析的其他几个变量定义。具体来说,author
块(这是您希望为脚本获得信任的方式)、license
块(出于分发目的,通常与 Nmap 相同,但如果您希望保护脚本的某些元素,可以以某些方式指定)categories
数组(其中列出了脚本应属于的类别)。例如,如果您的脚本是侵入性的,请确保将其标记为侵入性的。
对于我们的脚本,我们只需要几个必需的includes
,这使得我们的标题相对较短。让我们创建我们的 head 部分,如下所示(当然,可以随意修改脚本!)
local http = require "http"
local nmap = require "nmap"
description = [[
Checks to see if robots.txt exists on a web server.
]]
author = "Nmap Essentials readers"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default", "discovery", "safe"}
非常简单!我们需要 HTTP 模块来执行对所讨论的robots.txt
的 HTTP GET 请求(在开放端口 80 上),当然我们还需要 Nmap include 来利用 Nmap 脚本引擎。您可以看到,我们的描述非常简单,我们定义了作者、许可证和类别,以帮助用户确定脚本何时可以安全有效地运行。
现在我们的脚本的开头已经完成,让我们来看看规则。
Nmap 脚本的规则或 portrule 部分确定操作应该在何时发生(我们将在下一节中介绍)。清楚地定义这一点很重要,这样我们就可以确信脚本将在每次需要时运行(基于端口号和版本)。有两种方法可以实现这种类型的规则:标准 portrule 文档和 NSE 中构建的名为shortport的助手库。
定义规则实际上非常简单,这取决于我们要寻找的内容。在我们的robots.txt
检测脚本(恰当地命名为robots.nse
的情况下),我们只想在端口 80 上触发,看看robots.txt
是否存在。
如果我们编写的是生产脚本,而不是概念证明,那么最好使用 shortport 的端口或服务功能在端口 80 或 Nmap 通过其底层功能检测到的任何 web 服务器上触发。然而,在我们的例子中,我们可以简单地定义一些更容易理解的东西:
portrule = function(host, port)
return port.state == "open"
end
如您所见,这是一个非常小的 portrule,当port.state
为open
时,它将返回true
。这些是内置的 Nmap 功能,当脚本运行时,将根据 portrule 检查每个端口。
虽然我们的 portrule 有意让人非常容易理解,但许多生产脚本都有非常复杂的 portrules,这些 portrules 是根据特定的版本和配置设置来触发不同的分析元素的。要了解有关 advanced portrule 和 shortport 库的更多信息,您可以在Nmap 脚本引擎文档(NSEDoc门户)上阅读完整概述。
在定义了 portrule 之后,剩下的唯一一步就是定义 portrule 返回true
时执行的操作。在我们的例子中,我们想检查我们正在扫描的 web 服务器上是否存在robots.txt
。
为了确定服务器是否存在,我们需要了解一点关于超文本传输协议(HTTP的内容。首先,请求页面的方法是通过 HTTP GET 请求。例如,如果我们想去http://google.com/images ,我们的浏览器会向Google.com
的服务器发送包含GET /images
的请求。
如果 GET 请求的状态为OK
,则 web 服务器返回状态码200
。如果存在服务器端错误,将返回一个500
错误。如果页面移动,将返回范围为300
的错误。最后(出于我们的目的),如果存在授权错误或文件未找到错误,服务器将分别返回403
或404
。
为了定义动作功能,我们需要执行以下步骤:
- 请求
robots.txt
页面。 - 看看它是否在那里。
以下动作段定义了该请求:
action = function(host, port)
local robots = http.get(host, port, "/robots.txt")
if robots.status == 200 then
return "robots.txt status 200"
else
return "robots.txt status: " .. robots.status
end
end
正如您在前面的代码片段中所看到的,这是一个非常简单的操作部分。让我们一步一步地了解这个过程:
-
首先,我们定义接受参数的主机和端口的操作函数。一旦 portrule 触发,这些将自动传递到操作块。
-
接下来,我们定义一个局部变量(称为 robots),它是 NSE 的
http.get
请求的 HTTP 结果。在本例中,我们正在执行对当前正在扫描的主机和端口的访问,并向/robots.txt
发出请求。 -
一旦我们收到 HTTP 数据,我们就可以很容易地做出一个
if
语句来确定状态是200 OK
响应还是其他什么。我们可以将它与一个较短的if
语句(而不是 if/else)结合起来,但是看看如何为输出提供多种可能性是很有用的。 -
If the output is not
200
, we go to theelse
statement and see what the status is. For example, if the status is404
, we know that it simply doesn't exist; if we get a500
server error or a403 not authorized
, however, it might be worth looking into a greater depth:
如您所见,运行这个脚本(以及任何自定义 Nmap 脚本)非常简单。扫描scanme.nmap.org
时,您可以清楚地看到没有robots.txt
-我们只是收到一个404
错误。如果我们扫描一个有robots.txt
页面的服务,我在上创建了一个测试用例 http://dshaw.net/ 为此,我们看到了不同的结果。
在本例中,我们可以清楚地看到,200
状态 HTTP OK 表示robots.txt
确实存在:
如果这是一个生产 Nmap 脚本,那么可能值得更改与200 OK
响应关联的返回,以显示更多信息,例如不允许的文件和目录。但是,不要把时间花在这个特定的脚本上!在官方的 Nmap 存储库中已经有一个很棒的 HTTProbots.txt
脚本(以及更多)。
最后一组对于编写、理解和调试 Nmap 脚本非常有用的标志是--script-trace
和-d
(调试)标志。--script-trace
标志在线路上显示脚本自己发出的所有不同请求的信息,这对于确定具体发生的情况非常有用:
您可以在前面的屏幕截图中看到,虽然可能有一点信息过载,但通过使用--script-trace
标志,您可以确切地看到 Nmap 脚本正在做什么。用于调试的-d
标志的工作原理类似:如果您在编写脚本时遇到错误,请尝试使用-d
标志进行调试。你会惊讶于你能学到的伟大的东西!
本章向我们展示了如何编写自己的 Nmap 脚本!NSE 是一个功能强大(有时很复杂)的工具,可以帮助 Nmap 用户完成各种有趣的自动化任务。作为概念证明,我们编写的脚本可以轻松检测服务器上是否存在robots.txt
文件,但编写 Nmap 脚本供内部使用或检测特定漏洞的可能性几乎是无限的!
在下一章中,我们将学习如何使用随 Nmap 工具套件附带的工具,以及一些有用的技巧和技巧,以充分利用这些工具。