Identity Attacks: AiTM Phishing & Token Theft¶
Multi-factor authentication was supposed to be the silver bullet. Then Adversary-in-the-Middle (AiTM) phishing kits turned MFA into a speedbump. Attackers now routinely intercept session tokens in real-time, bypassing even hardware-backed MFA, and replaying stolen cookies to walk right into mailboxes, file shares, and cloud admin consoles.
This post dissects the AiTM attack chain, maps it to MITRE ATT&CK, and provides detection queries you can deploy today. We follow Pinnacle Manufacturing (fictional) through a real-time token theft incident that compromised their CFO's mailbox and nearly enabled a $2.8M wire transfer.
1. The MFA Bypass Problem¶
Traditional phishing steals passwords. AiTM phishing steals authenticated sessions — the token your identity provider issues after you successfully complete MFA. The attacker never needs your password or your second factor.
How it works:
User → Attacker Proxy → Real Login Page
← ←
(Session token intercepted in transit)
Attacker replays token → Full account access (MFA already satisfied)
Key Insight
AiTM doesn't break MFA — it bypasses it entirely. The user completes legitimate MFA with the real identity provider. The attacker's reverse proxy captures the resulting session cookie.
ATT&CK Mapping¶
| Technique | ID | Phase |
|---|---|---|
| Phishing: Spearphishing Link | T1566.002 | Initial Access |
| Adversary-in-the-Middle | T1557 | Credential Access |
| Steal Web Session Cookie | T1539 | Credential Access |
| Use Alternate Authentication Material | T1550.004 | Defense Evasion |
| Email Collection: Email Forwarding Rule | T1114.003 | Collection |
| Financial Theft | T1657 | Impact |
2. AiTM Attack Anatomy¶
Phase 1: Phishing Infrastructure Setup¶
The attacker deploys a reverse proxy (Evilginx, Modlishka, or custom) that sits between the victim and the real identity provider:
- Domain:
login-portal.pinnacle-mfg.example.com(typosquat ofportal.pinnacle-mfg.example.com) - SSL certificate: Let's Encrypt (valid cert, green padlock)
- Hosting: bulletproof VPS at
203.0.113.44
Phase 2: Credential & Token Harvest¶
1. Victim clicks link in phishing email
2. Reverse proxy forwards request to real Azure AD login
3. Victim enters username → proxy forwards to Azure AD
4. Azure AD prompts MFA → victim approves push notification
5. Azure AD issues session token → proxy INTERCEPTS token
6. Proxy forwards legitimate login page to victim (looks normal)
7. Attacker replays stolen session token from 198.51.100.77
Phase 3: Post-Compromise Actions¶
Within minutes of token capture, the attacker typically:
- Creates inbox rules — forward all emails to external address, delete sent items
- Reads email — searches for "wire transfer", "payment", "invoice", "bank"
- Registers new MFA device — adds attacker-controlled authenticator
- Modifies mailbox permissions — grants delegate access to another compromised account
- Initiates BEC — sends wire transfer request from legitimate account
3. Detection Strategies¶
Impossible Travel Detection¶
// Detect impossible travel: login from two locations faster than physically possible
let timeWindow = 10m;
SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType == 0 // Successful logins only
| where AppDisplayName != "Windows Sign In"
| extend City = tostring(LocationDetails.city)
| extend Country = tostring(LocationDetails.countryOrRegion)
| extend Lat = todouble(LocationDetails.geoCoordinates.latitude)
| extend Lon = todouble(LocationDetails.geoCoordinates.longitude)
| sort by UserPrincipalName, TimeGenerated asc
| serialize
| extend PrevCity = prev(City, 1), PrevCountry = prev(Country, 1),
PrevTime = prev(TimeGenerated, 1), PrevUser = prev(UserPrincipalName, 1),
PrevLat = prev(Lat, 1), PrevLon = prev(Lon, 1)
| where UserPrincipalName == PrevUser
| extend TimeDiffMinutes = datetime_diff('minute', TimeGenerated, PrevTime)
| where TimeDiffMinutes < 60 and City != PrevCity and Country != PrevCountry
| project TimeGenerated, UserPrincipalName, City, Country, PrevCity, PrevCountry,
TimeDiffMinutes, IPAddress, AppDisplayName
index=azure sourcetype=azure:signin status=Success
| sort 0 user, _time
| streamstats window=2 current=t earliest(_time) as prev_time,
earliest(src_ip) as prev_ip, earliest(City) as prev_city by user
| eval time_diff_min=round((_time - prev_time) / 60, 0)
| where time_diff_min < 60 AND City != prev_city AND isnotnull(prev_city)
| table _time, user, City, Country, prev_city, time_diff_min, src_ip, app
Suspicious Inbox Rule Creation¶
// Detect inbox rules that forward to external domains or delete items
OfficeActivity
| where TimeGenerated > ago(7d)
| where Operation in ("New-InboxRule", "Set-InboxRule", "Enable-InboxRule")
| extend RuleParams = tostring(Parameters)
| where RuleParams has_any ("ForwardTo", "ForwardAsAttachmentTo", "RedirectTo",
"DeleteMessage", "MoveToFolder")
| where RuleParams !has "@pinnacle-mfg.example.com" // External forwarding
| project TimeGenerated, UserId, Operation, RuleParams, ClientIP, SessionId
| sort by TimeGenerated desc
Token Replay from New IP¶
// Detect session token reuse from a different IP than the original authentication
let authSessions = SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType == 0
| summarize AuthIP = arg_min(TimeGenerated, IPAddress) by CorrelationId, UserPrincipalName;
SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType == 0
| join kind=inner authSessions on CorrelationId, UserPrincipalName
| where IPAddress != AuthIP
| project TimeGenerated, UserPrincipalName, IPAddress, AuthIP,
AppDisplayName, DeviceDetail, LocationDetails
New MFA Device Registration¶
// Alert on new MFA method registration — high priority after any phishing indicator
AuditLogs
| where TimeGenerated > ago(24h)
| where OperationName has_any ("User registered security info",
"User registered all required security info",
"Admin registered security info")
| extend UserPrincipalName = tostring(TargetResources[0].userPrincipalName)
| extend Method = tostring(AdditionalDetails[0].value)
| project TimeGenerated, UserPrincipalName, OperationName, Method,
InitiatedBy, CorrelationId
| sort by TimeGenerated desc
4. Case Study: Pinnacle Manufacturing¶
Scenario: CFO Mailbox Compromise via AiTM (Fictional)
Organization: Pinnacle Manufacturing (fictional, 3,200 employees) Target: CFO Victoria Chen (fictional) Method: AiTM phishing via typosquat domain Impact: Near-miss $2.8M wire transfer (caught by dual authorization)
Timeline¶
| Time (UTC) | Event | Detection |
|---|---|---|
| 09:12 | CFO receives email: "Q3 Financial Review — Action Required" from spoofed board member | — |
| 09:14 | CFO clicks link to portal.pinnacle-mfg.example.com (typosquat) | — |
| 09:15 | CFO enters credentials, approves MFA push notification | — |
| 09:15 | AiTM proxy captures session cookie | — |
| 09:18 | Attacker replays token from 198.51.100.77 (VPS) | Impossible travel alert fires |
| 09:19 | Attacker creates inbox rule: forward all "payment" emails to external | Inbox rule alert fires |
| 09:22 | Attacker reads 47 emails, finds vendor payment schedule | — |
| 09:31 | Attacker registers new MFA authenticator app | MFA registration alert fires |
| 09:45 | Attacker sends wire transfer request as CFO to Accounts Payable | — |
| 09:52 | AP analyst follows dual-authorization policy, calls CFO to verify | — |
| 09:55 | CFO confirms she did NOT send the request | IR activated |
| 10:05 | SOC revokes all active sessions, disables account | Containment |
| 10:15 | Attacker's MFA device removed, inbox rules deleted | Eradication |
| 10:30 | CFO re-authenticates with hardware security key only | Recovery |
What Saved Pinnacle¶
- Dual authorization for wire transfers — financial control caught what technical controls missed
- Behavioral detection — impossible travel + inbox rule alerts fired within 4 minutes
- SOC response time — 10 minutes from IR activation to full containment
- Session revocation capability — Azure AD Continuous Access Evaluation (CAE) enabled
What Failed¶
- MFA alone was insufficient — push notification MFA is vulnerable to AiTM relay
- No phishing-resistant MFA — hardware security keys (FIDO2) would have prevented token theft
- Domain monitoring gap — typosquat domain registered 48 hours prior, not flagged
5. Defense-in-Depth for AiTM¶
| Layer | Control | Effectiveness |
|---|---|---|
| Prevention | FIDO2/passkeys (phishing-resistant MFA) | Blocks AiTM entirely |
| Prevention | Conditional Access: compliant device required | Blocks token replay from unmanaged devices |
| Prevention | Token binding / CAE | Limits token replay window |
| Detection | Impossible travel analytics | Catches geographic anomalies |
| Detection | Inbox rule monitoring | Catches persistence setup |
| Detection | MFA registration alerts | Catches privilege persistence |
| Response | Session revocation automation | Rapid containment |
| Response | Dual authorization for financial transactions | Business process control |
The Bottom Line
The only MFA that stops AiTM is phishing-resistant MFA — FIDO2 security keys or device-bound passkeys. Push notifications, SMS codes, and TOTP are all vulnerable to real-time relay.
6. Key Takeaways¶
- AiTM bypasses MFA — it doesn't break it, it captures the session token after successful authentication
- Phishing-resistant MFA is mandatory — FIDO2/passkeys are the only technical prevention
- Behavioral detection fills the gap — impossible travel, inbox rules, MFA registration alerts catch what prevention misses
- Business process controls matter — dual authorization for wire transfers saved Pinnacle $2.8M
- Token hygiene — short-lived tokens, CAE, and conditional access reduce the replay window
Related Resources¶
- Chapter 33: Identity & Access Security — IAM fundamentals
- Chapter 25: Social Engineering — phishing and social engineering
- Chapter 39: Zero Trust Implementation — zero trust controls
- Chapter 4: Detection Engineering — building detection rules
- SC-015: Business Email Compromise — BEC attack scenario
- Detection Query Library — pre-built KQL/SPL queries
- SC-075: AI Deepfake Attack — deepfake social engineering