从零掌握Nuclei:自动化漏洞扫描与自定义模板编写实战指南

📅 2026/7/2 19:37:00 👁️ 阅读次数 📝 编程学习
从零掌握Nuclei:自动化漏洞扫描与自定义模板编写实战指南

1. 项目概述:为什么你需要掌握Nuclei

如果你是一名安全工程师、渗透测试人员,或者是一名负责应用安全的开发者,那么“自动化漏洞扫描”这个词对你来说一定不陌生。在当今快节奏的开发和部署环境中,手动测试早已无法覆盖海量的资产和层出不穷的漏洞。我们需要一个像瑞士军刀一样趁手、高效且精准的工具,来帮助我们自动化地发现那些潜在的安全风险。这就是Nuclei诞生的背景,也是它迅速成为安全圈内“顶流”的原因。

简单来说,Nuclei是一个基于YAML模板的现代化漏洞扫描器。它的核心思想非常巧妙:将漏洞检测的逻辑(比如发送什么请求、如何判断响应中存在漏洞)封装在一个个独立的YAML文件中,这些文件就是“模板”。社区里的安全研究员们不断贡献和维护着成千上万个模板,覆盖了从最新的CVE到常见的配置错误等各种场景。你只需要告诉Nuclei“扫描谁”和“用什么模板”,它就能自动完成所有检测工作。这听起来是不是有点像拥有了一个由全球安全专家组成的、不知疲倦的自动化团队?

但Nuclei的魅力远不止于此。它的真正威力在于“自定义”。当面对一个全新的、社区模板库中尚未覆盖的漏洞或内部特有的应用逻辑时,你能自己动手编写模板,将你的安全知识固化为可重复执行的自动化检测脚本。从“使用工具”到“创造工具”,这中间的跨越,正是从安全工具使用者进阶为安全能力构建者的关键一步。本指南的目的,就是带你从零开始,不仅学会如何运行Nuclei,更要深入理解其模板引擎,最终能够独立编写出高效、精准的自定义漏洞检测模板,将自动化安全测试真正融入你的工作流。

2. 环境搭建与核心概念解析

在开始编写或运行任何模板之前,我们需要一个稳固的“作战基地”。Nuclei的安装过程非常简单,但其背后的一些核心概念和配置选项,决定了你后续使用的效率和体验。

2.1 安装Nuclei:不止一种方式

最推荐的方式是使用Go语言包管理器直接安装,这能确保你获得最新的稳定版本:

go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest

安装完成后,在终端输入nuclei -version来验证安装是否成功。你会看到类似v3.2.0的版本号输出。除了Go安装,官方也提供了适用于Windows、macOS和Linux的预编译二进制包,你可以直接从GitHub Releases页面下载解压使用。

注意:如果你身处网络环境受限的区域,使用go install可能会因为无法访问github.com而失败。这时,预编译二进制包是更稳妥的选择。下载后,记得将其所在目录添加到系统的PATH环境变量中。

安装只是第一步。Nuclei的强大功能依赖于其庞大的模板库。首次运行任何扫描命令时,Nuclei会自动下载最新的社区模板到本地(默认在~/.config/nuclei/templates目录下)。你可以通过nuclei -update-templates命令手动更新模板库。理解这个目录结构很重要:模板按协议(HTTP、DNS、TCP等)和类别(CVE、漏洞、配置错误等)组织。当你需要查找或参考某个特定漏洞的检测方法时,直接翻阅这些YAML文件是最好的学习途径。

2.2 理解Nuclei的“工作流”:模板、目标与输出

运行一个最基本的扫描命令,例如nuclei -u https://example.com,背后发生了什么呢?

  1. 目标解析:Nuclei首先会解析你提供的目标(-u-l参数)。对于URL,它会自动进行标准化处理。
  2. 模板加载与过滤:接着,它会加载本地的模板库。如果没有指定-t参数,默认会运行所有符合条件的模板。你可以通过-tags-severity-author等参数进行精细过滤,例如nuclei -u https://example.com -tags cve,apache -severity critical,high只运行标记为CVE、Apache相关且严重性为高危或严重的模板。
  3. 并发执行:Nuclei采用高度并发的设计。-c参数控制并行执行的模板数量,-bs控制每个模板并行扫描的主机数量。合理调整这两个参数(例如-c 50 -bs 25),可以在不压垮目标服务的前提下,极大提升扫描速度。
  4. 请求与匹配:对于每个模板,Nuclei会按照其定义,向目标发送HTTP、TCP、DNS等协议请求。然后,使用模板中定义的“匹配器”(matchers)和“提取器”(extractors)来分析响应。
  5. 结果输出:一旦匹配成功,Nuclei就会按照你指定的格式(默认CLI输出,或通过-json-markdown-export指定文件)报告漏洞。

这里有一个非常关键的实操心得:初次对一个生产环境目标进行全量扫描时,务必使用-rate-limit参数限制请求速率,并加上-headless参数(如果目标有大量JavaScript渲染内容)。一个激进的命令可能是nuclei -u https://prod-app.com -rate-limit 50。先以较低的速率进行试探性扫描,观察目标服务的响应和负载情况,再逐步调整并发参数。盲目进行高并发扫描不仅可能触发对方的WAF(Web应用防火墙)封禁,更有可能对线上服务造成拒绝服务(DoS)影响,这是安全测试中的大忌。

3. 初阶实战:运行你的第一次扫描

理论说得再多,不如动手跑一遍。我们从最简单的场景开始,逐步增加复杂度,让你直观感受Nuclei的能力。

3.1 单目标与多目标扫描

对单个网站进行快速安全健康检查:

nuclei -u https://target-company.com -o initial_scan_results.txt

这个命令会对target-company.com运行所有默认模板,并将结果保存到initial_scan_results.txt文件中。-o参数用于指定输出文件。

在实际工作中,我们更常面对的是资产列表。假设你有一个subdomains.txt文件,里面是子域名枚举的结果:

app.target-company.com api.target-company.com admin.target-company.com dev.target-company.com

使用-l参数进行批量扫描:

nuclei -l subdomains.txt -o batch_scan_results.json -json

这里我们使用了-json参数,输出格式为JSONL(每行一个JSON对象),这种格式非常适合后续导入到SIEM(安全信息与事件管理)系统或用于自动化脚本处理。

3.2 模板过滤:精准打击,提升效率

运行所有模板虽然全面,但耗时且会产生大量无关输出(尤其是针对静态博客和简单页面的检测)。模板过滤是提升效率的核心技能。

  • 按标签过滤:这是最常用的方式。社区模板都打上了丰富的标签,如cvexssrcemisconfigwordpressjava等。
    # 只扫描与Apache Struts和Spring框架相关的漏洞 nuclei -l targets.txt -tags struts,spring -o framework_scan.txt
  • 按严重性过滤:在时间有限的应急响应中,优先关注高危漏洞。
    nuclei -l urls.txt -severity critical,high -silent
    -silent参数让Nuclei只输出找到的漏洞,不显示进度条等其他信息,使输出更干净。
  • 按作者过滤:如果你信任某位社区专家的模板质量,可以专门运行他的模板。
    nuclei -u https://target.com -author geeknik
  • 排除特定模板或类型:有时你想排除一些已知不适用或会产生干扰的检测。
    # 排除所有技术指纹识别(tech-detect)和信息类(info)低危模板 nuclei -l targets.txt -exclude-tags tech,info -exclude-severity low,info

3.3 理解输出与结果解读

Nuclei的默认CLI输出颜色丰富,信息清晰。一条典型的漏洞输出如下:

[Critical] [CVE-2021-44228] [http] https://log4j-vuln-app.com/api/users

它依次显示了:[严重等级][模板ID/名称][协议][目标URL]

但对于报告和归档,结构化格式更好。-json输出包含了最完整的信息,如请求、响应、匹配到的数据、模板详情等。-markdown-export参数则能生成一个包含漏洞详情、复现步骤和修复建议的完整Markdown报告目录,非常适合直接用于编写渗透测试报告。

注意事项:Nuclei的扫描结果,尤其是使用社区模板时,可能存在误报。一个模板报告了漏洞,绝不意味着百分百存在。安全工程师的核心价值之一就是“研判”。你需要手动验证关键漏洞:根据模板ID找到对应的YAML文件,理解其检测逻辑,然后尝试用Burp Suite或浏览器手动复现请求,分析响应。对于RCE(远程代码执行)、SQL注入等高危漏洞,验证是必须的步骤。切勿将自动化工具的结果不经确认就直接上报。

4. 中阶深入:解剖Nuclei模板的YAML结构

要真正驾驭Nuclei,必须读懂并会写模板。一个Nuclei模板就是一个YAML文件,其结构清晰,主要包含以下几个部分。

4.1 模板元信息:ID、信息与分类

这是模板的“身份证”和“说明书”。

id: CVE-2021-44228-log4shell info: name: Apache Log4j2 Remote Code Injection (Log4Shell) author: pdteam severity: critical description: | Apache Log4j2 <=2.14.1 JNDI features used in configuration, log messages, and parameters do not protect against attacker controlled LDAP and other JNDI related endpoints. reference: - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-44228 - https://nvd.nist.gov/vuln/detail/CVE-2021-44228 tags: cve,cve2021,log4j,rce,oast metadata: max-request: 2
  • id: 模板的唯一标识符,通常与CVE编号或漏洞特征相关。
  • info: 包含漏洞名称、作者、严重性、描述、参考链接和标签。tags非常重要,它决定了模板如何被过滤。
  • metadata: 可包含一些控制信息,比如max-request限制该模板最多发送的请求数。

4.2 请求定义:漏洞检测的“动作”

这是模板的核心,定义了要发送什么请求。

http: - method: GET path: - "{{BaseURL}}/api/actuator/gateway/routes" headers: User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 matchers-condition: and matchers: - type: word words: - "route_id" - "Spring Cloud Gateway" condition: and
  • http: 表示这是一个HTTP协议模板。也可以是dnstcpfile等。
  • method: HTTP方法,如 GET、POST、PUT。
  • path: 请求路径。{{BaseURL}}是一个变量,会被替换成实际的目标URL。
  • headers: 可以自定义请求头,用于绕过一些简单的WAF或匹配特定应用。
  • matchers-conditionmatchers: 定义如何判断响应中是否存在漏洞。上例中,它检查响应体中是否同时包含“route_id”和“Spring Cloud Gateway”这两个关键词。

4.3 匹配器与提取器:判断与抓取

匹配器(Matchers)用于判断请求是否成功(即是否存在漏洞)。提取器(Extractors)用于从响应中抓取有用的信息(如版本号、令牌、内部路径)。

匹配器类型丰富

  • word: 匹配关键词或短语。
  • regex: 使用正则表达式匹配。
  • status: 匹配HTTP状态码。
  • size: 匹配响应体大小。
  • dsl: 最强大的方式,使用内置的DSL(领域特定语言)函数进行复杂判断,如contains(body, “error”) && status_code == 500

一个复杂的匹配器示例

matchers: - type: dsl dsl: - "status_code == 200" - "contains(body, \"password\")" - "!contains(body, \"incorrect\")" - "len(body) < 10000" condition: and

这个匹配器要求:状态码为200,响应体包含“password”,不包含“incorrect”,且响应体长度小于10000字节。只有同时满足所有条件,才判定为匹配成功。

提取器示例

extractors: - type: regex name: version part: body regex: - "Version: (\\d+\\.\\d+\\.\\d+)" group: 1

这个提取器会从响应体中,通过正则表达式抓取类似“Version: 1.2.3”的字符串,并将版本号“1.2.3”提取出来,存储在变量version中,可以在后续的请求或输出中使用。

4.4 动态负载与变量:让模板“活”起来

静态的检测很容易被防御。Nuclei支持动态负载(Payloads)和变量(Variables),使得检测更具变化性和隐蔽性。

负载(Payloads):常用于模糊测试(Fuzzing)或撞库。例如,检测目录遍历时,可以定义一个包含各种../组合的负载列表。

http: - method: GET path: - "{{BaseURL}}/download?file={{payload}}" payloads: traversal: - "../../../etc/passwd" - "....//....//....//etc/passwd" - "%2e%2e%2fetc%2fpasswd"

在请求中,{{payload}}会被替换为payloads.traversal列表中的每一个值进行请求。

变量(Variables):可以从先前的请求响应中提取值,并用于后续请求,实现链式检测。

http: - method: GET path: - "{{BaseURL}}/login" extractors: - type: regex name: csrf_token part: body regex: - 'name="csrf_token" value="([^"]+)"' # 第一个请求获取CSRF令牌 - method: POST path: - "{{BaseURL}}/login" body: "username=admin&password=admin&csrf_token={{csrf_token}}" # 第二个请求使用提取到的令牌

这种能力使得Nuclei可以处理需要多步骤交互的复杂漏洞检测场景,如需要先登录再访问的管理后台漏洞。

5. 高阶实战:编写你的第一个自定义模板

现在,让我们动手为一个假设的漏洞编写一个检测模板。假设我们发现一个名为“SimpleCMS”的系统,其/admin/export?type=参数存在SQL注入,当参数值为user时,错误回显会暴露数据库路径。

5.1 规划模板逻辑

  1. 目标:检测SimpleCMS /admin/export接口的SQL注入漏洞。
  2. 检测方法:发送一个带有恶意负载的GET请求,触发数据库错误,并在响应中寻找特征字符串。
  3. 特征:错误响应中可能包含“MySQL”“near”等关键词。
  4. 避免误报:确保只在存在“SimpleCMS”标识的页面上进行检测。

5.2 编写YAML模板

我们将模板保存为simplecms-sqli.yaml

id: simplecms-admin-export-sqli info: name: SimpleCMS Admin Export SQL Injection author: your-name severity: high description: SQL Injection vulnerability in the export functionality of SimpleCMS admin panel. reference: - https://example.com/simplecms-advisory tags: sqli,simplecms,admin # 首先,发送一个正常请求来确认目标是SimpleCMS http: - method: GET path: - "{{BaseURL}}/admin/" # 第一个请求的匹配器:确认是SimpleCMS后台 matchers-condition: and matchers: - type: word words: - "SimpleCMS Admin Panel" - "Logout" condition: and part: body # 只有第一个请求匹配成功(即确认是目标系统),才会发送第二个注入检测请求 - method: GET path: - "{{BaseURL}}/admin/export?type=user' AND 1=1 UNION SELECT 1,2,3--" # 第二个请求的匹配器:检测SQL错误回显 matchers-condition: and matchers: - type: word words: - "MySQL" - "near" - "syntax" condition: or part: body - type: status status: - 200 # 即使出错,有些应用也返回200,但body里有错误信息

代码解读与实操要点

  • 分阶段检测:模板包含了两个HTTP请求块。Nuclei会顺序执行它们,并且第二个请求只有在第一个请求的所有匹配器都成功时才会发送。这是一种“预检测”机制,能有效减少对非目标系统的无效请求,提升扫描效率和隐蔽性。
  • 条件逻辑:第一个匹配器使用condition: and,要求页面必须同时包含“SimpleCMS Admin Panel”和“Logout”两个词。第二个匹配器使用condition: or,只要响应体中出现“MySQL”、“near”、“syntax”中的任何一个词,就认为可能触发了SQL错误。
  • 负载设计:注入负载user' AND 1=1 UNION SELECT 1,2,3--是一个经典的基于联合查询的探测载荷。在实际编写时,你可能需要根据目标的数据库类型(MySQL, PostgreSQL, SQL Server)调整负载和匹配的关键词。

5.3 测试与调试模板

编写完成后,不要直接用于大规模扫描。先在小范围测试。

# 使用 -validate 参数验证模板语法 nuclei -validate -t simplecms-sqli.yaml # 对单个测试目标运行该模板 nuclei -u http://test-simplecms.local -t simplecms-sqli.yaml -debug # 使用 -debug 参数,它会显示发送的请求和接收的响应,这对于调试匹配逻辑至关重要。

观察-debug的输出。检查:

  1. 请求的URL是否正确构建?
  2. 服务器的实际响应是什么?
  3. 你期望匹配的关键词是否出现在响应体的正确位置?

你可能需要反复调整匹配器的wordspart(可以是bodyheaderall)和condition,直到检测准确率达到满意水平。

6. 工程化与高级技巧

当你能熟练编写单个模板后,下一步就是思考如何将Nuclei集成到更庞大的安全体系中,并运用一些高级功能来应对复杂场景。

6.1 工作流模板:编排复杂的多步骤检测

有些漏洞的检测需要多个步骤,比如先爆破登录口,再用获取到的Cookie访问敏感接口。单个HTTP请求块难以处理这种带状态的操作。这时就需要“工作流模板”。

id: workflow-bruteforce-admin-then-dump info: name: Admin BruteForce and Data Dump Workflow severity: high # workflows 部分用于定义多个模板的执行顺序和逻辑 workflows: - template: /path/to/generic-login-bruteforce.yaml # 第一个模板:爆破登录 matchers: - name: login-success type: word words: - "Welcome, admin" # 定义输出变量,将爆破成功后的会话Cookie传递给下一个模板 outputs: session_cookie: "{{cookie}}" - template: /path/to/admin-data-dump.yaml # 第二个模板:使用上一个模板输出的cookie来访问管理接口 # 条件:只有第一个模板的 `login-success` 匹配器成功,才会执行此模板 condition: "{{login-success}}" # 注入变量:将 `session_cookie` 作为变量传递给第二个模板 variables: auth_cookie: "{{session_cookie}}"

工作流模板通过conditionoutputs/variables实现了模板间的数据传递和条件执行,能够构建出非常复杂的攻击链模拟。

6.2 集成与自动化:融入CI/CD和安全流程

Nuclei不是孤立的工具,它可以成为你安全自动化流水线的一环。

  • 与Httpx联动:ProjectDiscovery自家的httpx是一个优秀的HTTP探测工具。通常的流程是:使用子域名枚举工具(如subfinderassetfinder)获取目标列表,然后用httpx进行存活探测和基础信息获取(标题、状态码、技术栈),最后将存活的URL喂给nuclei
    subfinder -d target.com -silent | httpx -silent | nuclei -t exposures/ -o results.txt
  • CI/CD集成:你可以在GitLab CI、GitHub Actions或Jenkins的Pipeline中集成Nuclei扫描。例如,在每次代码部署到测试环境后,自动运行一组针对该应用技术栈(如-tags wordpress)的漏洞模板,并将结果以注释形式提交到Merge Request中或发送到安全团队频道。
    # 示例 GitHub Actions 步骤 - name: Nuclei Security Scan uses: projectdiscovery/nuclei-action@main with: target: ${{ env.STAGING_URL }} templates: 'cves/' severity: 'critical,high' output: 'nuclei-results.json'
  • 结果上报:使用-json-jsonl输出,然后编写脚本将结果解析并导入到Jira、DefectDojo、Elasticsearch等系统中,实现漏洞生命周期的闭环管理。

6.3 性能调优与资源控制

扫描大型资产时(如上万个子域名),性能配置不当会导致扫描时间过长或本地资源耗尽。

  • 速率限制-rate-limit-bulk-size是保护目标服务的首要参数。对于外部目标,建议从-rate-limit 30 -bs 10开始。
  • 并发控制-c控制模板并发数,-bs控制主机并发数。如果你的机器CPU核心多、网络好,可以适当调高(如-c 100 -bs 50)。但要注意,过高的并发可能导致本地文件描述符耗尽或内存不足。使用-timeout-retries处理网络超时。
  • 使用项目模式-project参数会让Nuclei在/tmp(或-project-path指定路径)下创建一个项目目录,缓存已发送的请求。当针对同一目标运行多个模板或多个扫描任务时,这可以避免重复发送相同的探测请求(如对/robots.txt的请求),显著提升效率。
  • 资源监控:在长时间扫描时,使用-stats-stats-interval 10参数,让Nuclei每隔10秒输出一次统计信息,包括请求数、错误数、匹配数等,方便你监控扫描状态。

7. 常见问题排查与实战心得

即使对工具很熟悉,在实际操作中依然会遇到各种“坑”。这里记录一些典型问题和我的处理经验。

7.1 扫描速度异常缓慢

  • 可能原因1:DNS解析慢。特别是扫描大量域名时,DNS查询会成为瓶颈。
    • 解决方案:使用-r参数指定一个快速的DNS解析器列表文件(如公共DNS8.8.8.8),或启用-system-resolvers回退到系统DNS。更好的做法是提前用dnsx等工具解析好域名,直接给Nuclei提供IP地址。
  • 可能原因2:模板过多或存在“重型”模板。一些模板可能包含复杂的JavaScript解析或Headless浏览器操作,极其耗时。
    • 解决方案:使用-exclude-tags headless,js排除这些模板。或者,先运行快速扫描(-tags quick如果模板有该标签),再针对性地运行深度扫描。
  • 可能原因3:网络延迟或目标响应慢
    • 解决方案:适当增加-timeout(如30秒),减少-rate-limit-c-bs的值。

7.2 误报率过高

  • 可能原因1:模板匹配条件过于宽松。很多通用指纹识别或信息泄露模板,可能匹配到一些相似但无关的页面。
    • 解决方案:这是社区模板的固有特点。务必进行人工验证。对于内部使用,你可以克隆社区模板库,修改那些误报高的模板,收紧其匹配条件(例如,增加matchers-condition: and的条目,或使用更精确的dsl正则匹配)。
  • 可能原因2:目标应用返回了通用错误页面。许多应用在遇到不存在的路径或参数错误时,会返回一个包含“error”、“not found”等词的定制化错误页,这可能被一些模板误判为漏洞。
    • 解决方案:编写模板时,尽量使用组合条件,避免仅靠单个关键词判断。可以利用dsl匹配器,结合状态码、响应头、正文长度和特定关键词进行综合判断。

7.3 漏报(该发现的没发现)

  • 可能原因1:目标需要认证,而模板未处理。大部分社区模板是针对未授权接口的。
    • 解决方案:使用-H参数添加认证Cookie或Token头,例如nuclei -u https://target.com -H “Cookie: session=abc123”。对于复杂的OAuth或JWT流程,可能需要先编写一个工作流模板来获取令牌。
  • 可能原因2:目标使用了现代前端框架(如React, Vue),路径是动态的。传统的基于路径的模板可能无法触发。
    • 解决方案:结合使用-headless参数运行浏览器渲染的模板,或者使用katanagau等工具先爬取目标的实际请求,再将爬取到的URL列表交给Nuclei扫描。
  • 可能原因3:WAF/IPS拦截了扫描请求
    • 解决方案:使用-rate-limit降低请求频率,增加随机延迟(目前Nuclei原生不支持,但可通过外部脚本控制)。修改-H中的User-Agent为更常见的浏览器标识。对于深度测试,可能需要使用代理池。

7.4 模板编写与调试心得

  • 从模仿开始:不要从零开始写模板。在~/.config/nuclei/templates目录下找到与你目标漏洞类似的模板,复制一份进行修改,这是最快的学习路径。
  • 善用-debug-store-resp-debug在终端输出请求响应,适合快速调试。-store-resp会将所有请求和响应保存到文件(默认output目录),方便你仔细分析。
  • 测试环境是关键:永远不要在未授权的情况下对生产系统测试新编写的模板。搭建一个与目标相似的测试环境(例如,用Docker快速拉起一个存在漏洞的旧版Web应用),在测试环境验证模板的准确性和安全性。
  • 关注DSL函数:Nuclei内置的DSL函数非常强大,如containslenregexto_number等。掌握它们可以写出逻辑更严谨的匹配条件。使用nuclei -list-dsl-function可以查看所有支持的函数。

掌握Nuclei模板的编写,本质上是在学习如何将安全测试人员的经验、对漏洞原理的理解,转化为机器可读、可重复执行的规则。这个过程会不断加深你对漏洞本身、对HTTP协议、对Web应用行为的认知。它不再是一个“黑盒”工具,而成为了你手臂的延伸。当你看到自己编写的模板在庞大的资产中精准地定位到一个深藏的安全隐患时,那种成就感,远非单纯运行一个扫描命令可比。安全之路,始于工具,终于能力。希望这篇指南能成为你从Nuclei使用者迈向安全自动化构建者的坚实一步。