A business logic vulnerability is a flaw in the rules an application follows, not in its code syntax. The request looks valid. Every field is the right type, the session is authenticated, and the server returns a clean 200 OK. The problem is that the request quietly breaks an assumption the app made about how people would use it. Because nothing looks malformed, a scanner waves it through, and that is what makes this class of bug so easy to miss.
What a business logic vulnerability actually is
Most security tools hunt for known bad input. They send a quote mark to look for SQL injection, or a script tag to look for cross site scripting. Those payloads are wrong on their face, so they are easy to detect and easy to block. A business logic vulnerability uses input that is completely legal. The attacker does not send garbage. They send a number, a coupon code, or a sequence of normal requests in an order the developer never expected.
Think of it this way. The developer wrote code to answer the question “is this input valid?” They forgot to answer a second question: “does this valid input still make sense for what the user is allowed to do?” The gap between those two questions is where these bugs live.
The request is valid. The assumption behind it is not. That gap is the whole bug.
Four invented examples of a business logic vulnerability
Here are four flaws in a made up shopping app we will call Acme Cart. None of these involve a special payload. Each one is a normal request that the server should have refused.
1. Applying a discount code twice
Acme Cart lets a shopper enter the code SAVE20 at checkout for twenty percent off. The intent is one use per order. But the apply endpoint never records that a code was already used on this cart. So the attacker just sends the same request again.
POST /cart/apply-coupon
{ "cart_id": "8841", "code": "SAVE20" }
POST /cart/apply-coupon
{ "cart_id": "8841", "code": "SAVE20" }
Each call stacks another twenty percent off. Send it five times and the total drops to almost nothing. The input is a real coupon code every single time. A scanner sees two identical, well formed requests and finds nothing to flag.
2. A negative quantity that pays you back
The cart accepts a quantity field. The developer assumed quantity would be one or more. The validation checks that the value is a number, but not that it is positive.
POST /cart/add-item
{ "sku": "MUG-01", "quantity": -3 }
Now the order total goes down by the price of three mugs. If the checkout flow refunds or credits the negative line, the attacker buys a real item, attaches a negative line item, and walks away owing less than zero. The number -3 is a valid integer. The assumption that quantities are positive lived only in the developer’s head.
3. Skipping a step in checkout
Acme Cart has a three step checkout: cart, then payment, then confirm. The payment step is where the card is charged. The confirm step creates the order. But the confirm endpoint trusts that payment already happened, because in the normal flow it always does.
POST /checkout/confirm
{ "cart_id": "8841" }
An attacker who calls /checkout/confirm directly, without ever hitting the payment step, gets a confirmed order and never pays. The request is shaped exactly like a real one. The flaw is the missing check that the payment state for this cart is actually complete.
4. Changing a price field the client should never set
The add to cart request includes the product price so the front end can show a running total. The server reads that price straight from the request body instead of looking it up from its own catalog.
POST /cart/add-item
{ "sku": "LAPTOP-15", "quantity": 1, "price": 1.00 }
The laptop now costs one dollar. The attacker did not break encryption or guess a password. They edited a field the server should have ignored and recalculated on its own.
Why automated tools miss a business logic vulnerability
A scanner works from a list. It knows what an injection string looks like, what a directory traversal looks like, what a default password looks like. It compares responses against those patterns. None of the four examples above match any pattern, because the input is data the app was built to accept.
To catch these, a tool would need to know things that live nowhere in the code in a checkable form:
- A coupon is meant to apply once per order.
- A quantity is meant to be positive.
- Payment must finish before an order is confirmed.
- Price is decided by the server, never the client.
These are facts about intent. They are the assumptions the application makes about how its own features should behave. A pattern matcher does not understand intent, so it cannot tell that a clean 200 OK hid a free laptop. You have to first learn how the feature is supposed to work, then ask what happens if a user refuses to play along. That is research, not scanning. Our writeups in attack teardowns walk through this kind of reasoning on invented apps.
How to find and prevent these bugs
The starting point is to write down the assumptions, because a rule you never wrote down is a rule you never enforced. For each feature, ask what the app silently expects, then test the opposite.
- List the invariants. One coupon per order. Quantity above zero. Payment before confirmation. Price from the catalog. Each one is a check the server must make on every request, not a hope about client behavior.
- Never trust the client for anything that affects money or access. Recompute prices, totals, and permissions on the server from trusted data.
- Enforce state, do not assume it. The confirm step should verify that this cart reached a paid state, rather than trusting the order of requests.
- Replay and reorder requests on purpose. Send the same action twice. Skip a step. Send a negative or a zero. Edit a field the UI never lets you touch. If the response stays clean when it should not, you found one.
- Turn a confirmed finding into a standing check. Once you prove that
quantity: -3lowers a total, add a test that fails if it ever works again.
The work is mostly about understanding the app and then questioning each thing it takes for granted. A clever input is rarely the hard part. Seeing the unstated rule is.
The takeaway
A business logic vulnerability is what is left after the obvious bugs are patched. The payload is legal, the response is clean, and the damage is real. Finding these means understanding what the app is meant to do and then testing each assumption underneath that, one at a time. This is exactly the kind of bug an autonomous researcher that tests assumptions is built to find, and you can read more about how we approach that on our about page.
