Nuclei Templates实战指南:从漏洞扫描到自动化安全验证平台
1. 项目概述从“扫描器”到“自动化安全验证平台”的认知升级提到Nuclei很多刚接触安全测试的朋友第一反应是“哦那个很火的漏洞扫描器。” 这个认知对但也不全对。在我过去几年的红队和渗透测试项目中Nuclei早已从一个单纯的PoC概念验证扫描工具演变成了我们团队日常工作中不可或缺的自动化安全验证与资产梳理平台。而这一切能力的核心都源于它的“灵魂”——Templates模板。简单来说Nuclei本身是一个强大的引擎但它“能做什么”、“做得多好”完全取决于你给它喂什么样的Templates。你可以把它想象成一把多功能军刀刀柄引擎是固定的但上面能安装什么工具头模板决定了你是用它来开罐头、拧螺丝还是剪电线。网络上流传的成千上万个模板就是这些功能各异的工具头。对于安全工程师、运维人员甚至开发自测来说掌握Nuclei Templates的使用意味着你拥有了一种将已知漏洞、配置错误、信息泄露等安全问题的检测能力快速转化为自动化脚本的能力。无论是应急响应时快速排查全网是否存在某个0day漏洞还是日常巡检中批量检查子域名、中间件版本或是验证WAF规则是否生效Templates都是最高效的武器。然而我发现很多新手会卡在几个地方模板从哪来怎么写怎么调试为什么别人的模板跑得出结果我的就不行这篇内容我就结合自己踩过的坑和实战经验带你系统性地掌握Nuclei Templates让你不仅能“用”更能“改”和“创”真正把自动化安全检测能力握在自己手里。2. Nuclei Templates 核心机制深度解析要玩转模板必须先理解引擎的工作原理。这能帮你从根本上避开大多数坑。2.1 模板的结构YAML 里的“攻击逻辑”一个Nuclei模板就是一个YAML文件它严格遵循一套预定义的语法结构。别被YAML吓到它的核心模块非常清晰。我们以一个最简单的检测网站标题的模板为例拆开看id: http-title-detection info: name: HTTP Title Detection author: yourname severity: info description: Extracts the title from HTTP responses. http: - method: GET path: - {{BaseURL}} matchers: - type: word part: body words: - titleid: 模板的唯一标识全局不能重复。建议用组织名-漏洞类型-组件名的格式如myteam-cve-2023-1234-apache。info: 元信息区。这里最重要的是severity严重等级它直接影响扫描报告的优先级。等级分为info信息、low低、medium中、high高、critical严重。实操心得不要滥用high和critical只有真正能导致RCE远程代码执行、严重数据泄露的漏洞才用。否则在大型扫描中会制造大量“噪音”让真正的高危告警被淹没。http: 这是模板的“心脏”定义了HTTP请求的所有细节。method、path、headers、body都在这里配置。{{BaseURL}}是一个变量运行时会被替换成目标URL。matchers: 匹配器决定如何判断请求是否成功即是否存在漏洞。上例中我们匹配响应体part: body中是否包含“title”这个词word。匹配器类型还有regex正则、status状态码、size响应大小等。为什么这么设计这种将攻击向量http块和结果判断matchers块分离的设计非常巧妙。它使得编写一个检测逻辑变得像填空一样简单我发起一个什么样的请求可能是一个特定的API路径或一个带有恶意参数的POST请求然后我期望在响应中看到什么特定的字符串、状态码或正则匹配模式。这种声明式的语法大大降低了安全自动化脚本的编写门槛。2.2 匹配器Matchers与提取器Extractors不仅仅是“有没有”更是“是什么”matchers告诉你目标“是否存在”某个特征而extractors则能帮你从响应中“提取出”具体的信息这是模板进阶使用的关键。假设我们要检测一个暴露的phpinfo页面并顺便提取出PHP版本和系统路径http: - method: GET path: - {{BaseURL}}/phpinfo.php matchers: - type: word words: - PHP Version - System condition: and # 必须同时满足两个关键词 extractors: - type: regex name: php_version part: body regex: - PHP Version (\d\.\d\.\d) group: 1 - type: regex name: system_path part: body regex: - trtd classeSystem /tdtd classv([^])/td/tr group: 1在这个模板中匹配器检查响应体中是否同时包含“PHP Version”和“System”这两个词以此高置信度地判断这是一个phpinfo页面。提取器使用两个正则表达式分别从页面HTML中提取出PHP版本号和系统路径并将结果命名为php_version和system_path。这些提取到的信息会清晰地展示在扫描结果中。注意事项正则表达式虽然强大但编写和维护成本高且容易因页面微小的HTML结构变化而失效。优先使用word或dsl匹配器。dsl领域特定语言是Nuclei提供的一个更强大的匹配方式允许你进行复杂的逻辑判断比如检查响应时间、JSON字段值等功能性强且不易出错。2.3 动态负载Payloads与模糊测试Fuzzing这是Nuclei模板真正发挥威力的地方。静态的检测是有限的很多漏洞需要尝试不同的参数或载荷才能触发。http: - method: POST path: - {{BaseURL}}/api/user/login body: username{{username}}password{{password}} payloads: username: - admin - administrator - test password: - admin123 - password - 123456 - {{username}}123 # 支持使用另一个payload变量 matchers: - type: status status: - 200 - type: word words: - \success\:true - 登录成功 condition: or这个模板模拟了一个简单的登录接口暴力破解。payloads块定义了两个字典username和password。Nuclei会自动进行笛卡尔积式的组合爆破3x412次请求。在body中我们使用{{username}}和{{password}}来引用这些动态载荷。更高级的用法是配合raw请求和fuzzing。对于复杂的HTTP请求如多层JSON、SOAP你可以将整个请求包保存为一个文件在模板中引用并在其中标记需要模糊测试的位置http: - raw: - | POST /graphql HTTP/1.1 Host: {{Host}} Content-Type: application/json {query: query { user(id: \§id§\) { name } }} fuzzing: - part: body type: replace mode: single fuzz: - 1 - or 11 - \ or 11--这里§id§就是一个模糊测试点Nuclei会用fuzz列表中的值依次替换它进行测试。这种方式非常适合测试SQL注入、NoSQL注入、GraphQL注入等需要精确控制请求格式的场景。3. 模板的获取、管理与自定义开发实战3.1 官方与社区模板库站在巨人的肩膀上最权威的模板来源是Nuclei官方项目 projectdiscovery/nuclei-templates 。这个仓库由ProjectDiscovery团队和维护者社区共同更新质量较高覆盖了CVE漏洞、错误配置、默认凭据、敏感信息泄露等几乎所有常见场景。使用方法# 初始化会克隆官方模板库到 ~/.local/nuclei-templates nuclei -ut # 更新模板 nuclei -update-templates但是直接使用公共模板库存在风险误报与漏报社区模板质量参差不齐有些匹配条件过于宽松或严格可能产生大量误报或漏掉真实漏洞。法律与授权风险一些模板具有攻击性如登录爆破、数据遍历。在未获得明确授权的情况下对第三方资产使用这类模板是违法的。不适合内部环境公共模板主要针对互联网通用组件对于公司内部自研的、特有的系统、API或中间件几乎没有现成的检测能力。因此建立自己的模板仓库是必经之路。我建议的路径是以官方库为学习和参考基础逐步筛选、优化、积累属于自己团队或业务的专用模板库。3.2 搭建私有模板工作流我的团队使用Git来管理私有模板库结构如下my-nuclei-templates/ ├── workflows/ # 工作流文件用于组合多个模板 ├── cves/ # CVE漏洞检测模板 ├── exposures/ # 信息泄露、配置错误模板 ├── misconfig/ # 错误配置模板 ├── fuzzing/ # 模糊测试模板 ├── internal/ # 内部业务系统专用模板 │ ├── api-xxx/ │ └── app-yyy/ └── scripts/ # 辅助脚本如模板校验、批量测试关键工具nuclei -validate在将模板加入仓库前必须使用验证命令检查语法nuclei -validate -t path/to/your-template.yaml它能帮你发现YAML格式错误、字段拼写错误等低级问题避免模板失效。3.3 手把手编写一个高质量模板以检测Spring Boot Actuator未授权访问为例让我们从零开始编写一个实用模板。Spring Boot Actuator端点未授权访问是一个经典的安全问题可能导致敏感信息泄露。第一步明确检测逻辑我们需要检测几个常见的Actuator端点如/actuator/actuator/env等是否在未认证的情况下可访问并且响应内容符合Actuator的特征。第二步编写模板id: springboot-actuator-unauth info: name: Spring Boot Actuator Unauthorized Access author: yourname severity: medium description: Detects unauthorized access to Spring Boot Actuator endpoints. reference: https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html tags: springboot,actuator,exposure http: - method: GET path: - {{BaseURL}}/actuator - {{BaseURL}}/actuator/env - {{BaseURL}}/actuator/health - {{BaseURL}}/actuator/metrics - {{BaseURL}}/actuator/beans matchers-condition: or # 只要任意一个路径匹配成功就报告 matchers: # 匹配器1通过状态码和响应体关键词判断 - type: word part: body words: - _links - beans - status - UP condition: or status: - 200 # 匹配器2针对 /actuator 根路径可能返回链接列表 - type: word part: body words: - actuator status: - 200第三步优化与增强上面的模板有误报风险比如一个普通的JSON API也返回{status: UP}。我们需要增加更精确的匹配条件利用Actuator响应的独特性。matchers: # 更精确的匹配结合路径特征和响应体特征 - type: dsl dsl: - contains(body, _links) contains(body, actuator) # /actuator 根目录 - contains(body, propertySources) contains(body, systemProperties) # /actuator/env - contains(body, \status\:\UP\) status_code 200 # /actuator/health 的典型JSON响应 - contains(body, names) contains(body, mem) # /actuator/metrics condition: or使用dsl匹配器我们可以编写更灵活、更精确的逻辑组合大幅降低误报率。第四步添加信息提取如果检测到/actuator/env我们可以尝试提取一些环境变量这些信息对后续渗透可能很有帮助。extractors: - type: regex part: body name: spring_profiles regex: - profiles:\[([^])\] group: 1 - type: regex part: body name: java_version regex: - java\.version:([^]) group: 1编写心得从简单开始先用宽泛的匹配确保能发现目标再逐步增加精确条件减少误报。善用dsldsl匹配器功能强大是编写高质量模板的利器多查阅官方文档熟悉其函数。利用tags给模板打上合适的标签如springbootexposure便于后期通过-tags参数进行筛选扫描。写好description和reference这不仅是为了别人能看懂几个月后你自己回头看时也能快速回忆起这个模板的用途和背景。4. 高级技巧与实战场景应用4.1 工作流Workflows串联检测逻辑单个模板是“点”的检测工作流则是“线”甚至“面”的自动化。工作流允许你定义模板的执行顺序和依赖关系。场景你想先检测目标是否是WordPress如果是再执行一系列WordPress相关的漏洞检测。id: wordpress-audit-workflow info: name: WordPress Audit Workflow author: yourname # 逻辑串行执行 workflows: - template: technologies/wordpress-detect.yaml # 步骤1识别 matchers: - name: wordpress-detected # 只有当这个模板的匹配器名为‘wordpress-detected’的结果为真时才继续 - template: vulnerabilities/wordpress/plugin-xss.yaml # 步骤2插件XSS检测 - template: vulnerabilities/wordpress/core-rce.yaml # 步骤3核心RCE检测 - template: exposures/wp-config-disclosure.yaml # 步骤4配置文件泄露检测工作流的价值提升效率避免对非WordPress站点运行无用的WordPress检测模板。逻辑编排可以实现“信息收集-漏洞检测-利用验证”的链条。条件执行基于上一步的结果通过matchers名称引用决定是否执行下一步。4.2 针对复杂认证场景的处理很多内部系统需要登录后才能访问。Nuclei支持多种认证方式。1. 使用-H头注入Cookie或Token最简单nuclei -u https://target.com -t my-auth-test.yaml -H Authorization: Bearer eyJhbGciOiJ...在模板中这个头部会自动添加到每个请求。2. 在模板中定义静态头部http: - method: GET path: [{{BaseURL}}/api/admin] headers: Authorization: Bearer {{token}}然后在命令行传入-var tokeneyJhbGciOiJ...。3. 使用http模块的attack类型进行认证爆破危险需授权 这通常用于测试登录接口的弱口令。你需要精心设计payloads和matchers来区分登录成功和失败。重要警告涉及认证和爆破的模板必须在获得明确书面授权的前提下在测试环境中使用。滥用可能导致账号锁定、法律风险。4.3 与现有工具链集成Nuclei不是孤岛它可以完美融入现有的DevSecOps或安全运营流程。输入集成Nuclei可以从多种来源读取目标。# 从文件读取URL nuclei -l urls.txt -t exposures/ # 从Subfinder、Assetfinder等子域名枚举工具接收结果 subfinder -d example.com -silent | nuclei -t cves/ -o results.txt # 从其他扫描器如Naabu的端口结果构造URL naabu -host example.com -silent | httpx -silent | nuclei -t exposures/输出集成Nuclei支持JSON、Markdown、CSV等多种格式方便导入到JIRA、DefectDojo、Elastic Stack等平台进行告警管理和数据分析。nuclei -u example.com -t cves/ -o results.json -json我团队的典型工作流使用subfinderhttpx获取活跃的域名和URL列表。使用一套基础的exposures/和misconfig/模板进行第一轮快速筛查发现明显弱点。对重要的资产使用完整的模板库CVE、fuzzing等进行深度扫描。将JSON格式的扫描结果通过脚本自动导入到内部的安全管理平台生成工单并指派。5. 常见问题、调试技巧与性能优化实录即使理解了所有原理在实际编写和运行模板时你依然会遇到各种问题。下面是我总结的“排坑指南”。5.1 模板不报结果一步步调试这是最常见的问题。请按以下顺序排查语法验证首先运行nuclei -validate -t template.yaml确保没有语法错误。单个目标测试使用-u对一个确定存在问题的目标进行测试排除目标不对的问题。启用详细调试使用-debug或-debug-req、-debug-resp标志。nuclei -u http://vuln-test-site.com -t your-template.yaml -debug -debug-req这会打印出Nuclei发送的每一个请求和接收到的原始响应。这是最强大的调试手段。你可以清晰地看到请求的URL、头部、正文是否正确。目标返回的实际状态码、响应体是什么。你的matchers逻辑是否与真实响应匹配。检查匹配逻辑在debug输出中找到响应体仔细核对你的words、regex或dsl是否能够匹配上。特别注意大小写word匹配默认区分大小写。使用case-insensitive选项或dsl的to_lower函数。空格与换行响应中的HTML或JSON格式可能包含不可见字符影响正则匹配。动态内容如果页面包含时间戳、随机Token等每次请求都变化的内容你的静态匹配就会失败。需要匹配那些不变的部分。调整请求速率如果目标有WAF或速率限制过快的请求可能导致被屏蔽返回错误页面。使用-rate-limit参数降低并发度或添加-retries和-timeout。5.2 误报太多提高模板的精确度误报浪费分析时间降低工具可信度。多用condition: and要求多个条件同时满足才匹配。结合status码很多漏洞只有在返回200或特定状态码时才成立。使用dsl进行复杂判断dsl可以计算响应长度、匹配多个正则、检查JSON路径值等过滤能力极强。添加negative匹配器明确排除一些会导致误报的响应特征。matchers: - type: word words: - vulnerable string part: body - type: word words: - Error Page - Not Found part: body negative: true # 如果包含这些词则不算匹配实战校准将模板在大量已知安全和无漏洞的资产上运行观察误报情况持续优化匹配条件。5.3 性能瓶颈与优化建议当扫描目标成千上万时性能至关重要。模板优化减少不必要的请求如果一个模板有多个path且它们之间是“或”的关系Nuclei会依次请求。考虑是否可以通过一个请求和更复杂的响应匹配来判断。使用max-redirects限制重定向次数避免陷入无限重定向或复杂的重定向链。合理设置timeout在模板的http块中设置适当的超时避免在无响应的目标上等待过久。扫描策略优化分级扫描先使用快速、轻量的信息收集模板-tags info,exposure进行初筛再对高风险目标进行深度漏洞扫描-tags cve,rce。利用-nc无颜色和-silent在自动化脚本中禁用控制台颜色和实时输出可以提升一些性能。控制并发度使用-c并发主机数和-rate-limit每秒请求数参数根据目标网络的承受能力进行调整避免把对方打挂或触发安防警报。资源层面升级Nuclei版本ProjectDiscovery团队持续进行性能优化新版本通常更快。增加系统资源对于海量目标扫描Nuclei的并发能力受限于CPU和网络I/O。确保运行主机有足够的资源。5.4 关于“无法更新Nuclei”或“无法更新模板”这是一个常见的网络问题通常是因为ProjectDiscovery的服务器位于海外国内访问可能不稳定。解决方案使用代理此处需注意合规性仅指企业内常见的网络代理服务。可以通过设置环境变量让go和nuclei使用代理export HTTP_PROXYhttp://your-proxy:port export HTTPS_PROXYhttp://your-proxy:port nuclei -update手动更新更新引擎去GitHub的 Release页面 手动下载最新版本二进制文件替换。更新模板直接进入模板目录默认在~/.local/nuclei-templates执行git pull拉取最新更改。使用镜像源一些社区提供了Go模块的镜像源但Nuclei的模板仓库更新仍需拉取GitHub镜像源可能无法解决全部问题。最根本的解决之道是在内网搭建一个模板同步镜像。写一个定时任务从GitHub拉取官方模板库更新到内部Git服务器然后团队所有成员从内网仓库更新即可速度又快又稳定。