这一部分的东西可以耐心的看看,有利于理解NSE扫描原理和nmap原理。以下部分如有错误望指正。。
为了验证脚本扫描阶段和rule函数执行逻辑。。我写了一个小脚本,大家可以执行看看:
# cat test.nse
local stdnse = require "stdnse"
prerule = function()
stdnse.debug1("=======================prerule")
return nmap.is_privileged() and
(stdnse.get_script_args("targets-sniffer.iface") or nmap.get_interface())
end
postrule = function()
stdnse.debug1("=======================postrule")
return false
end
portrule = function()
stdnse.debug1("=======================portrule")
return false
end
hostrule = function()
stdnse.debug1("=======================hostrule")
return false
end
action = function(host,port)
print("=======================action")
end
NSE脚本参数arguments
命令行指定参数
有些脚本是有参数的,不指定参数脚本执行会失败,具体参数是什么以及怎么使用,可以直接查看脚本文件。
举例说明:
$nmap -p80 --script http-trace --script-args http-trace.path <target>
#可以忽略参数的脚本名称
$nmap -p80 --script http-trace --script-args path <target>
#如果多个脚本的argument名称相同,必须进行区别
$nmap --script script_1,script_2 --script-args script_2.uri=/axis2/,uri=/majordomo/ <target>
$nmap --script script_1,script_2 --script-args uri=/axis2/,script_1.uri=/majordomo/ <target>
$nmap --script script_1,script_2 --script-args script_2.uri=/axis2/,script_1.uri=/majordomo/ <target>
文件中定义参数
将参数写到文件中并引用,可以避免重复指定参数。
--script-args-file option 可以指定一个绝对路径或者相对路径的参数文件,供脚本使用,例如:
nmap --script "discovery,broadcast" --script-args-file nmap-args.txt <target>
The nmap-args.txt file are as follows:
http.useragent=Not Nmap
http.max-connections=50
userdb=/path/to/usernames.lst
passdb=/path/to/dictionary.lst
强制执行NSE脚本
NSE脚本是针对特定服务设计的,并不是所有端口都执行NSE脚本,这个根据[W用1] 著名端口、发现的服务类型进行对应。后面随着研究深入,这里的强制执行并没有那么简单,在单个target上,强制执行会忽略该Script的所有rule函数(如果有的话),不执行rule函数代码,强制return true。
所以,这里强制执行NSE脚本需要慎用。
$nmap --script +<script selection> <<arg1, arg2, …>
$nmap --script +http-title -p1212 192.168.x.x
调试脚本
如果需要分析NSE发送和接收的流量,则使用--script-trace选项打开NSE的调试模式。
注释:
--script-trace如果要生效,必须配置--script选项
--script-trace包含所有的发送和接收的数据包,不仅仅是NSE发送和接收的。。这有点像--packet-trace
如果您希望在漏洞利用类别中查看NSE脚本发送的有效负载,则可以使用以下表达式:
#nmap --script exploit --script-trace <target>
在使用--script-trace的同时,可以配合使用-d选项,打开nmap的调试模式,指定-d[1-9],设置级别越高,显示的信息越丰富。
扫描阶段和NSE
Nmap扫描分为11个阶段,但NSE仅涉及其中三个阶段:Script pre-scanning、Script scanning、Script post-scanning。这三个阶段只要指定--script 或 -sC 就会被执行。
扫描阶段 | Scan Phases | 开启选项 | 跳过选项 | 描述 |
脚本预扫描 | Script pre-scanning | -sC or --script | default | 只有在使用-sC或--script选项时才执行预扫描阶段;它试图通过一组NSE脚本检索主机的附加信息。当然在script中必须有prerule代码块才行。 |
目标枚举 | Target enumeration | default | default | 在此阶段,Nmap将一个或多个目标解析为IP地址。 |
主机发现 | Host discovery | default | -Pn | 在此阶段,Nmap通过指定的主机发现技术,检测目标是否存活 |
反向DNS解析 | Reverse DNS resolution | default | -n | 在此阶段,Nmap执行反向DNS查找以获取每个目标的主机名。-n跳过该阶段,-R强制对每个IP进行DNS反查,即便该IP不存活。默认只对online状态IP进行DNS反查。 |
端口扫描 | Port scanning | default | -sn | 在此阶段,Nmap确定端口的状态。可以使用-sn参数跳过它。 |
版本探测 | Version detection | -sV | default | 此阶段负责对打开的端口进行高级版本检测。仅在设置-sV参数时执行。 |
操作系统探测 | OS detection | -O | default | 在此阶段,Nmap会尝试确定目标的操作系统。仅在存在-O选项时执行。 |
跟踪路由 | Trace route | --traceroute | default | 在此阶段,Nmap执行到目标的跟踪路由。此阶段仅在设置了--traceroute选项时运行。 |
脚本扫描 | Script scanning | default | default | 在此阶段,脚本按照它们的执行规则运行。这里主要是portrule、hostrule和action( |
输出 | Output | default |
| 在此阶段,Nmap格式化所有收集的信息,并以指定的格式将其返回给用户。 |
脚本扫描后期 | Script post-scanning | default | default | 在此阶段,只要设置--script或-sC就会执行。将评估具有postrule(post-scan execution rule)的NSE脚本,并给它们一个运行的机会。当然,在script中必须有postrule代码块才行。 |
这三个阶段只要强制执行脚本,没有下表中的绿色单元格(如果有rule函数,不执行rule函数,强制返回true)。
加载脚本 | NSE: Loaded 1 scripts for scanning. |
|
|
执行阶段 | NSE: Script Pre-scanning. | NSE: Script scanning 10.100.1.100. | NSE: Script Post-scanning. |
执行级别 | NSE: Starting runlevel 1 (of 1) scan. | NSE: Starting runlevel 1 (of 1) scan. | NSE: Starting runlevel 1 (of 1) scan. |
初始化 | Initiating NSE at 19:44 | Initiating NSE at 19:44 | Initiating NSE at 19:44 |
Rule函数 | NSE: [test2] =========prerule | NSE: [test2 10.100.1.100] =========hostrule | NSE: [test2] =========postrule |
Action函数 | NSE: Starting test2. | NSE: Starting test2 against 10.100.1.100. | NSE: Starting test2. |
Action函数 | =========action | =========action | =========action |
完成Action |
| NSE: Finished test2 against 10.100.1.100. |
|
Rule函数 |
| NSE: [test2 10.100.1.100:80] =========portrule |
|
Action函数 |
| NSE: Starting test2 against 10.100.1.100:80. |
|
Action函数 |
| =========action |
|
完成Action | NSE: Finished test2. | NSE: Finished test2 against 10.100.1.100:80. | NSE: Finished test2. |
完成本阶段 | Completed NSE at 19:44, 0.00s elapsed | Completed NSE at 19:44, 0.00s elapsed | Completed NSE at 19:44, 0.00s elapsed |
Script Pre-scanning:默认阶段,prerule函数返回true则在此过程执行NSE脚本中的action函数,否则不执行,同时退出该扫描阶段。
Script scanning:默认阶段,没有action函数脚本报错。hostrule函数和portrule函数在该过程中首先被调用,如果它们返回true则继续执行action剩余代码,否则退出该过程。
Script Post-scanning:默认阶段,postrule函数返回true则在此过程执行NSE脚本中的action函数,否则不执行,同时退出该扫描阶段。
NSE脚本rules
NSE scripts有4种不同类型的execution rule,不是说每个script都要有所有这4种:
prerule | postrule | hostrule | portrule
这些rule函数无需在NSE脚本的action函数中引用即可默认执行。
prerule 在Script pre-scanning阶段执行
hostrule、portrule在Script scanning阶段执行,先执行hostrule后执行portrule,portrule执行还需要开启Port scanning阶段才可以,否则被Skip掉。
postrule在Script post-scanning阶段执行
所有这些rule函数都必须返回boolean值: false or true
prerule()
以下是来自 targets-sniffer.nse NSE脚本的片段。它说明了我们如何使用 prerule 函数来检查Nmap是否以特权模式运行,以及它是否可以正确地确定网络接口:
prerule = function()
return nmap.is_privileged() and
(stdnse.get_script_args("targets-sniffer.iface") or nmap.get_interface())
end
hostrule()
sniffer-detect(检查本地局域网是否开启了混杂模式,这个脚本貌似有问题,没有检测出来)脚本的主机规则确定脚本仅对本地以太网Ipv4地址执行,也就是说只检查本地Ipv4局域网。不能是跨网段网络、不能是Ipv6网络:
hostrule = function(host)
if nmap.address_family() ~= 'inet' then
stdnse.debug1("is IPv4 compatible only.")
return false
end
if host.directly_connected == true and
host.mac_addr ~= nil and
host.mac_addr_src ~= nil and
host.interface ~= nil then
local iface = nmap.get_interface_info(host.interface)
if iface and iface.link == 'ethernet' then
return true
end
end
return false
end
portrule(host, port)
oracle-sid-brute.nse脚本中,portrule去匹配服务检测字符串中是否包含1521端口或oracle-tns服务
portrule = shortport.port_or_service(1521, 'oracle-tns')
jdwp-inject.nse脚本中,portrule同样匹配服务检测字符串中是否有tcpwrapped服务,且是tcp协议、open状态,端口和协议没有被except。
portrule = function(host, port)
return port.service == "tcpwrapped"
and port.protocol == "tcp" and port.state == "open"
and not(shortport.port_is_excluded(port.number,port.protocol))
end
postrule()
ssh- hostkey脚本使用 postrule 函数来检测共享相同SSH公钥的主机
postrule = function() return (nmap.registry.sshhostkey ~= nil) end