Introduction
Authentication mechanisms often include defensive features like IP blocking and account lockouts. However, when implemented incorrectly, these protections can create new attack surfaces. In this article I explore two PortSwigger labs that demonstrate how flawed authentication defenses can leak information and allow attackers to bypass protections. I’ll show the exact Burp setups, payload logic, and the little gotchas that tripped me up — plus screenshots and proof-of-concepts so you can reproduce the steps in your own lab environment in a follow-along style.
Preparation
The ethical hacking labs demonstrated herein were done in a sandboxed environment provided through PortSwigger free-of-charge. Do not run attacks out in the wild on a target without explicit permission. Consider also that you should be careful to not run noisy or destructive tests against production or third-party hosts. Several of these tests rapidly generate a ton of traffic on servers. These labs are at around an intermediate level, to follow along you’ll need familiarity with:
- Burp’s tooling: Proxy, Repeater and Intruder
- Linux fundamentals
- Web fundamentals: HTTP(S), common status codes, cookies/sessions, headers, HTML forms
- Basic scripting knowledge (bash/python)
Lab 1: Exploiting IP Block Logic Flaws
TL;DR: Found an IP-block bypass by alternating valid and invalid logins to reset failure counters, which allowed a remote brute-force against a target account; validated the exploit by obtaining account access.

The first lab we’ll dig into is broken brute-force protection, IP block. This lab scenario assumes that you already have working login credentials (wiener:peter), have already enumerated a known username, carlos, and you have a list of dumped passwords to try against the app. There are plenty of methods developers use to help reduce the capacity for malicious actors to brute-force their web applications, and one common one is to block an IP address that makes too many failed attempts within a span of time.
To start with, we’ll trigger a normal login:

This looks pretty normal in our HTTP history:

Next, send the Login POST request (#27 in the screenshot) over to repeater and we’re going to try logging in a few times with invalid credentials. Change out the last line of the request body to carlos and any random password:

As expected we get a normal failed login message after sending it:

Spamming it a few more times results in a notice we’ve tried too many times. It is now throttling us based on our IP address:

Where this application is crucially flawed though, is that if a malicious actor logs in with valid credentials, it resets the failure counter from one to zero if the user then logs in with valid credentials. I did a test of valid and then invalid credentials five times in Repeater and found that I was able to bypass the IP blocking. This can be readily expoited with clever crafting of an attack sequence.
In essence, we first want to build out a list of usernames that looks like this:
- correct user
- victim user
- correct user
- victim user
- … and so on
We also need a list of potential passwords that looks like:
- correct user password
- victim password attempt 1
- correct user password
- victim password attempt 2
- … and so on
One quick and easy way we can hack this username list together is with a short bash script that will produce an interleaved list for us. We’ll loop 100 times (since we already know our password list we’re brute forcing with has 100 potentials) and push carlos and wiener onto separate lines. When this is done, it shoots it out into a text file:
for i in $(seq 1 100); do
echo "carlos"
echo "wiener"
done > usernames_interleaved.txtTo produce our password list, we’ll use a short awk script to open our password list file and insert the known correct password after each line:
awk -v ins=peter '{print; print ins}' passwords > passwords_interleaved.txtHere is how both files look after running both scripts:

The next step is to send the POST login request from over to Intruder and surround our username and password fields with the section signs:

Intruder has several types of attacks that can be used to brute force. We’re going to use a pitchfork attack here because we’re testing two fields from the request in parallel. Under the payloads, change field one to be a simple list and upload the username file we created:

Repeat for the password payload position:

The next setting to change is the Resource pool. We want our attacks to be forced to always go in the order our files designate. Set the maximum number of concurrent requests to one:

Launch the attack!
Consider that when a correct login happens we are returned a 302, forcing a redirection. All of our wiener:peter successes issue 302‘s as expected:

If we filter on Status code and look at all the 302‘s, eventually near the end of the attack we have one for carlos that redirected to /my-account?id=carlos:

Let’s try out our new found credentials carlos:monitor

And, we’re in:

Lab 1: Application Protection
Protect your users by treating account and IP protections as separate, authoritative signals. Track failed attempts per account (not just per IP), apply progressive back-off or temporary lockouts on the account itself, and never let a successful login for one account reset counters for other accounts. Return identical responses for “invalid credentials” and “account locked” to stop username enumeration, and require CAPTCHA or step-up authentication (MFA) on suspicious or repeated attempts.
Lab 2: How Account Locks Reveal Valid Users
TL;DR: The account lockout response leaked which usernames exist, so I used a cluster bomb with controlled null payloads to trigger lock messages and identify a valid user, then brute-forced the password.

The next lab we’ll look at is username enumeration vis account lock. The protection in this web app is implemented in a way that will lock accounts after too many unsuccessful tries. Ironically, it is this protection attempt that also reveals to a malicous actor through reconnaissance which accounts actually exist at all.
To start with, we’ll send a failed login deliberately with fail:fail as the credentials:

Send the POST login request to Intruder and select a cluster bomb attack. This type of attack iterates through all possible combinations without worrying about the parallelism of the pitchfork attack seen in the previous lab example.
Surround the username in the section sign (§) and place another set after the password:

The reason we’re not actually surrounding the password is because we will iterate the usernames with a list we have, but also issue a null payload that we can control the quantity of (to intentionally trigger the account lock after, in this example, five incorrect tries). Set payload one to a simple list and load the username list provided by PortSwigger (be careful not to upload/paste the interleaved list we just created in the last example):

For payload two, set the type as null and adjust the quantity to five:

Send the attack:

The responses as the list runs make sense and tell us that we’re submitting invalid usernames/passwords:

But… once the attack is done, sorting by the response length shows that there is an account that returned two responses of “You’ve made too many incorrect login attempts” (also alerting us that account locks show up after three failed attempts, granting more recon data):

Now we know that a valid username in this case is anaheim. We can now go back to Intruder, change our attack type to a sniper attack, surround the password in the selection sign, load our password list and brute-force the password. Here is how the whole setup appears:

Run the attack. Sort by the length and one of the responses does not return an “Invalid username/password” error, it has a different length and is our target password credential:

Let’s try out those credentials we mined of anaheim:1234:

And we’re in:

Lab 2: Application Security
This lab showed how an account lock can unintentionally reveal valid usernames, so the simplest fix is to stop leaking that signal. Always return a uniform response for invalid credentials and locked accounts, with the same status code, body size, text, and similar timing so attackers cannot distinguish hits by response content or length. Enforce per-account throttling and progressive backoff, keep IP rate limits as a separate and independent control, and introduce CAPTCHA or step-up MFA for suspicious flows. Notify users out of band about account locks, log auth attempts with account and IP context, and alert on abnormal spikes. Add automated tests that simulate enumeration and interleaved attacks to validate your defenses.
Wrapping Up
This post walked through two authentication labs from PortSwigger, covering an IP-block bypass via interleaved requests and username enumeration via account lockouts. I used Burp Intruder, small scripts, and targeted wordlists to reproduce each issue and suggested application development fixes: per-account throttling, uniform error responses, progressive backoff or CAPTCHA with MFA for suspicious flows, plus logging and alerting. I will be covering more BSCP tracks, so check back for the next one.

