> For the complete documentation index, see [llms.txt](https://ganesha-hk.gitbook.io/cybersecurity-writeups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ganesha-hk.gitbook.io/cybersecurity-writeups/port-swigger-authentication-labs/lab-4.md).

# lab-4

## Broken Brute-Force Protection, IP Block

**Difficulty:** Practitioner

### Lab Description

This lab is vulnerable due to a logic flaw in its brute-force protection. The server blocks an IP after a set number of failed login attempts, but there is a way to reset that counter without changing IP.

**Credentials:** `wiener:peter`\
**Victim's username:** `carlos`\
**Wordlist:** [Candidate passwords](https://portswigger.net/web-security/authentication/auth-lab-passwords)

**Objective:** Brute-force carlos's password and access their account page.

### Solution

**Step 1 — Understand the flaw**

Log in as `wiener` and capture the login request. Test in Repeater how many failed attempts are allowed before the IP is blocked — in this lab it's **2 consecutive wrong attempts**. Headers like `X-Forwarded-For` are not respected by the backend, so IP spoofing won't work here.

However, there is a logic flaw: **a successful login resets the failed attempt counter for that IP.** This means if a valid login is slipped in after every 2 failed attempts, the counter never reaches the block threshold.

**Step 2 — Generate interleaved credential lists**

The attack needs two wordlists that alternate between `carlos` attempts and a reset login as `wiener` after every 2 tries.

Use the following Python scripts to generate them.

**Username list** (repeating the pattern `carlos → carlos → wiener`):

```python
# Adjust the range to cover the full password list length
for i in range(50):
    print("carlos")
    print("carlos")
    print("wiener")
```

**Password list** (inserting `peter` after every 2 candidate passwords):

```python
password_file = "passwords.txt"
with open(password_file, "r") as f:
    passwords = [line.strip() for line in f if line.strip()]

count = 0
for password in passwords:
    print(password)
    count += 1
    if count == 2:
        print("peter")
        count = 0
```

> **Note:** If the IP block triggers after a different number of attempts in your instance, adjust the scripts to insert the reset credentials at the appropriate interval.

**Step 3 — Run the Pitchfork attack**

Send the login request to Intruder and select **Pitchfork** attack mode. Set both the username and password fields as payload positions, then load the generated username list and password list into their respective payload slots.

Start the attack and sort by status code. A `302` redirect among the `carlos` rows reveals the correct password. Log in with it to complete the lab.

### Conclusion

The IP block protection counted consecutive failed attempts but reset on any successful login — including logging in as a different, known-good account. By weaving a valid login between every pair of attack attempts, the block counter was continuously reset, effectively nullifying the rate-limiting defence.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://ganesha-hk.gitbook.io/cybersecurity-writeups/port-swigger-authentication-labs/lab-4.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
