Broken brute-force protection, IP block

3 min read Medium PortSwigger
Contents

On this page

The lab

here is how portswigger describes it

This lab is vulnerable due to a logic flaw in its password brute-force protection. To solve the lab, brute-force the victim's password, then log in and access their account page.

Your credentials: wiener:peter
Victim's username: carlos

the timing lab beat the rate limit by spoofing a new ip on every request with X-Forwarded-For. this lab's protection is real and tied to our ip, but it has a logic flaw in how it clears the counter, and we abuse that flaw rather than hiding from it.

why a resettable counter defeats the block?#

the login here blocks your ip after three failed attempts in a row. that sounds solid until you ask what counts as in a row. the counter is tracking consecutive failures, and crucially a successful login resets it back to zero.

that reset is the flaw. we hold our own working credentials, wiener:peter, so we can hand the server a guaranteed success whenever we like. if we never let the failure count reach three before slipping in one good login, the counter keeps getting wiped and the block never triggers. so the plan is to interleave. guess carlos's password a couple of times, then log into our own account to reset the tally, then guess again, forever. we never hit three failures back to back, so the protection sits there doing nothing while we grind through the password list.

Step 1 - Build the interleaved wordlists#

the whole trick lives in how the two payload lists are lined up. we use a pitchfork attack, which walks both lists down in lockstep, taking the first item from each, then the second from each, and so on. that lets us pair a username with a password on every single request.

so we build the lists in a repeating pattern of three. the username list goes carlos, carlos, wiener, carlos, carlos, wiener, over and over. the password list is laid out to match, two real guesses from carlos's candidate passwords followed by peter, which is wiener's actual password.

usernames        passwords
carlos           123456
carlos           password
wiener           peter
carlos           12345678
carlos           qwerty
wiener           peter
...

read down the pairs and the rhythm appears. two carlos rows each try a fresh candidate password, then every third row is wiener:peter, a login we know will succeed and reset the failure counter to zero. because of that reset, carlos never accumulates three consecutive failures, so our ip is never blocked.

Step 2 - Set up the pitchfork attack#

we submit a junk login, capture the POST /login request, and send it to intruder. we switch the attack type to pitchfork and place a payload position on both the username and the password parameters, then load our two matched lists into positions one and two.

11111

Step 3 - Force the requests into order with a resource pool#

this attack only works if the requests arrive in the exact order we built, because the reset has to land between the guesses, not after them. by default intruder fires requests in parallel, which would scramble that ordering.

so we open the resource pool panel and set maximum concurrent requests to one. that forces intruder to send the attempts strictly one at a time in list order, so each wiener:peter reset falls exactly where we placed it, between every pair of carlos guesses.

22222

Step 4 - run the attack and find the password#

we start the attack and let it work through the list, the counter resetting every third request so the block never fires.

when it finishes we look at the results. the wiener rows all return 302 because that login always succeeds, so we ignore those. what we want is a 302 on a row where the username is carlos, since that is the one case where a carlos guess actually logged in. there is a single such row, and we read its password from the payload 2 column.

33333

we log in to carlos's account with that password and reach his account page.

44444

with this, the lab is solved!