0x1 记一次加密小程序的支付逻辑漏洞挖取
最近接到一个小程序测试任务,目标资产是一个医疗相关的应用。在获取资产后,我便开始使用 Burp Suite 进行常规渗透测试。很快,我就发现了一个值得注意的情况:这个小程序所有传输的数据包内容都经过了加密处理。

由于小程序通常采用 API 方式进行前后端通信,如果数据被加密,那么加密函数一定存在于前端代码中。通过 Burp Suite 仔细分析传输的 JavaScript 包,我识别出它使用了 CBC 模式的加密算法,并且密钥(secretKey)是硬编码在代码里的。

值得注意的是,这里使用的并非常见的 AES-CBC 算法,而是国密 SM4 的 CBC 加密模式。这一点可以从其引用的 sm4.js 文件推断出来。对于此类加密,我们可以借助在线工具进行解密测试,例如 http://lzltool.com/SM4。

在 CBC 模式下,密钥(Key)和初始向量(IV)需要保持一致。利用获取到的硬编码 secretKey 作为密钥和 IV,我成功解密了传输的数据,看到了清晰的业务参数。
这个小程序的核心功能之一是医院挂号系统,这必然涉及支付环节。于是,我将测试重点转向了支付流程。

我随意选择了一位医生,预约了一个时间段,点击进入了挂号确认页面。

点击“去支付”按钮的同时,我开启抓包工具进行拦截。在支付过程中,会产生一系列请求。经过分析,我发现小程序虽然在后端与数据库比对时校验了金额,但在关键的“申请微信支付”请求环节,却没有对客户端提交的订单金额进行二次验证。
整个支付流程大致如下:
创建订单 -> 后端对比数据库(校验金额) -> 申请微信支付 -> 创建微信支付单 -> 用户支付完成
为了验证漏洞,我对服务器返回的原始数据包进行拦截和解密。在解密后的 JSON 数据中,我找到了一个决定支付金额的关键参数。

我将这个金额参数的值从原来的 33.00 修改为 0.01,然后使用相同的密钥和 IV 重新加密,生成新的密文。接着,我用这个新密文替换掉原始响应包中的加密数据部分。
以下是关键的请求与响应交互过程(数据已脱敏):

完成替换后,我放行这个被篡改的响应包,但拦截了后续的请求。此时,前端接收到解密后的数据,认为本次挂号只需支付 0.01 元。

按照流程,我支付了这 0.01 元。

支付成功后,在“挂号记录”中查询,可以看到该订单状态显示为“已取号”,意味着利用支付逻辑漏洞完成的低价挂号已经成功。

漏洞原理与修复建议
这个漏洞本质上是一个业务逻辑漏洞,通常被称为“支付金额篡改”或“订单金额篡改”。其核心问题在于:服务端在创建支付订单时,过分信任客户端提交的数据,没有在支付网关调用前进行最终的金额校验。
漏洞点分析
- 加密可逆:虽然前端使用了 SM4 加密,但密钥硬编码在 JS 中,导致加密形同虚设。攻击者可以轻松解密、修改、重加密。
- 校验环节缺失:金额校验仅在订单创建时与数据库比对了一次(
hisMoney:'33.00')。在后续发起真正的微信支付请求时,服务器直接使用了客户端传来的(可能已被篡改的)金额参数,没有再次与系统预设的订单金额进行比对。
- 流程设计缺陷:正确的流程应该是:服务端生成一个固定金额的支付订单号,客户端仅提交此订单号至支付网关。金额应由服务端在调用支付接口时指定,而不应信任客户端。
修复建议
- 强化后端校验:在调用微信支付统一下单 API 时,金额参数必须从服务端数据库或缓存中重新读取原始订单金额,绝不能使用客户端传来的任何金额字段。
- 避免密钥硬编码:前端加密密钥不应硬编码。可以考虑使用动态密钥或对关键参数(如金额)在后端生成签名,前端提交时附带签名以供验证。
- 优化支付流程:采用“服务端预下单”模式。由服务端生成包含真实金额的支付订单(或获取支付参数),再将订单号或支付参数下发给前端。前端仅负责唤起支付,不参与金额的构造。
总结
这次对加密小程序的安全测试过程清晰地展示了一个经典逻辑漏洞的挖掘路径:从发现数据加密,到逆向分析加密方式,再到定位关键业务接口并测试参数可控性。对于开发者而言,尤其是在涉及支付、订单等核心业务时,必须牢记“永远不要信任客户端传来的数据”,所有关键逻辑的校验都应在可靠的服务端完成。对于从事逆向工程和渗透测试的安全研究者来说,微信小程序由于其独特的架构,常常会暴露出加解密实现不当、接口权限校验不严等问题,是一个值得持续关注的安全评估对象。
如果你对更多实战漏洞挖掘案例或安全技术感兴趣,欢迎到 云栈社区 交流讨论。
|