The bugs scanners can't find. Where pentesters earn their fee.
Two requests sent simultaneously:
# Burp Repeater → group → send in parallel
# or:
turbo-intruder (Burp extension)Common targets:
- Coupon application (apply same coupon twice).
- Balance transfer (transfer X twice when balance only allows once).
- 2FA enable/disable (race the state change to bypass).
- Account creation (race to claim same username).
- Old request sent again — does it succeed? (Idempotency).
- Refresh-token reuse after rotation.
- One-time tokens that aren't actually one-time.
# normal request
POST /checkout {"item": "abc", "qty": 1, "price": 9.99}
# tampered
POST /checkout {"item": "abc", "qty": 1, "price": 0.01}
POST /checkout {"item": "abc", "qty": -1, "price": 9.99} # negative qty
POST /checkout {"item": "abc", "qty": 1, "discount": 100} # 100% discount
If the server trusts client-supplied price → finding.
- Skip a step (e.g., payment confirmation) by going directly to the final endpoint.
- Modify earlier-step data after later step has consumed it.
- Refresh / back-button to replay confirmations.
- Send bulk request beyond user's quota (
POST /api/messages?ids=1..1000). - Trigger expensive operations the API doesn't rate-limit.
- Map the flow. Pen-and-paper or a Whimsical board. Every state transition and the user-facing intent.
- Identify trust boundaries. Each transition is "what does the server believe about the client's state?"
- Test the boundary. Skip transitions, replay them, run them simultaneously, supply impossible data.
| Bug | Symptom |
|---|---|
| Negative quantity / price | Refund-as-purchase |
| Coupon stacking | Multiple coupons compound discounts |
| Currency confusion | Send "USD" amount with "JPY" code (100x) |
| Weight-of-zero | Order with 0 items but non-zero shipping refund |
| Off-by-one limits | Spend exactly the budget = -$0.01 balance |
You can't lint your way to business logic bugs. They require understanding the application's intent well enough to spot when its enforcement is sloppy.
Read the docs, talk to the developer, draw the flowchart. Then break it.