背景描述
最开始知道这种缺陷是之前听月神在360BugClound的分享,后来自己挖到后,自己的感触更深了一些。
漏洞概述
折价充值:指所支付的实际金额低于储值卡的面值。
一个储值卡购买的功能,100元的充值面额的实际支付是100元。
但是这个漏洞最大可实现实际支付100元,充值200元,折价50%的效果。
详细说明
点击去支付时,会先创建订单、再进入支付环节。
创建订单时,修改cardDenomination参数的值为0.019元
在微信支付中,最小精度是0.01,处理原则是去尾法,即若是你传给微信支付的数值是0.019元时,那么它实际需要支付的是0.01元。
在该充值功能中,最小精度也是0.01,但是处理原则却用的四舍五入,即若是你传给现金卡订单中的面额是0.019元时,那么它会认为你充值的是0.02元。
至此,便可以实现充值0.01元获得0.02元,折价50%的效果。
另外若是觉得比较慢的话,还有另一种方式快速获利。
在购买现金卡时可以修改购买的数量,假设将单价设置为0.015,将数量设置为1w。
那么实际需要支付0.015*1w=150元,而实际充值了0.02*1w=200元,折价75%的效果。
充值成功后,点击上面这个【充入账户余额】可以将刚才设置的所有现金卡全部冲入账户余额,不用一张一张的点击充值。
真相
于是我到代码中去尝试寻找真相,一直追踪到数据库,大概发现了真相:
首先在代码中并没有做严谨的限制,导致保留2位小数这个功能更多的是依赖于数据库。
该项目在MySQL中用了decimal(20,2)数据类型,来限定金额保留2位小数,当插入的数据超过2位小数时,则会用四舍五入的形式进行保留。
测试一下看看
而微信支付的接口用的是去尾法。
另外我发现很多编程语言,默认使用的都是四舍五入的处理方式,下面是Python里面的format方法。