Techniques For Bypassing Rate Limiting on OTP/2FA Endpoints

Jeppe Bonde Weikop
5 min readJan 5, 2021

Introduction
When hunting for bugs (and bounties), I like to target 2FA implementations and OTP login systems, because I often find them to be vulnerable to authentication bypass via brute force. Most targets do implement rate limiting to prevent this, but in many cases these rate limiting systems are bypassable in one way or another. On these endpoints, a rate limiting bypass often leads to severe vulnerabilities and handsome payouts.

For this reason, I decided to make a list of the most common techniques that have worked for me. I mean for this post to serve as a cheatsheet/checklist for myself and others.

Identifying rate limiting systems

Before attempting any bypass technique, one should first try to figure out how the rate limiting on their target works. I generally do this in a Burp Suite Repeater tab by playing with cookies, headers, GET/POST parameters etc. Rate limiting is almost always based on one of the following:

  1. The client’s IP address
  2. A user identifier (username/email/phone number etc)
  3. A session token

I’ll give an overview of bypass techniques for each scenario below and afterwards explain every item in the list in a bit more detail.

Overview

1. IP-based rate limiting
1A: IP spoofing with HTTP headers.
1B: IP rotation

2. Username/phone/email-based rate limiting
2A: Appending null bytes, carriage returns, spaces etc. (%00, %0d, %20…)
2B: ASCII encoding
2C: Case swapping
2D: Server-side HTTP parameter pollution

3. Session ID-based rate limiting
3A: Requesting new session IDs upon expiry.

4. Other techniques
4A: URL manipulation
4B: Changing HTTP request method
4C: WAF bypass
4D: Identifying similar functionality on different domains/endpoints

1A: IP spoofing with HTTP headers
This is the perhaps most well-known technique. If the server trusts certain HTTP request headers, it is possible to spoof IP addresses, bypassing any IP-based rate limits. Such headers include:

X-Forwarded-For: 8.8.8.8     // works most commonly
X-Originating-IP: 8.8.8.8
X-True-IP: 8.8.8.8
X-Remote-IP: 8.8.8.8
X-Remote-Addr: 8.8.8.8
X-Client-IP: 8.8.8.8

Then the header value can be changed to a different IP every time an IP is blocked.

In addition, it might also be possible to use “127.0.0.1" or “localhost” as the header value if requests from localhost aren’t being rate limited due to a firewall setting.

1B: IP rotation
Even if the server is not vulnerable to IP spoofing, IP-based rate limit systems are still weak and generally shouldn’t be used as the only protection for 2FA/OTP logins, because they can easily be bypassed by simply shifting through IP addresses. This requires access to a lot of IP addresses, but that can be done pretty cheaply using cloud service providers like Amazon AWS.

The process can be automated using tools such as IP Rotate for Burp Suite.

2A: Appending null bytes, carriage returns, spaces etc.
For username-based rate limiting systems, it might be possible to cause a discrepancy between the lines of code that evaluate a login attempt and the lines of code that handles login attempt limiting by appending certain characters.

For example, test@email.com%00 might be understood as test@email.com by the credential-evaluating code, but considered a different email address by the rate-limiting code, effectively bypassing rate limitating.

Try appending or prepending %00, %20, %09, %0d and %0a.

2B: ASCII encoding
For username-based rate limiting systems, it might be possible to cause a discrepancy between the lines of code that evaluate a login attempt and the lines of code that handles login attempt limiting by partially encoding the submitted username/email/phone number.

For example, t%65st@em%61il.com might be decoded and understood as test@email.com by the credential-evaluating code, but considered a different email address by the rate-limiting code, effectively bypassing rate limiting.

For this to be useful, one must create a list of uniquely encoded emails. The amount of unique combinations for an email address of length n is 2 ** n.

2C: Case swapping
This technique is very similar to 2B, but usually only works for email addresses. Email addresses are not case sensitive, so test@mail.com is the same email address as tEsT@mAiL.cOm. If the rate limit counter erroneously resets when a random letter in the email address is capitalized, it’s often possible to bypass rate limiting by creating a list of uniquely capitalized email addresses.

The amount of unique combinations for an email address of length n is 2 ** n.

2D: Server-side HTTP parameter pollution
If the server is vulnerable to HPP, it is possible to submit 2 username parameters with different values. The server might use the value of one parameter to check if the request should be blocked (due to rate limiting) and the value of the other to actually perform the authentication attempt.

Example: GET /login/otp/?code=1234&email=bypass123@mail.com&email=victim@mail.com

3A: Requesting new session IDs upon expiry
This is the one I see in the wild most often, especially on APIs for mobile apps.

Some authentication flows work by having the user initiate a session by supplying their email address or phone number. When the user submits an email, the server returns a session ID and sends the user an OTP for them to authenticate themselves with. Oftentimes, the rate limiting on the OTP login attempts are based on the session ID, but the endpoint used for initiating sessions is not rate limited.

If that is the case, it is possible to bypass rate limiting by simply requesting a new session ID whenever the previous one expires or is blocked. This usually requires some custom scripting to exploit.

If endpoint for initating sessions is rate-limited, you can try the techniques described in this post on that endpoint.

4A: URL manipulation
Depending on how the rate limiting works, it might be tied to the path of the endpoint. If so, it could be possible to bypass the rate limiting by adding GET parameters or manipulating the URL in other ways.

Examples:
/path/login/
/path/login/?anything
/path/login/anything/../
/pAth/lOgIN/

4B: Changing HTTP request method
In some cases, it is possible to bypass poorly designed WAFs/blacklists by simply changing request method and moving POST parameters to GET parameters or vice versa. You can also try other methods, like PUT or PATCH.

4C: WAF bypass
If rate limiting is handled by a WAF such as Cloudflare, the rate limiting can be bypassed if the WAF can be bypassed. In Cloudflare’s case, the WAF can be bypassed by finding the origin IP address. For more information on techniques for bypassing Cloudflare, check out this article by Detectify.

4D: Identifying similar functionality on different domains/endpoints
While one endpoint might be properly protected with rate limiting, your target may have other endpoints that do the same thing (eg. a login page) that are either unprotected or vulnerable to any of the techniques described in this post. Maybe there’s an API or an Android/iOS app you could try?

Suggestions

I would love to add more techniques to this list. If I missed something or you know another technique you’d like to share, let me know at waycup@bugcrowdninja.com or @waike14 on Twitter.

--

--