Teacher
Professional
- Messages
- 2,670
- Reaction score
- 775
- Points
- 113

Even before I began to comprehend the complex science of information security, it seemed to me that 2FA authentication is a guaranteed way to protect your account and no "these hackers of yours" can, say, steal my internal currency to buy clothes for characters on a game account. But over time, it has been empirically proven that a two-factor authentication system can have a large number of vulnerabilities.
In simple terms, two-factor authentication is a confirmation of an action by entering a generated code to increase security and throwing a spoke in the wheels of conventional hackers while driving or before it starts.
The verification system with the help of a code is very widespread, it is used everywhere on various sites and can be connected for both primary and secondary logins. But the application is not limited to this - the developers attach a confirmation to the password recovery functionality, confirmation of registration / subscription, additional confirmation of financial transactions, changing passwords, changing personal data. Also, occasionally 2FA is used as a wall after logout, and not a password or other confirmation method.
In this article, I have collected ways to test 2FA for vulnerabilities, their exploitation, as well as possible options for bypassing existing protection against certain types of attacks. Let's take a look at the list of vulnerability checks that apply to 2FA:
1. Absence of Rate-limit
The Rate Limiting algorithm is used to test the ability of a user session (or IP address) to be limited in attempts or rate, and under what circumstances this occurs. If the user has made too many requests within a certain period of time, the web application can respond with 429 code (many requests) or apply a Rate limit without showing errors. The absence of a rate limit assumes that during a normal search there are no restrictions on the number of attempts and / or speed - it is allowed to iterate over codes an arbitrary number of times (at any speed) within the session / token validity period.
Quite often you come across a "silent" rate-limit - if you see that there are no errors and the HTTP body / code does not change in subsequent requests, it's too early to rejoice, and first you need to check the final result of the attack by applying a valid code.
2. Rate limit exists, but it can be bypassed
Cases that you had to meet before:
1) Limiting the flow rate without blocking after reaching a certain speed
Often, security researchers try to guess code using 5 or more threads in order to execute an attack faster (Burp Intruder has 5 threads by default without delay). But sometimes a brute-force safety system or a regular Load Balancer can only respond to this single factor. If you are trying to brute force with 5 threads, it is worth reducing the number to 1, and then to 1 with a delay of one second. Previously, I was lucky enough to observe such behavior, and it was with the help of such manipulations that the code was successfully selected, which led to the Account Takeover. If the 2FA code does not have a specific expiration date, then we have a lot of time to brute force. If the expiration date is present, then the success of the attack is reduced, but the potential danger of the vulnerability is still present,
2) The generated OTP code does not change
This does not apply to constantly changing codes like in Google Authenticator, but only static ones that come in SMS, email or personal messages in the messenger.
The essence of this bypass is that constantly or for some time, for example, 5 minutes, the same OTP code is sent to the SMS, which is valid during all this time. It is also worth making sure that a silent rate-limit does not occur.
Let's say the application generates a random code from 001 to 999 and sends it to the phone, within 10 minutes when the “resend” functionality is activated, we receive the same code. But a rate-limit is attached to the request, which limits the number of attempts per request token. We can constantly request new code, generate a new request token, apply it to the subsequent request (using the grep-match in the burp suite or using our own script) and brute force the range of numbers from 001 to 999. Thus, constantly using the new request token we will successfully select the correct code, since it does not change and is static in a certain period of time. The limitation of this attack is a long number or mixing letters with numbers as a confirmation code.
This situation should not be discouraged, you should try to iterate over at least part of our list, because there is a possibility that the generated code will end up in this part of the list, since it is generated randomly. When iterating over, you need to rely on randomness, but still there is a chance of hitting the right combination, which proves a vulnerability that definitely needs to be fixed.
3) Reset rate-limit-a when updating the code.
In the code verification request, the rate-limit is present, but after activating the code resending functionality, it is reset and allows you to continue brute-force code.
4) Bypassing the rate-limit by changing the IP address
A lot of blocking is based on limiting the reception of requests from an IP that has reached a threshold of a certain number of attempts when making a request. If you change the IP address, then it is possible to bypass this limitation. In order to check this method, just change your IP using a Proxy server / VPN and see if the blocking depends on the IP.
IP change methods:
- Proxies can be used in an attack using the IP Rotator add-on for the Burp Suite github.com/RhinoSecurityLabs/IPRotate_Burp_Extension. In my opinion, this is the best choice because it gives us ~ unlimited brute force attempts and IP addresses that allow a brute-force attack without 42x errors and interruptions.
- A python script with a proxy requests module can be a good option, but first you need to get a large number of valid proxies somewhere.
Since the IP rotate tool sends requests using AWS IP addresses, all requests will be blocked if the web application is behind a CloudFlare firewall.

In this case, you need to additionally discover the IP of the original web server or find a method that does not concern AWS IP addresses.
5) The site includes support for X-Forwarded-For
The built-in header X-Forwarded-For can be used to change IP. If your application has built-in handling of this header, simply send X-Forwarded-For: desired_IP to spoof the IP to bypass the restriction without using additional proxies. Every time a request is sent with X-Forwarded-For, the web server will think that our IP address matches the value passed through the header.
3. Bypass 2fa by substituting part of the request from the session of another account
If a parameter with a specific value is sent in the request to verify the code, try sending the value from the request of another account.
For example, when submitting an OTP code, the form ID, user ID, or cookie associated with the submission of the code is checked. If we apply the data from the parameters of the account on which you need to bypass the code-verification (Account 1), to the session of a completely different account (Account 2), we will receive the code and enter it on the second account, then we will be able to bypass the protection on the first account. After reloading the page, the 2FA should be gone.
4. Bypassing 2FA using the "memorization functionality"
Many sites that support 2FA authorization have a "remember me" functionality. It is useful in the case when the user does not want to enter the 2FA code on subsequent logins to the account. It is important to identify the way in which 2FA is "remembered". This can be a cookie, a value in session / local storage, or simply attaching 2FA to an IP address.
1) If 2FA is attached by setting a cookie, then the cookie value must be unguessable
That is, if a cookie consists of a set of numbers that increase for each account, then a brute-force attack can be applied to the cookie value and bypass 2FA. Developers should provide the cookie (along with the session key cookie and CSRF token) with the HttpOnly attribute so that it cannot be stolen using XSS and used to bypass 2FA.
2) If 2FA is attached to an IP address, then you can try to spoof it
To identify this method, log into your account using the 2FA remember function, then switch to another browser or incognito mode of the current browser and try to log in again. If 2FA is not requested at all, then 2FA has been attached to the IP address.
To spoof the IP address, you can use the X-Forwarded-For header at the stage of entering the login and password, if the web application supports it.
Using this header, you can also bypass the "IP address white-list" function, if such is present in the account settings. It can be used in conjunction with 2FA as additional account protection, or 2FA may not even be requested if the IP address matches the white-list (with the user's consent). Thus, even without attaching 2FA to an IP address, in some cases 2FA can be bypassed by bypassing the associated security methods.
In general, attaching 2FA to an IP address is not a completely secure method of protection, since while being on the same network, when connecting to the same VPN / Internet provider with a static IP address, 2FA can be bypassed.
The safest way to protect yourself is not to memorize 2FA at all, to the detriment of usability.
5. Improper access control bug on 2FA entry page
Sometimes the dialog page for entering 2FA is presented as a URL with parameters. Access to such a page with parameters in the URL with cookies that do not match those used when generating the page or without cookies at all is not safe. But if the developers decide to accept the risks, then you need to go through several important points:
- Whether the link for 2FA input expires;
- Whether the link is indexed in search engines.
If the link has a long lifetime and / or there are working links in search engines for entering 2FA / links can be indexed (there are no rules in robots.txt / meta tags), then there is a possibility of a bypass of the 2FA mechanism on the 2FA input page, in which it will be possible completely bypass the login and password entry, and gain access to someone else's account.
6. Insufficient censorship of personal data on the 2FA page
When sending an OTP code, the page uses censorship to protect personal data such as email, phone number, nickname, etc. But this data can be fully disclosed in API endpoints and other requests for which we have enough rights at the 2FA stage. If initially these data were not known, for example, we entered only a login without knowing the phone number, then this is considered a "Information Disclosure" vulnerability. Knowing the phone number / email can be used for subsequent phishing and brute-force attacks.
An example of exploiting a vulnerability using Credentials Stuffing. Let's say there is a publicly available database with logins and passwords for site A. Attackers can use data from this database on site B:
- First, they check if the user exists in the database of site B using the "Accounts Enumeration" bug in registration / password recovery. Usually, many sites do not consider this a vulnerability and accept the risks. "Vulnerability" lies in the presence of an error about the fact of the user's registration on the site. Ideally, a secure message on the password recovery page looks like this:
- After obtaining a database of existing users, attackers apply passwords to these accounts.
- If they encounter 2FA, they are stumped. But in case of insufficient censorship of the user's data, they can supplement their database with his personal data (if they were not in the original database).
7. Ignoring 2FA under certain circumstances
When performing some actions that lead to automatic login to your account, 2FA may not be prompted.
1) Ignoring 2FA when recovering a password
Many services automatically log into your account after completing the password recovery procedure. Since account access is instantaneous, 2FA may be bypassed and completely ignored upon logging into your account.
Impact of a similar hackerone report I posted recently:
If an attacker gains access to the victim's email (he can hack the account using phishing, brute-force attacks, credentials stuffing, etc.), he can bypass 2FA, although in this case 2FA must protect the account. At the moment there is a Google Authenticator or backup code validation for 2FA, but not an email code, so this Bypass makes sense.
2) Ignoring 2FA when logging in via social network
You can attach a social network to your user account to quickly log into your account and set up 2FA at the same time. When logging into an account via social media, 2FA can be ignored. If the victim's email is hacked, then it will be possible to recover the password to the social network account (if it allows it) and enter the service without entering 2FA.
Impact of one of the reports:
- Bundle with other vulnerabilities, such as the previously submitted OAuth misconfiguration # 577468, to completely hijack the account, overcoming 2FA.
- If an attacker has compromised a user's email, he can try to regain access to the social network account and log in to the account without additional verification.
- If the attacker once hacked the victim's account, he can link the social network to the account and log into the account in the future, completely ignoring 2FA and entering the username / password.
3) Ignoring 2FA in an older version of the application
Developers often add staging versions of web applications to domains / subdomains to test certain functions. Interestingly, if you log in with your username and password, 2FA will not be requested. Perhaps the developers are using an older version of the application that does not have protection for 2FA, 2FA itself is disabled, or it was deliberately disabled for testing.
Also, you can check other vulnerabilities at the same time - registering a new user on the staging server, whose email exists in the production version database can give us personal data from production; lack of rate-limit in important functionality, for example, in password recovery. An example of the latest vulnerability is the $ 15,000 facebook bug, which allowed hacking an account using a brute-force password recovery code on beta.facebook.com www.freecodecamp.org/news/responsible-disclosure-how-i-could-have-hacked-all-facebook-accounts-f47c0252ae4d .
4) Ignoring 2FA in case of cross-platform
2FA implementations in mobile or desktop versions may differ from the web version of the application. 2FA may be weaker than in the web version or not at all.
7. When disabling 2FA, the current code is not requested.
If, when disabling 2FA, additional confirmation is not requested, such as the current code from the google authenticator of the application, the code from the email / phone, then in this case there are certain risks. With a clean request, there is a possibility of a CSRF attack. If a vector to bypass the CSRF protection (if any) is found, then 2FA can be disabled. A clickjacking vulnerability can also be used - after a couple of clicks, 2FA will be disabled from an unsuspecting user. Verifying the previous code will add additional 2FA protection, considering potential CSRF / XSS / Clickjacking attacks as well as CORS misconfigurations.
As an example, I will give the site hackerone.com, - if you disable 2FA in one form, you need to enter two variables at the same time, - the current code from the google authenticator application and the password. This is the best and recommended protection.

8. Previously created sessions remain valid after 2FA activation
When 2FA is enabled, it is desirable that parallel sessions on the same account end and the 2FA entry dialog is displayed, the same with changing the password. If the account has been compromised and the victim's first reaction is to enable 2FA, then the attacker's session will be invalidated and the next login and password entry will require 2FA. Overall, this is the best practice to follow. I often see cryptocurrency exchanges add this kind of protection.
9. Absence of Rate-limit in the personal account
2FA can be implemented in various functionalities of the user's personal account for greater security. This can be changing the email address, password, confirmation of changing the code for financial transactions, etc. The presence of rate-limit-a in your personal account may differ from the presence of rate-limit-a in 2FA when you enter your account. I have come across similar cases more than once when it was possible to freely select the 2FA code in the personal account, while the "Strict" rate-limit was set at the entrance.
If the developers initially added protection against unauthorized changes to data, then this protection must be maintained and all possible bypasses must be corrected. If a bypass is found, it is considered a "security feature" bypass vulnerability that was implemented by the developers.
10. Manipulation of API versions
If you see something like / v * / in a web application request, where * is a number, then there is a chance that you can switch to an older version of the API. An older version of the API may have weak or no security. This is quite a rare occurrence and occurs if the developers forgot to remove the old version of the API in the production / staging environment.
For example, / endpoint / api / v4 / login makes a login request by checking the login and password. If 2FA is present in the account, this request must be followed by / endpoint / api / v4 / 2fa_check, not otherwise. If we replace the API version before 2FA, then, in some cases, we can avoid it. / endpoint / api / v3 / login can lead to / endpoint / v3 / login_successful? code = RANDOM, ignoring 2fa_check because in this version of the API, 2FA was simply not implemented.
One more example - rate-limit is present in the request / endpoint / api / v4 / 2fa_check, while / endpoint / api / v3 / 2fa_check allows you to iterate over codes without any restrictions.
11. Improper Access Control in requesting backup codes
Backup codes are generated immediately after 2FA is enabled and are available on a single request. After each subsequent call to the request, codes can be generated over a new one or remain unchanged (static codes). If there are CORS misconfigurations / XSS vulnerabilities and other bugs that allow "pulling" backup codes from the endpoint request response for displaying backup codes, then an attacker can steal the codes and bypass 2FA if the username and password are known.
In general, 2fa is one of the most reliable ways to protect an account, unless, of course, the developers store the current 2FA codes of all users of the web application in the admin panel - and this has happened in my practice. I would also like to note that the developers of some companies create applications for generating their own 2FA codes (examples are Salesforce, Valve) and due to insufficient security, such an emphasis on independence from the use of other applications for authentication gives attackers an additional entry point to bypass 2FA. The range of application of this protection is quite large and gives those who want to bypass 2FA protection more opportunities, space for creativity and increases the number of variations of 2FA bypass.
I hope this information was useful for information security researchers, bug bounty hunters, as well as for developers in order to minimize the number of vulnerabilities in the service being developed. Stay safe!
(c) https://habr.com/ru/post/483134/