Business logic flaws abuse valid application features in unintended ways — price manipulation, workflow skipping, and discount stacking that scanners rarely catch.
TL;DR
price=0.01 request is only a bug if the server accepts it.SELECT FOR UPDATE), idempotency keys, and a strict state machine.Most vulnerability classes have a tell. SQL injection leaves a syntax error in a stack trace. Reflected XSS bounces a payload back into the DOM. Broken access control trips an HTTP 403 that suddenly turns into a 200. A scanner can probe for any of those because the bug has a signature — a string, a pattern, a state change tied to a known shape of input. Business logic flaws have none of that. The HTTP request looks identical to a request the application receives a thousand times an hour from honest customers. The payload is well-formed JSON, the session is authenticated, the route is one the developer wrote on purpose. There is nothing to grep for.
What changes is the intent. The application was designed assuming a checkout would visit /cart, /address, /payment, /confirm in that order, once each. That quantity would be positive and a SAVE10 coupon would be applied at most once per order. That two people would not redeem the same single-use gift card in the same five-millisecond window. None of those assumptions are written down in the request schema, the OpenAPI spec, or the WAF ruleset — they live in the developer's head, which is exactly where attackers go looking. CWE-840 (Business Logic Errors) and CWE-841 (Improper Enforcement of Behavioral Workflow) cover the territory, and the new OWASP Top 10 for Business Logic Abuse 2025 finally gives the class the dedicated taxonomy it has needed for a decade. The OWASP Top 10:2025 refresh renames this category as A06:2025 — Insecure Design and elevates it alongside the new dedicated OWASP Top 10 for Business Logic Abuse 2025.
This is why business logic testing demands a different mindset than the rest of the OWASP Top 10. You cannot detect a flaw you do not understand. Before you can break a checkout, you have to know what an honest checkout looks like — what state the server tracks, what fields the client controls, which guardrails were added and which were skipped. That is why these bugs pay the highest bounties on HackerOne ($25K-$150K at Shopify, up to $500K critical elsewhere): the gap between commodity scanning and elite manual testing is widest here.
In June 2023, a researcher reported HackerOne #1849626 to Stripe. The flaw was almost embarrassingly small. Stripe's promotion code API accepts a max_redemptions parameter that merchants use to cap how many times a code can be used — usually 1 for one-shot promo codes. The redemption endpoint does the obvious sequential check: load the code, count redemptions, accept if count < max_redemptions, increment, commit. Five lines of code, executed millions of times a day. The check works perfectly when requests arrive one at a time.
Here is what the researcher did:
max_redemptions=1, observed the normal redemption request, captured the JWT and the body. A scanner sees one POST. The attacker sees the check and the use — two operations separated by a tiny race window.count < 1 check before any of them got a chance to increment the counter.A DAST scanner running against the same endpoint would have sent one POST, gotten one 200, and moved on. No payload to fuzz, no header to inject, no suspicious parameter. Every individual request was valid by Stripe's own semantics. The bug exists only at the intersection of a state-changing endpoint, a concurrency guarantee the developer assumed but never enforced, and an attacker willing to send 30 requests at once. That is the texture of the whole class.
The same primitive — concurrent requests against a single-use resource — keeps showing up. Instacart HackerOne #157996 (coupon stacking), Reverb HackerOne #759247 (gift card redemption), Dropbox HackerOne #59179 (Pro promo code), and Tesla 2020 (paid vehicle software) are all variants of the same race. Once you can recognize the shape, you find it everywhere.
Business logic flaws span dozens of named patterns, but the BreachVex /learn library organizes them around the six classes that dominate real-world bounty reports. Each has its own deep dive with payloads, detection logic, and remediation patterns.
The server trusts a client-supplied price field instead of recomputing the cost from a server-side catalog. Sending {"product_id": 123, "price": 0.01} against a $99.99 product is the canonical example, demonstrated in PortSwigger's "Excessive Trust in Client-Side Controls" lab and in HackerOne #614523 (Eternal). Variants include archived-price replay (Stripe HackerOne #1328278) and currency switching. Deep dive: Price Manipulation
The server accepts quantity=-5, amount=-1000, or signed numerics that flip the sign of an order total or transfer. CVE-2023-45854 (Shopkit 1.0, CVSS 7.5) wrapped qtd=2147483648 past INT32_MAX into negative territory, and HackerOne #364843 (Upserve/OLO) accepted negative line items that drove the order total to zero. Deep dive: Negative Quantity Abuse
The deduplication check only looks at the immediately previous coupon, not the full application history. PortSwigger's "Flawed Enforcement of Business Rules" lab demonstrates the alternating ABA pattern: SAVE10, then WELCOME5, then SAVE10 again — second SAVE10 accepted. The "infinite money" variant chains a discount with a refundable gift card. Bounty: HackerOne #157996 (Instacart). Deep dive: Discount Stacking
Direct POST to /checkout/confirm from a fresh session, skipping /cart, /address, and /payment. The app fails to enforce step ordering server-side, often relying on a hidden form field or client SPA state. HackerOne #271176 (Lyst), HackerOne #92481 (Shopify), and Magento2 issue #39145 all hit variants. Deep dive: Workflow Step Bypass
Per-user, per-day, or per-account caps that exist client-side but are not enforced server-side — or are enforced sequentially in a way that concurrent requests break. SMS spam via repeated OTP, free-tier API quota bypass, trial reactivation by recreating the account. The function_use_limit family in OWASP testing guidance. Deep dive: Limit Override
Float arithmetic and per-line-item rounding let attackers shave fractions of a cent across hundreds of items, or inflate refunds via reverse rounding. Devastating in fintech and crypto exchange contexts where a $0.001 edge multiplied by trading volume becomes real money. Deep dive: Currency Rounding Abuse
Every traditional DAST scanner — ZAP, Burp Scanner, Nuclei, Acunetix, Netsparker — works the same way at heart: crawl the app, list endpoints, replay each one with fuzz payloads, and watch the response for a known signature. That model is excellent for SQL injection (SQL syntax error), XSS (canary in the DOM), SSRF (OOB DNS hit on Burp Collaborator). It is structurally incapable of finding business logic flaws — not because of a bug or a missing feature, but because business logic exploits are not malformed inputs, they are well-formed sequences. The scanner does not have a hypothesis to test.
Three things compound the problem. Proof requires state verification: confirming "negative transfer credited the account" means reading the balance before and after, which requires tracking application-specific state across requests the scanner does not model. Multi-step workflows routinely require 3-10 coordinated requests in a specific order with specific data, and single-request fuzzing cannot reproduce them. Race conditions are timing-dependent: detection requires HTTP/2 single-packet attacks instead of sequential requests, which most commercial scanners still do not implement.
A new generation of tools has started to close the gap. Pynt replays captured API traffic with mutations. Escape runs a feedback-driven engine they call BLST (Business Logic Security Testing). StackHawk's BLT module ships in 2025 with similar ambitions, and AppCheck uses GoScript Flows to model multi-step user journeys. All four are real progress, and all four still require significant human configuration. The honest summary is that business logic detection in 2026 sits where DAST sat in 2008: practical for a handful of well-known patterns, useless for the long tail. BreachVex's business-logic engine sits at that same frontier — competent on the named classes, dependent on a custom YAML rules engine and AI-driven workflow analysis for the per-application long tail.
Manual business logic testing follows a methodical workflow. The order of operations matters more than the tooling.
Step 1 — Map the workflow. Browse the app as an honest user. Use Burp's passive scanner and sitemap to capture every request a complete journey makes — registration, login, search, cart, checkout, post-purchase. The output is a sequence diagram, not a list.
Step 2 — Identify state-changing endpoints. Filter the sitemap to POSTs, PATCHes, PUTs, and DELETEs. These are the endpoints worth attacking. GETs are for discovery and verification.
Step 3 — Test sequence violations. Replay the workflow with steps removed (skip payment, jump to confirm), repeated (same coupon twice in a row, twice with another between, three times alternating), and out of order. HTTP 200 with a real success body is your signal.
Step 4 — Test arithmetic bounds. For every numeric field — quantity, amount, price, count, limit — replay with -1, 0, 2147483648 (INT32_MAX+1), 9223372036854775808 (INT64_MAX+1), NaN, Infinity, 0.000001. Watch for 200s that should have been 400s.
Step 5 — Test concurrency. Pick endpoints that mutate single-use resources (coupon redemption, gift card application, OTP submission, role transition, withdrawal). Send 15-30 identical requests in a single HTTP/2 multiplexed burst using Burp Turbo Intruder's engine=HTTP2 mode or h2spacex for the single-packet attack. Look for success_count > 1 with inconsistent status codes — the fingerprint of a TOCTOU race.
The single-packet attack is the most underrated technique in modern web pentesting. James Kettle's 2023 paper demonstrated that HTTP/2 multiplexing collapses concurrent request jitter from milliseconds to microseconds, turning previously theoretical race conditions into reliable, programmatically detectable findings. Every race condition test in 2026 should use it by default.
The defense is architectural and lives in four principles. Each deserves its own deep dive (see the pillar page for the full Prevention section), but the headline rules are:
Server-Side Authority. Never trust a client-supplied price, total, discount_amount, is_admin, role, tenant_id, or any value that affects authorization or money. Recompute every financial total from a server-side catalog. Strip any extra fields from incoming JSON before binding to your model.
Atomic State Transitions. When a request mutates shared state (coupon redemption, balance withdrawal, role grant), wrap the read-check-write sequence in a database transaction with SELECT ... FOR UPDATE or an equivalent advisory lock. The transaction must be the smallest possible scope around the actual mutation.
Idempotency Keys. Require an Idempotency-Key header on every state-changing request. Store the key + response in a TTL'd cache; on duplicate keys within the window, replay the original response instead of executing again. This neutralizes the entire race-condition class as a side effect.
State Machine Enforcement. Model multi-step workflows as an explicit state machine on the server. Every transition is a method that asserts the current state. Skipping a step becomes a 403 Forbidden instead of a silent acceptance.
Here is principle 4 made concrete in Python:
class CheckoutState(str, Enum):
CART = "cart"
ADDRESS_PROVIDED = "address_provided"
PAYMENT_AUTHORIZED = "payment_authorized"
CONFIRMED = "confirmed"
ALLOWED_TRANSITIONS = {
CheckoutState.CART: {CheckoutState.ADDRESS_PROVIDED},
CheckoutState.ADDRESS_PROVIDED: {CheckoutState.PAYMENT_AUTHORIZED},
CheckoutState.PAYMENT_AUTHORIZED: {CheckoutState.CONFIRMED},
CheckoutState.CONFIRMED: set(),
}
def transition(order_id: int, target: CheckoutState) -> None:
with db.transaction():
order = db.orders.select_for_update(id=order_id) # row lock
if target not in ALLOWED_TRANSITIONS[order.state]:
raise Forbidden(f"Cannot go from {order.state} to {target}")
order.state = target
order.save()The select_for_update pairs principles 2 and 4: even if two concurrent requests both try to advance the same order from PAYMENT_AUTHORIZED to CONFIRMED, only one will hold the lock, and the second will see the updated state and correctly fail. This is the same pattern PostgreSQL ships in its documentation; the bug is almost always forgetting to use it, not lacking the primitive.
Pick your deep dives by the application you are about to test:
For the comprehensive cross-class reference with full prevention principles, see the Business Logic Vulnerabilities pillar. To operationalize this against your own targets, the BreachVex scanner brings dedicated business-logic detection across all six variant classes, a custom YAML rules engine, and AI-driven OpenAPI workflow analysis — exactly the long tail that commodity DAST cannot reach.
Technical vulnerabilities like SQL injection or XSS exploit malformed inputs that the application fails to sanitize — a single payload triggers the bug. Business logic flaws use perfectly well-formed requests in unintended sequences or combinations: applying the same coupon twice, sending a negative quantity, or skipping the payment step. The request looks legitimate to any scanner, which is why six dedicated variants exist (price manipulation, negative quantity, discount stacking, workflow bypass, limit override, currency rounding) and why they require workflow understanding rather than payload fuzzing.
Traditional DAST tools (ZAP, Burp Scanner, Nuclei, Acunetix) work by replaying endpoints with fuzz payloads and watching for known signatures — SQL errors, XSS canaries, OOB DNS hits. Business logic exploits are not malformed inputs; they are valid requests in invalid sequences. Detection requires modeling application state across multiple coordinated requests, recognizing that 30 successful redemptions on a one-shot coupon is a bug, and understanding what an honest checkout looks like. New tools like Pynt, Escape BLST, and StackHawk BLT are progressing but still need significant human configuration.
Start by browsing the target application as an honest user — register, log in, search, add to cart, checkout — and capture every request with Burp Suite. Then read the six variant deep dives in this library: price manipulation, negative quantity, discount stacking, workflow bypass, limit override, and currency rounding. Practice on PortSwigger's Web Security Academy labs, which include free business logic exercises mapped to each class. The mindset shift is harder than the tooling: you need to understand what the application is supposed to do before you can break it.
Price manipulation and workflow step bypass dominate real-world bounty reports in e-commerce, while discount stacking via race conditions tops fintech reports. The Stripe HackerOne #1849626 case (30 redemptions on a max_redemptions=1 coupon via HTTP/2 single-packet attack) shows how race conditions amplify limit-override flaws into six-figure bounties. For SaaS platforms, workflow step bypass and per-tier quota escapes are the most reported. The /learn library has dedicated deep dives for all six dominant classes.
Manual business logic testing takes 4-40 hours per target depending on complexity — significantly longer than automated scanning. Mapping the workflow alone (Step 1 of the methodology) takes 1-3 hours on a non-trivial application. Race condition testing requires Burp Turbo Intruder setup and HTTP/2 single-packet attack verification. The payoff is that these flaws pay $25K-$500K bounties because manual depth is the only way to find them, which is exactly why elite pentesters specialize in this class while DAST scanners stay blind.
The core toolkit is Burp Suite Professional (Repeater, Turbo Intruder for HTTP/2 single-packet attacks, Logger++) plus Postman or HTTPie for API replay. Specialized tools include h2spacex for race conditions, Pynt and Escape BLST for automated workflow testing, and StackHawk BLT (2025) for CI integration. BreachVex covers all six variant classes through a dedicated business-logic detection capability, plus a custom YAML rules engine and AI-driven OpenAPI workflow analysis for the long tail that commodity DAST cannot reach.
Yes — business logic vulnerabilities map to OWASP A04:2021 (Insecure Design) and the OWASP Top 10 BLA 2025 catalog, with CVSS scores ranging from 6.5 (medium) to 9.8 (critical) depending on impact. Real-world examples include CVE-2023-45854 (Shopkit, CVSS 7.5, integer overflow on quantity) and CVE-2025-31161 (fintech rounding abuse). The HackerOne bounty range ($25K-$500K) reflects the severity: these bugs typically result in direct financial loss, data integrity compromise, or full authorization bypass, with no payload to detect in WAF logs.