JWT Nightmare: How I Bypassed 2FA and Escalated Privileges in One Fintech Bug Bounty Session + Video

Listen to this Post

Featured Image

Introduction:

Modern web applications heavily rely on JSON Web Tokens (JWT) for session management and API authorization. However, misconfiguring JWT expiry, neglecting server-side 2FA enforcement, and trusting client‑supplied role parameters create devastating business‑logic flaws that automated scanners rarely catch. This article dissects three real‑world bug bounty findings—non‑expiring JWTs, full authentication bypass via direct API access, and privilege escalation through a client‑controlled role flag—and provides actionable step‑by‑step guides to discover, exploit, and remediate each vulnerability.

Learning Objectives:

  • Decode and analyze JWT payloads to identify missing expiry fields and improperly stored state.
  • Exploit unsynchronized frontend/backend validation to bypass 2FA using pre‑authentication tokens.
  • Detect and abuse client‑controlled role or identity parameters to escalate privileges.
  • Implement server‑side hardening techniques and derive roles from the authenticated token, not user input.

You Should Know

  1. How to Decode and Audit JWT Expiry (The “expiredAt: null” Flaw)

What this does:

JWTs are Base64Url‑encoded JSON objects. When a developer explicitly sets `”expiredAt”: null` or omits the standard `exp` claim, the token never expires—an attacker who steals it gains indefinite access. This step‑by‑step guide shows how to decode, inspect, and test JWT validity.

Step‑by‑step guide (Linux/macOS/Windows WSL):

  1. Capture the JWT – Use browser dev tools (Network tab) or a proxy like Burp Suite to copy the token from the `Authorization: Bearer ` header.

  2. Decode the JWT without tools – Split the token into three parts (header, payload, signature) using a delimiter ..

Example token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6InVzZXIiLCJleHBpcmVkQXQiOm51bGx9.dummy_signature`

3. Base64Url decode the payload (Linux/macOS):

echo "eyJzdWIiOiIxMjM0NTY3ODkwIiwicm9sZSI6InVzZXIiLCJleHBpcmVkQXQiOm51bGx9" | base64 -d 2>/dev/null | jq .

For Windows (PowerShell):


  1. Look for expiry flaws – Check if the `exp` claim exists and is a future Unix timestamp. If you see "expiredAt": null, "exp": null, or no expiry claim at all, the token is vulnerable.

  2. Test token longevity – Use `curl` to replay the same token against an authenticated endpoint after hours or days:

    curl -H "Authorization: Bearer <jwt>" https://target.com/api/user/profile
    

    If it still returns 200 OK, the session never expires.

Developer fix: Always set a short‑lived `exp` claim (e.g., 15–30 minutes) and validate it server‑side using a library like PyJWT or `jsonwebtoken` in Node.js.

  1. Bypassing 2FA by Direct API Access (Pre‑2FA JWT Abuse)

What this does:

Some applications issue a full JWT immediately after password authentication, before the user submits their 2FA code. The frontend redirects to the 2FA page, but the backend never checks the `twoFactor.passed` flag. An attacker can take that “pre‑2FA” token and directly call any protected API endpoint.

Step‑by‑step exploitation (using Burp Suite or curl):

  1. Intercept the login flow – After entering username/password, the response contains a JWT. Copy this token before any 2FA code is submitted.

  2. Examine the JWT payload – Decode it as shown in Section 1. Look for a field like `”twoFactor”: {“passed”: false}` or "mfa_verified": false. The mere presence of this field indicates that the backend expects the frontend to enforce 2FA.

  3. Directly call a sensitive endpoint using the unverified token:

    curl -X POST https://target.com/api/tickets/create \
    -H "Authorization: Bearer</p></li>
    </ol>
    
    <p><
    
    pre-2fa-jwt>" \
    -H "Content-Type: application/json" \
    -d '{"subject":"Test","message":"Bypass 2FA"}'
    

    If the server returns a 200 OK and creates a ticket (or sends a confirmation email), the 2FA is completely bypassed.

    1. Automate parameter fuzzing – Use `ffuf` to test which endpoints are accessible with the pre‑2FA token:
      ffuf -u https://target.com/api/FUZZ -H "Authorization: Bearer </li>
      </ol>
      
      <
      
      pre-2fa-jwt>" -w /usr/share/wordlists/api_endpoints.txt -fc 401,403
      

      Why this works: The backend never verifies that the user has actually completed 2FA. The flag `”passed”: false` is just a client‑side hint; the server must reject any request where MFA is not fully satisfied.

      Mitigation: Issue a temporary, restricted token after password auth (e.g., only allowed to call the 2FA verification endpoint). After successful 2FA, exchange it for a full‑access token.

      3. Privilege Escalation via Client‑Controlled Role Parameters

      What this does:

      Developers sometimes accept sensitive role or identity parameters directly in the request body or query string, assuming the client will send the correct value. Attackers can change these parameters (e.g., `isCreatedByMerchant: true` → false) to impersonate support operators, admins, or other high‑privileged users.

      Step‑by‑step exploitation (using Burp Repeater):

      1. Identify a request that modifies or creates resources – For example, a support ticket creation endpoint:
        POST /api/support/messages
        Content-Type: application/json
        Authorization: Bearer <user_jwt></li>
        </ol>
        
        {"message":"Hello","isCreatedByMerchant":true}
        
        1. Change the client‑controlled parameter – Flip the boolean or try other privilege‑related values ("role":"user""role":"admin", `”accountType”:”standard”` → "accountType":"operator").

        2. Resend the modified request using Burp Repeater or curl:

          curl -X POST https://target.com/api/support/messages \
          -H "Authorization: Bearer <user_jwt>" \
          -H "Content-Type: application/json" \
          -d '{"message":"Impersonating support","isCreatedByMerchant":false}'
          

          If the message appears as an official operator reply (or triggers an operator‑email notification), you have a privilege escalation.

        3. Scan for similar parameters – Use Burp Suite’s “Param Miner” extension or `arjun` to discover hidden parameters:

          arjun -u https://target.com/api/orders/123 -X POST -d '{"orderId":123}' --include cookies
          

        Root cause: The server trusts the client to declare its own role instead of deriving it from the authenticated token’s identity (e.g., from the `sub` claim linked to a database role).

        Secure coding rule: Never accept role, isAdmin, `permissions` or similar fields from the client. Always map the authenticated user ID (from the token) to their real role server‑side.

        1. Tools & Commands for Bug Bounty Hunting (Linux & Windows)

        Set up a testing environment: Use these verified commands to decode, modify, and replay JWTs.

        | Task | Linux/macOS Command | Windows (PowerShell) Command |

        ||||

        | Decode JWT payload | `echo “” \| base64 -d \| jq` | `[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(““))` |
        | Decode JWT header | Same as above (first segment) | Same as above (first segment) |
        | Verify JWT signature (HS256) using `jwt_tool` | `python3 jwt_tool.py -d` | `python jwt_tool.py -d` |
        | Crack weak HMAC secret with `jwtcrack` | `./jwtcrack ` | Not native; use WSL or `john jwt.jwt –format=HMAC-SHA256` |
        | Fuzz API endpoints | `ffuf -u https://target.com/api/FUZZ -w api_list.txt` | `ffuf.exe` (same) or `Invoke-WebRequest` loop |
        | Intercept and modify requests | curl -H "Authorization: Bearer <jwt>" -X POST -d '{"role":"admin"}' https://target.com/api/action` |Invoke-RestMethod -Headers @{Authorization=”Bearer “} -Method POST -Body ‘{“role”:”admin”}’ -Uri https://target.com/api/action` |

        Install essential tools:

        – `jq` (JSON processor): `sudo apt install jq` (Linux) / `winget install jq` (Windows)
        jwt_tool: `git clone https://github.com/ticarpi/jwt_tool`
        – `Burp Suite Community` (GUI) – for intercepting and replaying requests.
        – `Postman` – for manual API testing with token management.

        1. Hardening APIs Against Business Logic Flaws (Mitigation Steps)

        What this does:

        Prevents the three vulnerabilities described above by enforcing security at the API layer, never trusting client input for authentication or authorization decisions.

        Step‑by‑step hardening checklist:

        1. JWT expiry enforcement – Always include the `exp` claim and validate it server‑side. Example in Node.js:
          jwt.verify(token, secret, { algorithms: ['HS256'] }, (err, decoded) => {
          if (err || Date.now() >= decoded.exp  1000) throw new Error('Token expired');
          });
          

        2. 2FA server‑side enforcement – Issue a “partial” JWT after password success that contains a claim `mfa_required = true` and restrict its usage to only the 2FA verification endpoint. After successful 2FA, issue a new JWT without the `mfa_required` flag.

        3. Derive roles from token, not request – Extract the user ID from the JWT’s `sub` or `user_id` claim, then query your database for the user’s actual role. Never read `role` from the request body.

        4. Use middleware for authorization – Implement a global API middleware that:

        – Rejects any request with privilege‑like parameters in the body/query.
        – Rejects any request using a token that hasn’t completed MFA (if required).
        – Enforces role‑based access control (RBAC) from the server‑side lookup.

        1. Run automated business logic tests – Use tools like `ZAP` with custom scripts or `Postman` Newman to simulate flows where 2FA is skipped or role parameters are tampered.

        Example middleware in Python (FastAPI):

        async def get_current_user(token: str = Depends(oauth2_scheme)):
        payload = jwt.decode(token, SECRET, algorithms=["HS256"])
        user_id = payload.get("sub")
        user = await db.fetch_user(user_id)
        if not user.mfa_passed and payload.get("mfa_required"):
        raise HTTPException(403, "MFA not completed")
        return user  user.role is from DB, not token
        
        1. How to Replicate These Findings in Your Own Bug Bounty Program

        Step‑by‑step testing methodology:

        1. Map the authentication flow – Register two accounts (normal user + merchant/support if available). Capture every request from login to logout.

        2. Test JWT expiry – After logging in, extract the JWT and wait 24 hours. Replay it against any authenticated endpoint. If it works, report it.

        3. Test for 2FA bypass – During the login process, intercept the response after password verification but before entering the 2FA code. Copy that JWT and use it to call endpoints that require full authentication. Also try to change the password or initiate a money transfer.

        4. Test for client‑controlled roles – Use Burp Suite’s “Repeater” on every POST/PUT/PATCH request that creates or modifies resources. Add parameters like role=admin, is_admin=true, group=support, privilege_level=10. Observe if the server responds with elevated access.

        5. Automate parameter fuzzing – Use `Param Miner` (Burp extension) to guess hidden `role` or `group` parameters. For REST APIs, also test `X-Role` headers.

        6. Chain findings – Combine a non‑expiring JWT (from finding 1) with a privilege escalation (finding 3) to achieve persistent admin access even after the original user’s session is “logged out” on the frontend.

        Example chaining script (conceptual):

         Step 1: Obtain a pre-2FA JWT (manual interception)
        PRE_JWT="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
        
        Step 2: Use it to call a privilege escalation endpoint with role tampering
        curl -X PUT https://target.com/api/user/permissions \
        -H "Authorization: Bearer $PRE_JWT" \
        -H "Content-Type: application/json" \
        -d '{"userId":"attacker","role":"admin"}'
        

        What Undercode Say:

        • Key Takeaway 1: Frontend 2FA redirects are useless if the backend doesn’t verify the MFA state on every protected request. Always validate the token’s MFA claim server‑side.
        • Key Takeaway 2: Client‑controlled role parameters are one of the most overlooked business logic flaws. Any parameter that influences authorization must be derived from the authenticated session, never from user input.
        • Business logic vulnerabilities often yield critical impact because they bypass traditional security controls (like WAFs and signature‑based scanners). They require manual testing and deep understanding of the application’s state machine. Automated tools cannot infer that a JWT with `”twoFactor.passed”: false` should be rejected—this is where human intuition and methodical API probing win. As seen in this fintech bounty session, chaining a non‑expiring JWT with a direct 2FA bypass and a role escalation turns a few hour’s work into three high‑severity reports. The lesson is clear: trust nothing from the client, enforce every check on the server, and always decode your JWTs.

        Prediction:

        As more organizations adopt microservices and JWT‑based authentication, the prevalence of these “simple but devastating” logic flaws will increase—especially in fast‑paced fintech and healthtech startups. Attackers will shift focus from exploiting known CVEs to chaining business logic weaknesses like non‑expiring tokens and client‑supplied roles. In the next 12–18 months, expect a surge in bug bounty payouts for 2FA bypass and privilege escalation via parameter tampering, as vendors realize that secure coding guidelines do not automatically translate into secure implementations. Proactive red teams will build automation to detect these patterns, while defensive teams must adopt zero‑trust API gateways that enforce MFA and role derivation without exception.

        ▶️ Related Video (78% Match):

        🎯Let’s Practice For Free:

        IT/Security Reporter URL:

        Reported By: Pawanparmar Bugbounty – 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