Nuclei 支持基于 HTTP 请求中 fuzzing 部分定义的规则对 HTTP 请求进行 fuzzing。这允许创建针对通用 Web 应用程序漏洞(如 SQLi、SSRF、CMDi 等)的模板,而无需像传统 web fuzzer 那样获取目标的任何信息。我们将这一概念称为未知漏洞 Fuzzing。
pre-condition(前置条件)
通常情况下,我们只希望在有意义的请求上尝试 fuzzing。例如:
- 当存在 Body 时才对 Body 进行 Fuzz
- 忽略 PreFlight 和 CONNECT 请求
等等。在 Nuclei v3.2.4 中,我们引入了新的 pre-condition 部分,其中包含了 fuzzing 模板应该执行的条件。
pre-condition 可以被视为 nuclei 中 matchers 的孪生兄弟。它们支持所有匹配器类型,包括 DSL,唯一的区别是它们服务于不同的目的。
例如,要仅在具有某些 body 的 POST 请求上执行模板,可以使用以下过滤器:
- pre-condition:
- type: dsl
dsl:
- method == POST
- len(body) > 0
condition: and
目前,只有 header、host、input、method、path 等请求数据可用,但很快,一旦添加了加载响应和请求的支持,响应数据也将可用。
在编写/执行模板时,可以使用 -v -svd 标志查看应用过滤器之前可用的所有变量。
Part(部分)
Part 指定请求的哪一部分应该根据指定的规则进行 fuzzing。该参数的可用选项有:
query(默认)- 对 URL 的查询参数进行 fuzzing
fuzzing:
- part: query # 对 URL 查询中的参数进行 fuzzing
path - 对请求的路径参数进行 fuzzing
fuzzing:
- part: path # 对路径参数进行 fuzzing
header - 对请求的头部参数进行 fuzzing
fuzzing:
- part: header # 对头部进行 fuzzing
cookie - 对请求的 cookie 参数进行 fuzzing
fuzzing:
- part: cookie # 对 cookies 进行 fuzzing
body - 对请求的 body 参数进行 fuzzing
fuzzing:
- part: body # 对 body 中的参数进行 fuzzing
特殊部分
request - 对整个请求进行 fuzzing(上面提到的所有部分)
fuzzing:
- part: request # 对整个请求进行 fuzzing
多重选择部分
可以通过定义 parts 字段(即上述选项的复数形式)来选择多个部分进行 fuzzing。
fuzzing:
- parts:
- query
- body
- header
Type(类型)
Type 指定了要为 fuzzing 规则值执行的替换类型。该参数的可用选项有:
- replace(
默认)- 使用 payload 替换值
- prefix - 使用 payload 作为值的前缀
- postfix - 使用 payload 作为值的后缀
- infix - 使用 payload 作为值的中缀(放在中间)
- replace-regex - 使用正则表达式通过 payload 替换值
fuzzing:
- part: query
type: postfix # 对查询进行 fuzzing 并将 payload 作为参数的后缀
Key-Value 抽象
在 HTTP 请求中,有各种部分如 query、path、headers、cookies 和 body,每个部分在不同格式中有所不同。例如,query 部分是键值对,path 部分是值列表,body 部分可以是 JSON、XML 或 form-data。
为了有效地抽象这些部分并允许它们被 fuzzed,Nuclei 将这些值暴露为 key 和 value 对。这允许用户基于请求部分的键或值进行 fuzzing。
例如,下面的示例 HTTP 请求可以抽象为如下所示的键值对:
POST /reset-password?token=x0x0x0&source=app HTTP/1.1
Host: 127.0.0.1:8082
User-Agent: Go-http-client/1.1
Cookie: PHPSESSID=1234567890
Content-Length: 23
Content-Type: application/json
Accept-Encoding: gzip
Connection: close
{"password":"12345678"}
| key | value |
|---|
| token | x0x0x0 |
| source | app |
| key | value |
|---|
| value | /reset-password |
| key | value |
|---|
| Host | 127.0.0.1:8082 |
| User-Agent | Go-http-client/1.1 |
| Content-Length | 23 |
| Content-Type | application/json |
| Accept-Encoding | gzip |
| Connection | close |
| key | value |
|---|
| PHPSESSID | 1234567890 |
注意: XML、JSON、Form、Multipart-FormData 将采用 kv 格式,但如果 Body 是二进制或任何其他格式,整个 Body 将表示为单个键值对,键为 value,值为整个 Body。
| key | value |
|---|
| value | ”\x08\x96\x01\x12\x07\x74” |
这种抽象真正提升了效率,因为你只需要为 Body 编写一个规则,它就会应用于所有格式。例如,如果你检查 body 值中的 SQLi,单个规则将适用于所有格式,即 JSON、XML、Form、Multipart-FormData 等。
Mode(模式)
Mode 指定执行替换的模式。可用模式有:
- multiple(
默认)- 一次替换所有值
- single - 一次替换一个值
fuzzing:
- part: query
type: postfix
mode: multiple # 对查询进行 fuzzing,同时将 payloads 作为所有参数的后缀
注意:当未定义其他选项时,将设置/使用默认值。
组件数据过滤
支持多种过滤器,以将 fuzzing 的范围限制为仅感兴趣的参数键和值。Nuclei HTTP Fuzzing 引擎将请求部分转换为键和值,然后可以通过相关选项进行过滤。
支持以下过滤字段:
- keys - 要进行 fuzzing 的参数名称列表(精确匹配)
- keys-regex - 要进行 fuzzing 的参数正则表达式列表
- values - 要进行 fuzzing 的值正则表达式列表
这些过滤器可以组合使用,基于参数输入运行高度针对性的 fuzzing。下面提供了一些此类过滤的示例:
# 基于参数名称值进行命令注入 fuzzing
fuzzing:
- part: query
type: replace
mode: single
keys:
- "daemon"
- "upload"
- "dir"
- "execute"
- "download"
- "log"
- "ip"
- "cli"
- "cmd"
# 基于参数名称正则表达式进行开放重定向 fuzzing
fuzzing:
- part: query
type: replace
mode: single
keys-regex:
- "redirect.*"
# 基于参数值正则表达式进行 SSRF fuzzing
fuzzing:
- part: query
type: replace
mode: single
values:
- "https?://.*"
Fuzz
Fuzz 指定用于参数的 type 替换值。它支持 payloads、DSL 函数等,并允许用户充分利用现有的 nuclei 功能集进行 fuzzing。
# 使用 stop-at-first-match 进行 XSS fuzzing 的 fuzz 部分
payloads:
reflection:
- "6842'\"><9967"
stop-at-first-match: true
fuzzing:
- part: query
type: postfix
mode: single
fuzz:
- "{{reflection}}"
# 使用 interactsh-url 占位符进行 oob 测试
payloads:
redirect:
- "{{interactsh-url}}"
fuzzing:
- part: query
type: replace
mode: single
keys:
- "dest"
- "redirect"
- "uri"
fuzz:
- "https://{{redirect}}"
# 使用模板级变量进行 SSTI 测试
variables:
first: "{{rand_int(10000, 99999)}}"
second: "{{rand_int(10000, 99999)}}"
result: "{{to_number(first)*to_number(second)}}"
http:
...
payloads:
reflection:
- '{{concat("{{", "§first§*§second§", "}}")}}'
fuzzing:
- part: query
type: postfix
mode: multiple
fuzz:
- "{{reflection}}"
Analyzer(分析器)
Analyzers 是 nuclei fuzzing 中引入的一个新概念,它允许引擎基于特定逻辑进行额外的验证请求,以验证漏洞。
time_delay
time_delay 分析器验证请求的响应时间是否可由 fuzzed payload 控制。它使用从 ZAP 移植的线性回归算法,通过交替请求来确定服务器时间实际上是可控制的,而不仅仅是噪音。你可以像这样配置它:
# 创建一个新的时间延迟分析器
analyzer:
name: time_delay
# 可选地,你可以像下面这样定义分析器的参数。
#
# 默认值对于大多数用例已足够好。
parameters:
sleep_duration: 10 # 睡眠 10 秒(默认值:5)
requests_limit: 6 # 发出 6 个验证请求(默认值:4)
time_correlation_error_range: 0.30 # 时间相关性的错误范围(默认值:0.15)
time_slope_error_range: 0.40 # 时间斜率的错误范围(默认值:0.30)
以下动态占位符在带有 time_delay 分析器的 payloads 中可用:
[SLEEPTIME] - 时间延迟分析器的睡眠时间(以秒为单位)。
[INFERENCE] - 时间延迟分析器的推断条件 (%d=%d)。
这些值会在运行时用分析器的实际值替换。以下是一个通常的验证过程:
- 使用 payload 向目标发送请求,延迟 5 秒。
- 如果响应时间小于 5,不执行任何操作。
- 将请求发送到分析器,该分析器将其排队,延迟 5 秒。
- 接下来是 1 秒延迟
- 接下来是 5 秒延迟
- 最后,最后 1 秒延迟。
如果响应时间是可控的,分析器将报告漏洞。
分析器匹配的匹配也相当简单。与 interactsh 类似,你可以使用 part: analyzer 来匹配分析器响应。
matchers:
- type: word
part: analyzer
words:
- "true"
可选地,你还可以从分析器中提取 analyzer_details 进行匹配。
示例 Fuzzing 模板
下面提供了一个用于 fuzzing XSS 漏洞的示例模板:
id: fuzz-reflection-xss
info:
name: Basic Reflection Potential XSS Detection
author: pdteam
severity: low
http:
- pre-condition:
- type: dsl
dsl:
- 'method == "GET"' # 仅在方法为 GET 时运行
payloads:
reflection:
- "6842'\"><9967"
stop-at-first-match: true
fuzzing:
- part: query
type: postfix
mode: single
fuzz:
- "{{reflection}}"
matchers-condition: and
matchers:
- type: word
part: body
words:
- "{{reflection}}"
- type: word
part: header
words:
- "text/html"