Nmap NSE 缺陷

时间:2022-07-23
本文章向大家介绍Nmap NSE 缺陷,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

今天在编写 Nmap 脚本的时候发现了一个小缺陷:

Nmap 判定扫描结果为 filtered 的时候,就不会进入 portrule 了

首先给大家看一下代码

可以看到,我在 portrule 处直接返回了 true ,所以按照规则来说,无论端口是什么规则,都会执行,但是结果如下

可以看到:

  • 在 open 状态下,prerule、portrule以及 action 中的代码都执行了
  • 在 filtered 状态下,prerule 内容执行了,打印了 Weblogic Console Searching ...

但是,action 中代码并没有执行

我们简单调试一下,在 portrule 中输出一下:

测试一下

重新测试两次,刚好出现了open 和 filtered 的情况

  • open 状态下,打印了 portrule is here! ,并且执行了 action 中的扫描内容
  • filtered 状态下,执行了 prerule ,但是没有执行 portrule

从以上这些现象说明,在 filtered 状态下是不会执行到 portrule 这个规则的

但是这样的话,shortport 中的 port_or_service 函数的最后一个状态参数也没有意义了呀(这个现在还没在公众号发布,后续会发布)

所以我猜应该是我理解有问题,不然那么多写脚本的大哥肯定会发现的

在portrule 之前还有 hostrule, 我们看一下filtered 状态下会执行到这里吗?

测试一下

测试了好久终于出现了一个 open 一个filtered 的对比图

  • open 状态下,执行了两次 action 中的代码
  • filtered 状态下,进入了 hostrule 中,执行了其中的代码(打印了 hostrule is here!)

以上情况说明 filtered 状态下执行流程会停留在 hostrule 或者说 跳过 portrule

既然都整到这里了,也不差一个 postrule 了,把hostrule给注释了,免得有不必要的影响,反正已经确定了hostrule 的情况

测试一下

可以看到,prerule 和 postrule 都执行了,所以说从结果来说,遇到端口 filtered 会跳过 portrule


看似情况都清晰了,但还有一个小问题,为啥在 hostrule 中调用 action 中的代码会报错,而 postrule 中不会

我们使用 -d 参数来追踪一下

local stdnse = require "stdnse"
local shortport = require "shortport"
local http = require "http"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("Weblogic Console Searching ... ")
end

hostrule = function()
    print("hostrule is here!")
    return true
end

--portrule = function()
--    print("portrule is here!")
--    return true
--end-- shortport.port_or_service(7001, {"http", "https", "afs3-callback"}, "tcp")--  {"open", "filtered", "open|filtered"})

postrule = function()
    print("postrule is here!")
end

action = function(host, port)
    print('I am action')
    local output = stdnse.output_table()
    output.SCRIPT_NAME = SCRIPT_NAME
    output.SCRIPT_TYPE = SCRIPT_TYPE
    output.SCRIPT_PATH = SCRIPT_PATH

    -- 配置 http options
    local options = {header = {}}
    options["header"]["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36"
    local pipelines = http.pipeline_add("/console/", nil, nil, "GET")
    local pipelines2 = http.pipeline_add("/console/login/LoginForm.jsp", nil, pipelines,"GET")
    local pipelines3 = http.pipeline_add("/console/login/LoginfdForm.jsp", nil, pipelines2,"GET")
    local result = http.pipeline_go(host, port, pipelines3)
    output.result = {}
    --for _, v in pairs(result) do
    --    output.result[#result+1] = v
    --end

    -- 目前存在同一个数据包中发两个http包的情况,不知道因为啥,待处理

    output.result[#output.result+1] = result[1]["status"]
    output.result[#output.result+1] = result[2]["status"]
    output.result[#output.result+1] = result[3]["status"]
    return output
end

测试一下

其实很好理解,在 hostrule 这里,只有host 这个对象,还没有 port 这个对象,所以会报错

我们将 port 直接替换成 7001 进行测试

local stdnse = require "stdnse"
local shortport = require "shortport"
local http = require "http"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("Weblogic Console Searching ... ")
end

hostrule = function()
    print("hostrule is here!")
    return true
end

--portrule = function()
--    print("portrule is here!")
--    return true
--end-- shortport.port_or_service(7001, {"http", "https", "afs3-callback"}, "tcp")--  {"open", "filtered", "open|filtered"})

postrule = function()
    print("postrule is here!")
end

action = function(host)
    print('I am action')
    local output = stdnse.output_table()
    output.SCRIPT_NAME = SCRIPT_NAME
    output.SCRIPT_TYPE = SCRIPT_TYPE
    output.SCRIPT_PATH = SCRIPT_PATH

    -- 配置 http options
    local options = {header = {}}
    options["header"]["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36"
    local pipelines = http.pipeline_add("/console/", nil, nil, "GET")
    local pipelines2 = http.pipeline_add("/console/login/LoginForm.jsp", nil, pipelines,"GET")
    local pipelines3 = http.pipeline_add("/console/login/LoginfdForm.jsp", nil, pipelines2,"GET")
    local result = http.pipeline_go(host, 7001, pipelines3)
    output.result = {}
    --for _, v in pairs(result) do
    --    output.result[#result+1] = v
    --end

    -- 目前存在同一个数据包中发两个http包的情况,不知道因为啥,待处理

    output.result[#output.result+1] = result[1]["status"]
    output.result[#output.result+1] = result[2]["status"]
    output.result[#output.result+1] = result[3]["status"]
    return output
end

测试一下

这回正常了


总结如下 :

  • 端口状态为 filtered 状态下,是不会执行 portrule 的
  • port 对象是在 portrule 阶段被创建的,无法使用在 hostrule 阶段
  • Nmap 对于端口检测很飘忽,得多测几次

那么问题又来了:hostrule 是不是也存在被跳过的状态(不执行)

正好之前的目标是 ping 不通的,只有加 -Pn 参数才能确定主机是否存活,刚好来测试一下,加与不加 -Pn 参数对 hostrule 的执行会不会有什么影响

代码如下

local stdnse = require "stdnse"
local shortport = require "shortport"
local http = require "http"

description = [[
This is a test for http.lua's functions
]]

author = "test94"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default"}

prerule = function()
    print("Weblogic Console Searching ... ")
end

hostrule = function()
    print("hostrule is here!")
    return true
end

--portrule = function()
--    print("portrule is here!")
--    return true
--end-- shortport.port_or_service(7001, {"http", "https", "afs3-callback"}, "tcp")--  {"open", "filtered", "open|filtered"})

postrule = function()
    print("postrule is here!")
end

action = function(host)
    print('I am action')
    local output = stdnse.output_table()
    output.SCRIPT_NAME = SCRIPT_NAME
    output.SCRIPT_TYPE = SCRIPT_TYPE
    output.SCRIPT_PATH = SCRIPT_PATH

    -- 配置 http options
    local options = {header = {}}
    options["header"]["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36"
    local pipelines = http.pipeline_add("/console/", nil, nil, "GET")
    local pipelines2 = http.pipeline_add("/console/login/LoginForm.jsp", nil, pipelines,"GET")
    local pipelines3 = http.pipeline_add("/console/login/LoginfdForm.jsp", nil, pipelines2,"GET")
    local result = http.pipeline_go(host, 7001, pipelines3)
    output.result = {}
    --for _, v in pairs(result) do
    --    output.result[#result+1] = v
    --end

    -- 目前存在同一个数据包中发两个http包的情况,不知道因为啥,待处理

    output.result[#output.result+1] = result[1]["status"]
    output.result[#output.result+1] = result[2]["status"]
    output.result[#output.result+1] = result[3]["status"]
    return output
end

分别测试一下

可以看到

  • 加上 -Pn 时可以发现主机存活,可以执行 prerule、hostrule 和 postrule
  • 不加 -Pn 无法发现主机存活,也无法执行 hostrule,可以执行 prerule, postrule

总结如下 :

  • 端口状态为 filtered 状态下,是不会执行 portrule 的
  • port 对象是在 portrule 阶段被创建的,无法使用在 hostrule 阶段的 action 代码中
  • Nmap执行 NSE 是分阶段的,伴随着扫描的主机存活、端口开放的不同阶段进入不同的规则
  • Nmap 对于端口检测很飘忽,得多测几次