The automated scan on a checkout flow came back with nothing above low. No SQLi, no reflected XSS, no broken authentication, every OWASP Top 10 box quietly green. A junior pentester on the engagement was ready to write "no significant findings" and move to the next target, and honestly, most scanners would have agreed with them. Then someone on the team noticed the "apply discount code" endpoint didn't invalidate a single-use code until after the order confirmed, and asked the question a scanner never asks: what happens if you send this request twenty times before any of them finish?
The answer was a single-use 50% code getting redeemed forty times in under a second, using nothing more exotic than sending the same request concurrently. No injection, no broken access control in the traditional sense, just a gap between "check if this code is valid" and "mark this code as used" wide enough to drive a cart full of orders through.
This is the category automated scanning structurally cannot find, because a scanner tests requests one at a time. A race condition only exists in the space between two requests, and if you never send two requests at once, the vulnerability never has a chance to show itself.
Find the endpoints where it can happen. Anywhere there's a check-then-act pattern is a candidate: redeem a coupon, apply a referral bonus, withdraw a balance, submit a vote, claim a limited inventory item. The tell is a read (is this still valid?) followed by a write (mark it used) as two separate steps instead of one atomic operation. Most business logic that touches money or scarcity is built exactly this way, because it's simpler to write and code review rarely questions it.
Send the requests at the same instant, not just quickly. A normal script firing requests in a loop still has network jitter between them, enough that the server processes most of them sequentially anyway and the race never triggers. Tools built for this (Burp's race condition tab, Turbo Intruder) send every request within the same TCP connection or the same packet, collapsing that window to as close to zero as HTTP allows.
Read the responses for overlap, not just success codes. A 200 on all forty requests doesn't prove anything by itself, plenty of race conditions still return a clean success response on requests that shouldn't have gone through. Check the actual state afterward: how many times did the code apply, did the balance go negative, did the inventory count drop below zero. The proof is in the end state, not the response codes.
None of this shows up in a report generated by a scanner running one request at a time, however thorough the coverage looks. It's the kind of finding that only surfaces when someone stops trusting a clean scan and starts asking what the application does under concurrency instead of in sequence.
Codelivly's Advanced Web Application Penetration Testing Book (L2) is built entirely around this gap, business logic and chained exploitation past what any scanner catches. The free Race Condition lab lets you fire this exact attack at a real vulnerable endpoint, and the NimbusOps Kill Chain CTF chains several logic flaws together the way a real engagement actually plays out.
Top comments (0)