Learn / Attack archive / Lesson 24

Phishing that beats MFA

How modern phishing steals the session instead of the password, why codes from an app no longer stop it, and why passkeys do.

Last updated: 2026-09-23

What it is

Old phishing stole your password. A second factor (a code from an app or a text) stopped it, because the attacker did not have your phone. Modern phishing has three ways around that.

1. Adversary-in-the-middle (AiTM)

The fake login page is not a copy. It is a live relay. It passes everything you type to the real site and passes the real site's answers back to you. You type your password, the real site asks for your code, you type the code, and the real site hands over a session cookie. The relay keeps it. The attacker is now signed in as you, second factor and all.

Kits that do this (EvilProxy, Tycoon 2FA and others) are rented as a service for a few hundred dollars a month.

2. Device code phishing

Some sign-in flows are built for devices with no keyboard, like a TV: the TV shows a short code, and you type it on your phone at the real Microsoft or Google page. Attackers start that flow themselves and send you the code with a story: "enter this code to view the invoice". You sign in on the genuine site, with your genuine second factor, and approve the attacker's device.

3. ClickFix

A fake "verify you are human" box tells you to press a few keys: open the Run box, paste, press Enter. The page has already put a command on your clipboard. You run the attacker's malware yourself, and usually it is an infostealer.

Why it is a rule

Why it keeps working

How to do it

1. Passkeys and security keys

A passkey is bound to the domain it was made for. The browser will only use it on the real site. On a relay site the passkey simply does not appear, so there is nothing to phish. This is what "phishing-resistant MFA" means, and it is the only kind that stops AiTM.

js
const credential = await navigator.credentials.create({
  publicKey: {
    rp: { id: 'example.com', name: 'Example' },
    user: { id: userIdBytes, name: email, displayName: name },
    challenge: serverChallenge,
    pubKeyCredParams: [{ type: 'public-key', alg: -7 }],
    authenticatorSelection: { residentKey: 'required', userVerification: 'preferred' },
  },
})

2. Turn off flows you do not use

If nobody signs in from a TV, block the device code flow in your identity provider. Microsoft and Google both let administrators restrict it.

3. Watch the session, not just the login

A session that jumps to a new country or a new device fingerprint right after sign-in is a relayed one. Re-ask for a passkey on risky actions: changing email, adding a login method, exporting data.

4. Teach one rule for ClickFix

No real website ever asks you to paste something into the Run box or a terminal. Where you manage the machines, restrict the Run box and script engines for users who do not need them.

How we do it here

Sessions are signed, expire on their own, and are checked on every request. Cookies are HttpOnly, so a script on a page cannot read them. We treat the second factor as something that protects the sign-in moment, and the session rules as what protects everything after.

Benefits

Disadvantages

Checklist

Sources

Read this lesson as Markdown