
Automated scanners find approximately zero business logic flaws, and that is not a knock on the tools. There is simply nothing to flag. No quote to break out of, no script tag, no malformed payload. The request is well-formed, authenticated, and authorized; it just produces an outcome the design never intended, like checking out with a negative quantity to mint store credit, or replaying one gift-card code thirty times before the balance ever updates.
Payment tampering is the subclass with the most direct financial impact, and it is everywhere in e-commerce and SaaS billing. This post covers the recognizable patterns, how to test by reasoning about workflows rather than fuzzing inputs, what a confirmed price-tamper and a confirmed race condition look like, and the server-side controls that close them. These live in the WSTG Business Logic category (WSTG-BUSL).
Business logic vulnerabilities are weaknesses in the design and enforcement of an application's rules, where an attacker abuses legitimate functionality in a way the developers never intended. Unlike injection or XSS, there is no technically invalid input; the request is well-formed, authenticated, and authorized, but the outcome violates a business rule, like getting a product for free or escalating a workflow past a required approval.
Because they depend on understanding what the application is supposed to do, they cannot be found by pattern matching. You model the intended flow (cart, checkout, payment, fulfillment) and then ask what happens when you take steps out of order, supply boundary values, or repeat an action. This is why they are a manual-testing staple in our web app test cases.
Payment tampering works by manipulating values in the purchase flow (price, quantity, currency, discount, or the final amount) that the server trusts instead of recomputing. The classic case is an app that sends the item price to the client and reads it back at checkout, letting you edit it in Burp Suite. The confirmed finding is never the edited field, it is the receipt showing the lower charge:
POST /cart/checkout HTTP/1.1
Content-Type: application/json
{"item_id": 42, "price": 0.01, "qty": 1} <-- catalog price is 1499.00; client sent 0.01
HTTP/1.1 200 OK
{"order":"ORD-5582","total":0.01,"status":"confirmed"} <-- charged one cent, server trusted the field
# other variants on the same trust failure:
{"item_id": 42, "price": 1499.00, "qty": -3} # negative quantity -> credit
{"item_id": 42, "amount": 100, "currency": "IDR"} # pay 100 IDR instead of 100 USDThe "total":0.01 in the order record, matched against the payment gateway, is the proof of impact. Other variants include integer overflow on quantity, rounding abuse on per-unit discounts, and tampering with the gateway success callback so the app marks an order paid without the gateway confirming. The same class of trust failures shows up alongside parameter pollution and mass assignment in transactional APIs.
Most business logic findings fall into a handful of recognizable patterns, which gives you a checklist to work through on any transactional app. The trick is testing the workflow, not the field.
For payment race conditions specifically, Burp Suite's Turbo Intruder and the single-packet attack are the tools of choice.
Test business logic by mapping the intended workflow in full, then deliberately violating each assumption it makes about order, values, and repetition. This is reasoning work, not fuzzing, so it starts with understanding the feature: what are the steps, what does each one assume the previous one guaranteed, and where is a value taken on trust.
Work through the app with Burp Suite in front of you and ask: can I skip a step (go to /checkout/confirm directly), repeat a step (replay a coupon), reorder steps, supply a boundary or negative value, or change an ID to act on someone else's object. Run timing-sensitive flows (balance deductions, one-time tokens) through Turbo Intruder to test for races. This multi-step reasoning across an entire application is exactly where continuous, AI-driven agentic pentesting extends past a one-shot scan.
A confirmed race condition shows the same one-time action succeeding more than once because the requests landed before any of them updated state, the classic time-of-check to time-of-use flaw. Take a gift card where the balance is read, then debited in a separate step. Firing many redemptions in parallel lets several pass the check before any deduction commits:
# Turbo Intruder: 30 concurrent redemptions of a $50 card, single-packet attack
POST /wallet/redeem HTTP/2
{"code":"GC-50-USD"}
# Sequential expectation: 1 success, then "already redeemed"
# Race result (the bug): multiple 200s crediting the account
HTTP/2 200 {"credited":50.00,"balance":50.00}
HTTP/2 200 {"credited":50.00,"balance":100.00}
HTTP/2 200 {"credited":50.00,"balance":150.00} <-- three credits from one $50 cardThe escalating balance is the evidence you screenshot. On a recent assessment of a wallet-based marketplace, the win came from the single-packet attack landing all 30 requests inside the same millisecond, before the database row locked. The same rigor applies to price tampering: the finding is the charge, not the accepted request.
Scanners miss business logic flaws almost entirely because there is no malformed payload, no injection string, and no signature, just legitimate, authenticated requests used in an order or with values the design never anticipated. A scanner has no model of what a workflow means, so it cannot tell that checking out with quantity -3 is a credit exploit, or that replaying a single-use coupon is abuse. These require reasoning about intent, which keeps them a manual staple in our web application testing.
The false positive here is different from injection work: a server that accepts a tampered price but recomputes the real total at capture is not vulnerable, so confirm the charge, not just the accepted request. The false negative that costs teams most is the multi-step chain a scanner can never assemble: step A leaks an object ID, step B accepts it cross-tenant, step C fulfills without re-checking ownership, and each request looks fine in isolation. Modeling those sequences, holding state, and reasoning about what each step assumed the previous one guaranteed is precisely where reasoning-driven agentic pentesting reaches past a one-shot DAST run.
Prevent these flaws by enforcing every rule server-side and never trusting client-supplied values that carry financial or authorization meaning. For payments, look up prices, taxes, and discounts on the server from the catalog and recompute totals; never accept a price, amount, or currency from the request body as authoritative.
Beyond that: validate quantity ranges (reject negatives and absurd values), enforce single-use tokens and coupons with server-side state, use atomic transactions and row locking (or a database unique constraint on the redemption) to close TOCTOU races, verify each workflow step's prerequisites server-side rather than trusting the client reached it legitimately, and confirm payment status by querying the gateway directly instead of trusting a client redirect. These controls map to the access-control and verification themes in the OWASP Top 10, specifically A01 Broken Access Control and A04 Insecure Design.