第三讲 扫描引擎原理:插件机制与扫描流程
说真的,我刚接触安全扫描那会儿,总觉得扫描工具就是个黑盒子——输入一个IP,出来一堆报告。直到我第一次去读Nmap的源码,才发现里面其实是个精密的插件工厂。
这一讲,我就带你把扫描引擎的底裤颜色看明白。不管是Nmap还是OpenVAS,核心逻辑就两件事:插件怎么挂上去的,扫描过程怎么串起来的。
3.1 插件机制:扫描工具的“乐高积木”
为什么需要插件?你想啊,安全检查的种类太多了——端口开放算一条、SSL证书过期算一条、SMB漏洞又是一个。如果全写在一个大程序里,改一个地方就要重新编译整个包。
所以主流工具都用了插件架构。说白了,就是定义好接口,每个人写自己的检测逻辑,然后通过配置文件“插”进去。
3.1.1 Nmap的NSE(Nmap Scripting Engine)
Nmap的扩展能力全在NSE里。我记得第一次写自己的NSE脚本,是用来检测某个内部系统的Redis未授权访问。整个脚本不到30行,注册一下就能跑。
NSE脚本的基本结构:
-- 脚本描述
description = [[
检测目标是否运行了未授权的Redis服务
]]
-- 注册脚本到对应阶段
prerule = function() end
hostrule = function(host) return true end
action = function(host, port)
-- 你的检测逻辑
return "检测结果"
end
-- 分类信息
categories = {"safe", "default"}
这里有个关键:prerule、hostrule、portrule、postrule 这些阶段决定了脚本什么时候执行。我见过很多新手把检测逻辑一股脑塞进 action,却不配置 hostrule,结果每个端口都跑一遍,慢得要命。
hostrule 或 portrule 写得严一点。比如只对开放了6379端口的IP执行Redis检测,能省下90%的时间。
3.1.2 OpenVAS的NASL(Nessus Attack Scripting Language)
OpenVAS继承了Nessus的NASL语法。说实话,NASL比NSE门槛高一些,但它对漏洞细节的表达更精确。
一个简单的NASL脚本框架:
# 脚本标识
script_id(12345);
script_name("检测Apache版本");
script_category(ACT_GATHER_INFO);
# 核心检测逻辑
port = 80;
banner = recv_line(socket:socket, length:1024);
if("Apache/2.4.49" >< banner) {
security_message(port:port, data:"存在风险版本");
}
# 注册到扫描流程
script_add_preference(name:"超时时间", type:"entry", value:"5");
注意那个 script_category。OpenVAS把脚本分成好几类:ACT_GATHER_INFO、ACT_ATTACK、ACT_DENIAL 等等。不同类别在扫描流程里的执行顺序不一样。攻击类脚本要在信息收集之后才跑,这是基本规矩。
script_category,结果它在刚发现端口时就直接发了大量数据包,把客户的生产交换机打挂了。嗯,那天晚上我写报告写到凌晨三点。
3.2 扫描流程:从发现到报告的三阶段
不管用什么工具,扫描流程大致可以分成三个阶段。我习惯叫它“探测-枚举-验证”。
| 阶段 | 核心任务 | 插件类型 | 耗时占比 |
|---|---|---|---|
| 第一阶段 | 主机发现 + 端口探测 | 预扫描插件(probe) | 30% |
| 第二阶段 | 服务识别 + 信息收集 | 信息收集插件(gather) | 40% |
| 第三阶段 | 漏洞检测 + 结果验证 | 攻击/验证插件(attack) | 30% |
为什么是这个顺序?你想想看,如果连主机都还没发现,就直接去测漏洞,那不是瞎忙活吗?
3.2.1 第一阶段:探测与主机发现
这一步其实就是在问:“有人在家吗?”
Nmap用的是ICMP ping、TCP SYN到常用端口(比如80、443)、ARP请求这些方式。OpenVAS也类似,但它的预扫描插件更多,有时候会发送定制的探测包。
我记得有一次内网扫描,客户说所有主机都在线,结果Nmap只发现了一半。后来一查,是防火墙把ICMP全封了。换上 -Pn 参数,直接用TCP SYN扫端口,才把剩下的机器揪出来。
所以做DSPM扫描时,一定要清楚目标网络的过滤策略。不然后面的分析全是瞎子摸象。
3.2.2 第二阶段:服务识别与指纹收集
确定了主机和开放端口后,就该看看“门后面是什么”了。
这个阶段插件会做几件事:
- 抓取banner信息——很多协议在连接时会主动暴露版本号
- 发送探测请求——比如向HTTP服务发一个GET,看返回头的Server字段
- SSL/TLS握手分析——提取证书、支持的加密套件
- 服务主动指纹比对——Nmap的
service scan用的就是这种
这里有一个坑:有些服务会故意隐藏版本信息。比如改了Nginx源码,把 Server: nginx 改成 Server: Microsoft-IIS/10.0。如果检测插件只信banner,那后续的漏洞检测就全跑偏了。
3.2.3 第三阶段:漏洞检测与验证
这是最吃插件质量的一步。有了前面的信息,插件才知道该看什么。
举个例子:一个检测Log4j漏洞的插件,它会:
- 先确认目标是否运行了Java应用(从第二阶段收集的信息判断)
- 再检测是否存在特定的JNDI接口路径
- 发送精心构造的payload,看响应中是否有依赖库加载的特征
- 如果匹配,标记为“高危”
但问题来了——误报。我见过一个扫描器,把Tomcat的默认错误页面当成了漏洞证据,结果整个部门花了两天人工复测,发现是虚惊一场。
所以现在的扫描引擎会加一个“验证”步骤。比如检测到疑似漏洞后,再发一个无害的请求,确认那不是误报。OpenVAS的 ACT_MIXED_ATTACK 类型插件就是这样做的。
3.3 整个流程的串联逻辑
上面三个阶段不是各跑各的。扫描引擎内部有一个调度器,负责控制插件的执行顺序和数据传递。
下图是我自己整理的一个简化版扫描流程结构:
从这张图你能看到,调度器起了承上启下的作用。它手里维护着一张“插件注册表”,里面记录了每个插件的依赖条件、执行阶段、输入输出。
举个例子:一个检测MySQL弱口令的插件,它会在注册时声明:“我依赖端口扫描结果,只对3306端口执行,并且在服务识别之后才启动。”调度器看到这个声明,就知道该在什么时候把这个插件叫醒。
3.4 实战中的插件管理建议
做DSPM扫描,不可能上来就跑全部插件。我个人的做法是:
- 先走一遍精简版:只启用探测和基本信息收集插件,快速摸清资产底数。
- 再按风险分级:对关键资产启用全量检测插件;对非核心资产只跑高危漏洞检测。
- 最后做差异化验证:对扫描结果中的高危项,启动专门的验证插件再次确认。
这样分轮次执行,既保证了扫描深度,又不会一次性把目标网络压垮。
好了,这一讲的内容就是这些。扫描引擎的原理理解透了,你在后面做策略配置和结果分析时,心里就有底了。