Hacking 2FA: Why Client-Side Fixes Fail—Injecting Cookies to Bypass Security Controls + Video

Listen to this Post

Featured Image

Introduction:

Two-factor authentication (2FA) is often viewed as the final gatekeeper for account security. However, when the verification decision is made on the client side and stored in easily mutable browser storage, that gatekeeper becomes a paper tiger. A recent public write-up by Mahmoud Magdy details how a patched 2FA mechanism was still vulnerable because the application relied on an `isVerifyAuth` cookie stored in local storage—an artifact an attacker can manually create or modify to impersonate a fully authenticated session.

Learning Objectives:

  • Understand how client-side 2FA status flags can be abused even after an initial fix.
  • Learn to manually inject and manipulate browser local storage objects for security testing.
  • Identify server-side validation gaps by intercepting and replaying session tokens.
  • Apply defensive coding techniques to enforce 2FA checks on the backend for every sensitive request.

You Should Know:

1. Reconnaissance: Identifying Client‑Side 2FA Flags

Modern web applications often use JavaScript to check whether a user has completed 2FA. This status is frequently stored in localStorage, sessionStorage, or as a cookie with an `HttpOnly` flag missing. In the analyzed vulnerability, the key `isVerifyAuth` held a boolean value. After the initial fix, the developers changed the expected value or the key name, but they did not move the verification logic to the server.

Step‑by‑step guide to test for similar flaws (ethical testing only):
1. Log in to the target application and complete 2FA normally.
2. Open Developer Tools (F12) → Application tab → Storage → Local Storage.
3. Look for any key containing terms like verified, 2fa, auth, isVerify, mfa.

4. Copy the exact key–value pair.

5. Log out and clear all site data.

  1. Log in again but do not complete the 2FA step.
  2. Manually inject the previously observed key–value pair into Local Storage:

– In the browser console:

localStorage.setItem('isVerifyAuth', 'true');

– Or via the Application panel by double-clicking the value field.
8. Refresh the page or navigate to a protected endpoint.

Linux (curl) simulation for API testing:

If the 2FA status is sent as a cookie or header:

curl -X GET https://target.com/protected-resource \
-H "Cookie: isVerifyAuth=true; session=VALID_SESSION" \
-L -v

2. Exploitation Flow: Manual Cookie Injection After Fix

The write-up emphasizes that the application still used the same flawed architecture after the patch—only the key name was changed. Attackers can brute‑force common naming variations (2fa_done, mfa_verified, auth_level).

Step‑by‑step injection using browser DevTools:

  1. Intercept login request with Burp Suite or ZAP to capture the session token.

2. Replay the session without completing 2FA.

  1. Navigate to a page where the application reads the local storage flag.
  2. Use the Console to set a custom object:
    let userAuth = JSON.parse(localStorage.getItem('user') || '{}');
    userAuth.twoFactorPassed = true;
    localStorage.setItem('user', JSON.stringify(userAuth));
    
  3. Trigger any XMLHttpRequest or fetch call that the application uses to verify 2FA status.

Windows PowerShell equivalent (if testing a thick client or Electron app):

 Modify local storage via Chrome DevTools Protocol (CDP)
$Chrome = Start-Process chrome -ArgumentList "--remote-debugging-port=9222" -PassThru
 Then send CDP command to set localStorage item

3. API‑Side Vulnerabilities: Missing Server Enforcement

The root cause is not the local storage flag—it is that the server trusts it. After the injection, the browser includes the session cookie, but the server never checks whether the 2FA challenge was actually completed for that session.

Simulated exploitation with Python requests:

import requests

session = requests.Session()
 Perform login
login_resp = session.post('https://target.com/login', data={'user':'a','pass':'b'})
 Bypass 2FA by sending arbitrary flag
protected_resp = session.get('https://target.com/dashboard', 
cookies={'isVerifyAuth':'true'})
print(protected_resp.text)  Sensitive data exposed

4. Cloud & Infrastructure Misconfigurations

This vulnerability is often exacerbated when applications are hosted on cloud platforms with misconfigured identity providers. For AWS Cognito or Azure AD B2C, if the client‑side SDK controls the `id_token` or `access_token` and the backend trusts the `acr` (Authentication Context Class Reference) claim without verification, similar bypasses occur.

Hardening commands for AWS Cognito (CLI):

 Ensure that the access token is validated, not just the id_token
aws cognito-idp verify-software-token \
--user-pool-id us-east-1_XXXXX \
--access-token valid_token

Policy: Always re‑authenticate for high‑value actions, regardless of client flags.

5. Windows & IIS Specific Test Cases

For ASP.NET applications, view state or session flags like `IsMFAComplete` may be stored in `Session` but only checked on the client side via JavaScript.

IIS URL Rewrite rule to block flagged requests:

<rule name="Block_2FA_Bypass" stopProcessing="true">
<match url="." />
<conditions>
<add input="{HTTP_COOKIE}" pattern="isVerifyAuth=true" />
<add input="{SESSION}" pattern="2FA_Verified=false" />
</conditions>
<action type="CustomResponse" statusCode="403" />
</rule>

6. Mitigation: Defense in Depth with Token Binding

The permanent fix requires storing the 2FA completion status on the server and binding it to the session.

Example: Node.js/Express middleware:

app.use('/api/secure', (req, res, next) => {
if (req.session.twoFactorCompleted !== true) {
return res.status(403).json({ error: '2FA required' });
}
next();
});

Also, implement HTTP‑only, Secure, SameSite cookies for the session token and never expose the verification status in client‑side storage.

7. Vulnerability Reproduction for Bug Bounty

If you are testing under a responsible disclosure program, always document that client‑side 2FA status is inherently untrustworthy.

Checklist for reporting:

  • Screenshot of localStorage before and after injection.
  • Proof of access to authenticated endpoint without completing 2FA.
  • API call using cURL/PHP/Go to demonstrate server‑side flaw.
  • Suggested fix: Move verification to server‑side session.

What Undercode Say:

  • Key Takeaway 1: A 2FA “fix” that only renames a client‑side flag is not a fix—it is security theater. True remediation requires redesigning the trust boundary.
  • Key Takeaway 2: Client‑side storage (localStorage, sessionStorage) is not secure for any authentication decision. Attackers have full control over the browser environment and can tamper with any stored value.

This case illustrates a recurring pattern: developers treat client‑side flags as source of truth because they are easy to implement. The rush to patch often addresses the symptom (the specific key name) rather than the disease (lack of server‑side verification). Until security engineers enforce authentication invariants on the backend, 2FA will remain trivially bypassable.

Prediction:

As WebAuthn and passkeys gain adoption, we will see a resurgence of similar logical flaws—attackers will not break the cryptography; they will trick the application into believing the ceremony was successful. Future breaches will stem not from broken MFA algorithms, but from broken MFA state management. Expect bug bounty programs to elevate the severity of “2FA logic bypass” issues to critical once the industry fully realizes that client‑side verification decisions are obsolete.

▶️ Related Video (84% Match):

🎯Let’s Practice For Free:

IT/Security Reporter URL:

Reported By: Abhirup Konwar – Hackers Feeds
Extra Hub: Undercode MoN
Basic Verification: Pass ✅

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow UndercodeTesting & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin | 🦋BlueSky