https://nmap.org/nsedoc/lib/shortport.html
这个库是专门为了端口规则 (portrules) 而准备的,用来构造端口规则,从下面这个图就可以看出来了
基本上都是调用 shortport 的方法来判定是否符合相关规则,返回 true or false 来绝对是否进入 action 函数
函数名 | 描述 |
---|---|
http(host, port) | 匹配比较可能是 http 服务的端口规则 |
port_is_excluded(port, proto) | 检查是否给定的端口和协议已经被排除在外了 |
port_or_service(ports, services, protos, states) | 当给定开放端口匹配到端口或者服务规则,则返回一个令 portrule 为 true 的函数 |
port_range(range) | 当给定开放端口在指定的端口范围内时,返回一个令 portrule 为 true 的函数 |
portnumber(ports, protos, states) | 当给定的开放端口匹配到一个独立端口或者一系列端口,返回 true |
service(services, protos, states) | 当给定开放端口匹配到指定服务,则返回一个令 portrule 为 true 的函数 |
ssl(host, port) | 匹配到类似 ssl 服务时返回 true |
version_port_or_service(ports, services, protos, states, rarity) | 返回一个portrule,该portrule在给定与端口号或服务名称匹配的开放端口时返回true,并且未在nmap service probes文件的exclude port指令中列出。如果版本强度小于rarity,则portrule始终返回false(暂不理解这个版本强度) |
这回我们要折腾端口规则了,action 可以先放一放,端口规则同样是一个函数
port_is_excluded 有两个参数, port, proto ,即端口,协议 ,如果端口和协议被排除了,则返回 true
这个不是返回一个函数,而是返回一个值,所以无法作为端口规则,应该作为检查过程中调用
portnumber 函数有三个参数: ports, protos, states
我们看一下其他脚本是怎么使用这个规则的
我的编程与理解能力实在是不高,导致我一看上面这个图当时就蒙了,参数端口都是确定的,而不是我们我想象的扫描的端口或者端口们,所以我就从代码上看看咋回事吧!
portnumber = function(ports, protos, states)
protos = protos or "tcp"
states = states or {"open", "open|filtered"}
if type(ports) ~= "table" then
ports = {ports}
end
if type(protos) ~= "table" then
protos = {protos}
end
if type(states) ~= "table" then
states = {states}
end
return function(host, port)
return port_includes(ports, port.number)
and tableaux.contains(protos, port.protocol, true)
and tableaux.contains(states, port.state, true)
end
end
前面是比较好理解的,就是构造 protos、states、以及ports,关键在于 port_includes 和后面这几个函数,先看 port_includes 吧
local function port_includes(t, value)
for _, elem in ipairs(t) do
if elem == value then
return true
elseif type(elem) == "string" then
local pstart, pend = elem:match("^(%d+)%-(%d+)$")
if not pstart then
pstart = elem:match("^(%d+)$")
pend = pstart
end
pstart, pend = tonumber(pstart), tonumber(pend)
assert(pstart,"Incorrect port range specification.")
assert(pstart<=pend,"Incorrect port range specification, the starting port should have a smaller value than the ending port.")
assert(pstart>-1 and pend<65536, "Port range number out of range (0-65535).")
if value >= pstart and value <= pend then
return true
end
end
end
return false
end
portnumber 给port_includes 传递的参数为 ports, port.number ,其中 port.number 就是我们扫描的端口(们)。之后在 port_includes 中会将 ports 遍历,之后与我们扫描的做对比,如果我们扫描的端口在 portnumber 的端口参数范围内就会返回 true 那这样就可以理解了,这个函数参数就是限定了要去匹配的端口和服务,而不是全部都匹配
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---
local url = require "url"
local stdnse = require "stdnse"
local base64 = require "base64"
local shortport = require "shortport"
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("functest running")
end
portrule = shortport.portnumber({80, 443}, 'tcp')
action = function()
local output = stdnse.output_table()
local result = "True"
output.result = result
return output
end
这里只规定了80,443 ,协议为 tcp ,如果返回true,说明匹配上了
可以看到返回 True,匹配成功,现在我把端口不变,协议变成 udp
可以看到,我们规定匹配到的协议是 udp 后,没有 result 返回
service 函数有三个参数:services, protos, states
我们看一下其他脚本们是如何调用这个函数的
大家基本上很少直接使用这个,使用也就是直接用第一个参数
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---
local url = require "url"
local stdnse = require "stdnse"
local base64 = require "base64"
local shortport = require "shortport"
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("functest running")
end
portrule = shortport.service('http','tcp')
action = function()
local output = stdnse.output_table()
local result = "True"
output.result = result
return output
end
测试一下
可以看到,返回 true ,识别为http服务了,现在我们换成 https, tcp
可以看到,这回就没有返回 True 了
port_or_service 函数有四个参数:ports, services, protos, states
这个其实就是把上面的两个函数 portnumber 和 service 合在了一起,在我印象中应该是使用的更加频繁
参数比较完整的是这一条
portrule = shortport.port_or_service({50000,60000}, {"drda","ibm-db2"}, "tcp", {"open", "open|filtered"})
可以看到四个参数都用了
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---
local url = require "url"
local stdnse = require "stdnse"
local base64 = require "base64"
local shortport = require "shortport"
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("functest running")
end
portrule = shortport.port_or_service({80, 443},{'https','http'})
action = function()
local output = stdnse.output_table()
local result = "True"
output.result = result
return output
end
还是用 nc 来测试一下
可以看到成功识别到
version_port_or_service 有5个参数 ports, services, protos, states, rarity
这个其实就是将 port_or_service 和 port_is_excluded 再加上 nmap.version_intensity 结合在一起
我们看一下这个函数大家是怎么用的
基本上都是只用了四个参数,最后一个强度基本没有用的,我们演示尽量用的全一些吧
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---
local url = require "url"
local stdnse = require "stdnse"
local base64 = require "base64"
local shortport = require "shortport"
local nmap = require "nmap"
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("functest running")
end
portrule = shortport.version_port_or_service({80, 443},{'https','http'},'tcp', 'open', 8)
action = function()
local output = stdnse.output_table()
local result = "True"
output.result = result
return output
end
测试一下
可以看到,返回了 True
这里我就有一个好奇了,根据以下代码
version_port_or_service = function(ports, services, protos, states, rarity)
return function(host, port)
local p_s_check = port_or_service(ports, services, protos, states)
return p_s_check(host, port)
and not(port_is_excluded(port.number, port.protocol))
and (nmap.version_intensity() >= (rarity or 7))
end
end
只有当 nmap.version_intensity() >= 我们提供的值或者7的时候才会返回 true ,我们设置rarity为 8,那么 nmap.version_intensity() 是多少呢?搞一下不就知道了
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---
local url = require "url"
local stdnse = require "stdnse"
local base64 = require "base64"
local shortport = require "shortport"
local nmap = require "nmap"
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("functest running")
end
portrule = shortport.version_port_or_service({80, 443},{'https','http'},'tcp', 'open', 8)
action = function()
local output = stdnse.output_table()
local result = "True"
output.ver = nmap.version_intensity()
output.result = result
return output
end
我们把这个信息用 ver 来承接并打印出来
可以看到, nmap.version_intensity() 的值为9 所以肯定是大于等于 rarity (0-9),其实这个值我们可以在调用函数的时候设置超过 9,但是没有意义,超过 9 就没有定义了
为了显示效果,我想把 nmap.version_intensity() 降低一下,经过查询,nmap提供了一个参数 --version-intensity
现在我们调用时设置是 8, 我们把 nmap.version_intensity() 设置为 7 ,这样的话应该就不会显示 True 了
怪事来了,即使我们通过参数设置了 nmap.version_intensity() 为 7 ,但是结果还是 9 ,不知道因为啥
所以这样大家调用这个函数的时候都不加最后一个参数也就可以理解了,具体因为什么以后问问开发者吧!
http 函数有两个默认参数 host,port ,一般都是直接 shortport.http 不加参数的,我们看一下大家怎么用的
可以看到,大家都是不加参数直接用的,就表示是http服务
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---
local url = require "url"
local stdnse = require "stdnse"
local base64 = require "base64"
local shortport = require "shortport"
local nmap = require "nmap"
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("functest running")
end
portrule = shortport.http
action = function()
local output = stdnse.output_table()
local result = "True"
output.result = result
return output
end
测试一下
可以看到识别为了 http 服务,实际上是因为我们监听的是 80 端口,如果我换一个 2333 端口呢?
可以看到,已经不再被识别为 http了,自然脚本中 action 代码也就没有执行
那么问题来了,这个 http 函数是怎么判断 http 服务的呢?
http = port_or_service(LIKELY_HTTP_PORTS, LIKELY_HTTP_SERVICES)
这个函数就这么一行代码,其中有两个参数比较重要 LIKELY_HTTP_PORTS, LIKELY_HTTP_SERVICES ,那这两个常量都包含什么呢?
LIKELY_HTTP_PORTS = {
80, 443, 631, 7080, 8080, 8443, 8088, 5800, 3872, 8180, 8000
}
LIKELY_HTTP_SERVICES = {
"http", "https", "ipp", "http-alt", "https-alt", "vnc-http", "oem-agent",
"soap", "http-proxy", "caldav", "carddav", "webdav",
}
可以看到这个函数会将 80、443、631 .... 一些端口识别为 http ;会将 http、https、webdav ......一些服务识别为 http ,当然获取的目标信息都是由nmap主程序给的,所以如果我们觉得这里不全的可以添加进去,比如 weblogic 的 7001 ,那我们就是可以添加进去,就像我们之前修改 User-Agent
与 http 一样,默认参数
可以看到在端口规则处使用 ssl 函数的只有一个脚本,使用方法也是和 http 一毛一样
ssl 的判断更加复杂,其中有一项也是端口以及服务,我们看一下列表
local LIKELY_SSL_PORTS = {
261, -- nsiiops
271, -- pt-tls
324, -- rpki-rtr-tls
443, -- https
465, -- smtps
563, -- snews/nntps
585, -- imap4-ssl
636, -- ldapssl
853, -- domain-s
989, -- ftps-data
990, -- ftps-control
992, -- telnets
993, -- imaps
994, -- ircs
995, -- pop3s
2221, -- ethernet-ip-s
2252, -- njenet-ssl
2376, -- docker-s
3269, -- globalcatLDAPssl
3389, -- ms-wbt-server
4911, -- ssl/niagara-fox
5061, -- sip-tls
5986, -- wsmans
6679,
6697,
8443, -- https-alt
9001, -- tor-orport
8883, -- secure-mqtt
}
local LIKELY_SSL_SERVICES = {
"ftps", "ftps-data", "ftps-control", "https", "https-alt", "imaps", "ircs",
"ldapssl", "ms-wbt-server", "pop3s", "sip-tls", "smtps", "telnets", "tor-orport",
}
同样的,你觉得使用了 ssl 却不在里面的可以自行添加
port_range 只有一个参数:range 是一个范围,格式案例 "T:80,1-30,U:31337,21-25"
我们还是来看一下大家使用这个脚本的方法
只有一条,其中 portarg 似乎是一个自定义的参数
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by root.
--- DateTime: 2020/1/2 上午9:39
---
local url = require "url"
local stdnse = require "stdnse"
local base64 = require "base64"
local shortport = require "shortport"
local nmap = require "nmap"
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("functest running")
end
portrule = shortport.port_range("T:2333,4000-5555")
action = function()
local output = stdnse.output_table()
local result = "True"
output.result = result
return output
end
测试一下,nc监听 2333端口
可以看到返回 True ,匹配成功