Automated scanners are pattern matchers. They fire a payload, look for a signature in the response, and move on. That works for injection and misconfiguration — categories with a recognisable fingerprint. It fails completely for business logic flaws, because those bugs don't look wrong at the byte level. Every request is well-formed, every response is 200 OK, nothing is "injected". The vulnerability is that the application allowed a sequence of legitimate operations that, combined, break an assumption the business depended on. Finding them is the part of testing that stays stubbornly human.
What a logic flaw actually is
A logic flaw is a gap between what the developers intended the rules to be and what the code actually enforces. No malformed input required — you're using the app exactly as designed, just in an order or quantity or combination nobody planned for. The classics:
- Applying a discount code more than once, or stacking codes that were meant to be exclusive.
- Reaching step 4 of a checkout/KYC/approval flow without completing steps 1–3.
- Setting a quantity to a negative number and turning a purchase into a credit.
- Changing a
roleorpricefield the client "shouldn't" be sending and finding the server trusts it. - Racing two requests so a one-time action (redeem, withdraw, vote) happens twice.
A scanner sees valid requests returning valid responses and reports nothing. The money — sometimes literally — walks out the front door.
How I hunt them
1. Understand the business before the bytes. Before I touch a payload I map what the application is for. What has value here — money, credits, reputation, access, data? What are the rules around that value, and who is supposed to be allowed to do what? Every logic flaw is a violated rule, so I have to know the rules first. Reading the app like a product manager comes before reading it like a hacker.
2. Map the intended flow, then attack the sequence. I walk each multi-step process the way a normal user would and record it. Then I ask the questions the happy path never does:
- What if I do these steps out of order?
- What if I skip a step by jumping straight to the endpoint the next step calls?
- What if I repeat a step that was meant to happen once?
- What if I do two steps at the same time?
State machines that were only ever tested forwards fall apart when you arrive at a state from an unexpected direction.
3. Question every value the client controls. Any field the browser sends is attacker-controlled, full stop — including the ones the UI never lets you edit. Prices, quantities, user IDs, roles, status flags, isAdmin, totals computed client-side. The test is always the same: does the server re-derive and re-authorise this value, or does it trust what I sent? Trust is where the bug lives.
4. Look for missing quantity and boundary checks. Negative numbers, zero, absurdly large values, fractional quantities, and values that overflow. "Refund 5 of an item I bought 3 of." "Transfer -100 (which credits me)." "Order 0 and see if the coupon still applies." Boundaries are where developers assume sane input and attackers supply insane input.
5. Hunt race conditions on anything that should happen once. Redeem, withdraw, apply, submit, vote — if the check and the action aren't atomic, firing several requests within the same instant can slip multiple through before the "already done" flag is written. Any balance, quota, or one-time token is a candidate.
A concrete pattern
Consider a wallet withdrawal. The intended rule: you can withdraw up to your balance. The implementation:
1. read balance
2. check amount <= balance
3. send the money
4. subtract amount from balance
Steps 2 and 4 are not atomic. Fire ten withdrawal requests for the full balance simultaneously and several can each read the old balance at step 1, all pass the check at step 2, and all pay out — before any of them writes the new balance at step 4. The account withdraws far more than it held. Every individual request was legal. The flaw is in the sequence, and no signature-based scanner will ever see it.
Why this stays a human job
Business logic flaws require a mental model of intent, and intent isn't in the traffic. It's in what the product is supposed to do, which lives in a human's head, not in a response header. A tool can help you observe and replay; it can't tell you that redeeming a voucher twice is wrong, because "wrong" here is a business fact, not a technical one. This is precisely why an engagement built on human reasoning finds a whole class of high-impact bugs that a scan-only approach structurally cannot.
Defending against them
- Enforce every rule server-side, every time. The client is a convenience, never a control. Re-derive prices, re-check ownership, re-validate state on the server for each request.
- Make one-time actions atomic. Use locks, database constraints, or idempotency keys so "do this once" survives concurrent requests.
- Validate state transitions explicitly. A step should verify the required previous steps actually completed for this session, not assume the UI enforced order.
- Bound every quantity. Reject negatives, zeros where they're meaningless, and implausible magnitudes.
- Threat-model the abuse cases during design. For each valuable operation, ask "what happens if this runs twice, out of order, or with a hostile value?"
If your product moves money, credits, or access, this is the class of bug most worth having a human look for — and the class scanners will keep reporting clean while it's wide open. That deeper, logic-focused testing is the core of how I run a web & API engagement.