Authentication Vulnerabilities: The Complete Practical Guide

Bypassing Rate Limits and Account Lockout

How naive brute-force protection is defeated: spoofing the source IP via forwarding headers, resetting the counter, and weaponizing lockout as a DoS.

Published

Rate limits and lockouts are what stand between a credential attack and success, and they are often built on something the attacker controls. If the throttle trusts a forwarding header, or counts the wrong thing, or can be reset, the protection evaporates and spraying and brute force resume at full speed. This page covers the common ways naive protection fails. Practice on a free live target.

How naive throttles are built

A login throttle has to answer one question: who is making these attempts? The naive answers are all spoofable or wrong:

  • By source IP, read from a forwarding header. Trusts a value the client sends.
  • By exact username string. Misses casing and formatting variants.
  • By a resettable counter. A successful or well-timed request clears it.

Each shortcut is a bypass waiting to happen. Your first job is to find out which one the target uses by triggering the limit and then varying one input at a time to see what resets the counter.

Step 1: find the key

Hit the limit deliberately, then probe what it tracks. Change only the source header and retry: if attempts flow again, it is IP-keyed on a header. Change only the username casing: if that gets a fresh allowance, it keys on the raw string. This tells you exactly what to forge.

Step 2: spoof the source header

If the throttle is keyed on a client IP taken from a forwarding header, forge it and rotate a fresh value every request so each attempt looks like a new client:

X-Forwarded-For: 10.0.0.<n>            (increment per request to reset the counter)
X-Forwarded-For: 127.0.0.1
X-Originating-IP: 127.0.0.1
X-Remote-IP: 127.0.0.1
X-Remote-Addr: 127.0.0.1
X-Client-IP: 127.0.0.1
X-Real-IP: 127.0.0.1
True-Client-IP: 127.0.0.1
CF-Connecting-IP: 127.0.0.1
Forwarded: for=127.0.0.1

Send the one the app actually honours (you found it in step 1), with a new address each request. Because the limiter thinks every attempt is a different client, the counter never builds up.

Counter-reset and format tricks

When the source header is validated, other naive behaviours still give you room:

X-Forwarded-For: 1.2.3.4, 127.0.0.1    (last/first hop confusion in a comma list)
x-forwarded-for: 127.0.0.1             (header-name casing may dodge a naive filter)
Double header: send X-Forwarded-For twice; the throttle and the app may read different ones
Username casing: Admin / ADMIN / admin may hit different per-account counters
Null/space:  admin%00  or  "admin "    may be treated as a new account by the limiter

The comma-list trick exploits disagreement over which hop in X-Forwarded-For is the "real" client. The casing and whitespace tricks exploit a limiter that keys on the raw username while the login itself normalizes it, so Admin and admin log into the same account but count against different buckets.

Lockout as denial of service

A hard lockout is not just bypassable, it is a weapon. If N failed attempts lock an account, an attacker can deliberately fail against every known username and lock out the entire user base, turning the protection into an outage. That is worth noting in a report even when you are not using it offensively, because a lockout that can be triggered by anyone is itself a vulnerability.

CAPTCHA and its weak implementations

CAPTCHA is meant to stop automation, and it is often bolted on weakly: only shown after several failures (so the first few are free), validated once and then reusable, checked on the client, or solvable by a service. Probe whether the CAPTCHA token is actually required and single-use on every attempt, because frequently it is not.

Step 3: confirm the bypass

Resume the credential attack and verify that attempts past the original threshold still go through. If they do, the throttle is defeated and spraying or brute force proceeds unbounded, which is the whole point of this step.

Fixing it

  • Rate-limit server-side on the account and a trustworthy signal, never solely on a client-supplied header. Only trust a forwarding header from infrastructure you control.
  • Normalize the username before counting so casing and whitespace variants hit the same bucket.
  • Prefer exponential backoff over hard lockouts that enable denial of service.
  • Make CAPTCHA tokens required and single-use on every attempt, validated server-side.

Full defensive detail is in securing authentication. Next, see how a second factor gets skipped even when the password attack succeeds in MFA and OTP bypass, or try a bypass on a free lab.

← Back to Authentication Vulnerabilities: The Complete Practical Guide