nmap的NSE脚本如何开发

联启 网络工具 13

Nmap NSE 脚本开发实战指南(附完整示例与问答)

目录导读

  1. NSE脚本基础架构
  2. Lua语言核心速览
  3. 脚本分类与模板解析
  4. 实战开发:端口扫描与协议探测脚本
  5. 参数传递与结果输出优化
  6. 安全性与错误处理
  7. 常见问答Q&A

NSE脚本基础架构

NSE(Nmap Scripting Engine)是Nmap内置的脚本引擎,允许用户通过Lua语言编写自定义网络探测、漏洞检测、服务识别等功能,一个标准的NSE脚本包含三个核心部分:

nmap的NSE脚本如何开发-第1张图片-电脑手机工具软件下载 - 免费实用工具合集 | 联启科技

  • 脚本元数据:描述脚本名称、类别、作者、依赖版本等。
  • 主执行逻辑:即action函数,脚本被调用时自动执行。
  • 辅助函数库:调用Nmap内置的nmapsslhttp等库完成网络交互。

最小化脚本示例(保存为hello.nse):

description = [[一个简单的探测脚本,验证NSE引擎是否工作]]
author = "Your Name"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"safe", "discovery"}
prerule = function() end
hostrule = function(host) return true end
action = function(host, port)
    return "Hello from NSE! Host: " .. host.ip
end
  • 关键点:hostrule定义哪些主机或端口被处理,action返回结果字符串。

Lua语言核心速览

NSE脚本完全基于Lua 5.3,你需要掌握以下常用语法:

语法 示例 说明
变量声明 local port_num = 80 强制使用local避免全局污染
表(Table) local t = {key="value", [1]="array"} Lua唯一数据结构,兼作数组和字典
字符串操作 string.match(data, "HTTP/(%d%.%d)") 配合模式匹配提取信息
函数定义 local function check_port(host, port) 支持匿名函数和闭包
错误处理 local ok, err = pcall(f) 使用pcall捕获异常

高级技巧:利用NSE库nmap.new_socket()创建TCP/UDP连接,使用stdnse模块进行格式化输出。


脚本分类与模板解析

NSE脚本分为六类,选择类别需考虑脚本行为:

  • safe:安全探测,无副作用(如服务版本检测)
  • intrusive:可能触发告警(如暴力破解)
  • discovery:信息收集(如子域名扫描)
  • vuln:漏洞探测(如CVE检测)
  • exploit:漏洞利用(谨慎使用)
  • auth:认证绕过测试

标准脚本模板结构

local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
portrule = shortport.port_or_service(443, "https")
action = function(host, port)
    local socket = nmap.new_socket()
    socket:set_timeout(5000)
    local status, response = socket:connect(host.ip, port.number)
    -- 发送探测数据并解析
    socket:send("GET / HTTP/1.1\r\nHost: " .. host.ip .. "\r\n\r\n")
    local data = socket:receive_lines(10)
    socket:close()
    return stdnse.format_output(true, "Received %d bytes", #data)
end

实战开发:端口扫描与协议探测脚本

场景需求:识别目标主机开放的HTTP服务,并提取Server版本。
步骤分解

  1. 定义规则:只扫描80/8080端口

    portrule = function(host, port)
        return port.number == 80 or port.number == 8080
    end
  2. 建立连接:使用socket:connect三次握手

  3. 发送HTTP请求

    local request = "GET / HTTP/1.0\r\nHost: " .. host.ip .. "\r\n\r\n"
    socket:send(request)
  4. 解析响应:使用正则匹配Server:字段

    local server_header = string.match(data, "Server: ([^\r\n]+)")
    if server_header then
        return "HTTP Server version: " .. server_header
    end

完整脚本(保存为http-server-detect.nse):

description = [[简易HTTP服务器版本探测]]
author = "Dev@nse"
license = "Standard NSE License"
categories = {"safe", "discovery"}
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
portrule = shortport.port_or_service({80,8080}, "http")
action = function(host, port)
    local socket = nmap.new_socket()
    socket:set_timeout(5000)
    local status, err = socket:connect(host, port)
    if not status then
        return stdnse.format_output(false, "Connection failed: " .. err)
    end
    local request = "GET / HTTP/1.0\r\nHost: " .. host.ip .. "\r\n\r\n"
    socket:send(request)
    local data, part = socket:receive_bytes(2048)
    socket:close()
    if not data then
        return stdnse.format_output(false, "No response")
    end
    local server_header = string.match(data, "Server: ([^\r\n]+)")
    if server_header then
        return stdnse.format_output(true, "Server: %s", server_header)
    else
        return stdnse.format_output(true, "HTTP service detected, but no Server header found")
    end
end

参数传递与结果输出优化

1 脚本参数

通过--script-args传递参数,在脚本中读取:

local config_timeout = nmap.registry.args.timeout or 3000

使用示例:nmap -sV --script=http-server-detect --script-args timeout=5000

2 多样化输出格式

  • 简单字符串:直接返回字符串(如"Result: OK"
  • 结构化输出:使用stdnse.format_output(true, "key", "value")
  • 表格输出:返回表对象:
    return {
        name = "HTTP Detection",
        state = "open",
        product = server_header or "unknown"
    }

3 多主机扫描结果合并

利用nmap.registry存储全局数据,分阶段输出:

action = function(host, port)
    local result = custom_check(host)
    nmap.registry.results = nmap.registry.results or {}
    table.insert(nmap.registry.results, {ip=host.ip, result=result})
end
postrule = function() 
    -- 输出汇总结果
    for _, entry in ipairs(nmap.registry.results) do ... end
end

安全性与错误处理

1 超时与资源控制

  • 设置socket:set_timeout(ms)防止阻塞
  • 使用nmap.set_limit("max_sockets", 20)限制并发连接数

2 探测数据过滤

避免注入攻击:

local sanitized_input = string.gsub(user_input, "[\r\n]", "")

3 异常捕获

local ok, err = pcall(function()
    socket:send(payload)
end)
if not ok then
    stdnse.debug(1, "Send error: %s", err)
end

4 避免被反扫描

  • 添加随机延迟:nmap.sleep(math.random(100,500))
  • 随机化探测顺序(使用shuffle算法)

常见问答Q&A

Q1: 脚本运行后没有输出,可能原因是什么?
A: 检查hostruleportrule是否返回true;确认目标端口是否开放;使用nmap -d2 --script yourscript开启调试模式查看错误日志。

Q2: 如何复用其他NSE脚本的函数?
A: 脚本位于/usr/share/nmap/scripts/,使用require "script_name"导入(去除.nse后缀),例如local http = require "http"

Q3: 脚本能在Windows系统运行吗?
A: 完全支持,但需确保Nmap安装路径包含Lua解释器(默认携带),跨平台兼容性良好。

Q4: 脚本性能优化建议?
A: 使用nmap.new_socket()的异步模式connect配合nmap.set_payload();减少不必要的字符串拼接;利用stdnse.nmap_output函数替代直接print

Q5: 如何发布自己的NSE脚本?
A: 提交到官方GitHub仓库:https://github.com/nmap/nmap 或通过社区论坛分享,确保脚本通过nmap --script-updatedb测试。


NSE脚本开发的核心在于理解Lua网络编程模型与Nmap提供的丰富库函数,从简单的端口探测到复杂的漏洞利用,掌握上述模板与技巧后,你已能够编写出功能完善、安全健壮的脚本,建议将本文中的http-server-detect.nse保存并测试,逐步添加更多功能(如SSL检测、重定向跟踪),持续参考官方文档(https://nmap.org/book/nse.html)和现有脚本源码,是提升水平的最快路径。

标签: NSE脚本开发

抱歉,评论功能暂时关闭!