
Prova de conceito de exploit e laboratório para o bypass de pagamento via pontos de recompensa e Free Checkout no OpenCart 4.1.0.4, permitindo que um cliente autenticado mantenha os pontos e pague menos pelos pedidos.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · opencart-reward-free-checkout
OpenCart 4.1.0.4 - OpenCart Ltd
CVE-2025-15116 era uma corrida de cupom até a 4.1.0.3. Na 4.1.0.4, coupon.save remove payment_method quando o cupom muda. reward.save não. Aplique pontos suficientes para que getTotals seja <= 0.00, escolha Free Checkout, depois zere os pontos. O confirm reescreve a linha pendente para o preço de catálogo. free_checkout.confirm só verifica o código de pagamento da sessão.
Um cliente autenticado com pontos de recompensa pode enviar um SKU de catálogo como Free Checkout e manter os pontos.
| ID | ainda sem CVE |
| CWE | CWE-840, CWE-863 |
| CVSS | Alto: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N |
| Produto | OpenCart |
| Afetado | até 4.1.0.4 reward + Free Checkout |
| Autenticação | cliente autenticado (carrinho de convidado não é este bug) |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1 |
Faça login, coloque um SKU coberto por pontos no carrinho, aplique recompensa suficiente para que o Free Checkout apareça na lista, salve esse método, depois reward.save com 0. O segundo confirm grava o total de catálogo. free_checkout.confirm promove o pedido para pago. Os pontos nunca são debitados.
Eles não obtêm um shell. O checkout de convidado não é este bug: reward precisa de um cliente. O Free Checkout só aparece na lista com total <= 0. O bug é que zerar os pontos não o remove da lista, e o confirm não olha o total novamente.
Eu li reward.save, depois coupon.save, depois getMethods (total <= 0.00), depois editOrder enquanto o status é 0, depois free_checkout.confirm. Na 4.1.0.4, reward=0 remove session.reward. Não toca em payment_method. O cupom, após o endurecimento da 15116, toca.
O primeiro cliente que olhar para isso vai aplicar 100 pontos, escolher Free Checkout, confirmar uma vez, e obter um pedido honesto de total zero. Isso é a funcionalidade, não o bug.
Caminhos errados já registrados: coupon.save em vez de reward.save (esse caminho remove o método); checkout de convidado; produto com points=0 (o Free Checkout nunca aparece na lista); deixar reward na sessão através de free_checkout.confirm (pedido grátis honesto, os pontos deveriam ser debitados); olhar apenas para order_status_id=0 (pendente não é pago). O oráculo é status 1, total 100.00, payment free_checkout, debit 0.
Lab: login de cliente. Adicione ao carrinho o 36 (iPod Nano, points=100). reward.save 100. Os métodos de pagamento listam Free Checkout. Salve-o. O confirm grava o pedido 3 em 0. reward.save 0. O confirm reescreve o pedido 4 em 100.00. free_checkout.confirm. O seed remove imposto e frete para que a recompensa possa zerar getTotals exatamente. Isso não é o bug. Um SKU de catálogo cujos pontos cobrem a linha é o caso realista. O cliente ainda tem os 1000.
cd lab
./run.sh
Alvo apenas http://127.0.0.1:18108. Coloque o upload/ do OpenCart 4.1.0.4 em lab/www primeiro. Essa árvore não está neste repositório.
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
Faça unset de payment_method em reward.save da mesma forma que coupon.save já faz, e faça free_checkout.confirm recusar total > 0.
reward.php · coupon.php · free_checkout.php controller · free_checkout.php model · confirm.phpGNU Affero GPL v3.0. Veja LICENSE. Apenas lab em loopback. Sem garantia.