Modern applications often rely on rate limiting to stop brute-force login attacks. But what happens when that protection is implemented incorrectly?
In this lab, I exploited a race condition in the login mechanism to bypass rate limits and successfully brute-force a user's password.

Lab Link: Here
Objective
The goal of the lab was to:
- Bypass the login rate limit using a race condition
- Brute-force the password for the user carlos
- Log in to the admin panel
- Delete the user carlos
The application already provided valid credentials for a low-privileged user:
wiener : peterUnderstanding the Vulnerability

The application attempted to prevent brute-force attacks by introducing a time delay after multiple failed login attempts.
However, the rate-limiting logic had a flaw:
It tracked login attempts sequentially, but did not handle multiple simultaneous requests correctly.
This means if several login attempts are sent at the same time, the server fails to update the attempt counter fast enough, allowing multiple guesses before the lockout triggers.
This is a classic race condition.
Step 1 — Capture a Login Request
Using Burp Suite:
- Log in as
wiener:peter - Log out
- Intercept a login request in Burp Proxy
The request looked like:
POST /login HTTP/2
username=carlos&password=wrongpassI highlighted the password parameter and sent this request to Turbo Intruder.

Step 2 — Launching the Race Condition Attack
Turbo Intruder allows us to send multiple HTTP/2 requests in parallel, which is key to exploiting race conditions.
Here's the script used:
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.BURP2
)
passwords = wordlists.clipboard
for password in passwords:
engine.queue(target.req, password, gate='1')
engine.openGate('1')
def handleResponse(req, interesting):
table.add(req)🔍 What This Script Does
- Uses HTTP/2 single-packet attack mode
- Queues all password attempts
- Releases them simultaneously
- Overwhelms the rate-limiting logic before it can react
Step 3 — Finding the Correct Password
The lab provided a list of potential passwords.
When the attack ran:
- Most responses returned the normal login failure
- One response returned HTTP 302 (redirect)
That indicated a successful login.
Step 4 — Accessing the Admin Panel
Using the valid credentials for carlos, I logged in and gained access to the admin panel.
From there, I deleted the user carlos, which solved the lab.
Why the Rate Limiting Failed
Normally, login protection works like this:
- Failed login → counter = 1
- Failed login → counter = 2
- Failed login → counter = 3 → lock account
But with parallel requests:
- Multiple login attempts hit the server at the same time
- The counter isn't updated fast enough
- Several guesses slip through before lockout
This is called state desynchronization, where security logic depends on timing and fails under concurrency.
How to Prevent This in Real Applications
Developers should:
- Apply rate limits per session and per IP
- Use atomic operations when updating login counters
- Queue or serialize authentication attempts
- Add CAPTCHA or progressive delays
- Monitor for burst login attempts
Race conditions are subtle and often missed in traditional testing.
Key Takeaways
✔ Rate limiting alone is not enough ✔ Security logic must be safe under concurrency ✔ HTTP/2 parallelism can be weaponized ✔ Turbo Intruder is powerful for advanced web attacks
NB: If you're learning web security, this lab is a great example of how timing flaws can completely break authentication protections.