编写可移植的 Nuclei 诊断检查

网络安全Beginner
立即练习

介绍

只看状态的扫描器同时匹配了真实诊断响应和文档示例。现在,构建一个能回答预期问题的检查:未经身份验证的请求是否收到了这个应用的实时内部诊断对象?

你将创建原生 Nuclei YAML 模板,测试其响应条件,并扫描六个获准的 Fieldwork Studio 站点。可复用模板和真实发现就是交付成果。本环境独立于上一个实验;文件和进程均不会延续。

请先完成使用真实响应确认诊断信息暴露,或熟悉 HTTP 状态、内容类型响应头、小型 JSON 对象和 Nuclei JSONL 发现的读取。本实验会解释所用的 YAML 和匹配表达式。所有目标配置均为合成内容。仅使用提供的只读范围,保留公共工作坊页面和日程。包含阅读和普通操作错误在内,预计需要约 65 分钟;这是作者估计。

明确检查的响应契约

本步骤将确认正常服务,以及模板必须回答的准确问题。打开 Workshops,浏览活动,并点击 Get the workshop schedule 下载真实的 fieldwork-schedule.csv。在整个评估过程中,Printmaking 和 Bookbinding 都必须保持可用。

公共工作坊页面与日程下载

两张工作坊卡片展示了你必须保留的公共服务。绿色 Get the workshop schedule 链接会下载其真实日程。

切换到 Auditor。它会进入 Kali 审计容器的 /home/labex/project 目录。阅读范围和获准的基础 URL:

cat scope.txt
cat targets.txt

六个站点为 http://workshop.test:8080/ 下的 studio、archive、manual、garden、status 和 members。仅使用只读 HTTP GET 请求测试这些 URL。不要修改目标站点或提供的范围文件。

再次查看实时诊断响应。与之前一样,curl -i 包含响应头:

curl -i http://workshop.test:8080/studio/maintenance/diagnostics

一项发现必须同时具备 HTTP 200、JSON 内容类型,以及应用顶层的内部诊断对象。其四个标量字段标识服务、可见性、绝对数据库文件路径,以及不带引号的非负整数请求计数。文件路径和计数可能变化。键的顺序和空白不影响含义。嵌套文档示例不是实时对象。

你的检查针对这个小型应用已声明的响应格式。它不能证明任意 JSON 端点都存在漏洞,也不能证明一个站点没有其他问题。尤其是公共页面、公共 JSON 摘要、健康响应、HTTP 拒绝或登录重定向,都不是所需的暴露证据。

如果目录发生变化,使用 pwd 检查位置,并先用 cd /home/labex/project 返回,再读取或创建相对文件名。重新连接时保留工作成果;打开终端不会重建场景。

构建原生响应匹配器

本步骤将编写可复用模板,而不是依赖 HTTP 200 匹配。留在 Auditor。

Nuclei 模板包含 id、描述性 info,以及 http 等协议部分。缩进将嵌套的 YAML 字段分组,短横线开始一个列表项。使用空格,不要使用制表符。method: GET 选择只读请求。{{BaseURL}} 来自传给 Nuclei 的目标,因此模板不会嵌入特定的主机名、端口或站点前缀。redirects: false 让登录重定向仍以重定向形式呈现。

匹配器(matcher)是应用于响应的条件。这个模板要求满足全部三组匹配条件:

分组 必需的观察结果 意义
状态 HTTP 200 拒绝或重定向不是请求的内部响应。
响应头 JSON 内容类型,可带参数 正常 HTML 页面不能证明诊断信息暴露。
正文 扁平 JSON 对象中已公布的实时诊断字段 公共 JSON、健康数据和嵌套示例是不同的结果。

matchers-condition: and 组合这些分组。如果没有它,默认的 OR 关系可能再次让 HTTP 200 单独产生发现。在 DSL 匹配器内部,condition: and 同样要求满足列出的每个正文表达式。

DSL 是领域专用语言的缩写:这里指检查响应的简短表达式。body 是响应文本。json_minify(body) 解析并压缩有效 JSON,移除无意义的空白,同时不依赖键顺序。contains(text, fragment) 查找字面片段。先规范化,就能让写作 "visibility" : "internal" 的字段与 "visibility":"internal" 匹配同一个条件。

regex(pattern, text) 应用正则表达式。下面的表达式使用少量符号:

  • /[A-Za-z0-9._/-]+ 要求以斜杠开头,后跟一个或多个路径字符:字母、数字、点、下划线、斜杠或连字符。它接受变化后的绝对路径,包括大写字母。
  • [0-9]+[,}] 要求数字后紧接逗号或右花括号。这能避免将带引号的计数、负值或小数的开头误当作整数计数。
  • ^\{[^{}]*\}$ 要求外层只有一对花括号,且内部没有嵌套花括号,用于匹配本课程的扁平四字段诊断格式,而不是文档示例中的对象。

响应头表达式使用 (?im) 进行不区分大小写的逐行匹配;\s* 允许空白。其可选的 (?: ... )? 分组接受 ; charset=utf-8 等参数。你可以完整复制这个表达式来检查 JSON 内容类型;无需构造通用 MIME 解析器。

在下面的 YAML 中,>- 引出下一行缩进的文本值。它保留表达式中的引号和反斜杠,无需额外进行 YAML 引号转义。每个表达式内的单引号包围字面文本;文本中的双引号是要匹配的 JSON 字段标点。

创建 diagnostics.yaml。与上一个实验一样,> 替换此文件,带引号的 here-document 分隔符会原样复制代码块。结尾的 YAML 必须独占一行。

cat > diagnostics.yaml <<'YAML'
id: workshop-diagnostics-exposed
info:
  name: Public workshop diagnostics
  author: labex
  severity: medium
http:
  - method: GET
    path:
      - "{{BaseURL}}/maintenance/diagnostics"
    redirects: false
    matchers-condition: and
    matchers:
      - type: status
        status: [200]
      - type: regex
        part: header
        regex:
          - '(?im)^content-type:\s*application/json(?:\s*;[^\r\n]*)?\s*$'
      - type: dsl
        condition: and
        dsl:
          - >-
            contains(json_minify(body), '"service":"workshop-portal"')
          - >-
            contains(json_minify(body), '"visibility":"internal"')
          - >-
            regex('"database_file":"/[A-Za-z0-9._/-]+"', json_minify(body))
          - >-
            regex('"request_count":[0-9]+[,}]', json_minify(body))
          - >-
            regex('^\{[^{}]*\}$', json_minify(body))
YAML

让真实引擎验证模板。-validate 检查 Nuclei 模板语法;它不能证明检测行为正确。-t 选择你的文件,-duc 禁用更新检查,-ni 禁用外部 Interactsh 回调,-nc 去掉终端颜色代码。

nuclei -t diagnostics.yaml -validate -duc -ni -nc

预期会看到 All templates validated successfully。如果 Nuclei 报告 YAML 或表达式错误,请对照完整代码块比较缩进、引号和反斜杠,然后重新执行修正后的 here-document 和验证命令。保留 targets.txt 和 scope.txt。

本课程接受已教授的以下子集:在获准的 {{BaseURL}}/maintenance/diagnostics 路径上定义一个 HTTP GET,不重定向,并使用仅检查响应的 status、word、regex 或 DSL 匹配器。允许的 DSL 辅助函数为 contains、contains_all、contains_any、regex、json_minify、len、tolower、toupper、starts_with 和 ends_with,应用于 body、all_headers、status_code 或 content_length。顶层仅使用 id、info 和 http,请求字段仅使用 method、path、redirects、matchers-condition 和 matchers。模板不超过 32 KiB,匹配器最多十二个。其他协议、脚本、外部回调和本地文件访问不在本任务范围内。欢迎使用范围内等效的匹配器组合。

验证会使用原生 Nuclei 引擎,针对一次性正例和负例响应运行你未作改动的模板。这些示例包含变化的端口、主机名、值、JSON 布局和内容类型参数。只通过语法验证、始终匹配或永不匹配的文件无法通过这些功能检查。

运行检查并审阅交付成果

本步骤将把模板应用于真实的获准站点,并保留可供审阅的证据。留在 Auditor。由于路径相对于 BaseURL,目标列表变化时可以复用同一个文件。

运行真实评估。-l 提供基础 URL,-o 保存发现,-elog 保存请求错误。-jsonl 为每项发现使用一个 JSON 对象;-ot 省略编码后的模板,同时保留原生请求和响应。

nuclei -t diagnostics.yaml -l targets.txt -duc -ni -nc -jsonl -ot -o findings.jsonl -elog scan-errors.jsonl

在解释没有发现的情况前,检查原生请求错误日志:

cat scan-errors.jsonl

本次成功运行的日志应为空。即使 Nuclei 以零状态退出或打印 No results found,连接错误仍表示检查未完成。先修正拼写或连接问题,再得出结论;重试时使用新文件名,避免将先前证据与新运行混淆。最终接受的文件是 findings.jsonl 和 scan-errors.jsonl;如果重试使用了新名称,请用 cp 将成功的一对文件复制为这些最终文件名。例如,cp retry-findings.jsonl findings.jsonl 用第一个文件覆盖第二个;也要复制配套错误日志。

阅读发现:

cat findings.jsonl

预期只有一个以 /studio/maintenance/diagnostics 结尾的匹配 URL。阅读内嵌响应及其 X-Audit-Event,不要只看发现数量。正文必须满足你手动检查过的契约。

确认两个看似合理的替代响应为何不匹配:

curl -i http://workshop.test:8080/garden/maintenance/diagnostics

这是公共 JSON,visibility 设为 public。它不是内部诊断信息暴露。

curl -i http://workshop.test:8080/members/maintenance/diagnostics

预期 HTTP 302,Location 指向登录页面。模板不会跟随它并将登录页面误当作诊断结果。其他不匹配的站点是受保护的 archive、manual 的嵌套文档,以及 status 的最小健康响应。各站点仍会收到获准的只读请求;没有发现只说明不满足本模板条件,并不说明站点的整体安全情况。

交付 auditor 可读、自包含的普通文件。交付成果包括可复用的 diagnostics.yaml、真实的 findings.jsonl 和对应的 scan-errors.jsonl。范围文件也应一并保留。模板中不包含目标专用的数据库文件路径、请求计数、主机名或端口。验证先检查实时证据,再运行隔离示例,并保留你的文件。常规只读保留检查请求可能追加服务器日志并增加请求计数;它们不会成为学员评估证据。

独立挑战会提供新的部署列表和地址。运用同一个响应契约和同样的错误处理原则,同时保持公共工作坊功能及获授权的维护访问可用。

总结

你编写了原生 Nuclei HTTP 模板,组合状态、内容类型和规范化诊断字段。你测试了响应差异,保留了 BaseURL 可移植性,并评估了全部获准站点,没有把执行失败误当作“请求已成功完成,但未检出符合本课程暴露条件的响应”。交付成果是真实的模板、发现及配套的原生请求错误日志。