Documentation Index
Fetch the complete documentation index at: https://projectdiscovery.sec-lab.cn/llms.txt
Use this file to discover all available pages before exploring further.
模板流引擎在 nuclei v3 中引入,为 Nuclei 带来了两项重要增强功能:
这些功能通过 goja 后端使用 JavaScript (ECMAScript 5.1) 实现。
条件执行
在编写复杂模板时,我们可能需要在执行请求的某些部分之前添加一些额外的检查(或条件语句)。
一个理想的例子是使用默认用户名和密码暴力破解 WordPress 登录,但如果我们仔细重新评估这个模板,我们可以看到模板发送了 276 个请求,而没有检查 URL 是否实际存在或目标站点是否确实是 WordPress 站点。
随着 Nuclei v3 中 flow 的增加,我们可以重写此模板,首先检查目标是否为 WordPress 站点,如果是,则使用默认凭据暴力破解登录,这可以通过简单添加一行内容来实现,即 flow: http(1) && http(2),nuclei 将处理其余一切。
id: wordpress-bruteforce
info:
name: WordPress Login Bruteforce
author: pdteam
severity: high
flow: http(1) && http(2)
http:
- method: GET
path:
- "{{BaseURL}}/wp-login.php"
matchers:
- type: word
words:
- "WordPress"
- method: POST
path:
- "{{BaseURL}}/wp-login.php"
body: |
log={{username}}&pwd={{password}}&wp-submit=Log+In
attack: clusterbomb
payloads:
users: helpers/wordlists/wp-users.txt
passwords: helpers/wordlists/wp-passwords.txt
matchers:
- type: dsl
dsl:
- status_code == 302
- contains_all(header, "/wp-admin","wordpress_logged_in")
condition: and
更新后的模板现在看起来直接明了且易于理解。我们首先检查目标是否为 WordPress 站点,然后执行暴力破解请求。这只是条件执行的一个简单示例,flow 接受任何 JavaScript (ECMAScript 5.1) 表达式/代码,因此您可以自由创建任何条件执行逻辑。
请求执行编排
Flow 是 Nuclei 的一项强大功能,为执行请求提供了增强的编排能力。条件执行的简单性只是开始。使用 flow,您可以:
- 遍历值列表并为每个值执行请求
- 从请求中提取值,遍历它们,并为每个值执行另一个请求
- 获取和设置模板上下文(全局变量)中的值
- 将输出写入 stdout 用于调试目的或基于特定条件
- 在模板执行期间引入自定义逻辑
- 使用 ECMAScript 5.1 JavaScript 功能在运行时构建和修改变量
- 在运行时更新变量并在后续请求中使用它们
将请求执行编排视为 JavaScript 和 Nuclei 之间的桥梁,在特定模板内提供双向交互。
实际示例:Vhost 枚举
为了更好地说明 flow 的功能,让我们考虑开发一个用于 vhost(虚拟主机)枚举的模板。这组任务通常需要从头开始编写新工具。以下是我们需要遵循的步骤:
- 检索提供的 IP 的 SSL 证书(使用 tlsx)
- 从证书中提取
subject_cn(CN)
- 从证书中提取
subject_an(SAN)
- 从上述步骤中获取的值中删除通配符前缀
- 使用从 SSL 请求中找到的所有域名暴力破解请求
您可以利用 flow 来简化此任务。下面的 JavaScript 代码编排了 vhost 枚举:
ssl();
for (let vhost of iterate(template["ssl_domains"])) {
set("vhost", vhost);
http();
}
在此代码中,我们引入了 5 行额外的 JavaScript。这允许模板执行 vhost 枚举。最好的部分?您可以使用 Nuclei 的所有功能大规模运行它,使用支持的输入如 ASN、CIDR、URL。
让我们分解 JavaScript 代码:
ssl():此函数执行 SSL 请求。
template["ssl_domains"]:从模板上下文中检索 ssl_domains 的值。
iterate():辅助函数,可以遍历任何值类型,同时处理空值或 null 值。
set("vhost", vhost):在模板中创建一个新变量 vhost,并将 vhost 变量的值分配给它。
http():此函数执行 HTTP 请求。
通过理解并利用 Nuclei 的 flow,您可以重新定义编排请求执行的方式,使您的模板更强大、更高效。
以下是使用 flow 进行 vhost 枚举的工作模板:
id: vhost-enum-flow
info:
name: vhost enum flow
author: tarunKoyalwar
severity: info
description: |
vhost enumeration by extracting potential vhost names from ssl certificate.
flow: |
ssl();
for (let vhost of iterate(template["ssl_domains"])) {
set("vhost", vhost);
http();
}
ssl:
- address: "{{Host}}:{{Port}}"
http:
- raw:
- |
GET / HTTP/1.1
Host: {{vhost}}
matchers:
- type: dsl
dsl:
- status_code != 400
- status_code != 502
extractors:
- type: dsl
dsl:
- '"VHOST: " + vhost + ", SC: " + status_code + ", CL: " + content_length'
JS 绑定
本节简要描述所有 nuclei JS 绑定及其用法。
协议执行函数
在 nuclei 中,任何列出的协议都可以使用 protocol_name() 格式在 JavaScript 中调用或执行。例如,您可以使用 http()、dns()、ssl() 等。
如果您想执行协议的特定请求(参考 nuclei-flow-dns 示例),可以通过传递以下任一项来实现:
- 协议中该请求的索引(例如,
dns(1)、dns(2))
- 协议中该请求的 ID(例如,
dns("extract-vps")、http("probe-http"))
对于需要执行单个协议的多个请求的更高级场景,您可以一个接一个地指定它们的索引或 ID(例如,dns(“extract-vps”,“1”))。
使用索引数字或 ID 字符串调用特定协议请求的这种灵活性提供了定制执行的控制,允许您构建更复杂和高效的工作流。更复杂的用例中,单个协议的多个请求可以通过一个接一个地指定它们的索引或 id 来执行(例如:dns("extract-vps","1"))
Iterate 辅助函数
Iterate 是一个 nuclei js 辅助函数,可用于遍历任何类型的值,如数组、映射、字符串、数字,同时处理空/nil 值。
这是 nuclei 的附加辅助函数,用于省略检查值是否为空然后遍历它的样板代码
iterate(123,{"a":1,"b":2,"c":3})
// 使用自定义分隔符遍历数组
iterate([1,2,3,4,5], " ")
Set 辅助函数
当遍历值/数组或其他用例时,我们可能想使用自定义/给定值调用请求,这可以通过使用 set() 辅助函数来实现。调用时,它将给定变量添加到模板上下文(全局变量)中,该值在执行请求/协议期间使用。set() 的格式是 set("variable_name",value),例如:set("username","admin")。
for (let vhost of myArray) {
set("vhost", vhost);
http(1)
}
注意: 在上面的例子中,我们使用了 set("vhost", vhost),它将 vhost 添加到模板上下文(全局变量)中,然后调用 http(1),后者在请求中使用了这个值。
模板上下文
模板上下文只是一个包含所有数据的 map/jsonl,同时包含仅在运行时可用的内部/未导出数据(例如:从先前请求中提取的值,使用 set() 添加的变量等)。此模板上下文在 JavaScript 中作为 template 变量可用,可用于从中访问任何数据。例如:template["dns_cname"]、template["ssl_subject_cn"] 等。
template["ssl_domains"] // 返回执行 ssl 请求后模板上下文中可用的 ssl_domains 的值
template["ptrValue"] // 返回使用带有 internal: true 的正则表达式提取的 ptrValue 的值
很多时候我们不知道模板上下文中有哪些数据,可以通过使用 log() 函数将其打印到 stdout 轻松找到
Log 辅助函数
它是 nuclei js 替代 console.log 的函数,以可读格式漂亮地打印 map 数据
注意: 这应仅用于调试目的,因为它将数据打印到 stdout
Dedupe
很多时候仅有数组/切片是不够的,我们可能需要删除重复变量。例如,在前面的 vhost 枚举中,我们没有删除任何重复项,因为 ssl_subject_cn 和 ssl_subject_an 中总是有重复值的可能,这可以通过使用 dedupe() 对象来实现。这是 nuclei js 辅助函数,用于抽象从数组/切片中删除重复项的样板代码
let uniq = new Dedupe(); // 创建新的 dedupe 对象
uniq.Add(template["ptrValue"])
uniq.Add(template["ssl_subject_cn"]);
uniq.Add(template["ssl_subject_an"]);
log(uniq.Values())
就这样,它自动将任何切片/数组转换为 map 并从中删除重复项,并返回唯一值的切片/数组
类似于 DSL 辅助函数,我们可以使用 Javascript (ECMAScript 5.1) 提供的内置函数或使用 DSL 辅助函数,由用户决定使用哪一个。
在多协议/Flow 模板中跳过内部匹配器
在 nuclei v3.1.4 之前,像 CVE-2023-43177 这样的模板,它有多个请求/协议并使用 flow 进行逻辑处理,通常只返回一个结果,但当在 flow 中使用 for 循环时,这与逻辑冲突。为了解决这个问题,从 v3.1.4 开始,nuclei 引擎将打印模板中的所有事件/结果,模板编写者可以在匹配器中使用 internal: true 来跳过打印事件/结果,就像动态提取器一样。
注意:这仅在先前请求/协议中使用了匹配器/提取器时相关
使用新的 internal: true 逻辑的 CVE-2023-6553 示例如下:
id: CVE-2023-6553
info:
name: Worpress Backup Migration <= 1.3.7 - Unauthenticated Remote Code Execution
author: FLX
severity: critical
flow: http(1) && http(2)
http:
- method: GET
path:
- "{{BaseURL}}/wp-content/plugins/backup-backup/readme.txt"
matchers:
- type: dsl
dsl:
- 'status_code == 200'
- 'contains(body, "Backup Migration")'
condition: and
internal: true # <- 更新的逻辑(这将跳过打印此事件/结果)
- method: POST
path:
- "{{BaseURL}}/wp-content/plugins/backup-backup/includes/backup-heart.php"
headers:
Content-Dir: "{{rand_text_alpha(10)}}"
matchers:
- type: dsl
dsl:
- 'len(body) == 0'
- 'status_code == 200'
- '!contains(body, "Incorrect parameters")'
condition: and