4. Nmap高级用法:自定义NSE脚本编写、性能调优与大规模扫描策略

说实话,Nmap 这东西,很多人用了好几年还停留在 -sT -sV 的水平。我见过不少安全工程师,扫描内网就一个命令跑到底,结果要么被 WAF 封 IP,要么扫了三天三夜没出结果。嗯,这章咱们就来聊聊 Nmap 真正值钱的那部分——自定义 NSE 脚本、性能调优,还有大规模网络怎么扫才不翻车。

4.1 自定义NSE脚本编写——从复制粘贴开始

NSE(Nmap Scripting Engine)是 Nmap 的灵魂。我刚开始学的时候,觉得写脚本很难,后来发现其实就是 Lua 语法加几个 Nmap 的 API 调用。说白了,你只要会写 if-else 和 for 循环,就能写出能用的脚本。

4.1.1 脚本结构

一个最简单的 NSE 脚本长这样:

local shortport = require "shortport"
local stdnse = require "stdnse"

description = [[
检测目标是否开放了 8080 端口,并返回 Banner 信息
]]

author = "Your Name"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"safe", "discovery"}

-- 端口规则:只扫描 8080
portrule = shortport.portnumber(8080, "tcp")

action = function(host, port)
    local banner = port.version and port.version.product or "未知服务"
    return string.format("端口 %d 开放,服务: %s", port.number, banner)
end

我个人习惯把脚本分成三块:描述区、规则区、执行区。描述区写清楚这个脚本干嘛用的,规则区定义它该扫哪些端口,执行区才是真正干活的地方。

小技巧: 写脚本时先用 nmap --script-help 脚本名 看看 Nmap 自带的脚本是怎么写的。我早期很多脚本都是照着 http-title.nse 改出来的。

4.1.2 实战:写一个检测 Redis 未授权访问的脚本

我在项目中遇到过好几次 Redis 未授权的问题。写个脚本自动化检测,比手动敲命令快多了。

local shortport = require "shortport"
local comm = require "comm"
local stdnse = require "stdnse"

description = [[
检测 Redis 服务是否存在未授权访问漏洞
]]

author = "Your Name"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"safe", "vuln"}

portrule = shortport.portnumber(6379, "tcp")

action = function(host, port)
    local socket = nmap.new_socket()
    local status, err = socket:connect(host, port)
    if not status then
        return nil
    end

    -- 发送 PING 命令
    local payload = "*1\r\n$4\r\nPING\r\n"
    status, err = socket:send(payload)
    if not status then
        socket:close()
        return nil
    end

    local response
    status, response = socket:receive_lines(1)
    socket:close()

    if response and response:match("+PONG") then
        return "发现 Redis 未授权访问!无需密码即可执行命令。"
    end
    return nil
end

为什么会这样写?因为 Redis 默认配置下,6379 端口直接暴露在外网,连个密码都不设。你发个 PING,它回你 PONG,那就说明谁都能连上去搞事情。

注意: 写 NSE 脚本时,记得设置 categories 为 "safe" 或 "vuln"。safe 类脚本不会对目标造成破坏,vuln 类可能会触发告警。我曾经因为没注意分类,在客户生产环境扫出了告警,被运维追着骂了一下午。

4.2 性能调优——别让扫描变成龟速

很多人觉得 Nmap 慢,其实是你没调对参数。Nmap 默认的扫描速度很保守,怕把你网络打爆。但如果你在内网或者自己有授权的环境,完全可以放开手脚。

4.2.1 时序模板(Timing Templates)

Nmap 提供了 6 个时序模板,从 -T0 到 -T5。我一般只用 -T4 和 -T5:

模板 名称 适用场景 我的建议
-T0 Paranoid IDS/IPS 绕过 除非你确定目标有严格监控,否则别用
-T1 Sneaky 隐蔽扫描 渗透测试前期踩点可以用
-T2 Polite 常规扫描 默认值,慢但稳
-T3 Normal 日常使用 中规中矩
-T4 Aggressive 内网快速扫描 我 90% 的场景都用这个
-T5 Insane 极速扫描 小心丢包,适合带宽充足的内网

说白了,-T4 是我最常用的。它把超时时间缩短,并发数提高,但不会像 -T5 那样容易丢包。我记得有一次扫一个千兆内网,用 -T5 结果丢包率到了 15%,换回 -T4 反而更快扫完。

4.2.2 并行扫描参数

除了时序模板,还有几个参数能直接控制并发:

  • --min-hostgroup 64:最少同时扫 64 个目标
  • --max-hostgroup 256:最多同时扫 256 个目标
  • --min-parallelism 10:最少 10 个探测包并发
  • --max-parallelism 100:最多 100 个探测包并发
  • --min-rtt-timeout 100ms:最小 RTT 超时
  • --max-rtt-timeout 1000ms:最大 RTT 超时

举个例子,扫一个 C 段(254 个 IP),我通常会这样写:

nmap -sS -sV -T4 --min-hostgroup 64 --max-hostgroup 128 \
      --min-parallelism 20 --max-parallelism 50 \
      --min-rtt-timeout 100ms --max-rtt-timeout 500ms \
      192.168.1.0/24

这样配置,一个 C 段全端口扫描大概 5-8 分钟就能跑完。如果你用默认参数,可能要半小时以上。

核心思路: 性能调优的本质是平衡。并发太高会丢包,超时太短会漏端口。我一般先扫一个 IP 看看 RTT,再根据 RTT 调整参数。

4.3 大规模网络扫描策略——别把自己扫进去

扫一个 C 段和扫一个 B 段,完全是两码事。B 段有 65536 个 IP,如果还用扫 C 段的方法,要么被防火墙封,要么扫到天荒地老。

4.3.1 分阶段扫描

我习惯把大规模扫描分成三个阶段:

  1. 存活探测: 先用 -sn(Ping 扫描)找出哪些 IP 在线。一个 B 段用 -T4 --min-hostgroup 256,10 分钟左右就能跑完。
  2. 端口探测: 只扫存活 IP 的常用端口(Top 1000)。用 --top-ports 1000 配合 -T4。
  3. 深度扫描: 对开放了关键端口的 IP,单独做全端口扫描和服务版本检测。

为什么要这样?你想想看,一个 B 段里可能只有 10% 的 IP 在线。你上来就全端口扫,99% 的流量都浪费在空 IP 上。我见过有人直接 nmap -p 1-65535 10.0.0.0/16,结果扫了三天还没完,最后被网管直接拔了网线。

4.3.2 随机化与速率控制

大规模扫描时,别让目标觉得你在搞 DDoS。我一般会加这几个参数:

  • --randomize-hosts:随机打乱扫描顺序,避免连续扫一个网段
  • --scan-delay 10ms:每个探测包之间延迟 10 毫秒
  • --max-rate 100:限制每秒最多发 100 个包

举个例子:

nmap -sn -T4 --randomize-hosts --max-rate 500 10.0.0.0/16

这样扫,既不会触发 IDS 告警,也不会把出口带宽打满。我曾经在客户现场用这个策略扫了一个 /16 段,全程没被防火墙拦截,运维那边也没收到告警。

4.3.3 输出管理

大规模扫描的输出文件会很大。我习惯用 -oA 同时输出三种格式:

nmap -sS -sV -T4 -oA scan_result 192.168.1.0/24

这样会生成 scan_result.nmap(可读文本)、scan_result.gnmap(可解析格式)、scan_result.xml(结构化数据)。后续用 grep 或者写脚本解析都很方便。

避坑指南: 我曾经一次性扫了 5 个 B 段,输出文件直接到了 2GB。后来发现 .nmap 文件打开就卡死。建议每扫完一个网段就重命名输出文件,别把所有结果写到一个文件里。

4.4 知识体系总览

下面这张图是我自己总结的 Nmap 高级用法框架,你可以照着这个思路去实践:

Nmap高级用法知识体系 Nmap高级用法知识体系 NSE脚本编写 性能调优 大规模扫描策略 脚本结构(描述/规则/执行) API调用(socket/comm) 实战:Redis未授权检测 时序模板(-T0 到 -T5) 并行参数(hostgroup/parallelism) RTT超时与速率控制 分阶段扫描(存活→端口→深度) 随机化与延迟(避免触发告警) 输出管理(-oA 多格式) 核心原则 先存活探测 → 再端口扫描 → 最后深度检测,分阶段、控速率、管输出 写脚本:从复制开始 改自带的 NSE 脚本 调参数:-T4 是黄金档 并发和超时平衡 扫大网:分阶段执行 别一上来就全端口 Nmap 高级用法的核心:写脚本自动化、调参数提速度、分策略扫大网

嗯,这章的内容就到这儿。Nmap 的高级用法说白了就是三件事:写脚本、调参数、定策略。你把这三点吃透了,不管是扫一个 IP 还是一个 B 段,都能游刃有余。