| Field | Detail |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | Authentication — Broken Brute-Force Protection |
| Difficulty | Practitioner |
| Objective | Brute-force the victim's password, then log in and access their account page |
| Note | Own credentials wiener:peter, victim username carlos |
Broken Brute-Force Protection, IP Block¶
The app rate-limits login attempts, so I started by confirming exactly how the protection works.
After a few failed attempts against carlos, the app returned "You have made too many incorrect login attempts. Please try again in 1 minute(s)." Switching to wiener:peter, logging in successfully, then going back to carlos with a wrong password reset the counter entirely. That confirmed two things: the block is tracked per source IP rather than per account, and a single successful login from that IP clears it. The reset condition is the actual flaw — not the rate limiting itself.
That means interleaving a successful wiener:peter login between every couple of carlos attempts keeps the block from ever triggering. The challenge is automating it cleanly, which requires two aligned wordlists so Intruder's pitchfork attack sends username and password in lockstep.
For the username list, a short Python script alternates wiener into roughly every third slot with carlos filling the rest:
for i in range(0,200):
if i % 3 == 0:
print("wiener")
else:
print("carlos")
The password list follows the same alternation pattern — peter for the wiener slots, actual candidates from the wordlist for the carlos slots, keeping the two lists aligned line-for-line:
with open("passwords") as f:
lines = f.readlines()
i = 0
for line in lines:
if i % 3 == 0:
print("peter")
else:
print(line.strip())
i += 1
With both wordlists ready, I intercepted a login request and sent it to Intruder as a pitchfork attack with the two lists paired against the username and password fields.
I created a new resource pool with maximum concurrent requests set to 1. This is what makes the alternating pattern actually work — parallel or out-of-order requests would break the interleaving the attack depends on, since the reset needs to land between specific attempts, not just somewhere in the batch.
The results showed several 302s for wiener:peter (expected — those are just the reset logins doing their job) and exactly one 302 for carlos.
The password for carlos was ranger. Logging in as carlos:ranger landed on the account page.
Lab solved