Vulnerability
[FAIL] Unrestricted Resource Consumption - Authentication - Login Rate Limiting
Description
The /login endpoint applies no rate limiting, account lockout, progressive delay, or CAPTCHA. Consecutive failed login attempts are all served at full speed. Failure responses also differ by condition, making username enumeration easier.
Impact
Confidentiality (Medium): an attacker can brute force and credential stuff any account, including administrator, without restriction. Fifty consecutive attempts against the admin account triggered neither lockout nor delay, and after all those failures a login with the correct credentials was still accepted, proving there is no lockout mechanism at all.
CWE
CWE-307: Improper Restriction of Excessive Authentication Attempts
OWASP
A07:2021 - Identification and Authentication Failures
Scenario
Normal: one login attempt with a wrong password is rejected with HTTP 401. Exploitation: fifty consecutive attempts against the admin account are sent without delay -> all are served HTTP 401, with no HTTP 429 and no lockout. Impact: after 50 failures, logging in with the correct password still succeeds, showing the account is never locked so brute force can run to completion.
Endpoint
POST http://127.0.0.1:5001/login
Affected feature
User authentication
Remediation
- Apply per-account and per-IP rate limiting with progressive delay.
- Temporarily lock the account or require CAPTCHA after a number of consecutive failures.
- Standardize error messages so they do not disclose username existence.
- Log and alert on spikes in authentication failures, and enforce MFA.
Proof of concept
- (Screenshot 1) NORMAL: one login attempt with a wrong password -> HTTP 401 Invalid credentials.
- (Screenshot 2) EXPLOITATION: 50 consecutive failed attempts against the admin account -> all HTTP 401, with no HTTP 429 and no lockout.
- (Screenshot 3) CIA IMPACT: after 50 failures, logging in with the correct password is still accepted -> the account is never locked, so brute force can run until it succeeds.
The complete record
LAB All targets are lab systems. Nothing here is a client engagement. Evidence is shown as recorded; secrets are blacked out.
HTTP request
POST /login HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Content-Length: 53
Content-Type: application/json
{"username": "admin", "password": "[REDACTED-LAB-VALUE]"}
HTTP response
HTTP/1.1 401 UNAUTHORIZED
Content-Type: application/json
Content-Length: 162
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 03:12:54 GMT
{
"debug_info": {
"attempted_username": "admin",
"time": "2026-09-30 03:12:54.415082"
},
"message": "Invalid credentials",
"status": "error"
}
Test login rate limiting
- Consecutive failed logins for user 'admin' attempt 1 -> HTTP 401
attempt 2 -> HTTP 401
attempt 3 -> HTTP 401
attempt 4 -> HTTP 401
attempt 5 -> HTTP 401
...
attempt 46 -> HTTP 401
attempt 47 -> HTTP 401
attempt 48 -> HTTP 401
attempt 49 -> HTTP 401
attempt 50 -> HTTP 401
HTTP request
POST /login HTTP/1.1
User-Agent: python-requests/2.33.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: */*
Connection: keep-alive
Content-Length: 45
Content-Type: application/json
{"username": "admin", "password": "[REDACTED-LAB-VALUE]"}
HTTP response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 470
Set-Cookie: token=[REDACTED-LAB-TOKEN]; HttpOnly; Path=/
Access-Control-Allow-Origin: *
Server: Werkzeug/2.0.1 Python/3.9.25
Date: Wed, 30 Sep 2026 03:12:56 GMT
{
"accountNumber": "ADMIN001",
"debug_info": {
"account_number": "ADMIN001",
"is_admin": true,
"login_time": "2026-09-30 03:12:56.612726",
"user_id": 1,
"username": "admin"
},
"isAdmin": true,
"message": "Login successful",
"status": "success",
"token": "[REDACTED-LAB-VALUE]"
}
← Previous case: Unrestricted File Upload - Profile Picture - File Type Validation Next case: Unrestricted Resource Consumption - Password Reset - PIN Rate Limiting →