Imagine you're testing an e-commerce application. You add a $2,000 laptop to your cart. You proceed to the shipping page, enter your address, and apply a 10% coupon code. But right before you hit the final "Confirm Payment" button, you open a second tab, remove the laptop, and add a $5 sticker.
You go back to the first tab and hit confirm. The application charges your card $4.50 (the sticker price minus 10%), but the fulfillment backend processes the order ID that was originally tied to the laptop.
Congratulations, you just bought a laptop for less than a cup of coffee.
This is a business logic flaw, and automated scanners will never find it.
The Goal & Scope Rules
Business logic flaws exist because developers assume users will interact with an application sequentially (Step A → Step B → Step C). As a bug bounty hunter or penetration tester, your entire goal is to break that assumption. You want to skip steps, perform them out of order, or perform them simultaneously (race conditions).
Always ensure you are testing authorized targets, and never exploit logic flaws against real financial gateways using stolen cards or production funds. Always use the provided test credit cards (like Stripe's 4242 testing numbers) in authorized sandbox environments.
The Workflow: Breaking the Sequence
Hunting logic flaws requires a fundamental shift in how you use your proxy. If you rely entirely on automated tools, you are wasting your time. You have to manually map the application's intended state machine.
Stage 1: The Happy Path
First, walk through the application exactly as the developer intended. Buy a product, create a user, upgrade a subscription, or transfer funds. Record the entire flow in Burp Suite or Caido. You need to understand how state is passed between requests. Are they using hidden form fields, cookies, JWTs, or server-side sessions?
Stage 2: State Manipulation
Once you know the happy path, start dropping and replaying requests out of order.
A technical whiteboard sketch illustrating a classic state bypass where a user skips the payment gateway verification step.
What happens if you go straight from /cart to /order-success without hitting /checkout? If the application relies on client-side state (like a hidden payment_status=true field) instead of verifying the transaction cryptographically on the backend, you just bypassed payment.
Stage 3: Race Conditions
Sometimes the logic is completely sound, but the implementation cannot handle concurrency.
If you have a $50 account balance and you attempt to transfer $50 to a friend, the application checks your balance, confirms you have $50, and executes the transfer. But what if you send that exact same transfer request 20 times within a single millisecond?
If the database doesn't properly lock the row during the balance check, all 20 threads might read your balance as $50 simultaneously, allowing you to transfer $1,000 out of a $50 account.
An architecture diagram showing multiple concurrent API requests hitting a payment gateway before the database can update the ledger state.
The Tools: Exploiting Concurrency
You can't test race conditions effectively by just clicking fast. You need precision. A popular way to test this in authorized labs is using Python's concurrent.futures to blast an endpoint, or by utilizing Burp Suite's Turbo Intruder.
Here is a practical Python snippet demonstrating how you might test a coupon redemption endpoint for concurrency flaws in a lab environment:
import requests import concurrent.futures # Target lab endpoint and session cookie URL = "http://127.0.0.1:8080/api/apply-coupon" HEADERS = {"Cookie": "session=eyJ1c2VyIjogInRlc3RfdXNlciJ9"} DATA = {"coupon_code": "WELCOME10"} def apply_coupon(
If you see multiple [+] Success! lines in your terminal for a single-use coupon, you've successfully exploited a race condition.
If you prefer staying inside your proxy, Turbo Intruder is built specifically for this. It uses a custom HTTP stack to put all the requests into a single TCP packet (Single Packet Attack), practically guaranteeing they hit the server at the exact same microsecond.
# Turbo Intruder script for Single Packet Attack (Race Condition) def queueRequests(target, wordlists): engine = RequestEngine(endpoint=target.endpoint, concurrentConnections=1, requestsPerConnection=100, pipeline=False
How Defenders Catch This
Defenders hate logic flaws because WAFs (Web Application Firewalls) cannot detect them. A logic flaw looks exactly like legitimate traffic. It isn't a SQL injection payload or a cross-site scripting tag; it's just a normal HTTP request sent at a weird time.
To fix this, developers must implement strict state machines. The backend must independently verify that Step B was successfully completed before allowing the user to access Step C. For race conditions, databases must use atomic operations, optimistic concurrency control, or row-level locking (e.g., SELECT ... FOR UPDATE in SQL) to prevent simultaneous transactions from reading stale data.
The Verdict
Hunting logic flaws is an art form. It requires deep intuition about how a specific business operates. While you can automate your Modern Web Recon Workflow or run API Hacking scanners to find injection points, logic flaws require you to sit down, map the application, and think like a developer who was in a rush on a Friday afternoon.
Take the time to understand the application's state, and you'll find the bugs everyone else's scanners missed.
Related Blogs
References / Further reading
- PortSwigger: Business Logic Vulnerabilities - PortSwigger Research
- PortSwigger: Race Conditions - PortSwigger Research
- OWASP Testing for Business Logic - OWASP
- Turbo Intruder Official Repository - PortSwigger



