2FA broken logic

1 min read Easy PortSwigger
Authentication
Contents

On this page

The lab

here is how portswigger describes it

This lab's two-factor authentication is vulnerable due to its flawed logic. To solve the lab, access Carlos's account page.

Your credentials: wiener:peter
Victim's username: carlos

You also have access to the email server to receive your 2FA verification code.

the brute force labs chipped away at passwords. this one steps past the second factor entirely, not by guessing the code cleverly at first, but by exploiting a login flow that trusts the client to say whose code is being checked.

why this 2FA logic is broken?#

two factor authentication is supposed to be a second gate. you clear the password, then the site generates a one time code, sends it to the real owner, and checks it before letting you in. the strength of that gate rests on one thing, the code going to someone only the real owner can reach.

the flaw here is that the second stage decides whose account it is working on from a parameter in the request, the verify value, rather than from a locked in session that remembers who passed the password step. that means the client gets to name the victim. we can point verify at carlos to make the server generate and send a code for carlos, and then the code check is performed against carlos's account. we never see that code, but we do not need to, because once the account is decoupled from our real session we are free to just brute force the code against carlos directly.

Step 1 - Study the verify parameter#

we log in with our own wiener:peter and walk through the normal 2FA flow to see how it works. watching the requests, the POST /login2 request that submits the code carries a verify parameter naming the account being checked.

1111111

that is the tell. the server is reading who to verify from the request body instead of from a trusted session, so it will believe whatever name we give it.

Step 2 - Trigger a code for carlos#

now we abuse that. we change the verify value from our own name to carlos and send the request. that single change makes the server generate a fresh temporary 2FA code tied to carlos's account, exactly as if carlos were logging in.

the code gets sent to carlos and we never see it, but it now exists and is valid, which is all we need for the next step.

Step 3 - Brute force carlos's code#

we go back to the login page and sign in with our own username and password, then submit any invalid 2FA code to produce a POST /login2 request we can work with. we send that request to intruder.

in intruder we set the verify parameter to carlos so the check runs against his account, and put a payload position on the mfa-code parameter. the code is only four digits, which is a tiny space, so we brute force every value from 0000 to 9999. 2222222

the vast majority of guesses fail, but one matches the code the server generated for carlos, and that request returns a 302 redirect instead of the usual error. the redirect means the second factor was accepted and we are being sent into carlos's logged in area.

3333333

Step 4 - Load the success response and solve#

the 302 response is the one that completes carlos's login, so we load that request in the browser to follow the redirect into his session.

from there we click my account, which lands on carlos's account page.

with this, the lab is solved!