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,那就说明谁都能连上去搞事情。
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 分钟就能跑完。如果你用默认参数,可能要半小时以上。
4.3 大规模网络扫描策略——别把自己扫进去
扫一个 C 段和扫一个 B 段,完全是两码事。B 段有 65536 个 IP,如果还用扫 C 段的方法,要么被防火墙封,要么扫到天荒地老。
4.3.1 分阶段扫描
我习惯把大规模扫描分成三个阶段:
- 存活探测: 先用
-sn(Ping 扫描)找出哪些 IP 在线。一个 B 段用-T4 --min-hostgroup 256,10 分钟左右就能跑完。 - 端口探测: 只扫存活 IP 的常用端口(Top 1000)。用
--top-ports 1000配合-T4。 - 深度扫描: 对开放了关键端口的 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 或者写脚本解析都很方便。
.nmap 文件打开就卡死。建议每扫完一个网段就重命名输出文件,别把所有结果写到一个文件里。
4.4 知识体系总览
下面这张图是我自己总结的 Nmap 高级用法框架,你可以照着这个思路去实践:
嗯,这章的内容就到这儿。Nmap 的高级用法说白了就是三件事:写脚本、调参数、定策略。你把这三点吃透了,不管是扫一个 IP 还是一个 B 段,都能游刃有余。