BugForge - Daily - Cheesy Does It

5 min read

Daily - Cheesy Does It

Vulnerabilities Covered

Business Logic Flaw Single-Use Coupon Bypass Improper Enforcement of a Unique Action Parameter Injection Price Manipulation

Summary

The Cheesy Does It application advertises a FOUNDERS20 promo code that grants 20% off your first order and is explicitly marked one per customer. The checkout flow enforces this limit correctly, rejecting a second use with You have already used this coupon. The Reorder feature, however, re-implements coupon handling on POST /api/orders/<id>/reorder without the same guard. The endpoint silently accepts a coupon_code value in the request body, a parameter the front-end never sends, and re-applies the discount without re-checking the per-customer redemption limit or requiring any payment. Redeeming FOUNDERS20 once at checkout and then replaying it through the reorder endpoint flips coupon_reused to true and discloses the flag, confirming the one-time-use constraint is not enforced on this path.

Reference

1

Application Analysis

Begin by exploring the Cheesy Does It application on first load. The menu page greets new customers with a Founders Circle banner offering 20% off your first order with the code FOUNDERS20, to be applied at checkout.

Menu - FOUNDERS20 Promo Banner

2

Placing the First Order with the Coupon

Add a pizza to the cart and proceed to checkout. In the Discounts & Rewards section, enter FOUNDERS20 in the promo code field. The helper text reinforces the rule, “First order? Use FOUNDERS20 for 20% off (one per customer).”, and the coupon applies cleanly, taking -$2.20 off the $10.99 subtotal for a total of $8.79.

Checkout - FOUNDERS20 Applied

Complete the payment and place the order. This is the single, legitimate redemption of FOUNDERS20, the coupon is now consumed against our account. Attempting to use it a second time through the normal checkout is rejected with You have already used this coupon, confirming the limit is enforced on the primary order flow.

3

Discovering the Reorder Feature

Navigate to the Orders page to view the order history. Each past order exposes a Reorder button alongside View Details, a convenience feature that re-places a previous order in a single click.

Order History - Reorder Button

4

Investigating the Reorder Request

Click Reorder and capture the request in Caido. The front-end issues a POST to /api/orders/<id>/reorder with an empty body (Content-Length: 0). Despite sending no coupon data, the JSON response contains a full set of coupon fields:

{
  "id": 2,
  "order_number": "CDI-1783946285456-7CN0YRWG7",
  "message": "Reorder placed successfully",
  "status": "received",
  "subtotal": 10.99,
  "coupon_code": null,
  "coupon_discount": 0,
  "coupon_reused": false,
  "points_earned": 10,
  "total": 10.99
}

Caido - Default Reorder Response

Two things stand out. First, the endpoint clearly has coupon logic, it reports coupon_code, coupon_discount, and a telling coupon_reused flag, yet the browser never sends a coupon. Second, the reorder is placed at full price ($10.99) with no payment step at all. The presence of coupon_reused strongly hints that the intended bug is coupon reuse, and the fact that the server returns coupon fields for an input it “never received” suggests the handler is reading a coupon_code body parameter that the front-end simply leaves out.

5

Injecting the Coupon and Retrieving the Flag

Send the reorder request to Caido Replay and modify it in two ways:

  • Add a Content-Type: application/json header, without it the server’s body parser ignores the payload and coupon_code is never read.
  • Add the JSON body {"coupon_code":"FOUNDERS20"}, re-supplying the coupon that was already consumed at checkout.
POST /api/orders/2/reorder HTTP/1.1
Content-Type: application/json

{
  "coupon_code": "FOUNDERS20"
}

The server accepts the already-used coupon, re-applies the -$2.20 discount, and returns "coupon_reused": true with the message Reorder placed successfully — promo applied. The flag is disclosed inline:

Caido - Coupon Reused and Flag

The reorder endpoint honoured FOUNDERS20 a second time even though the account had already redeemed it, proving the application does not correctly enforce the coupon’s one-per-customer constraint on this path.

Why the Content-Type Header Matters

The back-end parses request bodies with a JSON body parser (Express express.json()), which by default only runs when the request’s Content-Type is application/json. Two byte-identical reorder requests behave completely differently depending on this header:

  • With Content-Type: application/json, the body is parsed, coupon_code is read, the discount is re-applied, coupon_reused becomes true, and the flag is returned.
  • Without it, the parser skips the body, coupon_code is undefined, and the server places a plain full-price reorder with coupon_reused: false and no flag.

This is a common gotcha for parameter-injection findings, an injected field is silently ignored until the Content-Type matches what the server’s body parser expects.

Impact
  • Unlimited reuse of a coupon that is meant to be redeemed only once per customer
  • Repeated discounts applied through the reorder endpoint, resulting in direct financial loss
  • Orders placed with no payment validation, the reorder path skips the checkout validateprocesspayment_token pipeline entirely
  • Loyalty points earned on every reordered order, allowing point farming on top of the discount abuse
  • The flaw is exploitable against the attacker’s own account with a single crafted request, requiring no elevated privileges
Vulnerability Classification
  • OWASP Top 10: A04:2021 - Insecure Design (business-logic abuse of a sensitive flow)
  • OWASP API Security Top 10: API6:2023 - Unrestricted Access to Sensitive Business Flows
  • Vulnerability Type: Business Logic Flaw - single-use coupon reuse via an alternate endpoint that omits the redemption check and accepts a client-supplied coupon_code
  • Attack Surface: POST /api/orders/<id>/reorder
  • CWE: CWE-837 - Improper Enforcement of a Single, Unique Action, CWE-841 - Improper Enforcement of Behavioral Workflow, CWE-915 - Improperly Controlled Modification of Dynamically-Determined Object Attributes
Root Cause

The reorder handler duplicates the checkout’s coupon-application logic but drops its guards. Where POST /api/orders validates the coupon, checks the per-user redemption record, records the redemption, and requires a valid payment token, the reorder endpoint trusts a coupon_code value passed in the request body and applies the discount without re-verifying that the coupon has not already been redeemed by that customer and without requiring payment. The server even computes a coupon_reused flag that correctly identifies the reuse, but grants the discount anyway rather than rejecting it. Because the single-use constraint is enforced only on the primary checkout path and not on this secondary flow, the “one per customer” rule can be bypassed indefinitely.

Remediation
  • Enforce coupon validation centrally so every code path that applies a discount, including reorder, runs the same redemption checks
  • Reject any coupon whose per-customer or global redemption limit has already been reached, and treat coupon_reused: true as a hard failure rather than an informational flag
  • Record coupon redemptions atomically at the point the order is created to prevent race conditions and replay
  • Do not accept a client-supplied coupon_code on the reorder endpoint; derive pricing server-side from the referenced order and re-validate it
  • Require the full payment authorisation flow for reorders instead of allowing orders to be placed with no payment token
  • Ignore or strip body parameters that the endpoint is not designed to accept, and validate the request Content-Type explicitly
  • Add server-side monitoring for repeated use of single-use codes and anomalous reorder volumes
Zw4rts

© 2026 Zw4rts. All rights reserved.