0-click-ATO — Account Takeover via OTP Brute Force + Email Case-Sensitivity Bypass
Exploit a case-sensitive OTP tracking system to brute-force the admin password reset
Learn to find this bug
This bug paid $600 on YesWeHack.
Create your account and practice real bugs that got paid. Download the environment, find it and learn the exact technique — your path to your first bounty.
hunters training
labs from real hacks
completions
paid out for these bugs
Access to all labs · no commitment · cancel anytime
Hackers who solved it· 8
Objectives
Achievement you'll earn
Solve this lab to unlock this shareable achievement
0-click-ATO — Account Takeover via OTP Brute Force + Email Case-Sensitivity Bypass
Community writeups
Deployment
# Using autodeploy
bash autodeploy.sh
# Or manually
docker compose up --build -d
Access the lab at: http://localhost:1337
Attack Chain
1. Login as user@securecam.io
2. Use "Invite to Camera Group" to discover admin@securecam.io
3. Analyze password reset flow:
- POST /api/auth/forgot-password → generates 4-digit OTP
- POST /api/auth/reset-password → email + new_password + otp
- 4 failed attempts → email blocked
4. Discover: OTP tracking is case-sensitive, password reset is case-insensitive
5. Write Python3 exploit:
- Generate email case variants (admin@, Admin@, aDmin@, etc.)
- For each variant: request OTP → try 4 codes → move to next variant
6. Run exploit → password reset → login as admin → flag
Vulnerability Details
The server stores OTP attempts using the exact email string as the key (case-sensitive).
After 4 failed attempts for a specific email string, it is blocked.
However, the actual user lookup for password reset uses LOWER(email) = LOWER(?),
meaning all case variants of the email update the same user's password.
This means each case variant of the admin email (e.g., Admin@securecam.io, aDmIn@securecam.io)
gets its own 4 fresh OTP attempts, effectively bypassing the rate limit.
With 16 alphabetic characters in admin@securecam.io, there are 2^16 = 65,536 possible case variants,
each with 4 attempts = 262,144 total attempts, far exceeding the 10,000 possible 4-digit OTP codes.
Remediation
- Normalize email before OTP tracking: Convert to lowercase before storing in the OTP rate-limit store
- Global rate limiting: Implement rate limiting based on the normalized email, not the raw input
- Longer OTP codes: Use 6+ digit codes to increase the keyspace
- Account lockout: Lock the actual account after N failed reset attempts (not just the email variant)
- Time-based OTP expiration: Expire OTPs after a short window (e.g., 5 minutes)
Interactive Writeup
Access the step-by-step interactive writeup at: http://localhost:1337/writeup
Attribution
Bug Bounty Labs by @_.gorkaaa.b