Application Security Testing of the 2FA form guidelines.

INTRODUCTION
This is the sixth article in the AppSec series which describes how to test 2FA forms to ensure a secure authentication process.
The advice in this article is based on:
- OWASP Web Security Testing Guide
- OWASP Application Security Verification Standard
- NIST recommendations
- bug bounty reports
- own experience.
I will provide a short test sample, a potential impact or an attack scenario, and a possible solution to the problem at each point.
GUIDELINES
In most of the below examples, the attacker is able to bypass two-factor authentication.
I. FORCE BROWSING
Skip to the restricted area when asked for a 2FA code.


2FA should be checked before granting authorization to any restricted area.
II. BROKEN LOGIC
Check the integrity of the 2FA.
- An attacker could hijack the victim’s account by using their own credentials and then changing the value of the account cookie to any arbitrary email address when submitting the verification code in the second step of the signing-in process.

The entire authorization process should be consistent.
The next steps should refer to the previously entered data.
III. BROKEN VERIFICATION CODE VALIDATION
Fuzz the code parameter value.

The API should properly validate the verification code.
IV. SHARED VERIFICATION CODE
Use generated by the attacker 2FA code on the victim account.

The OTP should be associated with the account for which it was generated.
V. ANOTHER USER REUSABLE VERIFICATION CODE
Reuse an attacker’s OTP on the victim's account.

The verification code should not be reusable and associated with a single account.
VI. STATIC & REUSABLE VERIFICATION CODE
Resend the verification code and check if it changes.

The verification code should be random for each resend.
The old verification code should be invalidated after generating a new one.
VII. TIME-BASED VERIFICATION CODE GENERATOR
Generate verification code at the same time for the two different users.
import requests
url = "https://2FA_GENERATOR_URL"
headers = {}
victim = {"LOGIN": "victim", "PASS":"password123"}
attacker = {"LOGIN": "attacker", "PASS":"password123"}
requests.post(url, headers=headers, data=victim, verify=False)
requests.post(url, headers=headers, data=attacker, verify=False)
Verification code should be generated using a secure source of randomness, single-use, time-limited and bound to account.
VIII. BRUTE-FORCIBLE VERIFICATION CODE
Check if the verification code can be guessed.


Rate-limiting should be implemented.
The code should expire after 10 minutes.
Code should contain at least 20 bits of entropy (at least 6 random numbers).
Verification should be limited to 5 incorrect attempts per verification code.
IX. NO RATE-LIMITING — CODE GENERATION
Generate the OTP 1000 times in a short time.
- The attacker could conduct a DoS attack on a victim’s phone or cause a financial loss to the company by generating tons of SMS.

Restrict the consecutive requests through mechanisms like time delay, CAPTCHA, or other controls.
X. LOCKING MECHANISM BYPASS— HEADER
Use additional headers after triggering the OTP generator lock.
- An attacker could bypass the locking mechanism on the login page.

X-Originating-IP: 127.0.0.1
X-Forwarded-For: 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-Host: 127.0.0.1
X-Forwarded-Host: 127.0.0.1
The locking mechanism should not be based on other request elements than the user account variable. Additionally, avoid using unnecessary headers.
XII. LOCKING MECHANISM BYPASS— PATH & METHODS
Use similar paths, random parameters, and methods after the lock.
- The attacker may bypass the locking mechanism.

The locking mechanism should not be based on other variables than the user account.
XIII. LOCKING MECHANISM BYPASS— SOURCE IP ROTATION
Use a different IP address to bypass the lock.

The locking mechanism should not be based only on the IP address.
XIV. DENIAL OF SESSION LIMITING MECHANISM
Check if the locking mechanism also destroys an active user session.
- An attacker could block the victim’s account.

The locking mechanism should not invalidate active user sessions.
XV. DENIAL OF AUTHENTICATION LIMITING MECHANISM
Check if the locking mechanism also destroys an active user session.
- An attacker could block the victim’s account.

The locking mechanism should not invalidate active user sessions.
XVI. CLIENT-SIDE VERIFICATION
Issue a wrong code and manipulate the server response.
- The technique can be used to find new endpoints, and trigger some new requests without looking into a JS code. It is very handy when the JS code is very big and you have a little time.


Ensure that client-side security checks are duplicated on the server-side.
XVII. CREDENTIALS OVER AN UNENCRYPTED CHANNEL
Check if data is transferred via HTTP or as a parameter in the URL.
- Sensitive data may be logged by the browser, the webserver, and forward or reverse proxy servers between the two endpoints.
- It could be also displayed on-screen, bookmarked, or emailed around by users.
- They may be disclosed to third parties via the Referer header when any off-site links are followed.

Sensitive data in the URL and sent using not secure Hypertext Transfer Protocol increases the risk that it will be captured.
XVIII. VERIFICATION CODE LEAK
Check the source code of the response, cookies, and headers.

The verification code should not be disclosed in any place.
XIX. PERSONAL INFORMATION LEAKAGE ON THE 2FA PAGE
Check if the application censors the personal data on the 2FA page.
The application should respond with generic information on the 2FA page.
If there is a mobile number being used it should be censored.
XX. IGNORED 2FA
Check if 2FA is required in all critical points of the application.

2FA should be enforced at all endpoints that may access or affect a user’s account.
XXI. UNVERIFIED ADDITION | CHANGE | DELETION OF 2FA
Check if the application does verify the modification of 2FA settings.
- If the attacker successfully exploits another flaw, like CSRF or XSS in the application, he could modify or disable 2FA.
- With temporary access to the victim’s account (e.g. physical access to a device with an active session), an attacker could modify or disable 2FA.

The application should enforce a verification on the 2FA settings page.
On each 2FA settings modification, the user should be notified.
XXII. BROKEN SESSION INVALIDATION ON 2FA CHANGE
Check if the application invalidates the sessions after activation of 2FA.

The application should invalidate the session on the server-side after the user activates the 2FA feature or modify any 2FA settings.
XXIII. DISABLING 2FA VIA OTHER FEATURES
Check if other functionalities can disable 2FA.

2FA should be required for any critical app functionality such as password recovery, and it should not be disabled other than through 2FA settings.
XXIV. CSRF
Check the CSRF protection on the 2FA settings page.
- An attacker creates a web page, inserts a hidden CSRF form, then lures the victim to visit it — the 2FA will be disabled or modified automatically.

Implement an unpredictable token in each HTTP request.
Such tokens should, at a minimum, be unique per user session.
Check CSRF Prevention Cheat Sheet from OWASP.
XXV. INPUT VALIDATION
Check the AppSec Tales I and AppSec Tales II.
Check the Input Validation Cheat Sheet from OWASP.
XXVI. INPUT LENGTH LIMITATIONS
Check parameter values length limits on each 2FA form.
Implement proper length limitations on the data received from the user.
FINAL WORDS
Testing any element of a Web Application is like sailing the open ocean.
Treat the WSTG like the compass and the ASVS like azimuth.
However, do not forget that someone had to invent it. Therefore you always have to look for new ways that you will not find in the WSTG or here, but you have to find them yourself.
Nevertheless, I hope you will find this article useful and keep coming back to it. I also encourage you to comment if you have an idea for a point for this article or if you find any bugs here ;]