abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · opencart-reward-free-checkout
OpenCart 4.1.0.4 - OpenCart Ltd
CVE-2025-15116 是一个在 4.1.0.3 及之前版本中存在的优惠券竞态漏洞。在 4.1.0.4 中,coupon.save 在优惠券变更时会取消设置 payment_method。reward.save 则不会。应用足够的积分使 getTotals 为 <= 0.00,选择 Free Checkout,然后清除积分。确认操作会将待处理行重写为目录价格。free_checkout.confirm 仅检查会话中的支付代码。
拥有奖励积分的已登录客户可以将目录 SKU 作为 Free Checkout 发货,并保留积分。
| ID | 暂无 CVE |
| CWE | CWE-840, CWE-863 |
| CVSS | High: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N |
| 产品 | OpenCart |
| 受影响版本 | 至 4.1.0.4 的 reward + Free Checkout |
| 认证 | 已认证客户(访客购物车不属于此漏洞) |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅限 127.0.0.1 |
登录,将积分可覆盖的 SKU 加入购物车,应用足够的奖励积分使 Free Checkout 列出,保存该支付方式,然后以 0 调用 reward.save。第二次确认会写入目录总价。free_checkout.confirm 将订单提升为已支付。积分从未被扣除。
他们无法获得 shell。访客结账不属于此漏洞:reward 需要客户。Free Checkout 仅在 total <= 0 时列出。漏洞在于清除积分不会将其取消列出,且确认操作不会再次检查总价。
我阅读了 reward.save,然后是 coupon.save,然后是 getMethods(total <= 0.00),然后在状态为 0 时阅读 editOrder,最后是 free_checkout.confirm。在 4.1.0.4 中,reward=0 会丢弃 session.reward。它不会触及 payment_method。而在 15116 加固之后的优惠券则会。
第一个关注此问题的客户端会应用 100 积分,选择 Free Checkout,确认一次,然后得到一个诚实的零总价订单。那是功能,不是漏洞。
已记录的弯路:使用 coupon.save 而非 reward.save(该路径会取消设置支付方式);访客结账;points=0 的产品(Free Checkout 永远不会列出);在 free_checkout.confirm 过程中将 reward 保留在会话中(诚实的免费订单,积分应被扣除);仅查看 order_status_id=0(待处理不等于已支付)。判定标准是 status 1, total 100.00, payment free_checkout, debit 0。
实验环境:客户登录。购物车添加 36(iPod Nano,points=100)。reward.save 100。支付方式列出 Free Checkout。保存它。确认写入订单 3,总价为 0。reward.save 0。确认将订单 4 重写为 100.00。free_checkout.confirm。种子数据去除了税费和运费,因此 reward 可以精确地将 getTotals 归零。那不是漏洞。积分覆盖该行项目的目录 SKU 才是现实情况。客户仍然拥有那 1000 积分。
cd lab
./run.sh
目标仅限 http://127.0.0.1:18108。首先将 OpenCart 4.1.0.4 的 upload/ 放置到 lab/www。该目录树不在此仓库中。
SUCCESS OpenCart reward + Free Checkout underpay
IOC order-after-reward 3 total=0.0000 status=0 free_checkout.free_checkout
IOC order-after-clear 4 total=100.0000 status=0 free_checkout.free_checkout
IOC free_checkout.confirm redirect checkout/success
IOC order-final 4 total=100.0000 status=1 debit=0 balance=1000
在 reward.save 中像 coupon.save 已经做的那样 unset payment_method,并让 free_checkout.confirm 拒绝 total > 0。
reward.php · coupon.php · free_checkout.php 控制器 · free_checkout.php 模型 · confirm.phpGNU Affero GPL v3.0。参见 LICENSE。仅限回环实验环境。无担保。